From 78f7adce314ea548027f8d1801f1e173b5ac6442 Mon Sep 17 00:00:00 2001 From: Diego Marin Santos Date: Wed, 15 Dec 2021 12:55:24 +0100 Subject: [PATCH] Reformatting all files --- CODE_OF_CONDUCT.md | 1 - CONTRIBUTING.md | 8 +- build.gradle | 2 +- .../com/radixdlt/integration/FailOnEvent.java | 65 +- .../integration/api/ActorConfiguration.java | 43 +- .../com/radixdlt/integration/api/ApiTest.java | 394 +- .../integration/api/DeterministicActor.java | 12 +- .../ApiBalanceToRadixEngineChecker.java | 88 +- .../api/actors/ApiTxnSubmitter.java | 296 +- .../api/actors/BalanceReconciler.java | 178 +- .../api/actors/NativeTokenRewardsChecker.java | 83 +- .../integration/api/actors/NodeApiClient.java | 429 +-- .../api/actors/RadixEngineReader.java | 95 +- .../api/actors/RandomNodeRestarter.java | 22 +- .../api/actors/actions/BurnTokens.java | 56 +- .../actors/actions/CreateTokenDefinition.java | 158 +- .../api/actors/actions/MintTokens.java | 56 +- .../actors/actions/NodeTransactionAction.java | 13 +- .../api/actors/actions/RegisterValidator.java | 52 +- .../actions/SetAllowDelegationFlag.java | 50 +- .../api/actors/actions/SetValidatorFee.java | 50 +- .../api/actors/actions/SetValidatorOwner.java | 50 +- .../api/actors/actions/StakeTokens.java | 84 +- .../api/actors/actions/TransferTokens.java | 76 +- .../api/actors/actions/UnstakeStakeUnits.java | 82 +- .../distributed/MockedPeersViewModule.java | 39 +- .../deterministic/DeterministicNodes.java | 140 +- .../deterministic/DeterministicTest.java | 745 ++-- .../distributed/deterministic/NodeEvents.java | 55 +- .../deterministic/NodeEventsModule.java | 23 +- .../deterministic/SafetyCheckerModule.java | 31 +- .../configuration/EpochNodeWeightMapping.java | 97 +- .../configuration/NodeIndexAndWeight.java | 72 +- .../tests/bft_sync/SyncToTimeoutQcTest.java | 107 +- .../DifferentTimestampsCauseTimeoutTest.java | 330 +- .../FProposalDropperResponsiveTest.java | 212 +- .../OneProposalDropperResponsiveTest.java | 174 +- .../OneProposalTimeoutResponsiveTest.java | 137 +- .../PacemakerViewUpdateRaceConditionTest.java | 178 +- .../consensus/ProposerLoadBalancedTest.java | 354 +- .../QuorumWithoutALeaderWithTimeoutsTest.java | 101 +- .../RandomChannelOrderResponsiveTest.java | 71 +- .../MovingWindowValidatorsTest.java | 189 +- ...rocessCachedEventsWithTimeoutCertTest.java | 107 +- .../tests/ledger_sync/FullNodeSyncTest.java | 98 +- .../simulation/MockedSystemModule.java | 13 +- .../distributed/simulation/Monitor.java | 34 +- .../distributed/simulation/MonitorKey.java | 7 +- .../simulation/NetworkDroppers.java | 178 +- .../simulation/NetworkLatencies.java | 119 +- .../simulation/NetworkOrdering.java | 42 +- .../simulation/NodeNetworkMessagesModule.java | 152 +- .../simulation/SimulationNetworkModule.java | 35 +- .../simulation/SimulationTest.java | 1312 +++---- .../distributed/simulation/TestInvariant.java | 48 +- .../BFTValidatorSetNodeSelector.java | 22 +- .../application/EpochsNodeSelector.java | 28 +- .../application/IncrementalBytes.java | 14 +- .../LocalMempoolPeriodicSubmitter.java | 67 +- .../application/MempoolFillerStarter.java | 23 +- .../simulation/application/NodeSelector.java | 6 +- .../NodeValidatorRandomRegistrator.java | 84 +- .../application/NodeValidatorRegistrator.java | 69 +- .../RadixEngineUniqueGenerator.java | 42 +- .../simulation/application/TxnGenerator.java | 6 +- .../monitors/EventNeverOccursInvariant.java | 38 +- .../simulation/monitors/NodeEvents.java | 29 +- .../monitors/SimulationNodeEventsModule.java | 84 +- .../application/ApplicationMonitors.java | 48 +- .../application/CommittedChecker.java | 65 +- .../RegisteredValidatorChecker.java | 43 +- ...llProposalsHaveDirectParentsInvariant.java | 43 +- .../monitors/consensus/ConsensusMonitors.java | 199 +- .../monitors/consensus/LivenessInvariant.java | 69 +- .../consensus/MaxLedgerSyncLagInvariant.java | 60 +- .../monitors/consensus/SafetyInvariant.java | 36 +- .../monitors/consensus/SyncMonitors.java | 28 +- .../monitors/consensus/TimestampChecker.java | 72 +- .../consensus/VertexRequestRateInvariant.java | 59 +- .../monitors/epochs/EpochViewInvariant.java | 57 +- .../ConsensusToLedgerCommittedInvariant.java | 74 +- .../ledger/LedgerInOrderInvariant.java | 59 +- .../monitors/ledger/LedgerMonitors.java | 46 +- .../radix_engine/RadixEngineMonitors.java | 28 +- .../network/FProposalsPerViewDropper.java | 79 +- .../simulation/network/InOrderChannels.java | 66 +- .../simulation/network/LatencyProvider.java | 19 +- .../simulation/network/MessageDropper.java | 37 +- ...NodePerEpochLedgerStatusUpdateDropper.java | 33 +- .../network/OutOfOrderChannels.java | 33 +- .../network/RandomLatencyProvider.java | 46 +- .../simulation/network/RandomVoteDropper.java | 32 +- .../simulation/network/SimulationNetwork.java | 280 +- .../network/SimulationNetworkTest.java | 48 +- .../simulation/network/SimulationNodes.java | 419 ++- .../consensus/FPlusOneOutOfBoundsTest.java | 84 +- .../FProposalsPerViewDropperTest.java | 173 +- .../consensus/OneByzantineGenesisTest.java | 83 +- .../tests/consensus/OneOutOfBoundsTest.java | 53 +- .../tests/consensus/OneSlowNodeTest.java | 58 +- .../tests/consensus/OutOfOrderTest.java | 80 +- .../tests/consensus/RandomLatencyTest.java | 93 +- .../RandomVoteAndViewTimeoutDropperTest.java | 60 +- .../tests/consensus/UniformLatencyTest.java | 61 +- .../consensus_ledger/OneOutOfBoundsTest.java | 59 +- .../RandomVoteAndViewTimeoutDropperTest.java | 70 +- .../tests/consensus_ledger/SanityTest.java | 48 +- ...tPreviousVoteWithDroppedProposalsTest.java | 103 +- .../MovingWindowValidatorsTest.java | 157 +- .../OneNodeNeverSendEpochResponseTest.java | 76 +- .../RandomValidatorsTest.java | 109 +- .../StaticValidatorsTest.java | 107 +- .../IncreasingValidatorsTest.java | 75 +- .../RandomValidatorsTest.java | 102 +- .../RandomVoteAndViewTimeoutDropperTest.java | 105 +- .../SanityTest.java | 65 +- .../MempoolSanityTest.java | 95 +- .../ByzantineSyncTest.java | 152 +- ...FNodesNeverReceiveProposalDropperTest.java | 87 +- ...allBehindMultipleEpochsLedgerSyncTest.java | 125 +- ...ullNodeSyncingWithAnotherFullNodeTest.java | 141 +- .../OneNodeFallingBehindTest.java | 72 +- .../RandomValidatorsTest.java | 91 +- .../tests/full_function/MempoolFillTest.java | 132 +- .../full_function/OneOutOfBoundsTest.java | 119 +- .../tests/full_function/SanityTest.java | 101 +- .../tests/full_function_forks/SanityTest.java | 142 +- .../integration/invariants/SafetyChecker.java | 152 +- .../mempool/MempoolFillAndEmptyTest.java | 141 +- .../integration/mempool/MempoolRelayTest.java | 342 +- .../OneNodeAlwaysAliveSafetyTest.java | 472 +-- .../recovery/RecoveryLivenessTest.java | 662 ++-- .../MessageCentralFuzzyTest.java | 278 +- .../staking/UnstakingLockedTokensTest.java | 296 +- .../statemachine/LargeEpochChangeTest.java | 381 +- .../statemachine/RandomTxnTest.java | 124 +- .../java/org/radix/integration/RadixTest.java | 71 +- .../org/radix/benchmark/CodecBenchmark.java | 152 +- .../org/radix/benchmark/UInt128Benchmark.java | 158 +- .../org/radix/benchmark/UInt256Benchmark.java | 150 +- .../org/radix/benchmark/UInt384Benchmark.java | 150 +- .../java/com/radixdlt/ConsensusModule.java | 605 ++- .../com/radixdlt/ConsensusRecoveryModule.java | 99 +- .../main/java/com/radixdlt/CryptoModule.java | 74 +- .../java/com/radixdlt/DispatcherModule.java | 703 ++-- .../com/radixdlt/EpochsConsensusModule.java | 513 ++- .../java/com/radixdlt/EpochsSyncModule.java | 305 +- .../java/com/radixdlt/EventLoggerModule.java | 223 +- .../main/java/com/radixdlt/LedgerModule.java | 52 +- .../com/radixdlt/LedgerRecoveryModule.java | 189 +- .../main/java/com/radixdlt/ModuleRunner.java | 16 +- .../com/radixdlt/RadixEngineStoreModule.java | 17 +- .../java/com/radixdlt/RadixNodeModule.java | 313 +- .../java/com/radixdlt/SyncServiceModule.java | 201 +- .../java/com/radixdlt/SystemInfoModule.java | 98 +- .../main/java/com/radixdlt/SystemModule.java | 27 +- .../main/java/com/radixdlt/api/ApiModule.java | 119 +- .../java/com/radixdlt/api/HandlerRoute.java | 22 +- .../api/UnhandledExceptionHandler.java | 42 +- .../com/radixdlt/api/core/CoreApiModule.java | 100 +- .../radixdlt/api/core/CoreJsonRpcHandler.java | 100 +- .../handlers/ConstructionBuildHandler.java | 79 +- .../handlers/ConstructionDeriveHandler.java | 104 +- .../handlers/ConstructionFinalizeHandler.java | 48 +- .../handlers/ConstructionHashHandler.java | 33 +- .../handlers/ConstructionParseHandler.java | 90 +- .../handlers/ConstructionSubmitHandler.java | 69 +- .../handlers/EngineConfigurationHandler.java | 79 +- .../core/handlers/EngineStatusHandler.java | 112 +- .../api/core/handlers/EntityHandler.java | 106 +- .../api/core/handlers/KeyListHandler.java | 56 +- .../api/core/handlers/KeySignHandler.java | 90 +- .../api/core/handlers/MempoolHandler.java | 49 +- .../handlers/MempoolTransactionHandler.java | 85 +- .../handlers/NetworkConfigurationHandler.java | 68 +- .../core/handlers/NetworkStatusHandler.java | 105 +- .../core/handlers/TransactionsHandler.java | 233 +- .../api/core/model/CoreApiErrorCode.java | 49 +- .../api/core/model/CoreApiException.java | 62 +- .../api/core/model/CoreModelMapper.java | 2195 +++++------ .../api/core/model/DataOperation.java | 29 +- .../com/radixdlt/api/core/model/Entity.java | 42 +- .../api/core/model/EntityOperation.java | 23 +- .../com/radixdlt/api/core/model/KeyQuery.java | 68 +- ...NotEnoughNativeTokensForFeesException.java | 31 +- .../model/NotEnoughResourcesException.java | 52 +- .../api/core/model/OperationTxBuilder.java | 190 +- .../api/core/model/ParsedDataObject.java | 29 +- .../com/radixdlt/api/core/model/Resource.java | 8 +- .../api/core/model/ResourceOperation.java | 51 +- .../api/core/model/ResourceQuery.java | 63 +- .../core/model/ResourceUnsignedAmount.java | 8 +- .../api/core/model/StakeUnitResource.java | 8 +- .../api/core/model/SubstateOperation.java | 39 +- .../api/core/model/SubstateTypeMapping.java | 109 +- .../api/core/model/SubstateWithdrawal.java | 34 +- .../api/core/model/TokenResource.java | 8 +- .../model/entities/AccountVaultEntity.java | 147 +- ...tityDoesNotSupportDataObjectException.java | 31 +- ...oesNotSupportResourceDepositException.java | 31 +- ...esNotSupportResourceWithdrawException.java | 31 +- .../entities/ExitingStakeVaultEntity.java | 44 +- .../entities/InvalidDataObjectException.java | 21 +- .../entities/PreparedStakeVaultEntity.java | 98 +- .../entities/PreparedUnstakeVaultEntity.java | 66 +- .../api/core/model/entities/SystemEntity.java | 53 +- .../api/core/model/entities/TokenEntity.java | 116 +- .../core/model/entities/ValidatorEntity.java | 200 +- .../model/entities/ValidatorSystemEntity.java | 35 +- .../BerkeleyRecoverableProcessedTxnStore.java | 296 +- .../RecoverableProcessedTxn.java | 274 +- .../reconstruction/RecoverableSubstate.java | 7 +- .../RecoverableSubstateShutdown.java | 51 +- .../RecoverableSubstateVirtualShutdown.java | 41 +- .../api/system/AddressBookHandler.java | 34 +- .../api/system/ConfigurationHandler.java | 86 +- .../radixdlt/api/system/HealthHandler.java | 42 +- .../radixdlt/api/system/MetricsHandler.java | 38 +- .../com/radixdlt/api/system/PeersHandler.java | 35 +- .../radixdlt/api/system/SystemApiModule.java | 52 +- .../api/system/SystemGetJsonHandler.java | 41 +- .../api/system/SystemModelMapper.java | 315 +- .../radixdlt/api/system/VersionHandler.java | 30 +- .../api/system/health/HealthInfoService.java | 221 +- .../api/system/health/MovingAverage.java | 85 +- .../api/system/health/NodeStatus.java | 15 +- .../health/ScheduledStatsCollecting.java | 13 +- .../prometheus/PrometheusApiModule.java | 27 +- .../system/prometheus/PrometheusHandler.java | 44 +- .../system/prometheus/PrometheusService.java | 459 +-- .../radixdlt/consensus/BFTConfiguration.java | 89 +- .../radixdlt/consensus/BFTEventProcessor.java | 74 +- .../com/radixdlt/consensus/BFTFactory.java | 35 +- .../com/radixdlt/consensus/BFTHeader.java | 330 +- .../radixdlt/consensus/ConsensusEvent.java | 52 +- .../radixdlt/consensus/ConsensusHasher.java | 74 +- .../com/radixdlt/consensus/HashSigner.java | 36 +- .../com/radixdlt/consensus/HashVerifier.java | 23 +- .../java/com/radixdlt/consensus/HighQC.java | 261 +- .../java/com/radixdlt/consensus/Ledger.java | 22 +- .../com/radixdlt/consensus/LedgerHeader.java | 314 +- .../com/radixdlt/consensus/LedgerProof.java | 271 +- .../com/radixdlt/consensus/PendingVotes.java | 560 ++- .../java/com/radixdlt/consensus/Proposal.java | 201 +- .../radixdlt/consensus/QuorumCertificate.java | 205 +- .../com/radixdlt/consensus/Sha256Hasher.java | 42 +- .../consensus/TimeoutCertificate.java | 164 +- .../consensus/TimestampedECDSASignature.java | 129 +- .../consensus/TimestampedECDSASignatures.java | 293 +- .../radixdlt/consensus/UnverifiedVertex.java | 282 +- .../java/com/radixdlt/consensus/Vote.java | 331 +- .../java/com/radixdlt/consensus/VoteData.java | 102 +- .../radixdlt/consensus/bft/BFTBuilder.java | 238 +- .../consensus/bft/BFTCommittedUpdate.java | 59 +- .../consensus/bft/BFTEventPreprocessor.java | 415 +-- .../consensus/bft/BFTEventReducer.java | 406 +- .../consensus/bft/BFTEventVerifier.java | 219 +- .../consensus/bft/BFTHighQCUpdate.java | 63 +- .../consensus/bft/BFTInsertUpdate.java | 99 +- .../com/radixdlt/consensus/bft/BFTNode.java | 81 +- .../consensus/bft/BFTRebuildUpdate.java | 58 +- .../com/radixdlt/consensus/bft/BFTSyncer.java | 36 +- .../radixdlt/consensus/bft/BFTValidator.java | 165 +- .../consensus/bft/BFTValidatorSet.java | 220 +- .../consensus/bft/EmptyBFTEventProcessor.java | 62 +- .../consensus/bft/MissingParentException.java | 10 +- .../com/radixdlt/consensus/bft/NoVote.java | 56 +- .../consensus/bft/PacemakerMaxExponent.java | 9 +- .../radixdlt/consensus/bft/PacemakerRate.java | 9 +- .../consensus/bft/PacemakerTimeout.java | 10 +- .../consensus/bft/PersistentVertexStore.java | 6 +- .../consensus/bft/PreparedVertex.java | 149 +- .../java/com/radixdlt/consensus/bft/Self.java | 10 +- .../consensus/bft/ValidationState.java | 254 +- .../consensus/bft/VerifiedVertex.java | 200 +- .../consensus/bft/VerifiedVertexChain.java | 74 +- .../bft/VerifiedVertexStoreState.java | 325 +- .../bft/VertexInsertionException.java | 16 +- .../radixdlt/consensus/bft/VertexStore.java | 754 ++-- .../java/com/radixdlt/consensus/bft/View.java | 171 +- .../consensus/bft/ViewQuorumReached.java | 62 +- .../radixdlt/consensus/bft/ViewUpdate.java | 144 +- .../consensus/bft/ViewVotingResult.java | 179 +- .../consensus/bft/VoteProcessingResult.java | 247 +- .../consensus/epoch/BFTSyncFactory.java | 11 +- .../epoch/BFTSyncRequestProcessorFactory.java | 6 +- .../radixdlt/consensus/epoch/EpochChange.java | 77 +- .../consensus/epoch/EpochManager.java | 664 ++-- .../radixdlt/consensus/epoch/EpochView.java | 86 +- .../consensus/epoch/EpochViewUpdate.java | 75 +- .../com/radixdlt/consensus/epoch/Epoched.java | 83 +- .../consensus/epoch/VertexStoreFactory.java | 17 +- .../liveness/EpochLocalTimeoutOccurrence.java | 49 +- ...ExponentialPacemakerTimeoutCalculator.java | 67 +- .../liveness/LocalTimeoutOccurrence.java | 68 +- .../consensus/liveness/NextTxnsGenerator.java | 22 +- .../consensus/liveness/Pacemaker.java | 474 +-- .../consensus/liveness/PacemakerFactory.java | 29 +- .../consensus/liveness/PacemakerReducer.java | 30 +- .../consensus/liveness/PacemakerState.java | 87 +- .../liveness/PacemakerStateFactory.java | 16 +- .../liveness/PacemakerTimeoutCalculator.java | 14 +- .../consensus/liveness/ProposerElection.java | 159 +- .../liveness/ScheduledLocalTimeout.java | 101 +- .../consensus/liveness/VoteTimeout.java | 110 +- .../liveness/WeightedRotatingLeaders.java | 296 +- .../safety/PersistentSafetyStateStore.java | 12 +- .../consensus/safety/SafetyRules.java | 340 +- .../consensus/safety/SafetyState.java | 401 +- .../com/radixdlt/consensus/sync/BFTSync.java | 1022 +++--- .../consensus/sync/BFTSyncPatienceMillis.java | 10 +- .../sync/GetVerticesErrorResponse.java | 36 +- .../consensus/sync/GetVerticesRequest.java | 62 +- .../consensus/sync/GetVerticesResponse.java | 26 +- .../consensus/sync/VertexRequestTimeout.java | 48 +- .../VertexStoreBFTSyncRequestProcessor.java | 74 +- .../com/radixdlt/counters/SystemCounters.java | 417 ++- .../radixdlt/counters/SystemCountersImpl.java | 183 +- .../com/radixdlt/environment/Dispatchers.java | 287 +- .../com/radixdlt/environment/Environment.java | 16 +- .../radixdlt/environment/EventDispatcher.java | 2 +- .../radixdlt/environment/EventProcessor.java | 5 +- .../environment/EventProcessorOnDispatch.java | 31 +- .../environment/EventProcessorOnRunner.java | 135 +- .../com/radixdlt/environment/LocalEvents.java | 18 +- .../environment/ProcessOnDispatch.java | 10 +- .../environment/RemoteEventDispatcher.java | 8 +- .../environment/RemoteEventProcessor.java | 8 +- .../RemoteEventProcessorOnRunner.java | 74 +- .../com/radixdlt/environment/Runners.java | 13 +- .../environment/ScheduledEventDispatcher.java | 2 +- .../ScheduledEventProducerOnRunner.java | 69 +- .../radixdlt/environment/StartProcessor.java | 2 +- .../environment/StartProcessorOnRunner.java | 24 +- .../environment/rx/ModuleRunnerImpl.java | 322 +- .../environment/rx/ObservableProvider.java | 44 +- .../radixdlt/environment/rx/RemoteEvent.java | 45 +- .../environment/rx/RxEnvironment.java | 154 +- .../environment/rx/RxEnvironmentModule.java | 484 +-- .../environment/rx/RxRemoteDispatcher.java | 36 +- .../environment/rx/RxRemoteEnvironment.java | 6 +- .../epochs/EpochsLocalSyncService.java | 235 +- .../epochs/LocalSyncServiceFactory.java | 13 +- .../com/radixdlt/keygen/KeyGenerator.java | 254 +- .../radixdlt/keys/InMemoryBFTKeyModule.java | 51 +- .../src/main/java/com/radixdlt/keys/Keys.java | 161 +- .../radixdlt/keys/PersistedBFTKeyManager.java | 76 +- .../radixdlt/keys/PersistedBFTKeyModule.java | 79 +- .../com/radixdlt/ledger/AccumulatorState.java | 90 +- .../ledger/ByzantineQuorumException.java | 20 +- .../ledger/CommittedBadTxnException.java | 20 +- .../com/radixdlt/ledger/DtoLedgerProof.java | 108 +- .../com/radixdlt/ledger/DtoTxnsAndProof.java | 134 +- .../radixdlt/ledger/LedgerAccumulator.java | 8 +- .../ledger/LedgerAccumulatorVerifier.java | 18 +- .../com/radixdlt/ledger/LedgerUpdate.java | 74 +- .../SimpleLedgerAccumulatorAndVerifier.java | 111 +- .../radixdlt/ledger/StateComputerLedger.java | 465 +-- .../radixdlt/ledger/VerifiedTxnsAndProof.java | 77 +- .../java/com/radixdlt/mempool/Mempool.java | 43 +- .../java/com/radixdlt/mempool/MempoolAdd.java | 21 +- .../radixdlt/mempool/MempoolAddSuccess.java | 83 +- .../com/radixdlt/mempool/MempoolConfig.java | 51 +- .../mempool/MempoolDuplicateException.java | 11 +- .../mempool/MempoolFullException.java | 20 +- .../com/radixdlt/mempool/MempoolMaxSize.java | 17 +- .../com/radixdlt/mempool/MempoolMetadata.java | 82 +- .../mempool/MempoolReceiverModule.java | 60 +- .../mempool/MempoolRejectedException.java | 16 +- .../mempool/MempoolRelayInitialDelay.java | 18 +- .../mempool/MempoolRelayMaxPeers.java | 17 +- .../mempool/MempoolRelayRepeatDelay.java | 18 +- .../radixdlt/mempool/MempoolRelayTrigger.java | 39 +- .../com/radixdlt/mempool/MempoolRelayer.java | 129 +- .../mempool/MempoolRelayerModule.java | 74 +- .../radixdlt/mempool/MempoolThrottleMs.java | 17 +- .../java/com/radixdlt/mempool/Mempools.java | 62 +- .../radixdlt/mempoolfiller/MempoolFiller.java | 251 +- .../mempoolfiller/MempoolFillerKey.java | 19 +- .../mempoolfiller/MempoolFillerModule.java | 50 +- .../mempoolfiller/MempoolFillerUpdate.java | 135 +- .../mempoolfiller/ScheduledMempoolFill.java | 12 +- .../network/ConsensusEventMessage.java | 157 +- .../GetVerticesErrorResponseMessage.java | 78 +- .../network/GetVerticesRequestMessage.java | 88 +- .../network/GetVerticesRequestRateLimit.java | 163 +- .../network/GetVerticesResponseMessage.java | 66 +- .../network/LedgerStatusUpdateMessage.java | 64 +- .../network/MempoolAddMessage.java | 67 +- .../network/MessageCentralBFTNetwork.java | 106 +- .../network/MessageCentralLedgerSync.java | 218 +- .../network/MessageCentralMempool.java | 59 +- .../network/MessageCentralPeerDiscovery.java | 87 +- .../network/MessageCentralPeerLiveness.java | 87 +- .../network/MessageCentralValidatorSync.java | 185 +- .../network/StatusRequestMessage.java | 44 +- .../network/StatusResponseMessage.java | 64 +- .../network/SyncRequestMessage.java | 64 +- .../network/SyncResponseMessage.java | 64 +- .../network/hostip/EnvironmentHostIp.java | 68 +- .../com/radixdlt/network/hostip/HostIp.java | 53 +- .../radixdlt/network/hostip/HostIpModule.java | 25 +- .../network/hostip/NetworkQueryHostIp.java | 256 +- .../hostip/NonroutableInterfaceHostIp.java | 140 +- .../hostip/RoutableInterfaceHostIp.java | 143 +- .../hostip/RuntimePropertiesHostIp.java | 64 +- .../network/hostip/StandardHostIp.java | 34 +- .../network/messaging/EventQueueFactory.java | 2 +- .../network/messaging/InboundMessage.java | 2 +- .../network/messaging/MessageCentral.java | 51 +- .../MessageCentralConfiguration.java | 97 +- .../network/messaging/MessageCentralImpl.java | 315 +- .../messaging/MessageCentralModule.java | 42 +- .../network/messaging/MessageDispatcher.java | 159 +- .../network/messaging/MessageFromPeer.java | 56 +- .../messaging/MessagePreprocessor.java | 123 +- .../network/messaging/MessagingErrors.java | 36 +- .../network/messaging/MessagingModule.java | 316 +- .../messaging/OutboundMessageEvent.java | 190 +- .../messaging/SimpleBlockingQueue.java | 57 +- .../SimplePriorityBlockingQueue.java | 118 +- .../java/com/radixdlt/network/p2p/NodeId.java | 75 +- .../com/radixdlt/network/p2p/P2PConfig.java | 286 +- .../com/radixdlt/network/p2p/P2PModule.java | 112 +- .../com/radixdlt/network/p2p/PeerControl.java | 16 +- .../network/p2p/PeerDiscoveryModule.java | 88 +- .../com/radixdlt/network/p2p/PeerEvent.java | 144 +- .../p2p/PeerLivenessMonitorModule.java | 110 +- .../com/radixdlt/network/p2p/PeerManager.java | 549 ++- .../network/p2p/PeerManagerPeersView.java | 52 +- .../com/radixdlt/network/p2p/PeersView.java | 230 +- .../p2p/PendingOutboundChannelsManager.java | 224 +- .../radixdlt/network/p2p/RadixNodeUri.java | 177 +- .../network/p2p/addressbook/AddressBook.java | 432 +-- .../p2p/addressbook/AddressBookEntry.java | 606 +-- .../addressbook/AddressBookPeerControl.java | 25 +- .../addressbook/AddressBookPersistence.java | 17 +- .../network/p2p/discovery/DiscoverPeers.java | 43 +- .../network/p2p/discovery/GetPeers.java | 39 +- .../network/p2p/discovery/PeerDiscovery.java | 207 +- .../network/p2p/discovery/PeersResponse.java | 59 +- .../p2p/liveness/PeerLivenessMonitor.java | 122 +- .../network/p2p/liveness/PeerPingTimeout.java | 51 +- .../liveness/PeersLivenessCheckTrigger.java | 43 +- .../radixdlt/network/p2p/liveness/Ping.java | 39 +- .../radixdlt/network/p2p/liveness/Pong.java | 39 +- .../network/p2p/transport/FrameCodec.java | 298 +- .../network/p2p/transport/PeerChannel.java | 510 ++- .../p2p/transport/PeerChannelInitializer.java | 275 +- .../p2p/transport/PeerOutboundBootstrap.java | 2 +- .../transport/PeerOutboundBootstrapImpl.java | 101 +- .../p2p/transport/PeerServerBootstrap.java | 103 +- .../handshake/AuthHandshakeResult.java | 69 +- .../transport/handshake/AuthHandshaker.java | 481 +-- .../handshake/AuthInitiateMessage.java | 141 +- .../handshake/AuthResponseMessage.java | 78 +- .../p2p/transport/handshake/Secrets.java | 59 +- .../p2p/transport/logging/LogSink.java | 88 +- .../p2p/transport/logging/LoggingHandler.java | 705 ++-- .../properties/PersistedProperties.java | 383 +- .../properties/RuntimeProperties.java | 113 +- .../qualifier/LatencyProviderBase.java | 18 +- .../com/radixdlt/qualifier/LocalSigner.java | 18 +- .../statecomputer/EpochCeilingView.java | 10 +- .../statecomputer/EpochProofVerifierV2.java | 90 +- .../statecomputer/InvalidProposedTxn.java | 79 +- .../statecomputer/LedgerAndBFTProof.java | 70 +- .../statecomputer/MaxSigsPerRound.java | 15 +- .../radixdlt/statecomputer/MaxValidators.java | 9 +- .../statecomputer/NoValidatorsException.java | 6 +- .../com/radixdlt/statecomputer/REOutput.java | 27 +- .../statecomputer/RadixEngineMempool.java | 297 +- .../statecomputer/RadixEngineModule.java | 123 +- .../RadixEngineStateComputer.java | 720 ++-- .../RadixEngineStateComputerModule.java | 20 +- .../com/radixdlt/statecomputer/Rewards.java | 166 +- .../statecomputer/TxnsRemovedFromMempool.java | 14 +- .../statecomputer/checkpoint/Genesis.java | 19 +- .../checkpoint/GenesisBuilder.java | 123 +- .../checkpoint/GenesisProvider.java | 143 +- .../RadixEngineCheckpointModule.java | 10 +- .../statecomputer/forks/ForkConfig.java | 65 +- .../ForkOverwritesFromPropertiesModule.java | 80 +- ...ForkOverwritesWithShorterEpochsModule.java | 40 +- .../radixdlt/statecomputer/forks/Forks.java | 32 +- .../statecomputer/forks/ForksModule.java | 47 +- .../forks/MainnetForkConfigsModule.java | 146 +- .../radixdlt/statecomputer/forks/RERules.java | 108 +- .../statecomputer/forks/RERulesConfig.java | 338 +- .../statecomputer/forks/RERulesVersion.java | 240 +- .../RadixEngineForksLatestOnlyModule.java | 42 +- .../forks/StokenetForkConfigsModule.java | 74 +- .../com/radixdlt/store/DatabaseCacheSize.java | 19 +- .../radixdlt/store/DatabaseEnvironment.java | 312 +- .../com/radixdlt/store/DatabaseLocation.java | 19 +- .../store/DatabasePropertiesModule.java | 38 +- .../com/radixdlt/store/LastEpochProof.java | 10 +- .../java/com/radixdlt/store/LastProof.java | 10 +- .../com/radixdlt/store/LastStoredProof.java | 19 +- .../com/radixdlt/store/PersistenceModule.java | 81 +- .../java/com/radixdlt/store/SearchCursor.java | 163 +- .../java/com/radixdlt/store/StoreConfig.java | 25 +- .../berkeley/BerkeleyAdditionalStore.java | 25 +- .../BerkeleyAddressBookPersistence.java | 457 ++- .../berkeley/BerkeleyLedgerEntryStore.java | 2175 +++++------ .../berkeley/BerkeleySafetyStateStore.java | 563 ++- .../berkeley/BerkeleyStoreException.java | 154 +- .../berkeley/SerializedVertexStoreState.java | 154 +- .../store/berkeley/atom/AppendLog.java | 138 +- .../berkeley/atom/CompressedAppendLog.java | 135 +- .../store/berkeley/atom/SimpleAppendLog.java | 291 +- .../com/radixdlt/sync/CommittedReader.java | 45 +- .../com/radixdlt/sync/LocalSyncService.java | 1090 +++--- .../com/radixdlt/sync/RemoteSyncService.java | 247 +- .../java/com/radixdlt/sync/SyncConfig.java | 113 +- .../java/com/radixdlt/sync/SyncState.java | 665 ++-- .../sync/messages/local/LocalSyncRequest.java | 66 +- .../local/SyncCheckReceiveStatusTimeout.java | 43 +- .../sync/messages/local/SyncCheckTrigger.java | 43 +- .../local/SyncLedgerUpdateTimeout.java | 58 +- .../messages/local/SyncRequestTimeout.java | 76 +- .../messages/remote/LedgerStatusUpdate.java | 62 +- .../sync/messages/remote/StatusRequest.java | 43 +- .../sync/messages/remote/StatusResponse.java | 63 +- .../sync/messages/remote/SyncRequest.java | 63 +- .../sync/messages/remote/SyncResponse.java | 64 +- .../RemoteSyncResponseSignaturesVerifier.java | 57 +- ...emoteSyncResponseValidatorSetVerifier.java | 33 +- .../systeminfo/InMemorySystemInfo.java | 112 +- .../java/com/radixdlt/utils/Compress.java | 79 +- .../com/radixdlt/utils/DurationParser.java | 270 +- .../radixdlt/utils/MemoryLeakDetector.java | 178 +- .../com/radixdlt/utils/RTTStatistics.java | 152 +- .../com/radixdlt/utils/RateCalculator.java | 30 +- .../com/radixdlt/utils/ThreadFactories.java | 110 +- .../java/com/radixdlt/utils/TimeSupplier.java | 19 +- .../java/org/radix/GenerateUniverses.java | 270 +- .../src/main/java/org/radix/Radix.java | 398 +- .../main/java/org/radix/TokenIssuance.java | 47 +- .../org/radix/containers/BasicContainer.java | 180 +- .../org/radix/network/discovery/SSLFix.java | 179 +- .../discovery/SeedNodesConfigParser.java | 113 +- .../radix/network/discovery/Whitelist.java | 428 ++- .../network/messages/GetPeersMessage.java | 40 +- .../network/messages/PeerPingMessage.java | 40 +- .../network/messages/PeerPongMessage.java | 40 +- .../messages/PeersResponseMessage.java | 59 +- .../org/radix/network/messaging/Message.java | 237 +- .../java/org/radix/time/NtpException.java | 12 +- .../main/java/org/radix/time/NtpMessage.java | 954 +++-- .../main/java/org/radix/time/NtpService.java | 401 +- .../src/main/java/org/radix/time/Time.java | 26 +- .../main/java/org/radix/utils/IOUtils.java | 121 +- .../radix/utils/InterruptibleSupplier.java | 2 +- .../java/org/radix/utils/LedgerFileSync.java | 186 +- .../org/radix/utils/SimpleThreadPool.java | 189 +- .../org/radix/utils/shell/RadixShell.java | 535 +-- .../com/radixdlt/ConsensusModuleTest.java | 461 +-- .../com/radixdlt/FunctionalNodeModule.java | 197 +- .../test/java/com/radixdlt/GenesisTest.java | 89 +- .../java/com/radixdlt/JSONFormatterTest.java | 59 +- .../java/com/radixdlt/MockedCryptoModule.java | 117 +- .../java/com/radixdlt/MockedKeyModule.java | 38 +- .../MockedPersistenceStoreModule.java | 53 +- .../PersistedNodeForTestingModule.java | 66 +- .../com/radixdlt/RadixNodeModuleTest.java | 80 +- ...odeAndPeersDeterministicNetworkModule.java | 79 +- .../test/java/com/radixdlt/api/ApiTest.java | 340 +- .../api/UnhandledExceptionHandlerTest.java | 29 +- .../core/ConstructionBuildFeePayerTest.java | 77 +- .../api/core/ConstructionBuildFeeTest.java | 168 +- .../core/ConstructionBuildMessageTest.java | 118 +- .../core/ConstructionBuildMintBurnTest.java | 121 +- .../ConstructionBuildTokenDefinitionTest.java | 337 +- ...tructionBuildTransferStakeUnstakeTest.java | 442 ++- .../core/ConstructionBuildValidatorTest.java | 259 +- .../core/ConstructionDeriveHandlerTest.java | 348 +- .../api/core/ConstructionFinalizeTest.java | 180 +- .../api/core/ConstructionHashHandlerTest.java | 69 +- .../core/ConstructionParseMessageTest.java | 106 +- .../core/ConstructionParseTransferTest.java | 165 +- .../api/core/ConstructionSubmitTest.java | 259 +- .../core/EngineConfigurationHandlerTest.java | 45 +- .../api/core/EngineStatusHandlerTest.java | 45 +- .../radixdlt/api/core/EntityHandlerTest.java | 489 +-- .../radixdlt/api/core/KeyListHandlerTest.java | 37 +- .../radixdlt/api/core/KeySignHandlerTest.java | 167 +- .../radixdlt/api/core/MempoolHandlerTest.java | 112 +- .../core/MempoolTransactionHandlerTest.java | 176 +- .../core/NetworkConfigurationHandlerTest.java | 45 +- .../api/core/NetworkStatusHandlerTest.java | 86 +- .../api/core/TransactionsHandlerTest.java | 162 +- .../api/system/AddressBookHandlerTest.java | 70 +- .../api/system/ConfigurationHandlerTest.java | 30 +- .../api/system/HealthHandlerTest.java | 30 +- .../api/system/HealthInfoServiceTest.java | 94 +- .../api/system/MetricsHandlerTest.java | 31 +- .../api/system/MovingAverageTest.java | 232 +- .../radixdlt/api/system/PeersHandlerTest.java | 30 +- .../api/system/PrometheusHandlerTest.java | 30 +- .../api/system/VersionHandlerTest.java | 30 +- .../consensus/BFTConfigurationTest.java | 17 +- .../com/radixdlt/consensus/BFTHeaderTest.java | 82 +- .../com/radixdlt/consensus/HighQCTest.java | 295 +- .../radixdlt/consensus/LedgerHeaderTest.java | 86 +- .../radixdlt/consensus/LedgerProofTest.java | 272 +- .../com/radixdlt/consensus/PacemakerTest.java | 250 +- .../radixdlt/consensus/PendingVotesTest.java | 637 ++-- .../com/radixdlt/consensus/ProposalTest.java | 118 +- .../consensus/QuorumCertificateTest.java | 52 +- .../consensus/TimeoutCertificateTest.java | 49 +- .../TimestampedECDSASignatureTest.java | 53 +- .../TimestampedECDSASignaturesTest.java | 31 +- .../consensus/UnverifiedVertexTest.java | 137 +- .../java/com/radixdlt/consensus/ViewTest.java | 129 +- .../com/radixdlt/consensus/VoteDataTest.java | 34 +- .../java/com/radixdlt/consensus/VoteTest.java | 177 +- .../bft/BFTEventPreprocessorTest.java | 251 +- .../consensus/bft/BFTEventReducerTest.java | 413 +-- .../consensus/bft/BFTEventVerifierTest.java | 267 +- .../consensus/bft/BFTHighQCUpdateTest.java | 14 +- .../consensus/bft/BFTInsertUpdateTest.java | 14 +- .../radixdlt/consensus/bft/BFTNodeTest.java | 13 +- .../consensus/bft/BFTValidatorSetTest.java | 286 +- .../consensus/bft/BFTValidatorTest.java | 44 +- .../radixdlt/consensus/bft/NoVoteTest.java | 14 +- .../consensus/bft/ValidationStateTest.java | 71 +- .../bft/VerifiedVertexChainTest.java | 14 +- .../VerifiedVertexStoreStateCreationTest.java | 107 +- .../bft/VerifiedVertexStoreStateTest.java | 14 +- .../consensus/bft/VerifiedVertexTest.java | 21 +- .../consensus/bft/VertexStoreTest.java | 319 +- .../consensus/bft/ViewQuorumReachedTest.java | 9 +- .../consensus/bft/ViewUpdateTest.java | 12 +- .../consensus/bft/ViewVotingResultTest.java | 21 +- .../bft/VoteProcessingResultTest.java | 27 +- .../consensus/epoch/EpochChangeTest.java | 54 +- .../consensus/epoch/EpochManagerTest.java | 380 +- .../consensus/epoch/EpochViewTest.java | 47 +- .../consensus/epoch/EpochViewUpdateTest.java | 12 +- .../radixdlt/consensus/epoch/EpochedTest.java | 15 +- ...nentialPacemakerTimeoutCalculatorTest.java | 56 +- .../liveness/LocalTimeoutOccurrenceTest.java | 14 +- .../liveness/PacemakerStateTest.java | 154 +- .../consensus/liveness/PacemakerTest.java | 290 +- .../liveness/ScheduledLocalTimeoutTest.java | 14 +- .../consensus/liveness/VoteTimeoutTest.java | 21 +- .../liveness/WeightedRotatingLeadersTest.java | 218 +- .../consensus/safety/SafetyRulesTest.java | 550 +-- .../consensus/safety/SafetyStateTest.java | 210 +- .../sync/GetVerticesRequestTest.java | 14 +- .../sync/VertexRequestTimeoutTest.java | 15 +- .../counters/SystemCountersImplTest.java | 186 +- .../environment/NoEpochsConsensusModule.java | 249 +- .../environment/NoEpochsSyncModule.java | 178 +- .../deterministic/DeterministicProcessor.java | 114 +- .../deterministic/LastEventsModule.java | 29 +- .../MultiNodeDeterministicRunner.java | 131 +- .../SingleNodeDeterministicRunner.java | 74 +- .../deterministic/network/ChannelId.java | 67 +- .../network/ControlledMessage.java | 119 +- .../network/ControlledSender.java | 114 +- .../network/DeterministicNetwork.java | 213 +- .../deterministic/network/MessageMutator.java | 89 +- .../deterministic/network/MessageQueue.java | 270 +- .../deterministic/network/MessageRank.java | 83 +- .../network/MessageSelector.java | 56 +- .../radixdlt/ledger/AccumulatorStateTest.java | 32 +- .../radixdlt/ledger/DtoLedgerProofTest.java | 42 +- .../radixdlt/ledger/DtoTxnsAndProofTest.java | 47 +- ...aysAcceptingAccumulatorVerifierModule.java | 61 +- .../com/radixdlt/ledger/LedgerUpdateTest.java | 16 +- .../com/radixdlt/ledger/MockPrepared.java | 16 +- .../ledger/MockedCommandGeneratorModule.java | 12 +- .../radixdlt/ledger/MockedLedgerModule.java | 62 +- ...impleLedgerAccumulatorAndVerifierTest.java | 90 +- .../ledger/StateComputerLedgerTest.java | 330 +- .../ledger/VerifiedTxnsAndProofTest.java | 42 +- .../mempool/MempoolAddSuccessTest.java | 12 +- .../radixdlt/mempool/MempoolMetadataTest.java | 13 +- .../mempool/MempoolRelayTriggerTest.java | 12 +- .../radixdlt/mempool/MempoolRunnerTest.java | 108 +- .../com/radixdlt/mempool/MempoolTest.java | 589 +-- .../com/radixdlt/mempool/SimpleMempool.java | 142 +- .../messages/MempoolAddMessageTest.java | 63 +- .../mempoolfiller/MempoolFillerTest.java | 105 +- .../network/ConsensusEventMessageTest.java | 82 +- ...icesErrorResponseMessageSerializeTest.java | 33 +- .../GetVerticesErrorResponseMessageTest.java | 69 +- ...etVerticesRequestMessageSerializeTest.java | 15 +- .../GetVerticesRequestMessageTest.java | 58 +- ...tVerticesResponseMessageSerializeTest.java | 20 +- .../GetVerticesResponseMessageTest.java | 66 +- ...edgerStatusUpdateMessageSerializeTest.java | 18 +- .../LedgerStatusUpdateMessageTest.java | 24 +- .../network/MessageCentralBFTNetworkTest.java | 38 +- .../network/MessageCentralLedgerSyncTest.java | 150 +- .../MessageCentralValidatorSyncTest.java | 135 +- .../network/StatusResponseMessageTest.java | 26 +- .../SyncRequestMessageSerializeTest.java | 15 +- .../network/SyncRequestMessageTest.java | 24 +- .../SyncResponseMessageSerializeTest.java | 22 +- .../network/SyncResponseMessageTest.java | 24 +- .../network/hostip/EnvironmentHostIpTest.java | 57 +- .../hostip/NetworkQueryHostIpTest.java | 164 +- .../NonroutableInterfaceHostIpTest.java | 173 +- .../hostip/RoutableInterfaceHostIpTest.java | 168 +- .../hostip/RuntimePropertiesHostIpTest.java | 58 +- .../network/messaging/InboundMessageTest.java | 36 +- .../MessageCentralConfigurationTest.java | 32 +- .../messaging/MessageCentralImplTest.java | 160 +- .../messaging/MessageCentralMockProvider.java | 43 +- .../messaging/MessageFromPeerTest.java | 10 +- .../messaging/MessagePreprocessorTest.java | 333 +- .../messaging/OutboundMessageEventTest.java | 180 +- .../SimplePriorityBlockingQueueTest.java | 71 +- .../network/p2p/FailedHandshakeTest.java | 59 +- .../radixdlt/network/p2p/NoOpPeerControl.java | 72 +- .../radixdlt/network/p2p/PeerManagerTest.java | 199 +- .../network/p2p/RadixNodeUriTest.java | 16 +- .../AddressBookEntrySerializeEmptyTest.java | 27 +- .../AddressBookEntrySerializeTest.java | 52 +- .../p2p/addressbook/AddressBookEntryTest.java | 9 +- .../p2p/addressbook/AddressBookTest.java | 177 +- .../InMemoryAddressBookPersistence.java | 55 +- .../p2p/discovery/DiscoverPeersTest.java | 9 +- .../network/p2p/discovery/GetPeersTest.java | 9 +- .../p2p/discovery/PeerDiscoveryTest.java | 77 +- .../p2p/discovery/PeersResponseTest.java | 9 +- .../p2p/liveness/PeerLivenessMonitorTest.java | 228 +- .../p2p/liveness/PeerPingTimeoutTest.java | 9 +- .../PeersLivenessCheckTriggerTest.java | 9 +- .../network/p2p/liveness/PingTest.java | 9 +- .../network/p2p/liveness/PongTest.java | 9 +- .../p2p/test/DeterministicP2PNetworkTest.java | 94 +- .../network/p2p/test/MockP2PNetwork.java | 177 +- .../p2p/test/P2PTestNetworkRunner.java | 275 +- .../radixdlt/network/p2p/test/TestNode.java | 16 +- .../network/p2p/transport/FrameCodecTest.java | 81 +- .../network/p2p/transport/LogSinkTest.java | 60 +- .../p2p/transport/LoggingHandlerTest.java | 687 ++-- .../handshake/AuthHandshakerTest.java | 80 +- .../properties/PersistedPropertiesTest.java | 252 +- .../properties/RuntimePropertiesTest.java | 135 +- .../recovery/MockedRecoveryModule.java | 100 +- .../com/radixdlt/recovery/RecoveryTest.java | 464 ++- .../SanityTestSuiteExecutor.java | 106 +- .../SanityTestSuiteTestLoader.java | 70 +- .../model/SanityTestSuiteRoot.java | 250 +- .../model/SanityTestVector.java | 7 +- .../scenario/SanityTestScenarioRunner.java | 69 +- .../DeserializationTestVector.java | 22 +- .../hashing/HashingTestScenarioRunner.java | 30 +- .../scenario/hashing/HashingTestVector.java | 27 +- .../JsonSerializationTestVector.java | 23 +- .../keygen/KeyGenTestScenarioRunner.java | 46 +- .../scenario/keygen/KeyGenTestVector.java | 18 +- .../keysign/KeySignTestScenarioRunner.java | 56 +- .../scenario/keysign/KeySignTestVector.java | 34 +- .../KeyVerifyTestScenarioRunner.java | 53 +- .../keyverify/KeyVerifyTestVector.java | 26 +- .../RadixHashingTestScenarioRunner.java | 33 +- .../radixhashing/RadixHashingTestVector.java | 28 +- .../SerializationTestVector.java | 25 +- .../AtomsRemovedFromMempoolTest.java | 14 +- .../statecomputer/InvalidProposedTxnTest.java | 14 +- .../statecomputer/LedgerAndBFTProofTest.java | 15 +- .../MockedMempoolStateComputerModule.java | 128 +- .../statecomputer/MockedStateComputer.java | 110 +- .../MockedStateComputerModule.java | 8 +- .../MockedStateComputerWithEpochs.java | 88 +- .../MockedStateComputerWithEpochsModule.java | 8 +- .../RadixEngineStateComputerTest.java | 689 ++-- .../RandomHashTxnsGenerator.java | 13 +- .../radixdlt/statecomputer/StakesTest.java | 15 +- .../checkpoint/MockedGenesisModule.java | 94 +- .../MutableTokenAndResourceFeeTest.java | 322 +- .../store/MockedRadixEngineStoreModule.java | 18 +- .../BerkeleySafetyStateStoreTest.java | 87 +- .../SerializedVertexStoreStateTest.java | 14 +- .../berkeley/atom/SimpleAppendLogTest.java | 131 +- .../sync/InMemoryCommittedReader.java | 128 +- .../radixdlt/sync/LocalSyncServiceTest.java | 1128 +++--- .../sync/MockedCommittedReaderModule.java | 23 +- .../sync/MockedSyncServiceModule.java | 272 +- .../radixdlt/sync/RemoteSyncServiceTest.java | 223 +- .../SometimesByzantineCommittedReader.java | 336 +- .../java/com/radixdlt/sync/SyncStateTest.java | 274 +- .../messages/local/LocalSyncRequestTest.java | 50 +- .../SyncCheckReceiveStatusTimeoutTest.java | 12 +- .../messages/local/SyncCheckTriggerTest.java | 12 +- .../local/SyncLedgerUpdateTimeoutTest.java | 12 +- .../local/SyncRequestTimeoutTest.java | 12 +- .../remote/LedgerStatusUpdateTest.java | 12 +- .../messages/remote/StatusRequestTest.java | 12 +- .../messages/remote/StatusResponseTest.java | 12 +- .../sync/messages/remote/SyncRequestTest.java | 12 +- .../messages/remote/SyncResponseTest.java | 12 +- ...eSyncResponseValidatorSetVerifierTest.java | 58 +- .../radixdlt/utils/DurationParserTest.java | 225 +- .../com/radixdlt/utils/JSONFormatter.java | 112 +- .../java/com/radixdlt/utils/RandomHasher.java | 39 +- .../utils/SerializerTestDataGenerator.java | 163 +- .../java/com/radixdlt/utils/TypedMocks.java | 67 +- .../org/radix/RadixVersionStringTest.java | 71 +- ...icContainerSubTypesEqualsVerifierTest.java | 136 +- .../radix/network/discovery/SSLFixTest.java | 228 +- .../discovery/SeedNodesConfigParserTest.java | 47 +- .../network/messages/GetPeersMessageTest.java | 36 +- .../network/messages/PeerPingMessageTest.java | 18 +- .../network/messages/PeerPongMessageTest.java | 18 +- .../messages/PeersResponseMessageTest.java | 54 +- .../radix/serialization/AIDSerializeTest.java | 172 +- .../serialization/BFTHeaderSerializeTest.java | 19 +- .../radix/serialization/DummyTestObject.java | 227 +- .../radix/serialization/DummyTestObject2.java | 64 +- .../MempoolAddMessageSerializeTest.java | 15 +- .../PeersResponseMessageSerializeTest.java | 19 +- .../serialization/ProposalSerializeTest.java | 35 +- .../org/radix/serialization/REAddrTest.java | 12 +- .../org/radix/serialization/RadixTest.java | 63 +- .../serialization/SerializationTestUtils.java | 49 +- .../serialization/SerializeMessageObject.java | 13 +- .../radix/serialization/SerializeObject.java | 188 +- .../radix/serialization/SerializeValue.java | 81 +- .../radix/serialization/Serializer2Test.java | 169 +- .../radix/serialization/TestSetupUtils.java | 254 +- .../UnverifiedVertexSerializeTest.java | 35 +- .../serialization/VoteDataSerializeTest.java | 25 +- .../serialization/VoteSerializeTest.java | 40 +- radixdlt-engine/CHANGELOG.md | 4 +- radixdlt-engine/README.md | 4 +- .../com/radixdlt/DefaultSerialization.java | 34 +- .../accounting/REResourceAccounting.java | 116 +- .../misc/SplitTokenConstructor.java | 33 +- .../radixdlt/application/system/FeeTable.java | 44 +- .../system/NextValidatorSetEvent.java | 21 +- .../system/ValidatorBFTDataEvent.java | 42 +- .../system/ValidatorMissedProposalsEvent.java | 31 +- .../CreateSystemConstructorV2.java | 41 +- .../FeeReserveCompleteConstructor.java | 131 +- .../FeeReserveCompleteException.java | 16 +- .../FeeReservePutConstructor.java | 29 +- .../FeeReserveTakeConstructor.java | 10 +- .../construction/InvalidRoundException.java | 11 +- .../construction/NextEpochConstructorV3.java | 466 +-- .../construction/NextViewConstructorV3.java | 68 +- .../system/scrypt/EndPrevRound.java | 14 +- .../scrypt/EpochUpdateConstraintScrypt.java | 1791 ++++----- .../scrypt/RoundUpdateConstraintScrypt.java | 210 +- .../system/scrypt/StartNextRound.java | 22 +- .../application/system/scrypt/Syscall.java | 36 +- .../system/scrypt/SystemConstraintScrypt.java | 443 +-- .../scrypt/UpdatingValidatorBFTData.java | 96 +- .../system/scrypt/ValidatorScratchPad.java | 183 +- .../application/system/state/EpochData.java | 48 +- .../system/state/HasEpochData.java | 2 +- .../application/system/state/RoundData.java | 65 +- .../application/system/state/StakeBucket.java | 73 +- .../system/state/StakeOwnership.java | 109 +- .../system/state/StakeOwnershipBucket.java | 107 +- .../application/system/state/SystemData.java | 3 +- .../system/state/UnclaimedREAddr.java | 74 +- .../system/state/ValidatorBFTData.java | 93 +- .../system/state/ValidatorStakeData.java | 257 +- .../system/state/VirtualParent.java | 47 +- .../radixdlt/application/tokens/Amount.java | 71 +- .../radixdlt/application/tokens/Bucket.java | 13 +- .../tokens/ResourceCreatedEvent.java | 35 +- .../application/tokens/ResourceInBucket.java | 5 +- .../application/tokens/TokenUtils.java | 23 +- .../construction/BurnTokenConstructor.java | 48 +- .../CreateFixedTokenConstructor.java | 54 +- .../CreateMutableTokenConstructor.java | 51 +- .../DelegateStakePermissionException.java | 31 +- .../construction/MinimumStakeException.java | 31 +- .../construction/MintTokenConstructor.java | 12 +- .../StakeTokensConstructorV3.java | 86 +- .../construction/SymbolLengthException.java | 11 +- .../TransferTokensConstructorV2.java | 50 +- .../UnstakeOwnershipConstructor.java | 63 +- .../UnstakeTokensConstructorV2.java | 76 +- .../scrypt/StakeOwnershipHoldingBucket.java | 102 +- .../scrypt/StakingConstraintScryptV4.java | 345 +- .../tokens/scrypt/TokenHoldingBucket.java | 80 +- .../application/tokens/scrypt/Tokens.java | 146 +- .../scrypt/TokensConstraintScryptV3.java | 438 +-- .../tokens/state/AccountBucket.java | 115 +- .../state/DeprecatedResourceInBucket.java | 46 +- .../tokens/state/ExitingStake.java | 168 +- .../tokens/state/ExittingOwnershipBucket.java | 76 +- .../tokens/state/ExittingStakeBucket.java | 87 +- .../tokens/state/PreparedStake.java | 112 +- .../tokens/state/PreparedStakeBucket.java | 79 +- .../state/PreparedUnstakeOwnership.java | 100 +- .../tokens/state/ResourceData.java | 2 +- .../tokens/state/TokenResource.java | 165 +- .../tokens/state/TokenResourceMetadata.java | 139 +- .../tokens/state/TokensInAccount.java | 107 +- .../unique/scrypt/MutexConstraintScrypt.java | 18 +- .../InvalidRakeIncreaseException.java | 31 +- .../RegisterValidatorConstructor.java | 19 +- .../UnregisterValidatorConstructor.java | 22 +- .../UpdateAllowDelegationFlagConstructor.java | 21 +- .../construction/UpdateRakeConstructor.java | 64 +- .../UpdateValidatorMetadataConstructor.java | 26 +- .../UpdateValidatorOwnerConstructor.java | 24 +- ...ateValidatorSystemMetadataConstructor.java | 19 +- .../scrypt/ValidatorConstraintScryptV2.java | 330 +- .../ValidatorRegisterConstraintScrypt.java | 165 +- .../ValidatorUpdateOwnerConstraintScrypt.java | 177 +- .../ValidatorUpdateRakeConstraintScrypt.java | 287 +- .../validators/state/AllowDelegationFlag.java | 59 +- .../validators/state/ValidatorData.java | 2 +- .../validators/state/ValidatorFeeCopy.java | 105 +- .../validators/state/ValidatorMetaData.java | 252 +- .../validators/state/ValidatorOwnerCopy.java | 91 +- .../state/ValidatorRegisteredCopy.java | 89 +- .../state/ValidatorSystemMetadata.java | 71 +- .../state/ValidatorUpdatingData.java | 7 +- .../com/radixdlt/atom/ActionConstructor.java | 5 +- .../com/radixdlt/atom/CloseableCursor.java | 238 +- .../radixdlt/atom/FixedTokenDefinition.java | 82 +- .../java/com/radixdlt/atom/LocalSubstate.java | 60 +- .../atom/MessageTooLongException.java | 21 +- .../radixdlt/atom/MutableTokenDefinition.java | 104 +- .../atom/NoSubstateFoundException.java | 11 +- .../atom/NotEnoughResourcesException.java | 35 +- .../java/com/radixdlt/atom/REConstructor.java | 80 +- .../radixdlt/atom/REFieldSerialization.java | 500 +-- .../main/java/com/radixdlt/atom/Substate.java | 75 +- .../java/com/radixdlt/atom/SubstateId.java | 189 +- .../java/com/radixdlt/atom/SubstateStore.java | 34 +- .../com/radixdlt/atom/SubstateTypeId.java | 103 +- .../main/java/com/radixdlt/atom/TxAction.java | 7 +- .../java/com/radixdlt/atom/TxBuilder.java | 980 +++-- .../com/radixdlt/atom/TxBuilderException.java | 10 +- .../com/radixdlt/atom/TxLowLevelBuilder.java | 532 ++- .../com/radixdlt/atom/TxValidatorAction.java | 6 +- .../src/main/java/com/radixdlt/atom/Txn.java | 67 +- .../radixdlt/atom/TxnConstructionRequest.java | 232 +- .../com/radixdlt/atom/UnsignedTxnData.java | 34 +- .../radixdlt/atom/actions/ActionErrors.java | 74 +- .../com/radixdlt/atom/actions/BurnToken.java | 42 +- .../atom/actions/CreateFixedToken.java | 114 +- .../atom/actions/CreateMutableToken.java | 110 +- .../radixdlt/atom/actions/CreateSystem.java | 15 +- .../atom/actions/FeeReserveComplete.java | 20 +- .../radixdlt/atom/actions/FeeReservePut.java | 24 +- .../radixdlt/atom/actions/FeeReserveTake.java | 24 +- .../com/radixdlt/atom/actions/MintToken.java | 40 +- .../com/radixdlt/atom/actions/NextEpoch.java | 14 +- .../com/radixdlt/atom/actions/NextRound.java | 54 +- .../atom/actions/RegisterValidator.java | 24 +- .../com/radixdlt/atom/actions/SplitToken.java | 34 +- .../radixdlt/atom/actions/StakeTokens.java | 48 +- .../radixdlt/atom/actions/TransferToken.java | 61 +- .../com/radixdlt/atom/actions/Unknown.java | 9 +- .../atom/actions/UnregisterValidator.java | 17 +- .../atom/actions/UnstakeOwnership.java | 48 +- .../radixdlt/atom/actions/UnstakeTokens.java | 48 +- .../actions/UpdateAllowDelegationFlag.java | 26 +- .../atom/actions/UpdateValidatorFee.java | 26 +- .../atom/actions/UpdateValidatorMetadata.java | 41 +- .../atom/actions/UpdateValidatorOwner.java | 26 +- .../UpdateValidatorSystemMetadata.java | 30 +- .../java/com/radixdlt/atomos/CMAtomOS.java | 45 +- .../com/radixdlt/atomos/ConstraintScrypt.java | 19 +- .../radixdlt/atomos/ConstraintScryptEnv.java | 79 +- .../main/java/com/radixdlt/atomos/Loader.java | 21 +- .../radixdlt/atomos/SubstateDefinition.java | 197 +- .../constraintmachine/Authorization.java | 35 +- .../radixdlt/constraintmachine/CallData.java | 60 +- .../constraintmachine/ConstraintMachine.java | 882 ++--- .../ConstraintMachineConfig.java | 54 +- .../constraintmachine/DownProcedure.java | 68 +- .../constraintmachine/DownReducer.java | 4 +- .../constraintmachine/EndProcedure.java | 68 +- .../constraintmachine/EndReducer.java | 2 +- .../constraintmachine/ExecutionContext.java | 296 +- .../constraintmachine/IndexedReducer.java | 12 +- .../IndexedSubstateIterator.java | 56 +- .../constraintmachine/KeyDeserializer.java | 3 +- .../constraintmachine/KeySerializer.java | 2 +- .../constraintmachine/OpSignature.java | 64 +- .../radixdlt/constraintmachine/Particle.java | 7 +- .../constraintmachine/PermissionLevel.java | 11 +- .../radixdlt/constraintmachine/Procedure.java | 15 +- .../constraintmachine/ProcedureKey.java | 61 +- .../constraintmachine/Procedures.java | 43 +- .../radixdlt/constraintmachine/REEvent.java | 3 +- .../constraintmachine/REInstruction.java | 522 +-- .../com/radixdlt/constraintmachine/REOp.java | 44 +- .../constraintmachine/REProcessedTxn.java | 199 +- .../constraintmachine/REStateUpdate.java | 154 +- .../constraintmachine/RawSubstateBytes.java | 24 +- .../constraintmachine/ReadIndexProcedure.java | 63 +- .../constraintmachine/ReadProcedure.java | 62 +- .../constraintmachine/ReadReducer.java | 3 +- .../constraintmachine/ReducerResult.java | 28 +- .../constraintmachine/ReducerState.java | 3 +- .../radixdlt/constraintmachine/Resources.java | 2 +- .../ShutdownAllProcedure.java | 63 +- .../SubstateDeserialization.java | 86 +- .../SubstateDeserializer.java | 3 +- .../constraintmachine/SubstateIndex.java | 170 +- .../SubstateSerialization.java | 139 +- .../constraintmachine/SubstateSerializer.java | 2 +- .../SystemCallProcedure.java | 69 +- .../constraintmachine/SystemCallReducer.java | 2 +- .../constraintmachine/SystemMapKey.java | 89 +- .../constraintmachine/UpProcedure.java | 69 +- .../radixdlt/constraintmachine/UpReducer.java | 3 +- .../constraintmachine/UpSubstate.java | 42 +- .../constraintmachine/VirtualMapper.java | 2 +- .../VirtualSubstateDeserialization.java | 46 +- .../constraintmachine/VoidReducerState.java | 6 +- .../exceptions/AuthorizationException.java | 18 +- .../exceptions/CallDataAccessException.java | 12 +- .../ConstraintMachineException.java | 45 +- .../DefaultedSystemLoanException.java | 12 +- .../DepletedFeeReserveException.java | 32 +- .../ExecutionContextDestroyException.java | 6 +- .../InvalidDelegationException.java | 6 +- .../exceptions/InvalidHashedKeyException.java | 6 +- .../InvalidPermissionException.java | 6 +- .../exceptions/InvalidResourceException.java | 14 +- .../InvalidVirtualSubstateException.java | 6 +- .../LocalSubstateNotFoundException.java | 6 +- .../exceptions/MeterException.java | 6 +- .../exceptions/MinimumStakeException.java | 6 +- .../exceptions/MismatchException.java | 12 +- .../exceptions/MissingProcedureException.java | 8 +- .../MultipleFeeReserveDepositException.java | 3 +- .../exceptions/NotAResourceException.java | 6 +- .../NotEnoughResourcesException.java | 26 +- .../exceptions/ProcedureException.java | 12 +- .../exceptions/ReservedSymbolException.java | 6 +- ...urceAllocationAndDestructionException.java | 6 +- .../exceptions/SignedSystemException.java | 6 +- .../exceptions/SubstateNotFoundException.java | 18 +- .../VirtualParentStateDoesNotExist.java | 6 +- .../VirtualSubstateAlreadyDownException.java | 6 +- .../meter/FixedFeeMeter.java | 74 +- .../constraintmachine/meter/Meter.java | 57 +- .../constraintmachine/meter/Meters.java | 56 +- .../meter/SigsPerRoundMeter.java | 44 +- .../meter/TxnSizeFeeMeter.java | 81 +- .../meter/UpSubstateFeeMeter.java | 66 +- .../java/com/radixdlt/crypto/ECKeyOps.java | 64 +- .../main/java/com/radixdlt/crypto/Hasher.java | 29 +- .../com/radixdlt/engine/BatchVerifier.java | 13 +- .../engine/FeeConstructionException.java | 21 +- .../radixdlt/engine/MetadataException.java | 6 +- .../java/com/radixdlt/engine/RadixEngine.java | 1019 +++--- .../radixdlt/engine/RadixEngineErrorCode.java | 10 +- .../radixdlt/engine/RadixEngineException.java | 66 +- .../radixdlt/engine/RadixEngineReader.java | 26 +- .../radixdlt/engine/RadixEngineResult.java | 61 +- .../com/radixdlt/engine/parser/ParsedTxn.java | 78 +- .../com/radixdlt/engine/parser/REParser.java | 335 +- ...REInstructionDataDeserializeException.java | 6 +- .../SubstateDeserializationException.java | 7 +- .../exceptions/TrailingBytesException.java | 6 +- .../parser/exceptions/TxnParseException.java | 48 +- .../main/java/com/radixdlt/store/CMStore.java | 16 +- .../java/com/radixdlt/store/EngineStore.java | 25 +- .../radixdlt/store/InMemoryEngineStore.java | 287 +- .../com/radixdlt/store/ResourceStore.java | 7 +- .../radixdlt/store/TransientEngineStore.java | 141 +- .../java/com/radixdlt/TestSetupUtils.java | 263 +- .../application/BucketEqualsHashCode.java | 33 +- .../application/ParticlesEqualsHashCode.java | 37 +- .../application/system/FixedFeeTest.java | 311 +- .../application/system/NextEpochV2Test.java | 258 +- .../application/system/NextViewV2Test.java | 227 +- .../application/system/ResourceFeeTest.java | 191 +- .../application/system/TxnSizeFeeTest.java | 511 +-- .../application/tokens/BurnTokensV3Test.java | 317 +- .../tokens/DelegationFlagTest.java | 317 +- .../application/tokens/MintTokensTest.java | 291 +- .../application/tokens/NativeTokensTest.java | 149 +- .../application/tokens/StakeTokensTest.java | 288 +- .../application/tokens/TokenResourceTest.java | 297 +- .../tokens/TransferTokensTest.java | 259 +- .../tokens/UnstakeTokensV2Test.java | 479 +-- .../application/unique/UniqueTest.java | 128 +- .../validators/RegisterValidatorTest.java | 159 +- .../validators/UpdateValidatorFeeTest.java | 90 +- .../UpdateValidatorMetadataTest.java | 85 +- .../UpdateValidatorSystemMetadataTest.java | 85 +- .../com/radixdlt/atom/LocalSubstateTest.java | 14 +- .../com/radixdlt/atom/SubstateIdTest.java | 14 +- .../constraintmachine/OpSignatureTest.java | 12 +- .../constraintmachine/ProcedureKeyTest.java | 12 +- .../constraintmachine/SubstateIndexTest.java | 12 +- .../SerializationTestUtilsEngine.java | 54 +- .../serialization/SerializeObjectEngine.java | 97 +- .../com/radixdlt/test/utils/TypedMocks.java | 39 +- radixdlt-java-common/CHANGELOG.md | 4 +- radixdlt-java-common/README.md | 8 +- .../java/com/radixdlt/SecurityCritical.java | 85 +- .../application/TokenUnitConversions.java | 266 +- .../crypto/BouncyCastleKeyHandler.java | 141 +- .../crypto/ConcatKDFBytesGenerator.java | 159 +- .../com/radixdlt/crypto/ECDSASignature.java | 416 +-- .../java/com/radixdlt/crypto/ECIESCoder.java | 220 +- .../java/com/radixdlt/crypto/ECKeyPair.java | 292 +- .../java/com/radixdlt/crypto/ECKeyUtils.java | 596 ++- .../crypto/ECMultiplicationScalar.java | 2 +- .../java/com/radixdlt/crypto/ECPublicKey.java | 209 +- .../java/com/radixdlt/crypto/HashHandler.java | 233 +- .../java/com/radixdlt/crypto/HashUtils.java | 392 +- .../java/com/radixdlt/crypto/IESEngine.java | 645 ++-- .../java/com/radixdlt/crypto/KeyHandler.java | 263 +- .../radixdlt/crypto/LinuxSecureRandom.java | 181 +- .../crypto/MGF1BytesGeneratorExt.java | 110 +- .../com/radixdlt/crypto/RadixKeyStore.java | 606 +-- .../com/radixdlt/crypto/SHAHashHandler.java | 110 +- .../com/radixdlt/crypto/SignatureScheme.java | 19 +- .../java/com/radixdlt/crypto/Signing.java | 34 +- .../crypto/exception/CryptoException.java | 165 +- .../crypto/exception/KeyStoreException.java | 150 +- .../crypto/exception/PrivateKeyException.java | 142 +- .../crypto/exception/PublicKeyException.java | 150 +- .../java/com/radixdlt/errors/ApiErrors.java | 139 +- .../java/com/radixdlt/errors/Category.java | 35 +- .../com/radixdlt/errors/ClientErrors.java | 48 +- .../com/radixdlt/errors/InternalErrors.java | 44 +- .../com/radixdlt/errors/ProtocolErrors.java | 43 +- .../java/com/radixdlt/identifiers/AID.java | 493 ++- .../identifiers/AccountAddressing.java | 136 +- .../java/com/radixdlt/identifiers/EUID.java | 626 ++-- .../radixdlt/identifiers/NodeAddressing.java | 151 +- .../java/com/radixdlt/identifiers/REAddr.java | 524 +-- .../identifiers/ResourceAddressing.java | 103 +- .../identifiers/ValidatorAddressing.java | 96 +- .../com/radixdlt/networks/AddressType.java | 13 +- .../com/radixdlt/networks/Addressing.java | 181 +- .../java/com/radixdlt/networks/Network.java | 165 +- .../java/com/radixdlt/networks/NetworkId.java | 18 +- .../ApiSerializationModifier.java | 67 +- .../ClassScanningSerializationPolicy.java | 515 +-- .../ClassScanningSerializerIds.java | 412 +-- .../serialization/DeserializeException.java | 60 +- .../serialization/DsonAnyProperties.java | 2 +- .../radixdlt/serialization/DsonOutput.java | 179 +- .../radixdlt/serialization/JsonJavaType.java | 19 +- .../com/radixdlt/serialization/MapHelper.java | 531 +-- .../radixdlt/serialization/Polymorphic.java | 4 +- .../radixdlt/serialization/Serialization.java | 548 +-- .../serialization/SerializationPolicy.java | 21 +- .../serialization/SerializationUtils.java | 171 +- .../serialization/SerializeWithHid.java | 13 +- .../serialization/SerializerConstants.java | 22 +- .../serialization/SerializerDummy.java | 11 +- .../radixdlt/serialization/SerializerId2.java | 15 +- .../radixdlt/serialization/SerializerIds.java | 53 +- .../serialization/SerializerIdsException.java | 60 +- .../serialization/SerializerRoot.java | 7 +- .../ClasspathScanningSerializationPolicy.java | 229 +- .../core/ClasspathScanningSerializerIds.java | 223 +- .../serialization/mapper/DsonFieldFilter.java | 90 +- .../mapper/DsonFilteringIntrospector.java | 22 +- .../mapper/DsonTypeIdResolver.java | 62 +- .../mapper/DsonTypeResolverBuilder.java | 69 +- .../mapper/JacksonCborMapper.java | 275 +- .../JacksonCborObjectBytesDeserializer.java | 35 +- .../JacksonCborObjectBytesSerializer.java | 31 +- .../mapper/JacksonCodecConstants.java | 52 +- .../mapper/JacksonJsonBytesDeserializer.java | 35 +- .../mapper/JacksonJsonBytesSerializer.java | 28 +- .../JacksonJsonHashCodeDeserializer.java | 35 +- .../mapper/JacksonJsonHashCodeSerializer.java | 28 +- .../mapper/JacksonJsonMapper.java | 279 +- .../JacksonJsonObjectStringDeserializer.java | 34 +- .../JacksonJsonObjectStringSerializer.java | 29 +- .../mapper/JacksonJsonStringDeserializer.java | 33 +- .../mapper/JacksonJsonStringSerializer.java | 28 +- .../JacksonSerializerDummyDeserializer.java | 30 +- .../JacksonSerializerDummySerializer.java | 57 +- .../serialization/mapper/MapperConstants.java | 8 +- .../serialization/mapper/PackageVersion.java | 13 +- .../mapper/RadixCBORFactory.java | 827 ++--- .../mapper/RadixCBORGenerator.java | 3252 ++++++++--------- .../mapper/RadixObjectMapperConfigurator.java | 52 +- .../com/radixdlt/utils/AWSSecretManager.java | 633 ++-- .../utils/AWSSecretsOutputOptions.java | 36 +- .../main/java/com/radixdlt/utils/Base58.java | 389 +- .../main/java/com/radixdlt/utils/Bits.java | 73 +- .../main/java/com/radixdlt/utils/Bytes.java | 397 +- .../java/com/radixdlt/utils/Instants.java | 29 +- .../main/java/com/radixdlt/utils/Ints.java | 341 +- .../com/radixdlt/utils/KeyComparator.java | 14 +- .../main/java/com/radixdlt/utils/Longs.java | 377 +- .../main/java/com/radixdlt/utils/Offset.java | 18 +- .../src/main/java/com/radixdlt/utils/POW.java | 223 +- .../main/java/com/radixdlt/utils/Pair.java | 289 +- .../java/com/radixdlt/utils/PrivateKeys.java | 38 +- .../com/radixdlt/utils/RadixConstants.java | 10 +- .../java/com/radixdlt/utils/RuntimeUtils.java | 26 +- .../main/java/com/radixdlt/utils/Shorts.java | 191 +- .../main/java/com/radixdlt/utils/Strings.java | 87 +- .../main/java/com/radixdlt/utils/UInt128.java | 1604 ++++---- .../main/java/com/radixdlt/utils/UInt256.java | 1699 +++++---- .../java/com/radixdlt/utils/UInt256s.java | 274 +- .../main/java/com/radixdlt/utils/UInt384.java | 1644 +++++---- .../java/com/radixdlt/utils/UIntUtils.java | 326 +- .../radixdlt/utils/functional/Failure.java | 163 +- .../utils/functional/FunctionalUtils.java | 223 +- .../radixdlt/utils/functional/Functions.java | 284 +- .../radixdlt/utils/functional/Optionals.java | 474 +-- .../radixdlt/utils/functional/Promise.java | 210 +- .../com/radixdlt/utils/functional/Result.java | 1222 ++++--- .../utils/functional/ThrowingSupplier.java | 2 +- .../com/radixdlt/utils/functional/Tuple.java | 990 ++--- .../java/com/radixdlt/TestSetupUtils.java | 263 +- .../radixdlt/crypto/ECDSASignatureTest.java | 350 +- .../com/radixdlt/crypto/ECKeyPairTest.java | 356 +- .../com/radixdlt/crypto/ECKeyUtilsTest.java | 327 +- .../com/radixdlt/crypto/ECPublicKeyTest.java | 26 +- .../com/radixdlt/crypto/HashUtilsTest.java | 343 +- .../radixdlt/crypto/RadixKeyStoreTest.java | 413 +-- .../radixdlt/crypto/SHAHashHandlerTest.java | 286 +- .../com/radixdlt/crypto/SecureRandomTest.java | 23 +- .../com/radixdlt/errors/ApiErrorsTest.java | 43 +- .../com/radixdlt/errors/ClientErrorsTest.java | 19 +- .../radixdlt/errors/InternalErrorsTest.java | 18 +- .../com/radixdlt/identifiers/AIDTest.java | 251 +- .../identifiers/AccountAddressingTest.java | 150 +- .../com/radixdlt/identifiers/EUIDTest.java | 307 +- .../identifiers/ResourceAddressingTest.java | 202 +- .../identifiers/ValidatorAddressingTest.java | 101 +- .../InterfaceSerializationTest.java | 267 +- .../radixdlt/middleware2/TestClientAtom.java | 284 +- .../middleware2/TestDifferentClientAtom.java | 284 +- .../TestEmbeddedInterfaceAtom.java | 168 +- .../middleware2/TestExtendedClientAtom.java | 247 +- .../radixdlt/middleware2/TestLedgerAtom.java | 141 +- .../ApiSerializationModifierTest.java | 148 +- .../serialization/CBORLengthTest.java | 38 +- .../ECDSASignatureSerializationTest.java | 95 +- .../radixdlt/serialization/MapHelperTest.java | 112 +- .../SerializationTestUtilsEngine.java | 112 +- .../serialization/SerializeObjectEngine.java | 153 +- .../RadixObjectMapperConfiguratorTest.java | 480 ++- .../java/com/radixdlt/utils/BytesTest.java | 367 +- .../java/com/radixdlt/utils/InstantsTest.java | 33 +- .../java/com/radixdlt/utils/LongsTest.java | 18 +- .../java/com/radixdlt/utils/PairTest.java | 143 +- .../java/com/radixdlt/utils/UInt128Test.java | 1103 +++--- .../java/com/radixdlt/utils/UInt256Test.java | 1125 +++--- .../java/com/radixdlt/utils/UInt256sTest.java | 407 +-- .../java/com/radixdlt/utils/UInt384Test.java | 1155 +++--- .../com/radixdlt/utils/UIntUtilsTest.java | 172 +- .../radixdlt/utils/functional/ResultTest.java | 27 +- .../java/com/radixdlt/cloud/AWSSecrets.java | 598 +-- .../radixdlt/identity/LocalRadixIdentity.java | 14 +- .../client/lib/api/AccountAddress.java | 71 +- .../radixdlt/client/lib/api/ActionType.java | 67 +- .../radixdlt/client/lib/api/EventType.java | 39 +- .../client/lib/api/NavigationCursor.java | 59 +- .../radixdlt/client/lib/api/NodeAddress.java | 65 +- .../client/lib/api/TransactionRequest.java | 307 +- .../radixdlt/client/lib/api/TxTimestamp.java | 73 +- .../client/lib/api/ValidatorAddress.java | 65 +- .../client/lib/api/action/Action.java | 3 +- .../client/lib/api/action/BurnAction.java | 18 +- .../api/action/CreateFixedTokenAction.java | 55 +- .../api/action/CreateMutableTokenAction.java | 39 +- .../client/lib/api/action/MintAction.java | 18 +- .../api/action/RegisterValidatorAction.java | 18 +- .../client/lib/api/action/StakeAction.java | 70 +- .../client/lib/api/action/TransferAction.java | 22 +- .../api/action/UnregisterValidatorAction.java | 18 +- .../client/lib/api/action/UnstakeAction.java | 18 +- ...ateValidatorAllowDelegationFlagAction.java | 15 +- .../api/action/UpdateValidatorFeeAction.java | 14 +- .../action/UpdateValidatorMetadataAction.java | 18 +- .../action/UpdateValidatorOwnerAction.java | 14 +- .../client/lib/api/async/AsyncRadixApi.java | 789 ++-- .../client/lib/api/async/RadixApi.java | 731 ++-- .../client/lib/api/rpc/BasicAuth.java | 56 +- .../radixdlt/client/lib/api/rpc/EndPoint.java | 117 +- .../client/lib/api/rpc/ErrorInfo.java | 101 +- .../client/lib/api/rpc/JsonRpcRequest.java | 124 +- .../client/lib/api/rpc/JsonRpcResponse.java | 145 +- .../client/lib/api/rpc/PortSelector.java | 4 +- .../client/lib/api/rpc/RadixApiBase.java | 348 +- .../client/lib/api/rpc/RpcMethod.java | 164 +- .../lib/api/sync/ImperativeRadixApi.java | 1230 +++---- .../client/lib/api/sync/RadixApi.java | 731 ++-- .../lib/api/sync/RadixApiException.java | 6 +- .../client/lib/api/sync/SyncRadixApi.java | 879 ++--- .../radixdlt/client/lib/api/token/Amount.java | 34 +- .../client/lib/dto/AccountBalance.java | 83 +- .../com/radixdlt/client/lib/dto/Action.java | 220 +- .../client/lib/dto/AddressBookEntry.java | 103 +- .../client/lib/dto/ApiConfiguration.java | 56 +- .../com/radixdlt/client/lib/dto/ApiData.java | 68 +- .../radixdlt/client/lib/dto/ApiDataCount.java | 63 +- .../client/lib/dto/ApiDataElapsed.java | 62 +- .../radixdlt/client/lib/dto/ApiDbCount.java | 169 +- .../radixdlt/client/lib/dto/ApiDbElapsed.java | 111 +- .../com/radixdlt/client/lib/dto/Balance.java | 84 +- .../client/lib/dto/BalanceStakes.java | 84 +- .../client/lib/dto/BuiltTransaction.java | 113 +- .../radixdlt/client/lib/dto/ChannelType.java | 24 +- .../radixdlt/client/lib/dto/Checkpoint.java | 76 +- .../lib/dto/ConsensusConfiguration.java | 80 +- .../client/lib/dto/ConsensusData.java | 374 +- .../client/lib/dto/ConsensusDataSync.java | 76 +- .../com/radixdlt/client/lib/dto/Count.java | 61 +- .../client/lib/dto/CurrentEpochInfo.java | 253 +- .../client/lib/dto/DelegatedStake.java | 76 +- .../radixdlt/client/lib/dto/EpochData.java | 62 +- .../radixdlt/client/lib/dto/EpochInfo.java | 79 +- .../client/lib/dto/EpochValidatorData.java | 174 +- .../com/radixdlt/client/lib/dto/Event.java | 88 +- .../com/radixdlt/client/lib/dto/FeeTable.java | 80 +- .../client/lib/dto/FinalizedTransaction.java | 274 +- .../radixdlt/client/lib/dto/ForkDetails.java | 122 +- .../lib/dto/ForkDetailsConfiguration.java | 340 +- .../radixdlt/client/lib/dto/KnownAddress.java | 108 +- .../radixdlt/client/lib/dto/LocalAccount.java | 76 +- .../client/lib/dto/LocalValidatorInfo.java | 170 +- .../client/lib/dto/MempoolConfiguration.java | 76 +- .../radixdlt/client/lib/dto/MempoolData.java | 190 +- .../client/lib/dto/MempoolDataErrors.java | 88 +- .../client/lib/dto/NetworkChannel.java | 145 +- .../client/lib/dto/NetworkConfiguration.java | 348 +- .../radixdlt/client/lib/dto/NetworkData.java | 76 +- .../client/lib/dto/NetworkDataMessages.java | 77 +- .../lib/dto/NetworkDataMessagesInbound.java | 95 +- .../lib/dto/NetworkDataMessagesOutbound.java | 112 +- .../client/lib/dto/NetworkDataNetworking.java | 116 +- .../lib/dto/NetworkDataNetworkingTcp.java | 117 +- .../lib/dto/NetworkDataNetworkingUdp.java | 64 +- .../radixdlt/client/lib/dto/NetworkId.java | 54 +- .../radixdlt/client/lib/dto/NetworkPeer.java | 79 +- .../radixdlt/client/lib/dto/NetworkStats.java | 61 +- .../radixdlt/client/lib/dto/Notification.java | 93 +- .../client/lib/dto/PerUpSubstateFee.java | 265 +- .../com/radixdlt/client/lib/dto/Proof.java | 90 +- .../radixdlt/client/lib/dto/ProofHeader.java | 194 +- .../client/lib/dto/RadixEngineData.java | 102 +- .../radixdlt/client/lib/dto/ReadWrite.java | 76 +- .../client/lib/dto/ReadWriteStats.java | 103 +- .../client/lib/dto/SignatureDetails.java | 91 +- .../com/radixdlt/client/lib/dto/Size.java | 61 +- .../client/lib/dto/StakePositions.java | 89 +- .../client/lib/dto/SyncConfiguration.java | 180 +- .../com/radixdlt/client/lib/dto/SyncData.java | 210 +- .../com/radixdlt/client/lib/dto/TimeDTO.java | 61 +- .../client/lib/dto/TokenBalances.java | 84 +- .../radixdlt/client/lib/dto/TokenInfo.java | 359 +- .../client/lib/dto/TransactionDTO.java | 262 +- .../client/lib/dto/TransactionHistory.java | 135 +- .../client/lib/dto/TransactionStatus.java | 8 +- .../client/lib/dto/TransactionStatusDTO.java | 84 +- .../client/lib/dto/TransactionsDTO.java | 137 +- .../com/radixdlt/client/lib/dto/TxBlob.java | 138 +- .../radixdlt/client/lib/dto/TxBlobDTO.java | 109 +- .../com/radixdlt/client/lib/dto/TxDTO.java | 67 +- .../client/lib/dto/UnstakePositions.java | 183 +- .../com/radixdlt/client/lib/dto/Updates.java | 183 +- .../radixdlt/client/lib/dto/ValidatorDTO.java | 362 +- .../client/lib/dto/ValidatorEntry.java | 75 +- .../client/lib/dto/ValidatorsResponse.java | 82 +- .../AccountAddressDeserializer.java | 22 +- .../serializer/AccountAddressSerializer.java | 20 +- .../serializer/ECPublicKeyDeserializer.java | 26 +- .../dto/serializer/ECPublicKeySerializer.java | 16 +- .../serializer/NodeAddressDeserializer.java | 30 +- .../dto/serializer/NodeAddressSerializer.java | 20 +- .../ValidatorAddressDeserializer.java | 22 +- .../ValidatorAddressSerializer.java | 20 +- .../addressing/AddressSerializationTest.java | 72 +- .../api/async/AsyncRadixApiAccountTest.java | 368 +- .../lib/api/async/AsyncRadixApiApiTest.java | 126 +- .../api/async/AsyncRadixApiConsensusTest.java | 114 +- .../api/async/AsyncRadixApiCreationTest.java | 490 +-- .../AsyncRadixApiHistoryPaginationTest.java | 182 +- .../lib/api/async/AsyncRadixApiLocalTest.java | 364 +- .../api/async/AsyncRadixApiMempoolTest.java | 110 +- .../lib/api/async/AsyncRadixApiSyncTest.java | 111 +- .../lib/api/async/AsyncRadixApiTest.java | 485 ++- .../lib/api/async/AsyncRadixApiTokenTest.java | 104 +- ...syncRadixApiTransactionPaginationTest.java | 87 +- .../lib/api/sync/SyncRadixApiAccountTest.java | 379 +- .../lib/api/sync/SyncRadixApiApiTest.java | 114 +- .../api/sync/SyncRadixApiConsensusTest.java | 104 +- .../api/sync/SyncRadixApiCreationTest.java | 505 +-- .../SyncRadixApiHistoryPaginationTest.java | 188 +- .../lib/api/sync/SyncRadixApiLocalTest.java | 343 +- .../lib/api/sync/SyncRadixApiMempoolTest.java | 101 +- .../lib/api/sync/SyncRadixApiSyncTest.java | 101 +- .../client/lib/api/sync/SyncRadixApiTest.java | 442 ++- .../lib/api/sync/SyncRadixApiTokenTest.java | 112 +- ...SyncRadixApiTransactionPaginationTest.java | 85 +- .../com/radixdlt/client/lib/dto/DtoTest.java | 735 ++-- .../com/radixdlt/test/util/TypedMocks.java | 39 +- radixdlt-regression/README.md | 8 +- .../com/radixdlt/test/RadixNetworkTest.java | 207 +- .../com/radixdlt/test/account/Account.java | 489 ++- .../radixdlt/test/account/AsyncAccount.java | 357 +- .../radixdlt/test/account/RadixAccount.java | 95 +- .../com/radixdlt/test/crypto/BIP32Path.java | 168 +- .../test/crypto/BitcoinJBIP32Path.java | 296 +- .../crypto/BitcoinJHDKeyPairDerivation.java | 161 +- .../BitcoinJMnemonicToSeedConverter.java | 195 +- .../crypto/DefaultHDKeyPairDerivation.java | 257 +- .../radixdlt/test/crypto/DefaultHDPath.java | 96 +- .../DefaultMnemonicToSeedConverter.java | 234 +- .../com/radixdlt/test/crypto/HDKeyPair.java | 198 +- .../test/crypto/HDKeyPairDerivation.java | 116 +- .../java/com/radixdlt/test/crypto/HDPath.java | 184 +- .../com/radixdlt/test/crypto/HDPaths.java | 190 +- .../test/crypto/errors/HDPathException.java | 70 +- .../test/crypto/errors/MnemonicException.java | 74 +- .../test/network/DockerConfiguration.java | 195 +- .../test/network/FaucetException.java | 75 +- .../radixdlt/test/network/RadixNetwork.java | 314 +- .../network/RadixNetworkConfiguration.java | 322 +- .../test/network/RadixNetworkNodeLocator.java | 351 +- .../com/radixdlt/test/network/RadixNode.java | 175 +- .../test/network/SshConfiguration.java | 141 +- .../radixdlt/test/network/checks/Check.java | 67 +- .../network/checks/CheckFailureException.java | 78 +- .../radixdlt/test/network/checks/Checks.java | 116 +- .../test/network/checks/EpochView.java | 109 +- .../test/network/checks/LivenessCheck.java | 161 +- .../test/network/client/HttpException.java | 82 +- .../radixdlt/test/network/client/Metrics.java | 128 +- .../test/network/client/RadixHttpClient.java | 242 +- .../client/docker/DisabledDockerClient.java | 105 +- .../network/client/docker/DockerClient.java | 87 +- .../client/docker/DockerNetworkCreator.java | 650 ++-- .../client/docker/LocalDockerClient.java | 257 +- .../client/docker/RemoteDockerClient.java | 283 +- .../test/utils/TestFailureException.java | 81 +- .../com/radixdlt/test/utils/TestingUtils.java | 400 +- .../radixdlt/test/utils/TransactionUtils.java | 426 ++- .../test/utils/universe/UniverseUtils.java | 169 +- .../utils/universe/UniverseVariables.java | 97 +- .../test/utils/universe/ValidatorKeypair.java | 97 +- .../com/radixdlt/TokenCreationProperties.java | 160 +- .../com/radixdlt/acceptance/fees/Fees.java | 230 +- .../com/radixdlt/acceptance/fees/RunFees.java | 11 +- .../FixedSupplyTokens.java | 176 +- .../RunFixedSupplyTokens.java | 12 +- .../acceptance/messaging/Messaging.java | 151 +- .../acceptance/messaging/RunMessaging.java | 9 +- .../MultiActionTransactions.java | 165 +- .../RunMultiActionTransactions.java | 7 +- .../MutableSupplyTokens.java | 174 +- .../RunMutableSupplyTokens.java | 12 +- .../acceptance/staking/RunStaking.java | 9 +- .../radixdlt/acceptance/staking/Staking.java | 207 +- .../RunTokenConstraints.java | 11 +- .../token_constraints/TokenConstraints.java | 115 +- .../token_transfer/RunTokenTransfer.java | 9 +- .../token_transfer/TokenTransfer.java | 215 +- .../RunTransactionLookup.java | 7 +- .../transaction_lookup/TransactionLookup.java | 231 +- .../com/radixdlt/assertions/Assertions.java | 161 +- radixdlt-regression/system-tests/README.md | 18 +- radixdlt-regression/system-tests/build.gradle | 1 - .../test/TransactionHistoryCreator.java | 80 +- .../chaos/ansible/AnsibleImageWrapper.java | 226 +- .../test/chaos/ansible/CmdHelper.java | 81 +- .../com/radixdlt/test/system/SmokeTests.java | 81 +- .../test/system/TransactionsTests.java | 366 +- .../test/system/harness/SystemTest.java | 119 +- 1474 files changed, 105182 insertions(+), 99520 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 92ef233e2c..935425af41 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -74,4 +74,3 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd97aff7b5..8baa8a3b14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,13 +11,13 @@ - [Features](#features) - [Release candidates](#release-candidates) - [Releases](#releases) - - [Hotfixes](#hotfixes) + - [Hotfixes](#hotfixes) - [Contribute](#contribute) - [Code style](#code-style) - [Code structure](#code-structure) - [Commit messages](#commit-messages) - [Opening a pull request](#opening-a-pull-request) - + ## Code of conduct @@ -32,7 +32,7 @@ Please report unacceptable behavior to [hello@radixdlt.com](mailto:hello@radixdl * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/radixdlt/radixdlt-parent/issues). * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/radixdlt/radixdlt-parent/issues/new). Be sure to include: * a **title**, - * a **clear description**, + * a **clear description**, * as much **relevant information** as possible, * a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. @@ -85,7 +85,7 @@ When QA gives the green light, a new release branch is created These branches will stay alive forever, or at least while we support the release, thereby allowing us to release security hotfixes for older versions. -If QA discovers a bug with any of the features before a release happens, it is fixed in the feature branch taken from the release branch and then merged into the release again. +If QA discovers a bug with any of the features before a release happens, it is fixed in the feature branch taken from the release branch and then merged into the release again. These changes should immediately be propagated to the current release candidate branch. diff --git a/build.gradle b/build.gradle index 9c54c0eee5..97883f07f4 100644 --- a/build.gradle +++ b/build.gradle @@ -147,7 +147,7 @@ allprojects { } java { // don't need to set target, it is inferred from java - + targetExclude('**/openapitools/**/*.java') // apply a specific flavor of google-java-format googleJavaFormat('1.13.0').reflowLongStrings() diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/FailOnEvent.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/FailOnEvent.java index 1163704935..e7e1a1f1b2 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/FailOnEvent.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/FailOnEvent.java @@ -68,43 +68,42 @@ import com.google.inject.Module; import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.environment.EventProcessorOnDispatch; - import java.util.Optional; import java.util.function.Function; public final class FailOnEvent { - private FailOnEvent() { - throw new IllegalStateException("Cannot instantiate"); - } + private FailOnEvent() { + throw new IllegalStateException("Cannot instantiate"); + } - public static Module asModule(Class eventClass) { - return new AbstractModule() { - @ProvidesIntoSet - EventProcessorOnDispatch failOnEvent() { - return new EventProcessorOnDispatch<>( - eventClass, - i -> { - throw new IllegalStateException("Invalid event: " + i); - } - ); - }; - }; - } + public static Module asModule(Class eventClass) { + return new AbstractModule() { + @ProvidesIntoSet + EventProcessorOnDispatch failOnEvent() { + return new EventProcessorOnDispatch<>( + eventClass, + i -> { + throw new IllegalStateException("Invalid event: " + i); + }); + } + ; + }; + } - public static Module asModule(Class eventClass, Function> mapper) { - return new AbstractModule() { - @ProvidesIntoSet - EventProcessorOnDispatch failOnEvent() { - return new EventProcessorOnDispatch<>( - eventClass, - i -> { - var maybeError = mapper.apply(i); - if (maybeError.isPresent()) { - throw new IllegalStateException("Invalid event: " + i, maybeError.get()); - } - } - ); - }; - }; - } + public static Module asModule(Class eventClass, Function> mapper) { + return new AbstractModule() { + @ProvidesIntoSet + EventProcessorOnDispatch failOnEvent() { + return new EventProcessorOnDispatch<>( + eventClass, + i -> { + var maybeError = mapper.apply(i); + if (maybeError.isPresent()) { + throw new IllegalStateException("Invalid event: " + i, maybeError.get()); + } + }); + } + ; + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ActorConfiguration.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ActorConfiguration.java index d984941b29..5b9b48f13f 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ActorConfiguration.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ActorConfiguration.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,28 +67,30 @@ import java.util.function.Supplier; /** - * Configuration which describes how often an actor will be called upon to act in a deterministic test. + * Configuration which describes how often an actor will be called upon to act in a deterministic + * test. */ public final class ActorConfiguration { - private final Supplier actorSupplier; - private final int numerator; - private final int denominator; + private final Supplier actorSupplier; + private final int numerator; + private final int denominator; - public ActorConfiguration(Supplier actorSupplier, int numerator, int denominator) { - this.actorSupplier = actorSupplier; - this.numerator = numerator; - this.denominator = denominator; - } + public ActorConfiguration( + Supplier actorSupplier, int numerator, int denominator) { + this.actorSupplier = actorSupplier; + this.numerator = numerator; + this.denominator = denominator; + } - public int getNumerator() { - return numerator; - } + public int getNumerator() { + return numerator; + } - public int getDenominator() { - return denominator; - } + public int getDenominator() { + return denominator; + } - public DeterministicActor createActor() { - return actorSupplier.get(); - } + public DeterministicActor createActor() { + return actorSupplier.get(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ApiTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ApiTest.java index 676338a0fc..fb222ea166 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ApiTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/ApiTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,33 +65,6 @@ package com.radixdlt.integration.api; import com.google.common.collect.EvictingQueue; -import com.google.inject.Scopes; -import com.google.inject.multibindings.Multibinder; -import com.radixdlt.api.core.reconstruction.BerkeleyRecoverableProcessedTxnStore; -import com.radixdlt.environment.Environment; -import com.radixdlt.environment.deterministic.LastEventsModule; -import com.radixdlt.integration.FailOnEvent; -import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; -import com.radixdlt.integration.api.actors.ApiBalanceToRadixEngineChecker; -import com.radixdlt.integration.api.actors.ApiTxnSubmitter; -import com.radixdlt.integration.api.actors.BalanceReconciler; -import com.radixdlt.integration.api.actors.NativeTokenRewardsChecker; -import com.radixdlt.integration.api.actors.RandomNodeRestarter; -import com.radixdlt.networks.Network; -import com.radixdlt.networks.NetworkId; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.store.berkeley.BerkeleyAdditionalStore; -import com.radixdlt.utils.PrivateKeys; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import com.google.common.collect.ImmutableList; import com.google.common.collect.Streams; import com.google.inject.AbstractModule; @@ -99,33 +73,49 @@ import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.multibindings.Multibinder; import com.google.inject.util.Modules; import com.radixdlt.PersistedNodeForTestingModule; +import com.radixdlt.api.core.reconstruction.BerkeleyRecoverableProcessedTxnStore; +import com.radixdlt.application.system.FeeTable; import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.environment.Environment; import com.radixdlt.environment.EventDispatcher; +import com.radixdlt.environment.deterministic.LastEventsModule; +import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; +import com.radixdlt.integration.FailOnEvent; +import com.radixdlt.integration.api.actors.ApiBalanceToRadixEngineChecker; +import com.radixdlt.integration.api.actors.ApiTxnSubmitter; +import com.radixdlt.integration.api.actors.BalanceReconciler; +import com.radixdlt.integration.api.actors.NativeTokenRewardsChecker; +import com.radixdlt.integration.api.actors.RandomNodeRestarter; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.mempool.MempoolRelayTrigger; import com.radixdlt.network.p2p.PeersView; +import com.radixdlt.networks.Network; +import com.radixdlt.networks.NetworkId; import com.radixdlt.statecomputer.InvalidProposedTxn; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; -import com.radixdlt.application.system.FeeTable; import com.radixdlt.statecomputer.forks.ForkOverwritesWithShorterEpochsModule; import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.DatabaseLocation; +import com.radixdlt.store.berkeley.BerkeleyAdditionalStore; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; import com.radixdlt.sync.messages.local.SyncCheckTrigger; - +import com.radixdlt.utils.PrivateKeys; import java.util.Collection; import java.util.List; import java.util.Map; @@ -133,198 +123,200 @@ import java.util.Random; import java.util.function.Supplier; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; /** - * Test which runs a 20 node consensus network. Random transactions are submitted and nodes - * are rebooted while checks occur on the api to make sure that invariants are not broken. + * Test which runs a 20 node consensus network. Random transactions are submitted and nodes are + * rebooted while checks occur on the api to make sure that invariants are not broken. */ @RunWith(Parameterized.class) public class ApiTest { - private static final Logger logger = LogManager.getLogger(); - private static final int ACTION_ROUNDS = 5000; - private static final RERulesConfig config = RERulesConfig.testingDefault().overrideMaxSigsPerRound(2); - private static final Amount PER_BYTE_FEE = Amount.ofMicroTokens(2); - private static final List ACTOR_CONFIGURATIONS = List.of( - new ActorConfiguration(ApiTxnSubmitter::new, 1, 2), - new ActorConfiguration(BalanceReconciler::new, 1, 10), - new ActorConfiguration(RandomNodeRestarter::new, 1, 10), - new ActorConfiguration(NativeTokenRewardsChecker::new, 1, 100), - new ActorConfiguration(ApiBalanceToRadixEngineChecker::new, 1, 200) - ); + private static final Logger logger = LogManager.getLogger(); + private static final int ACTION_ROUNDS = 5000; + private static final RERulesConfig config = + RERulesConfig.testingDefault().overrideMaxSigsPerRound(2); + private static final Amount PER_BYTE_FEE = Amount.ofMicroTokens(2); + private static final List ACTOR_CONFIGURATIONS = + List.of( + new ActorConfiguration(ApiTxnSubmitter::new, 1, 2), + new ActorConfiguration(BalanceReconciler::new, 1, 10), + new ActorConfiguration(RandomNodeRestarter::new, 1, 10), + new ActorConfiguration(NativeTokenRewardsChecker::new, 1, 100), + new ActorConfiguration(ApiBalanceToRadixEngineChecker::new, 1, 200)); - @Parameterized.Parameters - public static Collection forksModule() { - return List.of(new Object[][]{ - {new RadixEngineForksLatestOnlyModule(config.overrideMaxRounds(100)), null}, - {new ForkOverwritesWithShorterEpochsModule(config), null}, - { - new ForkOverwritesWithShorterEpochsModule(config), - new ForkOverwritesWithShorterEpochsModule(config.removeSigsPerRoundLimit()) - }, - { - new RadixEngineForksLatestOnlyModule( - config.overrideMaxRounds(100).overrideFeeTable( - FeeTable.create( - PER_BYTE_FEE, - Map.of() - ) - ) - ), null}, - { - new ForkOverwritesWithShorterEpochsModule( - config.overrideFeeTable( - FeeTable.create( - PER_BYTE_FEE, - Map.of() - ) - ) - ), null}, - }); - } + @Parameterized.Parameters + public static Collection forksModule() { + return List.of( + new Object[][] { + {new RadixEngineForksLatestOnlyModule(config.overrideMaxRounds(100)), null}, + {new ForkOverwritesWithShorterEpochsModule(config), null}, + { + new ForkOverwritesWithShorterEpochsModule(config), + new ForkOverwritesWithShorterEpochsModule(config.removeSigsPerRoundLimit()) + }, + { + new RadixEngineForksLatestOnlyModule( + config + .overrideMaxRounds(100) + .overrideFeeTable(FeeTable.create(PER_BYTE_FEE, Map.of()))), + null + }, + { + new ForkOverwritesWithShorterEpochsModule( + config.overrideFeeTable(FeeTable.create(PER_BYTE_FEE, Map.of()))), + null + }, + }); + } - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - private DeterministicNetwork network; - private final ImmutableList nodeKeys; - private final Module radixEngineConfiguration; - private final Module byzantineModule; - private MultiNodeDeterministicRunner deterministicRunner; + @Rule public TemporaryFolder folder = new TemporaryFolder(); + private DeterministicNetwork network; + private final ImmutableList nodeKeys; + private final Module radixEngineConfiguration; + private final Module byzantineModule; + private MultiNodeDeterministicRunner deterministicRunner; - public ApiTest(Module forkModule, Module byzantineModule) { - this.nodeKeys = PrivateKeys.numeric(1) - .limit(20) - .collect(ImmutableList.toImmutableList()); - this.radixEngineConfiguration = Modules.combine( - new MainnetForkConfigsModule(), - new ForksModule(), - forkModule - ); - this.byzantineModule = byzantineModule; - } + public ApiTest(Module forkModule, Module byzantineModule) { + this.nodeKeys = PrivateKeys.numeric(1).limit(20).collect(ImmutableList.toImmutableList()); + this.radixEngineConfiguration = + Modules.combine(new MainnetForkConfigsModule(), new ForksModule(), forkModule); + this.byzantineModule = byzantineModule; + } - @Before - public void setup() { - this.network = new DeterministicNetwork( - nodeKeys.stream().map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()), - MessageSelector.firstSelector(), - MessageMutator.nothing() - ); + @Before + public void setup() { + this.network = + new DeterministicNetwork( + nodeKeys.stream() + .map(k -> BFTNode.create(k.getPublicKey())) + .collect(Collectors.toList()), + MessageSelector.firstSelector(), + MessageMutator.nothing()); - List allNodes = nodeKeys.stream() - .map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); - var nodeCreators = Streams.mapWithIndex(nodeKeys.stream(), (k, i) -> - (Supplier) () -> createRunner(i == 1, k, allNodes)).collect(Collectors.toList()); + List allNodes = + nodeKeys.stream().map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); + var nodeCreators = + Streams.mapWithIndex( + nodeKeys.stream(), + (k, i) -> (Supplier) () -> createRunner(i == 1, k, allNodes)) + .collect(Collectors.toList()); - deterministicRunner = new MultiNodeDeterministicRunner( - nodeCreators, - this::stopDatabase, - network - ); - deterministicRunner.start(); - } + deterministicRunner = + new MultiNodeDeterministicRunner(nodeCreators, this::stopDatabase, network); + deterministicRunner.start(); + } - private void stopDatabase(Injector injector) { - injector.getInstance(BerkeleyLedgerEntryStore.class).close(); - injector.getInstance(PersistentSafetyStateStore.class).close(); - injector.getInstance(DatabaseEnvironment.class).stop(); - } + private void stopDatabase(Injector injector) { + injector.getInstance(BerkeleyLedgerEntryStore.class).close(); + injector.getInstance(PersistentSafetyStateStore.class).close(); + injector.getInstance(DatabaseEnvironment.class).stop(); + } - @After - public void teardown() { - deterministicRunner.tearDown(); - } + @After + public void teardown() { + deterministicRunner.tearDown(); + } - private Injector createRunner(boolean byzantine, ECKeyPair ecKeyPair, List allNodes) { - var reConfig = byzantine && byzantineModule != null - ? Modules.override(this.radixEngineConfiguration).with(byzantineModule) - : this.radixEngineConfiguration; + private Injector createRunner(boolean byzantine, ECKeyPair ecKeyPair, List allNodes) { + var reConfig = + byzantine && byzantineModule != null + ? Modules.override(this.radixEngineConfiguration).with(byzantineModule) + : this.radixEngineConfiguration; - return Guice.createInjector( - new MockedGenesisModule( - nodeKeys.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), - Amount.ofTokens(100000), - Amount.ofTokens(1000) - ), - MempoolConfig.asModule(10, 10), - reConfig, - new PersistedNodeForTestingModule(), - new LastEventsModule(LedgerUpdate.class), - FailOnEvent.asModule(InvalidProposedTxn.class), - new AbstractModule() { - @Override - protected void configure() { - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); - bind(Environment.class).toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); - bindConstant().annotatedWith(DatabaseLocation.class) - .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); - bindConstant().annotatedWith(NetworkId.class).to(Network.LOCALNET.getId()); - bind(BerkeleyRecoverableProcessedTxnStore.class).in(Scopes.SINGLETON); - Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class) - .addBinding().to(BerkeleyRecoverableProcessedTxnStore.class); - } + return Guice.createInjector( + new MockedGenesisModule( + nodeKeys.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), + Amount.ofTokens(100000), + Amount.ofTokens(1000)), + MempoolConfig.asModule(10, 10), + reConfig, + new PersistedNodeForTestingModule(), + new LastEventsModule(LedgerUpdate.class), + FailOnEvent.asModule(InvalidProposedTxn.class), + new AbstractModule() { + @Override + protected void configure() { + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); + bind(Environment.class) + .toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); + bindConstant().annotatedWith(NetworkId.class).to(Network.LOCALNET.getId()); + bind(BerkeleyRecoverableProcessedTxnStore.class).in(Scopes.SINGLETON); + Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class) + .addBinding() + .to(BerkeleyRecoverableProcessedTxnStore.class); + } - @Provides - private PeersView peersView(@Self BFTNode self) { - return () -> allNodes.stream() - .filter(n -> !self.equals(n)) - .map(PeersView.PeerInfo::fromBftNode); - } - } - ); - } + @Provides + private PeersView peersView(@Self BFTNode self) { + return () -> + allNodes.stream().filter(n -> !self.equals(n)).map(PeersView.PeerInfo::fromBftNode); + } + }); + } - private static class RunningActor { - private final DeterministicActor actor; - private final int numerator; - private final int denominator; - private final Queue lastResults = EvictingQueue.create(5); - private int numActions; + private static class RunningActor { + private final DeterministicActor actor; + private final int numerator; + private final int denominator; + private final Queue lastResults = EvictingQueue.create(5); + private int numActions; - RunningActor(DeterministicActor actor, int numerator, int denominator) { - this.actor = actor; - this.numerator = numerator; - this.denominator = denominator; - } + RunningActor(DeterministicActor actor, int numerator, int denominator) { + this.actor = actor; + this.numerator = numerator; + this.denominator = denominator; + } - void tryExecute(MultiNodeDeterministicRunner runner, Random random) throws Exception { - if (random.nextInt(denominator) < numerator) { - var result = actor.execute(runner, random); - lastResults.offer(result); - logger.info("Actor {} -> {}", actor.getClass().getSimpleName(), result); - numActions++; - } - } + void tryExecute(MultiNodeDeterministicRunner runner, Random random) throws Exception { + if (random.nextInt(denominator) < numerator) { + var result = actor.execute(runner, random); + lastResults.offer(result); + logger.info("Actor {} -> {}", actor.getClass().getSimpleName(), result); + numActions++; + } + } - void printLastResults() { - lastResults.forEach(result -> logger.info("\t{}", result)); - } - } + void printLastResults() { + lastResults.forEach(result -> logger.info("\t{}", result)); + } + } - /** - * TODO: Figure out why if run for long enough, # of validators - * trends to minimum. - */ - @Test - public void api_test() throws Exception { - var random = new Random(12345); + /** TODO: Figure out why if run for long enough, # of validators trends to minimum. */ + @Test + public void api_test() throws Exception { + var random = new Random(12345); - var actors = ACTOR_CONFIGURATIONS.stream() - .map(c -> new RunningActor(c.createActor(), c.getNumerator(), c.getDenominator())) - .collect(Collectors.toList()); + var actors = + ACTOR_CONFIGURATIONS.stream() + .map(c -> new RunningActor(c.createActor(), c.getNumerator(), c.getDenominator())) + .collect(Collectors.toList()); - for (int i = 0; i < ACTION_ROUNDS; i++) { - deterministicRunner.processForCount(100); - for (var actor : actors) { - actor.tryExecute(deterministicRunner, random); - } - deterministicRunner.dispatchToAll(new Key>() {}, MempoolRelayTrigger.create()); - deterministicRunner.dispatchToAll(new Key>() {}, SyncCheckTrigger.create()); - } + for (int i = 0; i < ACTION_ROUNDS; i++) { + deterministicRunner.processForCount(100); + for (var actor : actors) { + actor.tryExecute(deterministicRunner, random); + } + deterministicRunner.dispatchToAll( + new Key>() {}, MempoolRelayTrigger.create()); + deterministicRunner.dispatchToAll( + new Key>() {}, SyncCheckTrigger.create()); + } - logger.info("===Test Results==="); - for (var actor : actors) { - logger.info("Actor {} count={}", actor.actor.getClass().getSimpleName(), actor.numActions); - actor.printLastResults(); - } - } + logger.info("===Test Results==="); + for (var actor : actors) { + logger.info("Actor {} count={}", actor.actor.getClass().getSimpleName(), actor.numActions); + actor.printLastResults(); + } + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/DeterministicActor.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/DeterministicActor.java index fbde0d86b5..ccb7fcdcdf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/DeterministicActor.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/DeterministicActor.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,12 +65,9 @@ package com.radixdlt.integration.api; import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; - import java.util.Random; -/** - * Executes an actor in a deterministic test - */ +/** Executes an actor in a deterministic test */ public interface DeterministicActor { - String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception; + String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception; } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiBalanceToRadixEngineChecker.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiBalanceToRadixEngineChecker.java index 0b4b0ca005..4c4450406c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiBalanceToRadixEngineChecker.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiBalanceToRadixEngineChecker.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.integration.api.actors; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.api.core.model.CoreModelMapper; import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.crypto.ECKeyPair; @@ -70,53 +73,60 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.integration.api.DeterministicActor; import com.radixdlt.utils.PrivateKeys; - import java.math.BigInteger; import java.util.List; import java.util.Random; import java.util.stream.Collectors; -import static org.assertj.core.api.Assertions.assertThat; - /** - * Reads the balances from the api of all accounts and verifies that it matches - * the state in the Radix Engine. + * Reads the balances from the api of all accounts and verifies that it matches the state in the + * Radix Engine. */ public final class ApiBalanceToRadixEngineChecker implements DeterministicActor { - public List getAccountUnstakes(REAddr addr, NodeApiClient nodeClient) { - return PrivateKeys.numeric(1).limit(20) - .map(ECKeyPair::getPublicKey) - .flatMap(validatorKey -> nodeClient.getUnstakes(addr, validatorKey).stream()) - .collect(Collectors.toList()); - } + public List getAccountUnstakes(REAddr addr, NodeApiClient nodeClient) { + return PrivateKeys.numeric(1) + .limit(20) + .map(ECKeyPair::getPublicKey) + .flatMap(validatorKey -> nodeClient.getUnstakes(addr, validatorKey).stream()) + .collect(Collectors.toList()); + } - @Override - public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { - var injector = runner.getNode(0); - var nodeClient = injector.getInstance(NodeApiClient.class); - var coreModelMapper = injector.getInstance(CoreModelMapper.class); - var radixEngineReader = injector.getInstance(RadixEngineReader.class); + @Override + public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { + var injector = runner.getNode(0); + var nodeClient = injector.getInstance(NodeApiClient.class); + var coreModelMapper = injector.getInstance(CoreModelMapper.class); + var radixEngineReader = injector.getInstance(RadixEngineReader.class); - // Check that sum of api balances matches radixEngine numbers - var totalTokenBalance = PrivateKeys.numeric(1).limit(20) - .map(ECKeyPair::getPublicKey) - .map(REAddr::ofPubKeyAccount) - .flatMap(addr -> nodeClient.getEntity(coreModelMapper.entityIdentifier(addr)).getBalances().stream()) - .filter(r -> r.getResourceIdentifier().equals(nodeClient.nativeToken())) - .map(r -> new BigInteger(r.getValue())) - .reduce(BigInteger.ZERO, BigInteger::add); - assertThat(totalTokenBalance).isEqualTo(radixEngineReader.getTotalNativeTokensInAccounts()); + // Check that sum of api balances matches radixEngine numbers + var totalTokenBalance = + PrivateKeys.numeric(1) + .limit(20) + .map(ECKeyPair::getPublicKey) + .map(REAddr::ofPubKeyAccount) + .flatMap( + addr -> + nodeClient + .getEntity(coreModelMapper.entityIdentifier(addr)) + .getBalances() + .stream()) + .filter(r -> r.getResourceIdentifier().equals(nodeClient.nativeToken())) + .map(r -> new BigInteger(r.getValue())) + .reduce(BigInteger.ZERO, BigInteger::add); + assertThat(totalTokenBalance).isEqualTo(radixEngineReader.getTotalNativeTokensInAccounts()); - // Check that sum of api exiting stake balances matches radixEngine numbers - var totalUnstakingBalance = PrivateKeys.numeric(1).limit(20) - .map(ECKeyPair::getPublicKey) - .map(REAddr::ofPubKeyAccount) - .flatMap(addr -> getAccountUnstakes(addr, nodeClient).stream()) - .filter(r -> r.getResourceIdentifier().equals(nodeClient.nativeToken())) - .map(r -> new BigInteger(r.getValue())) - .reduce(BigInteger.ZERO, BigInteger::add); - assertThat(totalUnstakingBalance).isEqualTo(radixEngineReader.getTotalExittingStake()); + // Check that sum of api exiting stake balances matches radixEngine numbers + var totalUnstakingBalance = + PrivateKeys.numeric(1) + .limit(20) + .map(ECKeyPair::getPublicKey) + .map(REAddr::ofPubKeyAccount) + .flatMap(addr -> getAccountUnstakes(addr, nodeClient).stream()) + .filter(r -> r.getResourceIdentifier().equals(nodeClient.nativeToken())) + .map(r -> new BigInteger(r.getValue())) + .reduce(BigInteger.ZERO, BigInteger::add); + assertThat(totalUnstakingBalance).isEqualTo(radixEngineReader.getTotalExittingStake()); - return String.format("Okay{total_token_balance=%s}", totalTokenBalance); - } + return String.format("Okay{total_token_balance=%s}", totalTokenBalance); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiTxnSubmitter.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiTxnSubmitter.java index 4011d1ad7f..98ea654a93 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiTxnSubmitter.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/ApiTxnSubmitter.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -75,8 +76,11 @@ import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.api.core.openapitools.model.TokenData; import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; +import com.radixdlt.application.tokens.Amount; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; import com.radixdlt.consensus.bft.Self; import com.radixdlt.crypto.ECPublicKey; +import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; import com.radixdlt.integration.api.DeterministicActor; import com.radixdlt.integration.api.actors.actions.BurnTokens; import com.radixdlt.integration.api.actors.actions.CreateTokenDefinition; @@ -89,170 +93,174 @@ import com.radixdlt.integration.api.actors.actions.StakeTokens; import com.radixdlt.integration.api.actors.actions.TransferTokens; import com.radixdlt.integration.api.actors.actions.UnstakeStakeUnits; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; -import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; import com.radixdlt.utils.UInt256; - import java.util.Random; import java.util.Set; import java.util.stream.Collectors; -/** - * Submits a random transaction through a random node in a deterministic test. - */ +/** Submits a random transaction through a random node in a deterministic test. */ public final class ApiTxnSubmitter implements DeterministicActor { - private static final Set> OKAY_ERRORS = Set.of( - MempoolFullError.class, - NotEnoughResourcesError.class, - AboveMaximumValidatorFeeIncreaseError.class, - BelowMinimumStakeError.class, - NotValidatorOwnerError.class - ); - private int tokenId = 0; + private static final Set> OKAY_ERRORS = + Set.of( + MempoolFullError.class, + NotEnoughResourcesError.class, + AboveMaximumValidatorFeeIncreaseError.class, + BelowMinimumStakeError.class, + NotValidatorOwnerError.class); + private int tokenId = 0; - private Amount nextAmount(Random random) { - return Amount.ofTokens(random.nextInt(10) * 10 + 1); - } + private Amount nextAmount(Random random) { + return Amount.ofTokens(random.nextInt(10) * 10 + 1); + } - private TransferTokens transferTokens(NodeApiClient nodeClient, EntityIdentifier to, Random random) throws Exception { - var publicKey = nodeClient.getPublicKey(); - var accountIdentifier = nodeClient.deriveAccount(publicKey); - var response = nodeClient.getEntity(accountIdentifier); - var tokenTypes = response.getBalances() - .stream() - .map(ResourceAmount::getResourceIdentifier) - .filter(TokenResourceIdentifier.class::isInstance) - .map(TokenResourceIdentifier.class::cast) - .collect(Collectors.toList()); + private TransferTokens transferTokens( + NodeApiClient nodeClient, EntityIdentifier to, Random random) throws Exception { + var publicKey = nodeClient.getPublicKey(); + var accountIdentifier = nodeClient.deriveAccount(publicKey); + var response = nodeClient.getEntity(accountIdentifier); + var tokenTypes = + response.getBalances().stream() + .map(ResourceAmount::getResourceIdentifier) + .filter(TokenResourceIdentifier.class::isInstance) + .map(TokenResourceIdentifier.class::cast) + .collect(Collectors.toList()); - TokenResourceIdentifier tokenResourceIdentifier; - if (tokenTypes.isEmpty()) { - tokenResourceIdentifier = nodeClient.nativeToken(); - } else { - var nextIndex = random.nextInt(tokenTypes.size()); - tokenResourceIdentifier = tokenTypes.get(nextIndex); - } + TokenResourceIdentifier tokenResourceIdentifier; + if (tokenTypes.isEmpty()) { + tokenResourceIdentifier = nodeClient.nativeToken(); + } else { + var nextIndex = random.nextInt(tokenTypes.size()); + tokenResourceIdentifier = tokenTypes.get(nextIndex); + } - return new TransferTokens(nextAmount(random), tokenResourceIdentifier, to); - } + return new TransferTokens(nextAmount(random), tokenResourceIdentifier, to); + } - private TokenResourceIdentifier findTokenClientOwns(NodeApiClient nodeClient, Random random) throws Exception { - var publicKey = nodeClient.getPublicKey(); - var accountIdentifier = nodeClient.deriveAccount(publicKey); - var response = nodeClient.getEntity(accountIdentifier); - var tokenTypes = response.getBalances() - .stream() - .map(ResourceAmount::getResourceIdentifier) - .filter(TokenResourceIdentifier.class::isInstance) - .map(TokenResourceIdentifier.class::cast) - .filter(t -> { - try { - var entityResponse = nodeClient.getEntity(new EntityIdentifier().address(t.getRri())); - return entityResponse.getDataObjects().stream() - .filter(TokenData.class::isInstance) - .map(TokenData.class::cast) - .filter(d -> d.getOwner() != null) - .anyMatch(d -> d.getOwner().equals(accountIdentifier)); - } catch (Exception e) { - throw new IllegalStateException(e); - } - }) - .collect(Collectors.toList()); + private TokenResourceIdentifier findTokenClientOwns(NodeApiClient nodeClient, Random random) + throws Exception { + var publicKey = nodeClient.getPublicKey(); + var accountIdentifier = nodeClient.deriveAccount(publicKey); + var response = nodeClient.getEntity(accountIdentifier); + var tokenTypes = + response.getBalances().stream() + .map(ResourceAmount::getResourceIdentifier) + .filter(TokenResourceIdentifier.class::isInstance) + .map(TokenResourceIdentifier.class::cast) + .filter( + t -> { + try { + var entityResponse = + nodeClient.getEntity(new EntityIdentifier().address(t.getRri())); + return entityResponse.getDataObjects().stream() + .filter(TokenData.class::isInstance) + .map(TokenData.class::cast) + .filter(d -> d.getOwner() != null) + .anyMatch(d -> d.getOwner().equals(accountIdentifier)); + } catch (Exception e) { + throw new IllegalStateException(e); + } + }) + .collect(Collectors.toList()); - if (tokenTypes.isEmpty()) { - return null; - } else { - var nextIndex = random.nextInt(tokenTypes.size()); - return tokenTypes.get(nextIndex); - } - } + if (tokenTypes.isEmpty()) { + return null; + } else { + var nextIndex = random.nextInt(tokenTypes.size()); + return tokenTypes.get(nextIndex); + } + } - private MintTokens mintTokens(NodeApiClient nodeClient, EntityIdentifier to, Random random) throws Exception { - var tokenResourceIdentifier = findTokenClientOwns(nodeClient, random); - if (tokenResourceIdentifier == null) { - return null; - } + private MintTokens mintTokens(NodeApiClient nodeClient, EntityIdentifier to, Random random) + throws Exception { + var tokenResourceIdentifier = findTokenClientOwns(nodeClient, random); + if (tokenResourceIdentifier == null) { + return null; + } - return new MintTokens(nextAmount(random), tokenResourceIdentifier, to); - } + return new MintTokens(nextAmount(random), tokenResourceIdentifier, to); + } - private BurnTokens burnTokens(NodeApiClient nodeClient, Random random) throws Exception { - var tokenResourceIdentifier = findTokenClientOwns(nodeClient, random); - if (tokenResourceIdentifier == null) { - return null; - } - var publicKey = nodeClient.getPublicKey(); - var accountIdentifier = nodeClient.deriveAccount(publicKey); - return new BurnTokens(nextAmount(random), tokenResourceIdentifier, accountIdentifier); - } + private BurnTokens burnTokens(NodeApiClient nodeClient, Random random) throws Exception { + var tokenResourceIdentifier = findTokenClientOwns(nodeClient, random); + if (tokenResourceIdentifier == null) { + return null; + } + var publicKey = nodeClient.getPublicKey(); + var accountIdentifier = nodeClient.deriveAccount(publicKey); + return new BurnTokens(nextAmount(random), tokenResourceIdentifier, accountIdentifier); + } - private CreateTokenDefinition createTokenDefinition(NodeApiClient nodeClient, EntityIdentifier to, Random random) throws Exception { - var publicKey = nodeClient.getPublicKey(); - var owner = nodeClient.deriveAccount(publicKey); - var uint256Bytes = new byte[UInt256.BYTES]; - random.nextBytes(uint256Bytes); - var amount = UInt256.from(uint256Bytes); - if (random.nextBoolean()) { - return CreateTokenDefinition.mutableTokenSupply("test" + tokenId++, amount, owner, to); - } else { - return CreateTokenDefinition.fixedTokenSupply("test" + tokenId++, amount, to); - } - } + private CreateTokenDefinition createTokenDefinition( + NodeApiClient nodeClient, EntityIdentifier to, Random random) throws Exception { + var publicKey = nodeClient.getPublicKey(); + var owner = nodeClient.deriveAccount(publicKey); + var uint256Bytes = new byte[UInt256.BYTES]; + random.nextBytes(uint256Bytes); + var amount = UInt256.from(uint256Bytes); + if (random.nextBoolean()) { + return CreateTokenDefinition.mutableTokenSupply("test" + tokenId++, amount, owner, to); + } else { + return CreateTokenDefinition.fixedTokenSupply("test" + tokenId++, amount, to); + } + } - private String submitAction(NodeApiClient nodeClient, NodeTransactionAction action) { - try { - nodeClient.submit(action, false); - } catch (CoreApiException e) { - // Throw error if not expected - if (!OKAY_ERRORS.contains(e.toError().getDetails().getClass())) { - throw new IllegalStateException(String.format("Invalid failure on action %s", action), e); - } + private String submitAction(NodeApiClient nodeClient, NodeTransactionAction action) { + try { + nodeClient.submit(action, false); + } catch (CoreApiException e) { + // Throw error if not expected + if (!OKAY_ERRORS.contains(e.toError().getDetails().getClass())) { + throw new IllegalStateException(String.format("Invalid failure on action %s", action), e); + } - var errorName = e.toError().getDetails().getClass().getSimpleName(); - return String.format("BuildError{action=%s error=%s}", action.getClass().getSimpleName(), errorName); - } catch (Exception e) { - throw new IllegalStateException(String.format("Invalid failure on action %s", action), e); - } + var errorName = e.toError().getDetails().getClass().getSimpleName(); + return String.format( + "BuildError{action=%s error=%s}", action.getClass().getSimpleName(), errorName); + } catch (Exception e) { + throw new IllegalStateException(String.format("Invalid failure on action %s", action), e); + } - return String.format("Submitted{action=%s}", action.getClass().getSimpleName()); - } + return String.format("Submitted{action=%s}", action.getClass().getSimpleName()); + } - @Override - public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { - int size = runner.getSize(); - var nodeIndex = random.nextInt(size); - var nodeInjector = runner.getNode(nodeIndex); - var nodeClient = nodeInjector.getInstance(NodeApiClient.class); - var otherNodeIndex = random.nextInt(size); - var otherKey = runner.getNode(otherNodeIndex) - .getInstance(Key.get(ECPublicKey.class, Self.class)); - var next = random.nextInt(11); + @Override + public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { + int size = runner.getSize(); + var nodeIndex = random.nextInt(size); + var nodeInjector = runner.getNode(nodeIndex); + var nodeClient = nodeInjector.getInstance(NodeApiClient.class); + var otherNodeIndex = random.nextInt(size); + var otherKey = + runner.getNode(otherNodeIndex).getInstance(Key.get(ECPublicKey.class, Self.class)); + var next = random.nextInt(11); - // Don't let the last validator unregister - if (next == 4 && nodeIndex <= 0) { - return "Skipped"; - } + // Don't let the last validator unregister + if (next == 4 && nodeIndex <= 0) { + return "Skipped"; + } - NodeTransactionAction action = switch (next) { - case 0 -> transferTokens(nodeClient, nodeClient.deriveAccount(otherKey), random); - case 1 -> new StakeTokens(nextAmount(random), nodeClient.deriveValidator(otherKey)); - case 2 -> new UnstakeStakeUnits(nextAmount(random), nodeClient.deriveValidator(otherKey).getAddress()); - case 3 -> new RegisterValidator(true); - case 4 -> new RegisterValidator(false); - case 5 -> new SetValidatorFee(random.nextInt(ValidatorUpdateRakeConstraintScrypt.RAKE_MAX + 1)); - case 6 -> new SetValidatorOwner(nodeClient.deriveAccount(otherKey)); - case 7 -> new SetAllowDelegationFlag(random.nextBoolean()); - case 8 -> createTokenDefinition(nodeClient, nodeClient.deriveAccount(otherKey), random); - case 9 -> mintTokens(nodeClient, nodeClient.deriveAccount(otherKey), random); - case 10 -> burnTokens(nodeClient, random); - default -> throw new IllegalStateException("Unexpected value: " + next); - }; + NodeTransactionAction action = + switch (next) { + case 0 -> transferTokens(nodeClient, nodeClient.deriveAccount(otherKey), random); + case 1 -> new StakeTokens(nextAmount(random), nodeClient.deriveValidator(otherKey)); + case 2 -> new UnstakeStakeUnits( + nextAmount(random), nodeClient.deriveValidator(otherKey).getAddress()); + case 3 -> new RegisterValidator(true); + case 4 -> new RegisterValidator(false); + case 5 -> new SetValidatorFee( + random.nextInt(ValidatorUpdateRakeConstraintScrypt.RAKE_MAX + 1)); + case 6 -> new SetValidatorOwner(nodeClient.deriveAccount(otherKey)); + case 7 -> new SetAllowDelegationFlag(random.nextBoolean()); + case 8 -> createTokenDefinition(nodeClient, nodeClient.deriveAccount(otherKey), random); + case 9 -> mintTokens(nodeClient, nodeClient.deriveAccount(otherKey), random); + case 10 -> burnTokens(nodeClient, random); + default -> throw new IllegalStateException("Unexpected value: " + next); + }; - if (action == null) { - return "Skipped"; - } + if (action == null) { + return "Skipped"; + } - return submitAction(nodeClient, action); - } + return submitAction(nodeClient, action); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/BalanceReconciler.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/BalanceReconciler.java index 2a8f413e08..d9d87e106c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/BalanceReconciler.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/BalanceReconciler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.integration.api.actors; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.api.core.openapitools.model.CommittedTransaction; import com.radixdlt.api.core.openapitools.model.EntityIdentifier; import com.radixdlt.api.core.openapitools.model.Operation; @@ -71,7 +74,6 @@ import com.radixdlt.api.core.openapitools.model.ResourceIdentifier; import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; import com.radixdlt.integration.api.DeterministicActor; - import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; @@ -81,86 +83,98 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; - /** - * Reconciles the balances that are computed by reading the transaction stream and - * the /entity endpoint + * Reconciles the balances that are computed by reading the transaction stream and the /entity + * endpoint */ public final class BalanceReconciler implements DeterministicActor { - private final Map> balances = new HashMap<>(); - private long currentStateVersion = 0L; - - private static Map> balanceChanges(Stream operationGroups) { - return operationGroups.flatMap(group -> group.getOperations().stream()) - .filter(op -> op.getAmount() != null) - .collect(Collectors.groupingBy( - Operation::getEntityIdentifier, - Collectors.groupingBy( - op -> op.getAmount().getResourceIdentifier(), - Collectors.mapping( - op -> new BigInteger(op.getAmount().getValue()), - Collectors.reducing(BigInteger.ZERO, BigInteger::add) - ) - ) - )); - } - - @Override - public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { - var injector = runner.getNode(0); - var nodeApiClient = injector.getInstance(NodeApiClient.class); - var transactions = new ArrayList(); - - final long startingStateVersion = currentStateVersion; - // Sync fully to ledger - List loadedTransactions; - do { - loadedTransactions = nodeApiClient.getTransactions(currentStateVersion, random.nextLong(1, 10)); - transactions.addAll(loadedTransactions); - currentStateVersion = currentStateVersion + loadedTransactions.size(); - } while (!loadedTransactions.isEmpty()); - - var nodeStateVersion = nodeApiClient.getStateIdentifier().getStateVersion(); - assertThat(nodeStateVersion).isEqualTo(currentStateVersion); - - // Compute balance changes since last sync - var balanceChanges = balanceChanges(transactions.stream().flatMap(txn -> txn.getOperationGroups().stream())); - - // Update balance states - balanceChanges.forEach((identifier, balanceMap) -> - balanceMap.forEach((resource, value) -> { - if (value.equals(BigInteger.ZERO)) { - return; - } - - balances.merge(identifier, Map.of(resource, value), (b0, b1) -> - Stream.concat(b0.entrySet().stream(), b1.entrySet().stream()).collect( - Collectors.groupingBy( - Map.Entry::getKey, - Collectors.mapping(Map.Entry::getValue, Collectors.reducing(BigInteger.ZERO, BigInteger::add)) - ) - ).entrySet().stream() - .filter(e -> !e.getValue().equals(BigInteger.ZERO)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) - ); - }) - ); - - // Verify that updated balance states match the node - for (var entityIdentifier : balanceChanges.keySet()) { - var myBalances = balances.getOrDefault(entityIdentifier, Map.of()); - var response = nodeApiClient.getEntity(entityIdentifier); - assertThat(response.getStateIdentifier().getStateVersion()).isEqualTo(currentStateVersion); - - var nodeBalances = response.getBalances().stream() - .collect(Collectors.toMap(ResourceAmount::getResourceIdentifier, r -> new BigInteger(r.getValue()))); - - assertThat(nodeBalances) - .describedAs("Balance of %s", entityIdentifier) - .containsExactlyInAnyOrderEntriesOf(myBalances); - } - - return String.format("Okay{last_state_version=%s current_state_version=%s}", startingStateVersion, currentStateVersion); - } + private final Map> balances = + new HashMap<>(); + private long currentStateVersion = 0L; + + private static Map> balanceChanges( + Stream operationGroups) { + return operationGroups + .flatMap(group -> group.getOperations().stream()) + .filter(op -> op.getAmount() != null) + .collect( + Collectors.groupingBy( + Operation::getEntityIdentifier, + Collectors.groupingBy( + op -> op.getAmount().getResourceIdentifier(), + Collectors.mapping( + op -> new BigInteger(op.getAmount().getValue()), + Collectors.reducing(BigInteger.ZERO, BigInteger::add))))); + } + + @Override + public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { + var injector = runner.getNode(0); + var nodeApiClient = injector.getInstance(NodeApiClient.class); + var transactions = new ArrayList(); + + final long startingStateVersion = currentStateVersion; + // Sync fully to ledger + List loadedTransactions; + do { + loadedTransactions = + nodeApiClient.getTransactions(currentStateVersion, random.nextLong(1, 10)); + transactions.addAll(loadedTransactions); + currentStateVersion = currentStateVersion + loadedTransactions.size(); + } while (!loadedTransactions.isEmpty()); + + var nodeStateVersion = nodeApiClient.getStateIdentifier().getStateVersion(); + assertThat(nodeStateVersion).isEqualTo(currentStateVersion); + + // Compute balance changes since last sync + var balanceChanges = + balanceChanges(transactions.stream().flatMap(txn -> txn.getOperationGroups().stream())); + + // Update balance states + balanceChanges.forEach( + (identifier, balanceMap) -> + balanceMap.forEach( + (resource, value) -> { + if (value.equals(BigInteger.ZERO)) { + return; + } + + balances.merge( + identifier, + Map.of(resource, value), + (b0, b1) -> + Stream.concat(b0.entrySet().stream(), b1.entrySet().stream()) + .collect( + Collectors.groupingBy( + Map.Entry::getKey, + Collectors.mapping( + Map.Entry::getValue, + Collectors.reducing(BigInteger.ZERO, BigInteger::add)))) + .entrySet() + .stream() + .filter(e -> !e.getValue().equals(BigInteger.ZERO)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + })); + + // Verify that updated balance states match the node + for (var entityIdentifier : balanceChanges.keySet()) { + var myBalances = balances.getOrDefault(entityIdentifier, Map.of()); + var response = nodeApiClient.getEntity(entityIdentifier); + assertThat(response.getStateIdentifier().getStateVersion()).isEqualTo(currentStateVersion); + + var nodeBalances = + response.getBalances().stream() + .collect( + Collectors.toMap( + ResourceAmount::getResourceIdentifier, r -> new BigInteger(r.getValue()))); + + assertThat(nodeBalances) + .describedAs("Balance of %s", entityIdentifier) + .containsExactlyInAnyOrderEntriesOf(myBalances); + } + + return String.format( + "Okay{last_state_version=%s current_state_version=%s}", + startingStateVersion, currentStateVersion); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NativeTokenRewardsChecker.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NativeTokenRewardsChecker.java index 247522fb8d..64267a35ab 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NativeTokenRewardsChecker.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NativeTokenRewardsChecker.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,55 +64,57 @@ package com.radixdlt.integration.api.actors; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; import com.radixdlt.integration.api.DeterministicActor; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.math.BigInteger; import java.util.Random; - -import static org.assertj.core.api.Assertions.assertThat; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * Verifies that the amount of inflation occurring on the native token - * matches the rewards per proposal configuration + * Verifies that the amount of inflation occurring on the native token matches the rewards per + * proposal configuration */ public final class NativeTokenRewardsChecker implements DeterministicActor { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private BigInteger lastNativeTokenCount; - private Long lastEpoch; + private BigInteger lastNativeTokenCount; + private Long lastEpoch; - @Override - public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { - var injector = runner.getNode(0); - var nodeClient = injector.getInstance(NodeApiClient.class); - var radixEngineReader = injector.getInstance(RadixEngineReader.class); + @Override + public String execute(MultiNodeDeterministicRunner runner, Random random) throws Exception { + var injector = runner.getNode(0); + var nodeClient = injector.getInstance(NodeApiClient.class); + var radixEngineReader = injector.getInstance(RadixEngineReader.class); - var epochView = nodeClient.getEpochView(); - var epoch = epochView.getEpoch(); - var totalNativeTokenCount = radixEngineReader.getTotalNativeTokens(); - final String result; - if (lastEpoch != null) { - result = String.format("Okay{total_xrd=%s last_time=%s}", totalNativeTokenCount, lastNativeTokenCount); - if (epoch - lastEpoch > 1) { - var numEpochs = epoch - lastEpoch; - var maxEmissions = BigInteger.valueOf(nodeClient.getRoundsPerEpoch()) - .multiply(new BigInteger(1, nodeClient.getRewardsPerProposal().toByteArray())) - .multiply(BigInteger.valueOf(numEpochs)); - assertThat(totalNativeTokenCount).isGreaterThan(lastNativeTokenCount); - var diff = totalNativeTokenCount.subtract(lastNativeTokenCount); - assertThat(diff).isLessThanOrEqualTo(maxEmissions); - } - } else { - result = String.format("Okay{total_xrd=%s}", totalNativeTokenCount); - logger.info("total_xrd: {}", totalNativeTokenCount); - } + var epochView = nodeClient.getEpochView(); + var epoch = epochView.getEpoch(); + var totalNativeTokenCount = radixEngineReader.getTotalNativeTokens(); + final String result; + if (lastEpoch != null) { + result = + String.format( + "Okay{total_xrd=%s last_time=%s}", totalNativeTokenCount, lastNativeTokenCount); + if (epoch - lastEpoch > 1) { + var numEpochs = epoch - lastEpoch; + var maxEmissions = + BigInteger.valueOf(nodeClient.getRoundsPerEpoch()) + .multiply(new BigInteger(1, nodeClient.getRewardsPerProposal().toByteArray())) + .multiply(BigInteger.valueOf(numEpochs)); + assertThat(totalNativeTokenCount).isGreaterThan(lastNativeTokenCount); + var diff = totalNativeTokenCount.subtract(lastNativeTokenCount); + assertThat(diff).isLessThanOrEqualTo(maxEmissions); + } + } else { + result = String.format("Okay{total_xrd=%s}", totalNativeTokenCount); + logger.info("total_xrd: {}", totalNativeTokenCount); + } - lastEpoch = epoch; - lastNativeTokenCount = totalNativeTokenCount; + lastEpoch = epoch; + lastNativeTokenCount = totalNativeTokenCount; - return result; - } + return result; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NodeApiClient.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NodeApiClient.java index 1e232046ff..ace3647da3 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NodeApiClient.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/NodeApiClient.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,263 +65,281 @@ package com.radixdlt.integration.api.actors; import com.google.inject.Inject; -import com.radixdlt.api.core.handlers.ConstructionDeriveHandler; -import com.radixdlt.api.core.model.CoreApiException; -import com.radixdlt.api.core.model.CoreModelMapper; -import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequest; -import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequestMetadataAccount; -import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequestMetadataValidator; -import com.radixdlt.api.core.openapitools.model.EntityIdentifier; -import com.radixdlt.api.core.openapitools.model.NetworkIdentifier; -import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; -import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; +import com.radixdlt.api.core.handlers.ConstructionDeriveHandler; import com.radixdlt.api.core.handlers.ConstructionSubmitHandler; import com.radixdlt.api.core.handlers.EngineConfigurationHandler; import com.radixdlt.api.core.handlers.EngineStatusHandler; import com.radixdlt.api.core.handlers.EntityHandler; -import com.radixdlt.api.core.handlers.NetworkConfigurationHandler; -import com.radixdlt.api.core.handlers.NetworkStatusHandler; import com.radixdlt.api.core.handlers.KeyListHandler; import com.radixdlt.api.core.handlers.KeySignHandler; +import com.radixdlt.api.core.handlers.NetworkConfigurationHandler; +import com.radixdlt.api.core.handlers.NetworkStatusHandler; import com.radixdlt.api.core.handlers.TransactionsHandler; +import com.radixdlt.api.core.model.CoreApiException; +import com.radixdlt.api.core.model.CoreModelMapper; import com.radixdlt.api.core.openapitools.model.CommittedTransaction; import com.radixdlt.api.core.openapitools.model.CommittedTransactionsRequest; import com.radixdlt.api.core.openapitools.model.ConstructionBuildRequest; +import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequest; import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequestMetadata; +import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequestMetadataAccount; +import com.radixdlt.api.core.openapitools.model.ConstructionDeriveRequestMetadataValidator; import com.radixdlt.api.core.openapitools.model.ConstructionSubmitRequest; import com.radixdlt.api.core.openapitools.model.EngineConfigurationRequest; import com.radixdlt.api.core.openapitools.model.EngineStatusRequest; +import com.radixdlt.api.core.openapitools.model.EntityIdentifier; import com.radixdlt.api.core.openapitools.model.EntityRequest; import com.radixdlt.api.core.openapitools.model.EntityResponse; -import com.radixdlt.api.core.openapitools.model.NetworkStatusRequest; import com.radixdlt.api.core.openapitools.model.KeyListRequest; import com.radixdlt.api.core.openapitools.model.KeySignRequest; +import com.radixdlt.api.core.openapitools.model.NetworkIdentifier; +import com.radixdlt.api.core.openapitools.model.NetworkStatusRequest; import com.radixdlt.api.core.openapitools.model.PartialStateIdentifier; import com.radixdlt.api.core.openapitools.model.PublicKey; import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.api.core.openapitools.model.StateIdentifier; +import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.epoch.EpochView; +import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.integration.api.actors.actions.NodeTransactionAction; import com.radixdlt.utils.UInt256; - import java.util.ArrayList; import java.util.List; -/** - * Helper class to execute api commands - */ +/** Helper class to execute api commands */ final class NodeApiClient { - private final EntityHandler entityHandler; - private final NetworkConfigurationHandler networkConfigurationHandler; - private final NetworkStatusHandler networkStatusHandler; - private final KeyListHandler keyListHandler; - private final ConstructionBuildHandler constructionBuildHandler; - private final KeySignHandler keySignHandler; - private final ConstructionDeriveHandler constructionDeriveHandler; - private final ConstructionSubmitHandler constructionSubmitHandler; - private final EngineConfigurationHandler engineConfigurationHandler; - private final EngineStatusHandler engineStatusHandler; - private final TransactionsHandler transactionsHandler; - private final CoreModelMapper coreModelMapper; + private final EntityHandler entityHandler; + private final NetworkConfigurationHandler networkConfigurationHandler; + private final NetworkStatusHandler networkStatusHandler; + private final KeyListHandler keyListHandler; + private final ConstructionBuildHandler constructionBuildHandler; + private final KeySignHandler keySignHandler; + private final ConstructionDeriveHandler constructionDeriveHandler; + private final ConstructionSubmitHandler constructionSubmitHandler; + private final EngineConfigurationHandler engineConfigurationHandler; + private final EngineStatusHandler engineStatusHandler; + private final TransactionsHandler transactionsHandler; + private final CoreModelMapper coreModelMapper; - @Inject - NodeApiClient( - NetworkConfigurationHandler networkConfigurationHandler, - NetworkStatusHandler networkStatusHandler, - EntityHandler entityHandler, - KeyListHandler keyListHandler, - ConstructionBuildHandler constructionBuildHandler, - ConstructionDeriveHandler constructionDeriveHandler, - KeySignHandler keySignHandler, - ConstructionSubmitHandler constructionSubmitHandler, - EngineConfigurationHandler engineConfigurationHandler, - EngineStatusHandler engineStatusHandler, - TransactionsHandler transactionsHandler, - CoreModelMapper coreModelMapper - ) { - this.networkConfigurationHandler = networkConfigurationHandler; - this.networkStatusHandler = networkStatusHandler; - this.entityHandler = entityHandler; - this.keyListHandler = keyListHandler; - this.constructionDeriveHandler = constructionDeriveHandler; - this.constructionBuildHandler = constructionBuildHandler; - this.keySignHandler = keySignHandler; - this.constructionSubmitHandler = constructionSubmitHandler; - this.engineConfigurationHandler = engineConfigurationHandler; - this.engineStatusHandler = engineStatusHandler; - this.transactionsHandler = transactionsHandler; - this.coreModelMapper = coreModelMapper; - } + @Inject + NodeApiClient( + NetworkConfigurationHandler networkConfigurationHandler, + NetworkStatusHandler networkStatusHandler, + EntityHandler entityHandler, + KeyListHandler keyListHandler, + ConstructionBuildHandler constructionBuildHandler, + ConstructionDeriveHandler constructionDeriveHandler, + KeySignHandler keySignHandler, + ConstructionSubmitHandler constructionSubmitHandler, + EngineConfigurationHandler engineConfigurationHandler, + EngineStatusHandler engineStatusHandler, + TransactionsHandler transactionsHandler, + CoreModelMapper coreModelMapper) { + this.networkConfigurationHandler = networkConfigurationHandler; + this.networkStatusHandler = networkStatusHandler; + this.entityHandler = entityHandler; + this.keyListHandler = keyListHandler; + this.constructionDeriveHandler = constructionDeriveHandler; + this.constructionBuildHandler = constructionBuildHandler; + this.keySignHandler = keySignHandler; + this.constructionSubmitHandler = constructionSubmitHandler; + this.engineConfigurationHandler = engineConfigurationHandler; + this.engineStatusHandler = engineStatusHandler; + this.transactionsHandler = transactionsHandler; + this.coreModelMapper = coreModelMapper; + } - private NetworkIdentifier networkIdentifier() { - var networkResponse = networkConfigurationHandler.handleRequest((Void) null); - return networkResponse.getNetworkIdentifier(); - } + private NetworkIdentifier networkIdentifier() { + var networkResponse = networkConfigurationHandler.handleRequest((Void) null); + return networkResponse.getNetworkIdentifier(); + } - public TokenResourceIdentifier nativeToken() { - return coreModelMapper.nativeToken(); - } + public TokenResourceIdentifier nativeToken() { + return coreModelMapper.nativeToken(); + } - public PublicKey selfPublicKey() throws Exception { - var keyListResponse = keyListHandler.handleRequest(new KeyListRequest() - .networkIdentifier(networkIdentifier()) - ); - return keyListResponse.getPublicKeys().get(0).getPublicKey(); - } + public PublicKey selfPublicKey() throws Exception { + var keyListResponse = + keyListHandler.handleRequest(new KeyListRequest().networkIdentifier(networkIdentifier())); + return keyListResponse.getPublicKeys().get(0).getPublicKey(); + } - public EntityIdentifier selfDerive(ConstructionDeriveRequestMetadata metadata) { - try { - var response = constructionDeriveHandler.handleRequest(new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(selfPublicKey()) - .metadata(metadata) - ); - return response.getEntityIdentifier(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } + public EntityIdentifier selfDerive(ConstructionDeriveRequestMetadata metadata) { + try { + var response = + constructionDeriveHandler.handleRequest( + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(selfPublicKey()) + .metadata(metadata)); + return response.getEntityIdentifier(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } - public EntityIdentifier deriveValidator(ECPublicKey key) throws Exception { - var response = this.constructionDeriveHandler.handleRequest(new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(key)) - .metadata(new ConstructionDeriveRequestMetadataValidator()) - ); - return response.getEntityIdentifier(); - } + public EntityIdentifier deriveValidator(ECPublicKey key) throws Exception { + var response = + this.constructionDeriveHandler.handleRequest( + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(key)) + .metadata(new ConstructionDeriveRequestMetadataValidator())); + return response.getEntityIdentifier(); + } - public EntityIdentifier deriveAccount(PublicKey publicKey) throws Exception { - var response = this.constructionDeriveHandler.handleRequest(new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(publicKey) - .metadata(new ConstructionDeriveRequestMetadataAccount()) - ); - return response.getEntityIdentifier(); - } + public EntityIdentifier deriveAccount(PublicKey publicKey) throws Exception { + var response = + this.constructionDeriveHandler.handleRequest( + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(publicKey) + .metadata(new ConstructionDeriveRequestMetadataAccount())); + return response.getEntityIdentifier(); + } - public EntityIdentifier deriveAccount(ECPublicKey key) throws Exception { - return deriveAccount(coreModelMapper.publicKey(key)); - } + public EntityIdentifier deriveAccount(ECPublicKey key) throws Exception { + return deriveAccount(coreModelMapper.publicKey(key)); + } - public StateIdentifier getStateIdentifier() throws Exception { - var response = networkStatusHandler.handleRequest(new NetworkStatusRequest().networkIdentifier(networkIdentifier())); - return response.getCurrentStateIdentifier(); - } + public StateIdentifier getStateIdentifier() throws Exception { + var response = + networkStatusHandler.handleRequest( + new NetworkStatusRequest().networkIdentifier(networkIdentifier())); + return response.getCurrentStateIdentifier(); + } - public PublicKey getPublicKey() throws Exception { - var response = keyListHandler.handleRequest(new KeyListRequest().networkIdentifier(networkIdentifier())); - return response.getPublicKeys().get(0).getPublicKey(); - } + public PublicKey getPublicKey() throws Exception { + var response = + keyListHandler.handleRequest(new KeyListRequest().networkIdentifier(networkIdentifier())); + return response.getPublicKeys().get(0).getPublicKey(); + } - public EpochView getEpochView() throws Exception { - var statusResponse = engineStatusHandler - .handleRequest(new EngineStatusRequest().networkIdentifier(networkIdentifier())); - var epoch = statusResponse.getEngineStateIdentifier().getEpoch(); - var round = statusResponse.getEngineStateIdentifier().getRound(); - return EpochView.of(epoch, View.of(round)); - } + public EpochView getEpochView() throws Exception { + var statusResponse = + engineStatusHandler.handleRequest( + new EngineStatusRequest().networkIdentifier(networkIdentifier())); + var epoch = statusResponse.getEngineStateIdentifier().getEpoch(); + var round = statusResponse.getEngineStateIdentifier().getRound(); + return EpochView.of(epoch, View.of(round)); + } - public UInt256 getRewardsPerProposal() throws Exception { - var response = this.engineConfigurationHandler.handleRequest(new EngineConfigurationRequest() - .networkIdentifier(networkIdentifier()) - ); - return UInt256.from(response.getForks().get(0).getEngineConfiguration().getRewardsPerProposal().getValue()); - } + public UInt256 getRewardsPerProposal() throws Exception { + var response = + this.engineConfigurationHandler.handleRequest( + new EngineConfigurationRequest().networkIdentifier(networkIdentifier())); + return UInt256.from( + response.getForks().get(0).getEngineConfiguration().getRewardsPerProposal().getValue()); + } - public long getRoundsPerEpoch() throws Exception { - var response = this.engineConfigurationHandler.handleRequest(new EngineConfigurationRequest() - .networkIdentifier(networkIdentifier()) - ); - return response.getForks().get(0).getEngineConfiguration().getMaximumRoundsPerEpoch(); - } + public long getRoundsPerEpoch() throws Exception { + var response = + this.engineConfigurationHandler.handleRequest( + new EngineConfigurationRequest().networkIdentifier(networkIdentifier())); + return response.getForks().get(0).getEngineConfiguration().getMaximumRoundsPerEpoch(); + } - public long unstakingDelayEpochLength() { - try { - return engineConfigurationHandler.handleRequest(new EngineConfigurationRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - ).getForks().get(0).getEngineConfiguration().getUnstakingDelayEpochLength(); - } catch (CoreApiException e) { - throw new IllegalStateException(e); - } - } + public long unstakingDelayEpochLength() { + try { + return engineConfigurationHandler + .handleRequest( + new EngineConfigurationRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet"))) + .getForks() + .get(0) + .getEngineConfiguration() + .getUnstakingDelayEpochLength(); + } catch (CoreApiException e) { + throw new IllegalStateException(e); + } + } - public EntityResponse getEntity(EntityIdentifier entityIdentifier) { - try { - return entityHandler.handleRequest(new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(entityIdentifier) - ); - } catch (CoreApiException e) { - throw new IllegalStateException(e); - } - } + public EntityResponse getEntity(EntityIdentifier entityIdentifier) { + try { + return entityHandler.handleRequest( + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(entityIdentifier)); + } catch (CoreApiException e) { + throw new IllegalStateException(e); + } + } - public List getUnstakes(REAddr addr, ECPublicKey validatorKey) { - var networkIdentifier = new NetworkIdentifier().network("localnet"); - var unstakingDelayEpochLength = unstakingDelayEpochLength(); - var unstakes = new ArrayList(); - try { - var statusResponse = engineStatusHandler - .handleRequest(new EngineStatusRequest().networkIdentifier(networkIdentifier)); - var curEpoch = statusResponse.getEngineStateIdentifier().getEpoch(); - var maxEpoch = curEpoch + unstakingDelayEpochLength + 1; + public List getUnstakes(REAddr addr, ECPublicKey validatorKey) { + var networkIdentifier = new NetworkIdentifier().network("localnet"); + var unstakingDelayEpochLength = unstakingDelayEpochLength(); + var unstakes = new ArrayList(); + try { + var statusResponse = + engineStatusHandler.handleRequest( + new EngineStatusRequest().networkIdentifier(networkIdentifier)); + var curEpoch = statusResponse.getEngineStateIdentifier().getEpoch(); + var maxEpoch = curEpoch + unstakingDelayEpochLength + 1; - for (long epochUnstake = curEpoch; epochUnstake <= maxEpoch; epochUnstake++) { - var response = entityHandler.handleRequest(new EntityRequest() - .networkIdentifier(networkIdentifier) - .entityIdentifier(coreModelMapper.entityIdentifierExitingStake(addr, validatorKey, epochUnstake)) - ); - unstakes.addAll(response.getBalances()); - } - } catch (CoreApiException e) { - throw new IllegalStateException(e); - } + for (long epochUnstake = curEpoch; epochUnstake <= maxEpoch; epochUnstake++) { + var response = + entityHandler.handleRequest( + new EntityRequest() + .networkIdentifier(networkIdentifier) + .entityIdentifier( + coreModelMapper.entityIdentifierExitingStake( + addr, validatorKey, epochUnstake))); + unstakes.addAll(response.getBalances()); + } + } catch (CoreApiException e) { + throw new IllegalStateException(e); + } - return unstakes; - } + return unstakes; + } - public List getTransactions(long stateVersion, long limit) throws Exception { - var response = transactionsHandler.handleRequest(new CommittedTransactionsRequest() - .networkIdentifier(networkIdentifier()) - .stateIdentifier(new PartialStateIdentifier().stateVersion(stateVersion)) - .limit(limit) - ); - return response.getTransactions(); - } + public List getTransactions(long stateVersion, long limit) + throws Exception { + var response = + transactionsHandler.handleRequest( + new CommittedTransactionsRequest() + .networkIdentifier(networkIdentifier()) + .stateIdentifier(new PartialStateIdentifier().stateVersion(stateVersion)) + .limit(limit)); + return response.getTransactions(); + } - public void submit(NodeTransactionAction action, boolean disableResourceAllocateAndDestroy) throws Exception { - var networkIdentifier = networkIdentifier(); - var engineConfigurationResponse = engineConfigurationHandler.handleRequest( - new EngineConfigurationRequest().networkIdentifier(networkIdentifier) - ); - var keyListResponse = keyListHandler.handleRequest(new KeyListRequest().networkIdentifier(networkIdentifier)); - var nodePublicKey = keyListResponse.getPublicKeys().get(0).getPublicKey(); - var configuration = engineConfigurationResponse.getForks().get(0).getEngineConfiguration(); + public void submit(NodeTransactionAction action, boolean disableResourceAllocateAndDestroy) + throws Exception { + var networkIdentifier = networkIdentifier(); + var engineConfigurationResponse = + engineConfigurationHandler.handleRequest( + new EngineConfigurationRequest().networkIdentifier(networkIdentifier)); + var keyListResponse = + keyListHandler.handleRequest(new KeyListRequest().networkIdentifier(networkIdentifier)); + var nodePublicKey = keyListResponse.getPublicKeys().get(0).getPublicKey(); + var configuration = engineConfigurationResponse.getForks().get(0).getEngineConfiguration(); - var accountIdentifier = deriveAccount(nodePublicKey); - var operationGroups = action.toOperationGroups(configuration, this::selfDerive); + var accountIdentifier = deriveAccount(nodePublicKey); + var operationGroups = action.toOperationGroups(configuration, this::selfDerive); - var buildRequest = new ConstructionBuildRequest() - .networkIdentifier(networkIdentifier) - .feePayer(accountIdentifier) - .operationGroups(operationGroups) - .disableResourceAllocateAndDestroy(disableResourceAllocateAndDestroy); - var buildResponse = constructionBuildHandler.handleRequest(buildRequest); - var unsignedTransaction = buildResponse.getUnsignedTransaction(); + var buildRequest = + new ConstructionBuildRequest() + .networkIdentifier(networkIdentifier) + .feePayer(accountIdentifier) + .operationGroups(operationGroups) + .disableResourceAllocateAndDestroy(disableResourceAllocateAndDestroy); + var buildResponse = constructionBuildHandler.handleRequest(buildRequest); + var unsignedTransaction = buildResponse.getUnsignedTransaction(); - var response = keySignHandler.handleRequest(new KeySignRequest() - .networkIdentifier(networkIdentifier) - .publicKey(nodePublicKey) - .unsignedTransaction(unsignedTransaction) - ); + var response = + keySignHandler.handleRequest( + new KeySignRequest() + .networkIdentifier(networkIdentifier) + .publicKey(nodePublicKey) + .unsignedTransaction(unsignedTransaction)); - constructionSubmitHandler.handleRequest(new ConstructionSubmitRequest() - .networkIdentifier(networkIdentifier) - .signedTransaction(response.getSignedTransaction()) - ); - } + constructionSubmitHandler.handleRequest( + new ConstructionSubmitRequest() + .networkIdentifier(networkIdentifier) + .signedTransaction(response.getSignedTransaction())); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RadixEngineReader.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RadixEngineReader.java index 07b52f5573..f9422a7ecf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RadixEngineReader.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RadixEngineReader.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -72,52 +73,62 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.utils.UInt256; - import java.math.BigInteger; -/** - * Utility class for reading info in the radix engine - */ +/** Utility class for reading info in the radix engine */ public final class RadixEngineReader { - private final RadixEngine radixEngine; + private final RadixEngine radixEngine; - @Inject - public RadixEngineReader(RadixEngine radixEngine) { - this.radixEngine = radixEngine; - } + @Inject + public RadixEngineReader(RadixEngine radixEngine) { + this.radixEngine = radixEngine; + } - public BigInteger getTotalExittingStake() { - var totalStakeExitting = radixEngine.read(reader -> reader.reduce(ExitingStake.class, UInt256.ZERO, (u, t) -> u.add(t.getAmount()))); - return new BigInteger(1, totalStakeExitting.toByteArray()); - } + public BigInteger getTotalExittingStake() { + var totalStakeExitting = + radixEngine.read( + reader -> + reader.reduce(ExitingStake.class, UInt256.ZERO, (u, t) -> u.add(t.getAmount()))); + return new BigInteger(1, totalStakeExitting.toByteArray()); + } - public BigInteger getTotalNativeTokensInAccounts() { - var totalTokens = radixEngine.read(reader -> reader.reduceResources(TokensInAccount.class, TokensInAccount::getResourceAddr) - .get(REAddr.ofNativeToken())); - return new BigInteger(1, totalTokens.toByteArray()); - } + public BigInteger getTotalNativeTokensInAccounts() { + var totalTokens = + radixEngine.read( + reader -> + reader + .reduceResources(TokensInAccount.class, TokensInAccount::getResourceAddr) + .get(REAddr.ofNativeToken())); + return new BigInteger(1, totalTokens.toByteArray()); + } - private static BigInteger toBigInteger(UInt256 uInt256) { - return new BigInteger(1, uInt256.toByteArray()); - } + private static BigInteger toBigInteger(UInt256 uInt256) { + return new BigInteger(1, uInt256.toByteArray()); + } - public BigInteger getTotalNativeTokens() { - var tokensInAccounts = getTotalNativeTokensInAccounts(); - var totalStaked = radixEngine.read(reader -> reader.reduce( - ValidatorStakeData.class, - BigInteger.ZERO, - (u, t) -> u.add(toBigInteger(t.getAmount())) - )); - var totalStakePrepared = radixEngine.read(reader -> reader.reduce( - PreparedStake.class, - BigInteger.ZERO, - (u, t) -> u.add(toBigInteger(t.getAmount())) - )); - var totalStakeExitting = radixEngine.read(reader -> reader.reduce( - ExitingStake.class, - BigInteger.ZERO, - (u, t) -> u.add(toBigInteger(t.getAmount())) - )); - return tokensInAccounts.add(totalStaked).add(totalStakePrepared).add(totalStakeExitting); - } + public BigInteger getTotalNativeTokens() { + var tokensInAccounts = getTotalNativeTokensInAccounts(); + var totalStaked = + radixEngine.read( + reader -> + reader.reduce( + ValidatorStakeData.class, + BigInteger.ZERO, + (u, t) -> u.add(toBigInteger(t.getAmount())))); + var totalStakePrepared = + radixEngine.read( + reader -> + reader.reduce( + PreparedStake.class, + BigInteger.ZERO, + (u, t) -> u.add(toBigInteger(t.getAmount())))); + var totalStakeExitting = + radixEngine.read( + reader -> + reader.reduce( + ExitingStake.class, + BigInteger.ZERO, + (u, t) -> u.add(toBigInteger(t.getAmount())))); + return tokensInAccounts.add(totalStaked).add(totalStakePrepared).add(totalStakeExitting); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RandomNodeRestarter.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RandomNodeRestarter.java index 5e39736b2a..5eb3fd53ed 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RandomNodeRestarter.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/RandomNodeRestarter.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -65,17 +66,14 @@ import com.radixdlt.environment.deterministic.MultiNodeDeterministicRunner; import com.radixdlt.integration.api.DeterministicActor; - import java.util.Random; -/** - * Randomly restarts a node in a deterministic test - */ +/** Randomly restarts a node in a deterministic test */ public final class RandomNodeRestarter implements DeterministicActor { - @Override - public String execute(MultiNodeDeterministicRunner runner, Random random) { - var nodeIndex = random.nextInt(runner.getSize()); - runner.restartNode(nodeIndex); - return String.format("Restarted{node_index=%s}", nodeIndex); - } + @Override + public String execute(MultiNodeDeterministicRunner runner, Random random) { + var nodeIndex = random.nextInt(runner.getSize()); + runner.restartNode(nodeIndex); + return String.format("Restarted{node_index=%s}", nodeIndex); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/BurnTokens.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/BurnTokens.java index 54dba7f79a..59b44b9a1e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/BurnTokens.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/BurnTokens.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -71,37 +72,36 @@ import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; import com.radixdlt.application.tokens.Amount; - import java.util.List; import java.util.function.Function; public final class BurnTokens implements NodeTransactionAction { - private final Amount amount; - private final TokenResourceIdentifier tokenResourceIdentifier; - private final EntityIdentifier from; + private final Amount amount; + private final TokenResourceIdentifier tokenResourceIdentifier; + private final EntityIdentifier from; - public BurnTokens(Amount amount, TokenResourceIdentifier tokenResourceIdentifier, EntityIdentifier from) { - this.amount = amount; - this.tokenResourceIdentifier = tokenResourceIdentifier; - this.from = from; - } + public BurnTokens( + Amount amount, TokenResourceIdentifier tokenResourceIdentifier, EntityIdentifier from) { + this.amount = amount; + this.tokenResourceIdentifier = tokenResourceIdentifier; + this.from = from; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var operationGroup = new OperationGroup() - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(tokenResourceIdentifier) - .value("-" + amount.toSubunits().toString()) - ) - .entityIdentifier(from) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(tokenResourceIdentifier) + .value("-" + amount.toSubunits().toString())) + .entityIdentifier(from)); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/CreateTokenDefinition.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/CreateTokenDefinition.java index 2f863c57aa..471e52091f 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/CreateTokenDefinition.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/CreateTokenDefinition.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -76,93 +77,98 @@ import com.radixdlt.api.core.openapitools.model.TokenMetadata; import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.function.Function; public final class CreateTokenDefinition implements NodeTransactionAction { - private final String symbol; - private final UInt256 amount; - private final EntityIdentifier owner; - private final EntityIdentifier to; + private final String symbol; + private final UInt256 amount; + private final EntityIdentifier owner; + private final EntityIdentifier to; - private CreateTokenDefinition(String symbol, UInt256 amount, EntityIdentifier owner, EntityIdentifier to) { - this.symbol = symbol; - this.amount = amount; - this.owner = owner; - this.to = to; - } + private CreateTokenDefinition( + String symbol, UInt256 amount, EntityIdentifier owner, EntityIdentifier to) { + this.symbol = symbol; + this.amount = amount; + this.owner = owner; + this.to = to; + } - public static CreateTokenDefinition fixedTokenSupply(String symbol, UInt256 amount, EntityIdentifier to) { - return new CreateTokenDefinition(symbol, amount, null, to); - } + public static CreateTokenDefinition fixedTokenSupply( + String symbol, UInt256 amount, EntityIdentifier to) { + return new CreateTokenDefinition(symbol, amount, null, to); + } - public static CreateTokenDefinition mutableTokenSupply(String symbol, UInt256 amount, EntityIdentifier owner, EntityIdentifier to) { - return new CreateTokenDefinition(symbol, amount, owner, to); - } + public static CreateTokenDefinition mutableTokenSupply( + String symbol, UInt256 amount, EntityIdentifier owner, EntityIdentifier to) { + return new CreateTokenDefinition(symbol, amount, owner, to); + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var entityIdentifier = identifierFunction.apply(new ConstructionDeriveRequestMetadataToken() - .symbol(symbol) - .type("Token") - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var entityIdentifier = + identifierFunction.apply( + new ConstructionDeriveRequestMetadataToken().symbol(symbol).type("Token")); - var mintOperation = new Operation() - .type("Resource") - .amount(new ResourceAmount() - .value(amount.toString()) - .resourceIdentifier(new TokenResourceIdentifier().rri(entityIdentifier.getAddress()).type("Token")) - ) - .entityIdentifier(to); + var mintOperation = + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .value(amount.toString()) + .resourceIdentifier( + new TokenResourceIdentifier() + .rri(entityIdentifier.getAddress()) + .type("Token"))) + .entityIdentifier(to); - var createTokenGroup = new OperationGroup(); - createTokenGroup.addOperationsItem( - new Operation() - .type("Data") - .data(new Data().action(Data.ActionEnum.CREATE) - .dataObject(new TokenData() - .isMutable(owner != null) - .granularity("1") - .owner(owner) - .type(PreparedValidatorRegistered.class.getSimpleName()) - ) - ) - .entityIdentifier(entityIdentifier) - ); + var createTokenGroup = new OperationGroup(); + createTokenGroup.addOperationsItem( + new Operation() + .type("Data") + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new TokenData() + .isMutable(owner != null) + .granularity("1") + .owner(owner) + .type(PreparedValidatorRegistered.class.getSimpleName()))) + .entityIdentifier(entityIdentifier)); - if (owner == null) { - createTokenGroup.addOperationsItem(mintOperation); - } + if (owner == null) { + createTokenGroup.addOperationsItem(mintOperation); + } - createTokenGroup.addOperationsItem( - new Operation() - .type("Data") - .data(new Data().action(Data.ActionEnum.CREATE) - .dataObject(new TokenMetadata() - .symbol(symbol) - .name("some_name") - .description("some_description") - .url("") - .iconUrl("") - ) - ) - .entityIdentifier(entityIdentifier) - ); + createTokenGroup.addOperationsItem( + new Operation() + .type("Data") + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new TokenMetadata() + .symbol(symbol) + .name("some_name") + .description("some_description") + .url("") + .iconUrl(""))) + .entityIdentifier(entityIdentifier)); - if (owner == null) { - return List.of(createTokenGroup); - } + if (owner == null) { + return List.of(createTokenGroup); + } - var mintTokenGroup = new OperationGroup().addOperationsItem(mintOperation); - return List.of(createTokenGroup, mintTokenGroup); - } + var mintTokenGroup = new OperationGroup().addOperationsItem(mintOperation); + return List.of(createTokenGroup, mintTokenGroup); + } - @Override - public String toString() { - return String.format("%s{symbol=%s amount=%s}", this.getClass().getSimpleName(), this.symbol, this.amount); - } + @Override + public String toString() { + return String.format( + "%s{symbol=%s amount=%s}", this.getClass().getSimpleName(), this.symbol, this.amount); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/MintTokens.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/MintTokens.java index f81e1554f4..896538039e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/MintTokens.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/MintTokens.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -71,37 +72,36 @@ import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; import com.radixdlt.application.tokens.Amount; - import java.util.List; import java.util.function.Function; public final class MintTokens implements NodeTransactionAction { - private final Amount amount; - private final TokenResourceIdentifier tokenResourceIdentifier; - private final EntityIdentifier to; + private final Amount amount; + private final TokenResourceIdentifier tokenResourceIdentifier; + private final EntityIdentifier to; - public MintTokens(Amount amount, TokenResourceIdentifier tokenResourceIdentifier, EntityIdentifier to) { - this.amount = amount; - this.tokenResourceIdentifier = tokenResourceIdentifier; - this.to = to; - } + public MintTokens( + Amount amount, TokenResourceIdentifier tokenResourceIdentifier, EntityIdentifier to) { + this.amount = amount; + this.tokenResourceIdentifier = tokenResourceIdentifier; + this.to = to; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var operationGroup = new OperationGroup() - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(tokenResourceIdentifier) - .value(amount.toSubunits().toString()) - ) - .entityIdentifier(to) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(tokenResourceIdentifier) + .value(amount.toSubunits().toString())) + .entityIdentifier(to)); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/NodeTransactionAction.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/NodeTransactionAction.java index 9b9c2e79fd..1e8c6ac757 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/NodeTransactionAction.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/NodeTransactionAction.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,13 +68,11 @@ import com.radixdlt.api.core.openapitools.model.EngineConfiguration; import com.radixdlt.api.core.openapitools.model.EntityIdentifier; import com.radixdlt.api.core.openapitools.model.OperationGroup; - import java.util.List; import java.util.function.Function; public interface NodeTransactionAction { - List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ); + List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction); } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/RegisterValidator.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/RegisterValidator.java index 56fd2c60c7..99c7d559bd 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/RegisterValidator.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/RegisterValidator.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -71,33 +72,36 @@ import com.radixdlt.api.core.openapitools.model.Operation; import com.radixdlt.api.core.openapitools.model.OperationGroup; import com.radixdlt.api.core.openapitools.model.PreparedValidatorRegistered; - import java.util.List; import java.util.function.Function; public final class RegisterValidator implements NodeTransactionAction { - private final boolean register; + private final boolean register; - public RegisterValidator(boolean register) { - this.register = register; - } + public RegisterValidator(boolean register) { + this.register = register; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var operationGroup = new OperationGroup().addOperationsItem( - new Operation() - .type("Data") - .data(new Data().action(Data.ActionEnum.CREATE) - .dataObject(new PreparedValidatorRegistered().registered(register) - .type(PreparedValidatorRegistered.class.getSimpleName()) - ) - ) - .entityIdentifier(identifierFunction.apply(new ConstructionDeriveRequestMetadataValidator().type("Validator"))) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Data") + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new PreparedValidatorRegistered() + .registered(register) + .type(PreparedValidatorRegistered.class.getSimpleName()))) + .entityIdentifier( + identifierFunction.apply( + new ConstructionDeriveRequestMetadataValidator().type("Validator")))); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetAllowDelegationFlag.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetAllowDelegationFlag.java index 7c6b1b1552..dbd2757325 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetAllowDelegationFlag.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetAllowDelegationFlag.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -71,31 +72,36 @@ import com.radixdlt.api.core.openapitools.model.Operation; import com.radixdlt.api.core.openapitools.model.OperationGroup; import com.radixdlt.api.core.openapitools.model.ValidatorAllowDelegation; - import java.util.List; import java.util.function.Function; public final class SetAllowDelegationFlag implements NodeTransactionAction { - private final boolean allowDelegation; + private final boolean allowDelegation; - public SetAllowDelegationFlag(boolean allowDelegation) { - this.allowDelegation = allowDelegation; - } + public SetAllowDelegationFlag(boolean allowDelegation) { + this.allowDelegation = allowDelegation; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var operationGroup = new OperationGroup().addOperationsItem( - new Operation() - .type("Data") - .data(new Data().action(Data.ActionEnum.CREATE) - .dataObject(new ValidatorAllowDelegation().allowDelegation(allowDelegation).type(ValidatorAllowDelegation.class.getSimpleName())) - ) - .entityIdentifier(identifierFunction.apply(new ConstructionDeriveRequestMetadataValidator().type("Validator"))) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Data") + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new ValidatorAllowDelegation() + .allowDelegation(allowDelegation) + .type(ValidatorAllowDelegation.class.getSimpleName()))) + .entityIdentifier( + identifierFunction.apply( + new ConstructionDeriveRequestMetadataValidator().type("Validator")))); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorFee.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorFee.java index f9249a9862..03349b7be7 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorFee.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorFee.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -71,31 +72,36 @@ import com.radixdlt.api.core.openapitools.model.Operation; import com.radixdlt.api.core.openapitools.model.OperationGroup; import com.radixdlt.api.core.openapitools.model.PreparedValidatorFee; - import java.util.List; import java.util.function.Function; public final class SetValidatorFee implements NodeTransactionAction { - private final int fee; + private final int fee; - public SetValidatorFee(int fee) { - this.fee = fee; - } + public SetValidatorFee(int fee) { + this.fee = fee; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var operationGroup = new OperationGroup().addOperationsItem( - new Operation() - .type("Data") - .data(new Data().action(Data.ActionEnum.CREATE) - .dataObject(new PreparedValidatorFee().fee(fee).type(PreparedValidatorFee.class.getSimpleName())) - ) - .entityIdentifier(identifierFunction.apply(new ConstructionDeriveRequestMetadataValidator().type("Validator"))) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Data") + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new PreparedValidatorFee() + .fee(fee) + .type(PreparedValidatorFee.class.getSimpleName()))) + .entityIdentifier( + identifierFunction.apply( + new ConstructionDeriveRequestMetadataValidator().type("Validator")))); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorOwner.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorOwner.java index 13743f1cdc..08ff35bb47 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorOwner.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/SetValidatorOwner.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -71,31 +72,36 @@ import com.radixdlt.api.core.openapitools.model.Operation; import com.radixdlt.api.core.openapitools.model.OperationGroup; import com.radixdlt.api.core.openapitools.model.PreparedValidatorOwner; - import java.util.List; import java.util.function.Function; public final class SetValidatorOwner implements NodeTransactionAction { - private final EntityIdentifier owner; + private final EntityIdentifier owner; - public SetValidatorOwner(EntityIdentifier owner) { - this.owner = owner; - } + public SetValidatorOwner(EntityIdentifier owner) { + this.owner = owner; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var operationGroup = new OperationGroup().addOperationsItem( - new Operation() - .type("Data") - .data(new Data().action(Data.ActionEnum.CREATE) - .dataObject(new PreparedValidatorOwner().owner(owner).type(PreparedValidatorOwner.class.getSimpleName())) - ) - .entityIdentifier(identifierFunction.apply(new ConstructionDeriveRequestMetadataValidator().type("Validator"))) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Data") + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new PreparedValidatorOwner() + .owner(owner) + .type(PreparedValidatorOwner.class.getSimpleName()))) + .entityIdentifier( + identifierFunction.apply( + new ConstructionDeriveRequestMetadataValidator().type("Validator")))); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/StakeTokens.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/StakeTokens.java index f8e2fb692e..903142c736 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/StakeTokens.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/StakeTokens.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -72,52 +73,49 @@ import com.radixdlt.api.core.openapitools.model.OperationGroup; import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.application.tokens.Amount; - import java.util.List; import java.util.function.Function; public final class StakeTokens implements NodeTransactionAction { - private final Amount amount; - private final EntityIdentifier validator; + private final Amount amount; + private final EntityIdentifier validator; - public StakeTokens(Amount amount, EntityIdentifier validator) { - this.amount = amount; - this.validator = validator; - } + public StakeTokens(Amount amount, EntityIdentifier validator) { + this.amount = amount; + this.validator = validator; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var nativeToken = configuration.getNativeToken(); - var from = identifierFunction.apply(new ConstructionDeriveRequestMetadataAccount() - .type("Account") - ); - var to = identifierFunction.apply(new ConstructionDeriveRequestMetadataPreparedStakes() - .validator(validator) - .type("PreparedStakes") - ); - var operationGroup = new OperationGroup() - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(nativeToken) - .value("-" + amount.toSubunits().toString()) - ) - .entityIdentifier(from) - ) - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(nativeToken) - .value(amount.toSubunits().toString()) - ) - .entityIdentifier(to) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var nativeToken = configuration.getNativeToken(); + var from = + identifierFunction.apply(new ConstructionDeriveRequestMetadataAccount().type("Account")); + var to = + identifierFunction.apply( + new ConstructionDeriveRequestMetadataPreparedStakes() + .validator(validator) + .type("PreparedStakes")); + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(nativeToken) + .value("-" + amount.toSubunits().toString())) + .entityIdentifier(from)) + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(nativeToken) + .value(amount.toSubunits().toString())) + .entityIdentifier(to)); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/TransferTokens.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/TransferTokens.java index 2f35e4682d..cf65cee313 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/TransferTokens.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/TransferTokens.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -72,47 +73,46 @@ import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.api.core.openapitools.model.TokenResourceIdentifier; import com.radixdlt.application.tokens.Amount; - import java.util.List; import java.util.function.Function; public final class TransferTokens implements NodeTransactionAction { - private final Amount amount; - private final TokenResourceIdentifier tokenResourceIdentifier; - private final EntityIdentifier to; + private final Amount amount; + private final TokenResourceIdentifier tokenResourceIdentifier; + private final EntityIdentifier to; - public TransferTokens(Amount amount, TokenResourceIdentifier tokenResourceIdentifier, EntityIdentifier to) { - this.amount = amount; - this.tokenResourceIdentifier = tokenResourceIdentifier; - this.to = to; - } + public TransferTokens( + Amount amount, TokenResourceIdentifier tokenResourceIdentifier, EntityIdentifier to) { + this.amount = amount; + this.tokenResourceIdentifier = tokenResourceIdentifier; + this.to = to; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var from = identifierFunction.apply(new ConstructionDeriveRequestMetadataAccount().type("Account")); - var operationGroup = new OperationGroup() - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(tokenResourceIdentifier) - .value("-" + amount.toSubunits().toString()) - ) - .entityIdentifier(from) - ) - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(tokenResourceIdentifier) - .value(amount.toSubunits().toString()) - ) - .entityIdentifier(to) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var from = + identifierFunction.apply(new ConstructionDeriveRequestMetadataAccount().type("Account")); + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(tokenResourceIdentifier) + .value("-" + amount.toSubunits().toString())) + .entityIdentifier(from)) + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(tokenResourceIdentifier) + .value(amount.toSubunits().toString())) + .entityIdentifier(to)); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/UnstakeStakeUnits.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/UnstakeStakeUnits.java index 105d59d449..abff190fc9 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/UnstakeStakeUnits.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/api/actors/actions/UnstakeStakeUnits.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -73,51 +74,48 @@ import com.radixdlt.api.core.openapitools.model.ResourceAmount; import com.radixdlt.api.core.openapitools.model.StakeUnitResourceIdentifier; import com.radixdlt.application.tokens.Amount; - import java.util.List; import java.util.function.Function; public final class UnstakeStakeUnits implements NodeTransactionAction { - private final Amount amount; - private final String validatorAddress; + private final Amount amount; + private final String validatorAddress; - public UnstakeStakeUnits(Amount amount, String validatorAddress) { - this.amount = amount; - this.validatorAddress = validatorAddress; - } + public UnstakeStakeUnits(Amount amount, String validatorAddress) { + this.amount = amount; + this.validatorAddress = validatorAddress; + } - @Override - public List toOperationGroups( - EngineConfiguration configuration, - Function identifierFunction - ) { - var from = identifierFunction.apply(new ConstructionDeriveRequestMetadataAccount() - .type("Account") - ); - var to = identifierFunction.apply(new ConstructionDeriveRequestMetadataPreparedUnstakes() - .type("PreparedUnstakes") - ); - var resourceIdentifier = new StakeUnitResourceIdentifier().validatorAddress(validatorAddress).type("StakeUnits"); - var operationGroup = new OperationGroup() - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value("-" + amount.toSubunits().toString()) - ) - .entityIdentifier(from) - ) - .addOperationsItem( - new Operation() - .type("Resource") - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(amount.toSubunits().toString()) - ) - .entityIdentifier(to) - ); + @Override + public List toOperationGroups( + EngineConfiguration configuration, + Function identifierFunction) { + var from = + identifierFunction.apply(new ConstructionDeriveRequestMetadataAccount().type("Account")); + var to = + identifierFunction.apply( + new ConstructionDeriveRequestMetadataPreparedUnstakes().type("PreparedUnstakes")); + var resourceIdentifier = + new StakeUnitResourceIdentifier().validatorAddress(validatorAddress).type("StakeUnits"); + var operationGroup = + new OperationGroup() + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value("-" + amount.toSubunits().toString())) + .entityIdentifier(from)) + .addOperationsItem( + new Operation() + .type("Resource") + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value(amount.toSubunits().toString())) + .entityIdentifier(to)); - return List.of(operationGroup); - } + return List.of(operationGroup); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/MockedPeersViewModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/MockedPeersViewModule.java index c48bcf100e..49d35c5d19 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/MockedPeersViewModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/MockedPeersViewModule.java @@ -74,26 +74,27 @@ public class MockedPeersViewModule extends AbstractModule { - private final ImmutableMap> nodes; + private final ImmutableMap> nodes; - public MockedPeersViewModule(ImmutableMap> nodes) { - this.nodes = nodes; - } + public MockedPeersViewModule(ImmutableMap> nodes) { + this.nodes = nodes; + } - @Provides - public PeersView peersView(@Self BFTNode self, ImmutableList nodes) { - final var nodesFiltered = filterNodes(self, nodes); - return () -> nodesFiltered.stream() - .filter(n -> !n.equals(self)) - .map(PeersView.PeerInfo::fromBftNode); - } + @Provides + public PeersView peersView(@Self BFTNode self, ImmutableList nodes) { + final var nodesFiltered = filterNodes(self, nodes); + return () -> + nodesFiltered.stream().filter(n -> !n.equals(self)).map(PeersView.PeerInfo::fromBftNode); + } - private ImmutableList filterNodes(BFTNode self, ImmutableList allNodes) { - final var selfIndex = allNodes.indexOf(self); - if (nodes != null && nodes.containsKey(selfIndex)) { - return nodes.get(selfIndex).stream().map(allNodes::get).collect(ImmutableList.toImmutableList()); - } else { - return allNodes; - } - } + private ImmutableList filterNodes(BFTNode self, ImmutableList allNodes) { + final var selfIndex = allNodes.indexOf(self); + if (nodes != null && nodes.containsKey(selfIndex)) { + return nodes.get(selfIndex).stream() + .map(allNodes::get) + .collect(ImmutableList.toImmutableList()); + } else { + return allNodes; + } + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicNodes.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicNodes.java index 0167e95b28..25188d7f93 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicNodes.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicNodes.java @@ -89,86 +89,82 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.ThreadContext; -/** - * BFT Nodes treated as a single unit where one message is processed at a time. - */ +/** BFT Nodes treated as a single unit where one message is processed at a time. */ public final class DeterministicNodes { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - private final ImmutableList nodeInstances; - private final DeterministicNetwork network; - private final ImmutableBiMap nodeLookup; + private final ImmutableList nodeInstances; + private final DeterministicNetwork network; + private final ImmutableBiMap nodeLookup; - public DeterministicNodes( - List nodes, - DeterministicNetwork network, - Module baseModule, - Module overrideModule - ) { - this.network = network; - this.nodeLookup = Streams.mapWithIndex( - nodes.stream(), - (node, index) -> Pair.of(node, (int) index) - ).collect(ImmutableBiMap.toImmutableBiMap(Pair::getFirst, Pair::getSecond)); - this.nodeInstances = nodes.stream() - .map(node -> createBFTInstance(node, baseModule, overrideModule)) - .collect(ImmutableList.toImmutableList()); - } + public DeterministicNodes( + List nodes, DeterministicNetwork network, Module baseModule, Module overrideModule) { + this.network = network; + this.nodeLookup = + Streams.mapWithIndex(nodes.stream(), (node, index) -> Pair.of(node, (int) index)) + .collect(ImmutableBiMap.toImmutableBiMap(Pair::getFirst, Pair::getSecond)); + this.nodeInstances = + nodes.stream() + .map(node -> createBFTInstance(node, baseModule, overrideModule)) + .collect(ImmutableList.toImmutableList()); + } - private Injector createBFTInstance(BFTNode self, Module baseModule, Module overrideModule) { - Module module = Modules.combine( - new AbstractModule() { - @Override - public void configure() { - bind(BFTNode.class).annotatedWith(Self.class).toInstance(self); - bind(Environment.class).toInstance(network.createSender(self)); - bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - } - }, - baseModule - ); - if (overrideModule != null) { - module = Modules.override(module).with(overrideModule); - } - return Guice.createInjector(module); - } + private Injector createBFTInstance(BFTNode self, Module baseModule, Module overrideModule) { + Module module = + Modules.combine( + new AbstractModule() { + @Override + public void configure() { + bind(BFTNode.class).annotatedWith(Self.class).toInstance(self); + bind(Environment.class).toInstance(network.createSender(self)); + bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); + } + }, + baseModule); + if (overrideModule != null) { + module = Modules.override(module).with(overrideModule); + } + return Guice.createInjector(module); + } - public int numNodes() { - return this.nodeInstances.size(); - } + public int numNodes() { + return this.nodeInstances.size(); + } - public void start() { - for (int index = 0; index < this.nodeInstances.size(); index++) { - Injector injector = nodeInstances.get(index); - var processor = injector.getInstance(DeterministicProcessor.class); + public void start() { + for (int index = 0; index < this.nodeInstances.size(); index++) { + Injector injector = nodeInstances.get(index); + var processor = injector.getInstance(DeterministicProcessor.class); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - processor.start(); - } finally { - ThreadContext.remove("self"); - } - } - } + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + processor.start(); + } finally { + ThreadContext.remove("self"); + } + } + } - public void handleMessage(Timed timedNextMsg) { - ControlledMessage nextMsg = timedNextMsg.value(); - int senderIndex = nextMsg.channelId().senderIndex(); - int receiverIndex = nextMsg.channelId().receiverIndex(); - BFTNode sender = this.nodeLookup.inverse().get(senderIndex); + public void handleMessage(Timed timedNextMsg) { + ControlledMessage nextMsg = timedNextMsg.value(); + int senderIndex = nextMsg.channelId().senderIndex(); + int receiverIndex = nextMsg.channelId().receiverIndex(); + BFTNode sender = this.nodeLookup.inverse().get(senderIndex); - var injector = nodeInstances.get(receiverIndex); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - log.debug("Received message {} at {}", nextMsg, timedNextMsg.time()); - nodeInstances.get(receiverIndex).getInstance(DeterministicProcessor.class) - .handleMessage(sender, nextMsg.message(), nextMsg.typeLiteral()); - } finally { - ThreadContext.remove("self"); - } - } + var injector = nodeInstances.get(receiverIndex); + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + log.debug("Received message {} at {}", nextMsg, timedNextMsg.time()); + nodeInstances + .get(receiverIndex) + .getInstance(DeterministicProcessor.class) + .handleMessage(sender, nextMsg.message(), nextMsg.typeLiteral()); + } finally { + ThreadContext.remove("self"); + } + } - public SystemCounters getSystemCounters(int nodeIndex) { - return this.nodeInstances.get(nodeIndex).getInstance(SystemCounters.class); - } + public SystemCounters getSystemCounters(int nodeIndex) { + return this.nodeInstances.get(nodeIndex).getInstance(SystemCounters.class); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicTest.java index 9610311e0d..1013213918 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/DeterministicTest.java @@ -71,47 +71,46 @@ import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.google.inject.util.Modules; +import com.radixdlt.FunctionalNodeModule; +import com.radixdlt.MockedCryptoModule; import com.radixdlt.MockedKeyModule; +import com.radixdlt.MockedPersistenceStoreModule; import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.bft.Self; -import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidator; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.PacemakerMaxExponent; import com.radixdlt.consensus.bft.PacemakerRate; import com.radixdlt.consensus.bft.PacemakerTimeout; +import com.radixdlt.consensus.bft.Self; import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.bft.ViewUpdate; import com.radixdlt.consensus.epoch.EpochView; +import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.environment.EventProcessor; -import com.radixdlt.FunctionalNodeModule; -import com.radixdlt.MockedCryptoModule; -import com.radixdlt.MockedPersistenceStoreModule; +import com.radixdlt.environment.deterministic.network.ControlledMessage; +import com.radixdlt.environment.deterministic.network.DeterministicNetwork; +import com.radixdlt.environment.deterministic.network.MessageMutator; +import com.radixdlt.environment.deterministic.network.MessageSelector; +import com.radixdlt.integration.distributed.deterministic.configuration.EpochNodeWeightMapping; +import com.radixdlt.integration.distributed.deterministic.configuration.NodeIndexAndWeight; import com.radixdlt.ledger.LedgerUpdate; +import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; import com.radixdlt.network.p2p.NoOpPeerControl; import com.radixdlt.network.p2p.PeerControl; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.networks.Addressing; import com.radixdlt.networks.Network; import com.radixdlt.recovery.MockedRecoveryModule; -import com.radixdlt.integration.distributed.deterministic.configuration.EpochNodeWeightMapping; -import com.radixdlt.integration.distributed.deterministic.configuration.NodeIndexAndWeight; -import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; -import com.radixdlt.environment.deterministic.network.ControlledMessage; -import com.radixdlt.environment.deterministic.network.DeterministicNetwork; -import com.radixdlt.environment.deterministic.network.MessageMutator; -import com.radixdlt.environment.deterministic.network.MessageSelector; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.utils.KeyComparator; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.statecomputer.EpochCeilingView; import com.radixdlt.sync.MockedCommittedReaderModule; import com.radixdlt.sync.SyncConfig; +import com.radixdlt.utils.KeyComparator; +import com.radixdlt.utils.TimeSupplier; import com.radixdlt.utils.UInt256; - import io.reactivex.rxjava3.schedulers.Timed; import java.io.PrintStream; import java.util.Objects; @@ -123,365 +122,359 @@ import java.util.stream.Stream; /** - * A deterministic test where each event that occurs in the network - * is emitted and processed synchronously by the caller. + * A deterministic test where each event that occurs in the network is emitted and processed + * synchronously by the caller. */ public final class DeterministicTest { - private final DeterministicNodes nodes; - private final DeterministicNetwork network; - - private DeterministicTest( - ImmutableList nodes, - MessageSelector messageSelector, - MessageMutator messageMutator, - Module baseModule, - Module overrideModule - ) { - this.network = new DeterministicNetwork( - nodes, - messageSelector, - messageMutator - ); - - this.nodes = new DeterministicNodes( - nodes, - this.network, - baseModule, - overrideModule - ); - } - - public static class Builder { - private ImmutableList nodes = ImmutableList.of(BFTNode.create(ECKeyPair.generateNew().getPublicKey())); - private MessageSelector messageSelector = MessageSelector.firstSelector(); - private MessageMutator messageMutator = MessageMutator.nothing(); - private long pacemakerTimeout = 1000L; - private EpochNodeWeightMapping epochNodeWeightMapping = null; - private Module overrideModule = null; - private ImmutableList.Builder modules = ImmutableList.builder(); - - private Builder() { - // Nothing to do here - } - - public Builder numNodes(int numNodes) { - this.nodes = Stream.generate(ECKeyPair::generateNew) - .limit(numNodes) - .map(ECKeyPair::getPublicKey) - .sorted(KeyComparator.instance()) - .map(BFTNode::create) - .collect(ImmutableList.toImmutableList()); - return this; - } - - /** - * Override with an incorrect module which should cause a test to fail. - * TODO: Refactor to make the link between incorrect module and failing test - * more explicit. - * - * @param module the incorrect module - * @return the current builder - */ - public Builder overrideWithIncorrectModule(Module module) { - this.overrideModule = module; - return this; - } - - public Builder epochNodeIndexesMapping(LongFunction epochToNodeIndexesMapping) { - Objects.requireNonNull(epochToNodeIndexesMapping); - this.epochNodeWeightMapping = epoch -> equalWeight(epochToNodeIndexesMapping.apply(epoch)); - return this; - } - - public Builder epochNodeWeightMapping(EpochNodeWeightMapping epochNodeWeightMapping) { - this.epochNodeWeightMapping = Objects.requireNonNull(epochNodeWeightMapping); - return this; - } - - public Builder messageSelector(MessageSelector messageSelector) { - this.messageSelector = Objects.requireNonNull(messageSelector); - return this; - } - - public Builder messageMutator(MessageMutator messageMutator) { - this.messageMutator = Objects.requireNonNull(messageMutator); - return this; - } - - public Builder messageMutators(MessageMutator... messageMutators) { - final var combinedMutator = Stream.of(messageMutators).reduce(MessageMutator::andThen).get(); - return this.messageMutator(combinedMutator); - } - - public Builder pacemakerTimeout(long pacemakerTimeout) { - if (pacemakerTimeout <= 0) { - throw new IllegalArgumentException("Pacemaker timeout must be positive: " + pacemakerTimeout); - } - this.pacemakerTimeout = pacemakerTimeout; - return this; - } - - public DeterministicTest buildWithEpochs(View epochHighView) { - Objects.requireNonNull(epochHighView); - modules.add(new FunctionalNodeModule(true, true, false, false, false, true, false)); - addEpochedConsensusProcessorModule(epochHighView); - return build(); - } - - public DeterministicTest buildWithEpochsAndSync(View epochHighView, SyncConfig syncConfig) { - Objects.requireNonNull(epochHighView); - modules.add(new FunctionalNodeModule(true, true, false, false, false, true, true)); - modules.add(new MockedCommittedReaderModule()); - modules.add(new AbstractModule() { - @Provides - private PeersView peersView(@Self BFTNode self) { - return () -> nodes.stream() - .filter(n -> !self.equals(n)) - .map(PeersView.PeerInfo::fromBftNode); - } - - @Provides - private SyncConfig syncConfig() { - return syncConfig; - } - }); - addEpochedConsensusProcessorModule(epochHighView); - return build(); - } - - public DeterministicTest buildWithoutEpochs() { - modules.add(new FunctionalNodeModule(true, false, false, false, false, false, false)); - addNonEpochedConsensusProcessorModule(); - return build(); - } - - private DeterministicTest build() { - modules.add(new AbstractModule() { - @Override - public void configure() { - bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); - bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(50); - bindConstant().annotatedWith(PacemakerTimeout.class).to(pacemakerTimeout); - bindConstant().annotatedWith(PacemakerRate.class).to(2.0); - // Use constant timeout for now - bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); - bind(TimeSupplier.class).toInstance(System::currentTimeMillis); - bind(Random.class).toInstance(new Random(123456)); - bind(RateLimiter.class).annotatedWith(GetVerticesRequestRateLimit.class) - .toInstance(unlimitedRateLimiter()); - bind(PeerControl.class).toInstance(new NoOpPeerControl()); - } - }); - modules.add(new MockedKeyModule()); - modules.add(new MockedCryptoModule()); - modules.add(new MockedPersistenceStoreModule()); - modules.add(new MockedRecoveryModule()); - - return new DeterministicTest( - this.nodes, - this.messageSelector, - this.messageMutator, - Modules.combine(modules.build()), - overrideModule - ); - } - - private void addNonEpochedConsensusProcessorModule() { - final var validatorSet = validatorSetMapping().apply(1L); - modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(BFTValidatorSet.class).toInstance(validatorSet); - } - }); - } - - private void addEpochedConsensusProcessorModule(View epochHighView) { - // TODO: adapter from LongFunction to Function shouldn't be needed - Function epochToValidatorSetMapping = validatorSetMapping()::apply; - modules.add(new AbstractModule() { - @Override - public void configure() { - bind(View.class).annotatedWith(EpochCeilingView.class).toInstance(epochHighView); - bind(BFTValidatorSet.class).toInstance(epochToValidatorSetMapping.apply(1L)); - bind(new TypeLiteral>() { }).toInstance(epochView -> { }); - bind(new TypeLiteral>() { }).toInstance(t -> { }); - } - - @Provides - public Function epochToNodeMapper() { - return epochToValidatorSetMapping; - } - }); - } - - private LongFunction validatorSetMapping() { - return epochNodeWeightMapping == null - ? epoch -> completeEqualWeightValidatorSet(this.nodes) - : epoch -> partialMixedWeightValidatorSet(epoch, this.nodes, this.epochNodeWeightMapping); - } - - private static BFTValidatorSet completeEqualWeightValidatorSet(ImmutableList nodes) { - return BFTValidatorSet.from( - nodes.stream() - .map(node -> BFTValidator.from(node, UInt256.ONE)) - ); - } - - private static BFTValidatorSet partialMixedWeightValidatorSet( - long epoch, - ImmutableList nodes, - EpochNodeWeightMapping mapper - ) { - return BFTValidatorSet.from( - mapper.nodesAndWeightFor(epoch) - .map(niw -> BFTValidator.from(nodes.get(niw.index()), niw.weight())) - ); - } - - private static Stream equalWeight(IntStream indexes) { - return indexes.mapToObj(i -> NodeIndexAndWeight.from(i, UInt256.ONE)); - } - - private static RateLimiter unlimitedRateLimiter() { - return RateLimiter.create(Double.MAX_VALUE); - } - } - - public static Builder builder() { - return new Builder(); - } - - public DeterministicNetwork getNetwork() { - return this.network; - } - - public DeterministicNodes getNodes() { - return this.nodes; - } - - public interface DeterministicManualExecutor { - void start(); - void processNext(int senderIndex, int receiverIndex, Class eventClass); - } - - public DeterministicManualExecutor createExecutor() { - return new DeterministicManualExecutor() { - @Override - public void start() { - nodes.start(); - } - - @Override - public void processNext(int senderIndex, int receiverIndex, Class eventClass) { - Timed nextMsg = network.nextMessage(msg -> msg.channelId().senderIndex() == senderIndex - && msg.channelId().receiverIndex() == receiverIndex - && eventClass.isInstance(msg.message())); - - nodes.handleMessage(nextMsg); - } - }; - } - - public DeterministicTest runForCount(int count) { - this.nodes.start(); - - for (int i = 0; i < count; i++) { - Timed nextMsg = this.network.nextMessage(); - this.nodes.handleMessage(nextMsg); - } - - return this; - } - - public DeterministicTest runUntil(Predicate> stopPredicate) { - this.nodes.start(); - - while (true) { - Timed nextMsg = this.network.nextMessage(); - if (stopPredicate.test(nextMsg)) { - break; - } - - this.nodes.handleMessage(nextMsg); - } - - return this; - } - - /** - * Returns a predicate that stops processing messages after a specified number of epochs and - * views. - * - * @param maxEpochView the last epoch and view to process - * @return a predicate that halts - * processing after the specified number of epochs and views - */ - public static Predicate> hasReachedEpochView(EpochView maxEpochView) { - return timedMsg -> { - ControlledMessage message = timedMsg.value(); - if (!(message.message() instanceof Proposal)) { - return false; - } - Proposal proposal = (Proposal) message.message(); - EpochView nev = EpochView.of(proposal.getEpoch(), proposal.getView()); - return (nev.compareTo(maxEpochView) > 0); - }; - } - - - /** - * Returns a predicate that stops processing messages after a specified number of views. - * - * @param view the last view to process - * @return a predicate that return true after the specified number of views - */ - public static Predicate> hasReachedView(View view) { - final long maxViewNumber = view.previous().number(); - return timedMsg -> { - ControlledMessage message = timedMsg.value(); - if (!(message.message() instanceof Proposal)) { - return false; - } - Proposal p = (Proposal) message.message(); - return (p.getView().number() > maxViewNumber); - }; - } - - public static Predicate> viewUpdateOnNode(View view, int nodeIndex) { - return timedMsg -> { - final var message = timedMsg.value(); - if (!(message.message() instanceof ViewUpdate)) { - return false; - } - final var viewUpdate = (ViewUpdate) message.message(); - return message.channelId().receiverIndex() == nodeIndex - && viewUpdate.getCurrentView().gte(view); - }; - } - - public static Predicate> ledgerStateVersionOnNode(long stateVersion, int nodeIndex) { - return timedMsg -> { - final var message = timedMsg.value(); - if (!(message.message() instanceof LedgerUpdate)) { - return false; - } - final var ledgerUpdate = (LedgerUpdate) message.message(); - return message.channelId().receiverIndex() == nodeIndex - && ledgerUpdate.getTail().getStateVersion() >= stateVersion; - }; - } - - public SystemCounters getSystemCounters(int nodeIndex) { - return this.nodes.getSystemCounters(nodeIndex); - } - - public int numNodes() { - return this.nodes.numNodes(); - } - - // Debugging aid for messages - public void dumpMessages(PrintStream out) { - this.network.dumpMessages(out); - } + private final DeterministicNodes nodes; + private final DeterministicNetwork network; + + private DeterministicTest( + ImmutableList nodes, + MessageSelector messageSelector, + MessageMutator messageMutator, + Module baseModule, + Module overrideModule) { + this.network = new DeterministicNetwork(nodes, messageSelector, messageMutator); + + this.nodes = new DeterministicNodes(nodes, this.network, baseModule, overrideModule); + } + + public static class Builder { + private ImmutableList nodes = + ImmutableList.of(BFTNode.create(ECKeyPair.generateNew().getPublicKey())); + private MessageSelector messageSelector = MessageSelector.firstSelector(); + private MessageMutator messageMutator = MessageMutator.nothing(); + private long pacemakerTimeout = 1000L; + private EpochNodeWeightMapping epochNodeWeightMapping = null; + private Module overrideModule = null; + private ImmutableList.Builder modules = ImmutableList.builder(); + + private Builder() { + // Nothing to do here + } + + public Builder numNodes(int numNodes) { + this.nodes = + Stream.generate(ECKeyPair::generateNew) + .limit(numNodes) + .map(ECKeyPair::getPublicKey) + .sorted(KeyComparator.instance()) + .map(BFTNode::create) + .collect(ImmutableList.toImmutableList()); + return this; + } + + /** + * Override with an incorrect module which should cause a test to fail. TODO: Refactor to make + * the link between incorrect module and failing test more explicit. + * + * @param module the incorrect module + * @return the current builder + */ + public Builder overrideWithIncorrectModule(Module module) { + this.overrideModule = module; + return this; + } + + public Builder epochNodeIndexesMapping(LongFunction epochToNodeIndexesMapping) { + Objects.requireNonNull(epochToNodeIndexesMapping); + this.epochNodeWeightMapping = epoch -> equalWeight(epochToNodeIndexesMapping.apply(epoch)); + return this; + } + + public Builder epochNodeWeightMapping(EpochNodeWeightMapping epochNodeWeightMapping) { + this.epochNodeWeightMapping = Objects.requireNonNull(epochNodeWeightMapping); + return this; + } + + public Builder messageSelector(MessageSelector messageSelector) { + this.messageSelector = Objects.requireNonNull(messageSelector); + return this; + } + + public Builder messageMutator(MessageMutator messageMutator) { + this.messageMutator = Objects.requireNonNull(messageMutator); + return this; + } + + public Builder messageMutators(MessageMutator... messageMutators) { + final var combinedMutator = Stream.of(messageMutators).reduce(MessageMutator::andThen).get(); + return this.messageMutator(combinedMutator); + } + + public Builder pacemakerTimeout(long pacemakerTimeout) { + if (pacemakerTimeout <= 0) { + throw new IllegalArgumentException( + "Pacemaker timeout must be positive: " + pacemakerTimeout); + } + this.pacemakerTimeout = pacemakerTimeout; + return this; + } + + public DeterministicTest buildWithEpochs(View epochHighView) { + Objects.requireNonNull(epochHighView); + modules.add(new FunctionalNodeModule(true, true, false, false, false, true, false)); + addEpochedConsensusProcessorModule(epochHighView); + return build(); + } + + public DeterministicTest buildWithEpochsAndSync(View epochHighView, SyncConfig syncConfig) { + Objects.requireNonNull(epochHighView); + modules.add(new FunctionalNodeModule(true, true, false, false, false, true, true)); + modules.add(new MockedCommittedReaderModule()); + modules.add( + new AbstractModule() { + @Provides + private PeersView peersView(@Self BFTNode self) { + return () -> + nodes.stream().filter(n -> !self.equals(n)).map(PeersView.PeerInfo::fromBftNode); + } + + @Provides + private SyncConfig syncConfig() { + return syncConfig; + } + }); + addEpochedConsensusProcessorModule(epochHighView); + return build(); + } + + public DeterministicTest buildWithoutEpochs() { + modules.add(new FunctionalNodeModule(true, false, false, false, false, false, false)); + addNonEpochedConsensusProcessorModule(); + return build(); + } + + private DeterministicTest build() { + modules.add( + new AbstractModule() { + @Override + public void configure() { + bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); + bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(50); + bindConstant().annotatedWith(PacemakerTimeout.class).to(pacemakerTimeout); + bindConstant().annotatedWith(PacemakerRate.class).to(2.0); + // Use constant timeout for now + bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); + bind(TimeSupplier.class).toInstance(System::currentTimeMillis); + bind(Random.class).toInstance(new Random(123456)); + bind(RateLimiter.class) + .annotatedWith(GetVerticesRequestRateLimit.class) + .toInstance(unlimitedRateLimiter()); + bind(PeerControl.class).toInstance(new NoOpPeerControl()); + } + }); + modules.add(new MockedKeyModule()); + modules.add(new MockedCryptoModule()); + modules.add(new MockedPersistenceStoreModule()); + modules.add(new MockedRecoveryModule()); + + return new DeterministicTest( + this.nodes, + this.messageSelector, + this.messageMutator, + Modules.combine(modules.build()), + overrideModule); + } + + private void addNonEpochedConsensusProcessorModule() { + final var validatorSet = validatorSetMapping().apply(1L); + modules.add( + new AbstractModule() { + @Override + protected void configure() { + bind(BFTValidatorSet.class).toInstance(validatorSet); + } + }); + } + + private void addEpochedConsensusProcessorModule(View epochHighView) { + // TODO: adapter from LongFunction to Function + // shouldn't be needed + Function epochToValidatorSetMapping = validatorSetMapping()::apply; + modules.add( + new AbstractModule() { + @Override + public void configure() { + bind(View.class).annotatedWith(EpochCeilingView.class).toInstance(epochHighView); + bind(BFTValidatorSet.class).toInstance(epochToValidatorSetMapping.apply(1L)); + bind(new TypeLiteral>() {}).toInstance(epochView -> {}); + bind(new TypeLiteral>() {}) + .toInstance(t -> {}); + } + + @Provides + public Function epochToNodeMapper() { + return epochToValidatorSetMapping; + } + }); + } + + private LongFunction validatorSetMapping() { + return epochNodeWeightMapping == null + ? epoch -> completeEqualWeightValidatorSet(this.nodes) + : epoch -> partialMixedWeightValidatorSet(epoch, this.nodes, this.epochNodeWeightMapping); + } + + private static BFTValidatorSet completeEqualWeightValidatorSet(ImmutableList nodes) { + return BFTValidatorSet.from(nodes.stream().map(node -> BFTValidator.from(node, UInt256.ONE))); + } + + private static BFTValidatorSet partialMixedWeightValidatorSet( + long epoch, ImmutableList nodes, EpochNodeWeightMapping mapper) { + return BFTValidatorSet.from( + mapper + .nodesAndWeightFor(epoch) + .map(niw -> BFTValidator.from(nodes.get(niw.index()), niw.weight()))); + } + + private static Stream equalWeight(IntStream indexes) { + return indexes.mapToObj(i -> NodeIndexAndWeight.from(i, UInt256.ONE)); + } + + private static RateLimiter unlimitedRateLimiter() { + return RateLimiter.create(Double.MAX_VALUE); + } + } + + public static Builder builder() { + return new Builder(); + } + + public DeterministicNetwork getNetwork() { + return this.network; + } + + public DeterministicNodes getNodes() { + return this.nodes; + } + + public interface DeterministicManualExecutor { + void start(); + + void processNext(int senderIndex, int receiverIndex, Class eventClass); + } + + public DeterministicManualExecutor createExecutor() { + return new DeterministicManualExecutor() { + @Override + public void start() { + nodes.start(); + } + + @Override + public void processNext(int senderIndex, int receiverIndex, Class eventClass) { + Timed nextMsg = + network.nextMessage( + msg -> + msg.channelId().senderIndex() == senderIndex + && msg.channelId().receiverIndex() == receiverIndex + && eventClass.isInstance(msg.message())); + + nodes.handleMessage(nextMsg); + } + }; + } + + public DeterministicTest runForCount(int count) { + this.nodes.start(); + + for (int i = 0; i < count; i++) { + Timed nextMsg = this.network.nextMessage(); + this.nodes.handleMessage(nextMsg); + } + + return this; + } + + public DeterministicTest runUntil(Predicate> stopPredicate) { + this.nodes.start(); + + while (true) { + Timed nextMsg = this.network.nextMessage(); + if (stopPredicate.test(nextMsg)) { + break; + } + + this.nodes.handleMessage(nextMsg); + } + + return this; + } + + /** + * Returns a predicate that stops processing messages after a specified number of epochs and + * views. + * + * @param maxEpochView the last epoch and view to process + * @return a predicate that halts processing after the specified number of epochs and views + */ + public static Predicate> hasReachedEpochView(EpochView maxEpochView) { + return timedMsg -> { + ControlledMessage message = timedMsg.value(); + if (!(message.message() instanceof Proposal)) { + return false; + } + Proposal proposal = (Proposal) message.message(); + EpochView nev = EpochView.of(proposal.getEpoch(), proposal.getView()); + return (nev.compareTo(maxEpochView) > 0); + }; + } + + /** + * Returns a predicate that stops processing messages after a specified number of views. + * + * @param view the last view to process + * @return a predicate that return true after the specified number of views + */ + public static Predicate> hasReachedView(View view) { + final long maxViewNumber = view.previous().number(); + return timedMsg -> { + ControlledMessage message = timedMsg.value(); + if (!(message.message() instanceof Proposal)) { + return false; + } + Proposal p = (Proposal) message.message(); + return (p.getView().number() > maxViewNumber); + }; + } + + public static Predicate> viewUpdateOnNode(View view, int nodeIndex) { + return timedMsg -> { + final var message = timedMsg.value(); + if (!(message.message() instanceof ViewUpdate)) { + return false; + } + final var viewUpdate = (ViewUpdate) message.message(); + return message.channelId().receiverIndex() == nodeIndex + && viewUpdate.getCurrentView().gte(view); + }; + } + + public static Predicate> ledgerStateVersionOnNode( + long stateVersion, int nodeIndex) { + return timedMsg -> { + final var message = timedMsg.value(); + if (!(message.message() instanceof LedgerUpdate)) { + return false; + } + final var ledgerUpdate = (LedgerUpdate) message.message(); + return message.channelId().receiverIndex() == nodeIndex + && ledgerUpdate.getTail().getStateVersion() >= stateVersion; + }; + } + + public SystemCounters getSystemCounters(int nodeIndex) { + return this.nodes.getSystemCounters(nodeIndex); + } + + public int numNodes() { + return this.nodes.numNodes(); + } + + // Debugging aid for messages + public void dumpMessages(PrintStream out) { + this.network.dumpMessages(out); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEvents.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEvents.java index d923856c19..e7aa794c75 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEvents.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEvents.java @@ -68,46 +68,43 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.environment.EventProcessor; import com.radixdlt.environment.EventProcessorOnDispatch; - import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; -/** - * Manages events from a network of nodes. Used for processing events for tests. - */ +/** Manages events from a network of nodes. Used for processing events for tests. */ public final class NodeEvents { - public static class NodeEventProcessor { - private final Class eventClass; - private final BiConsumer processor; + public static class NodeEventProcessor { + private final Class eventClass; + private final BiConsumer processor; - public NodeEventProcessor(Class eventClass, BiConsumer processor) { - this.eventClass = eventClass; - this.processor = processor; - } + public NodeEventProcessor(Class eventClass, BiConsumer processor) { + this.eventClass = eventClass; + this.processor = processor; + } - public Class getEventClass() { - return eventClass; - } + public Class getEventClass() { + return eventClass; + } - private void process(BFTNode node, Object event) { - this.processor.accept(node, eventClass.cast(event)); - } - } + private void process(BFTNode node, Object event) { + this.processor.accept(node, eventClass.cast(event)); + } + } - private final Map, Set>> processors; + private final Map, Set>> processors; - @Inject - public NodeEvents(Map, Set>> processors) { - this.processors = Objects.requireNonNull(processors); - } + @Inject + public NodeEvents(Map, Set>> processors) { + this.processors = Objects.requireNonNull(processors); + } - public EventProcessor processor(BFTNode node, Class eventClass) { - return t -> processors.get(eventClass).forEach(c -> c.process(node, t)); - } + public EventProcessor processor(BFTNode node, Class eventClass) { + return t -> processors.get(eventClass).forEach(c -> c.process(node, t)); + } - public EventProcessorOnDispatch processorOnDispatch(BFTNode node, Class eventClass) { - return new EventProcessorOnDispatch<>(eventClass, processor(node, eventClass)); - } + public EventProcessorOnDispatch processorOnDispatch(BFTNode node, Class eventClass) { + return new EventProcessorOnDispatch<>(eventClass, processor(node, eventClass)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEventsModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEventsModule.java index 071fdacde9..93526014dc 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEventsModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/NodeEventsModule.java @@ -72,18 +72,17 @@ import java.util.Set; import java.util.stream.Collectors; -/** - * Manages events for a node network - */ +/** Manages events for a node network */ public class NodeEventsModule extends AbstractModule { - @Override - public void configure() { - bind(NodeEvents.class).in(Scopes.SINGLETON); - } + @Override + public void configure() { + bind(NodeEvents.class).in(Scopes.SINGLETON); + } - @Provides - public Map, Set>> safetyCheckProcessor(Set> processors) { - return processors.stream() - .collect(Collectors.groupingBy(NodeEventProcessor::getEventClass, Collectors.toSet())); - } + @Provides + public Map, Set>> safetyCheckProcessor( + Set> processors) { + return processors.stream() + .collect(Collectors.groupingBy(NodeEventProcessor::getEventClass, Collectors.toSet())); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/SafetyCheckerModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/SafetyCheckerModule.java index 196a37ef26..f903c6d947 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/SafetyCheckerModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/SafetyCheckerModule.java @@ -75,23 +75,20 @@ import com.radixdlt.integration.invariants.SafetyChecker; import java.util.Optional; -/** - * Module which checks for consensus safety and throws exception on failure. - */ +/** Module which checks for consensus safety and throws exception on failure. */ public class SafetyCheckerModule extends AbstractModule { - @Override - public void configure() { - bind(SafetyChecker.class).in(Scopes.SINGLETON); - } + @Override + public void configure() { + bind(SafetyChecker.class).in(Scopes.SINGLETON); + } - @ProvidesIntoSet - public NodeEventProcessor safetyCheckProcessor(SafetyChecker safetyChecker) { - return new NodeEventProcessor<>( - BFTCommittedUpdate.class, - (node, update) -> { - Optional maybeError = safetyChecker.process(node, update); - assertThat(maybeError).isEmpty(); - } - ); - } + @ProvidesIntoSet + public NodeEventProcessor safetyCheckProcessor(SafetyChecker safetyChecker) { + return new NodeEventProcessor<>( + BFTCommittedUpdate.class, + (node, update) -> { + Optional maybeError = safetyChecker.process(node, update); + assertThat(maybeError).isEmpty(); + }); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/EpochNodeWeightMapping.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/EpochNodeWeightMapping.java index a6d3912bcb..549507b5ec 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/EpochNodeWeightMapping.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/EpochNodeWeightMapping.java @@ -64,67 +64,62 @@ package com.radixdlt.integration.distributed.deterministic.configuration; +import com.radixdlt.utils.UInt256; import java.util.Arrays; import java.util.function.IntFunction; import java.util.stream.IntStream; import java.util.stream.Stream; -import com.radixdlt.utils.UInt256; - -/** - * Mapping from epoch to validator set. - */ +/** Mapping from epoch to validator set. */ @FunctionalInterface public interface EpochNodeWeightMapping { - Stream nodesAndWeightFor(long epoch); + Stream nodesAndWeightFor(long epoch); - /** - * Returns an {@code EpochValidatorSetMapping} of the specified size - * and all with the specified weight. - */ - static EpochNodeWeightMapping constant(int numNodes, long weight) { - return repeatingSequence(numNodes, UInt256.from(weight)); - } + /** + * Returns an {@code EpochValidatorSetMapping} of the specified size and all with the specified + * weight. + */ + static EpochNodeWeightMapping constant(int numNodes, long weight) { + return repeatingSequence(numNodes, UInt256.from(weight)); + } - /** - * Returns an {@code EpochValidatorSetMapping} of the specified size - * and all with the specified weight. - */ - static EpochNodeWeightMapping constant(int numNodes, UInt256 weight) { - return repeatingSequence(numNodes, weight); - } + /** + * Returns an {@code EpochValidatorSetMapping} of the specified size and all with the specified + * weight. + */ + static EpochNodeWeightMapping constant(int numNodes, UInt256 weight) { + return repeatingSequence(numNodes, weight); + } - /** - * Returns an {@code EpochValidatorSetMapping} of the specified size - * and with the specified weights. If the length of {@code weights} is - * less than {@code numNodes}, then the weights are cycled starting from - * the zeroth weight. - */ - static EpochNodeWeightMapping repeatingSequence(int numNodes, long... weights) { - UInt256[] weights256 = Arrays.stream(weights) - .mapToObj(UInt256::from) - .toArray(UInt256[]::new); - return repeatingSequence(numNodes, weights256); - } + /** + * Returns an {@code EpochValidatorSetMapping} of the specified size and with the specified + * weights. If the length of {@code weights} is less than {@code numNodes}, then the weights are + * cycled starting from the zeroth weight. + */ + static EpochNodeWeightMapping repeatingSequence(int numNodes, long... weights) { + UInt256[] weights256 = Arrays.stream(weights).mapToObj(UInt256::from).toArray(UInt256[]::new); + return repeatingSequence(numNodes, weights256); + } - /** - * Returns an {@code EpochValidatorSetMapping} of the specified size - * and with the specified weights. If the length of {@code weights} is - * less than {@code numNodes}, then the weights are cycled starting from - * the zeroth weight. - */ - static EpochNodeWeightMapping repeatingSequence(int numNodes, UInt256... weights) { - int length = weights.length; - return epoch -> IntStream.range(0, numNodes) - .mapToObj(index -> NodeIndexAndWeight.from(index, weights[index % length])); - } + /** + * Returns an {@code EpochValidatorSetMapping} of the specified size and with the specified + * weights. If the length of {@code weights} is less than {@code numNodes}, then the weights are + * cycled starting from the zeroth weight. + */ + static EpochNodeWeightMapping repeatingSequence(int numNodes, UInt256... weights) { + int length = weights.length; + return epoch -> + IntStream.range(0, numNodes) + .mapToObj(index -> NodeIndexAndWeight.from(index, weights[index % length])); + } - /** - * Returns an {@code EpochValidatorSetMapping} of the specified size - * and with each weight computed using the specified function. - */ - static EpochNodeWeightMapping computed(int numNodes, IntFunction function) { - return epoch -> IntStream.range(0, numNodes) - .mapToObj(index -> NodeIndexAndWeight.from(index, function.apply(index))); - } + /** + * Returns an {@code EpochValidatorSetMapping} of the specified size and with each weight computed + * using the specified function. + */ + static EpochNodeWeightMapping computed(int numNodes, IntFunction function) { + return epoch -> + IntStream.range(0, numNodes) + .mapToObj(index -> NodeIndexAndWeight.from(index, function.apply(index))); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/NodeIndexAndWeight.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/NodeIndexAndWeight.java index 7d7aa18453..99bce32aa3 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/NodeIndexAndWeight.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/configuration/NodeIndexAndWeight.java @@ -64,52 +64,48 @@ package com.radixdlt.integration.distributed.deterministic.configuration; -import java.util.Objects; import com.radixdlt.utils.UInt256; +import java.util.Objects; -/** - * A node index, together with its weight. - */ +/** A node index, together with its weight. */ public final class NodeIndexAndWeight { - private final int index; - private final UInt256 weight; + private final int index; + private final UInt256 weight; - private NodeIndexAndWeight(int index, UInt256 weight) { - this.index = index; - this.weight = Objects.requireNonNull(weight); - } + private NodeIndexAndWeight(int index, UInt256 weight) { + this.index = index; + this.weight = Objects.requireNonNull(weight); + } - /** - * Returns a {@code NodeIndexAndWeight} from specified values. - */ - public static NodeIndexAndWeight from(int index, UInt256 weight) { - return new NodeIndexAndWeight(index, weight); - } + /** Returns a {@code NodeIndexAndWeight} from specified values. */ + public static NodeIndexAndWeight from(int index, UInt256 weight) { + return new NodeIndexAndWeight(index, weight); + } - public int index() { - return this.index; - } + public int index() { + return this.index; + } - public UInt256 weight() { - return this.weight; - } + public UInt256 weight() { + return this.weight; + } - @Override - public int hashCode() { - return weight.hashCode() * 31 + this.index; - } + @Override + public int hashCode() { + return weight.hashCode() * 31 + this.index; + } - @Override - public boolean equals(Object o) { - if (o instanceof NodeIndexAndWeight) { - NodeIndexAndWeight that = (NodeIndexAndWeight) o; - return this.index == that.index && this.weight.equals(that.weight); - } - return false; - } + @Override + public boolean equals(Object o) { + if (o instanceof NodeIndexAndWeight) { + NodeIndexAndWeight that = (NodeIndexAndWeight) o; + return this.index == that.index && this.weight.equals(that.weight); + } + return false; + } - @Override - public String toString() { - return String.format("%s[%s:%s]", getClass().getSimpleName(), this.index, this.weight); - } + @Override + public String toString() { + return String.format("%s[%s:%s]", getClass().getSimpleName(), this.index, this.weight); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/bft_sync/SyncToTimeoutQcTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/bft_sync/SyncToTimeoutQcTest.java index 17bed72b8e..1a0bab2c41 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/bft_sync/SyncToTimeoutQcTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/bft_sync/SyncToTimeoutQcTest.java @@ -64,6 +64,8 @@ package com.radixdlt.integration.distributed.deterministic.tests.bft_sync; +import static org.junit.Assert.assertEquals; + import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; @@ -72,72 +74,63 @@ import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; import com.radixdlt.integration.distributed.deterministic.DeterministicTest; -import org.junit.Test; - import java.util.Random; - -import static org.junit.Assert.assertEquals; +import org.junit.Test; /** -* If quorum is formed on a timeout (timeout certificate), and there's a node that's a single view behind - * (i.e. it didn't participate in forming of TC). Then it should be able to sync up (move to next view) - * as soon as it receives a proposal (with a TC). BFTSync should then immediately switch to next view - * without any additional sync requests. - * The setup is as follows: - * 1. there are 4 nodes - * 2. proposal is sent by the leader (0) but only received by 2 nodes (including the leader): 0 and 1 - * 3. two nodes vote on a proposal (0 and 1) - * 4. two nodes vote on an empty timeout vertex and broadcast the vote (2 and 3) - * 5. nodes 0 and 1 resend (broadcast) their vote with a timeout flag - * 6. node 0 doesn't receive any of the above votes - * 7. nodes 1, 2 and 3 can form a valid TC out of the votes they received, and they switch to the next view - * 8. next leader (node 1) sends out a proposal - * 9. proposal (with a valid TC) is received by node 0 (which is still on previous view) - * 10. node 0 is able to move to the next view just by processing the proposal's TC (no additional sync requests) - * Expected result: node 0 is at view 2 and no sync requests have been sent + * If quorum is formed on a timeout (timeout certificate), and there's a node that's a single view + * behind (i.e. it didn't participate in forming of TC). Then it should be able to sync up (move to + * next view) as soon as it receives a proposal (with a TC). BFTSync should then immediately switch + * to next view without any additional sync requests. The setup is as follows: 1. there are 4 nodes + * 2. proposal is sent by the leader (0) but only received by 2 nodes (including the leader): 0 and + * 1 3. two nodes vote on a proposal (0 and 1) 4. two nodes vote on an empty timeout vertex and + * broadcast the vote (2 and 3) 5. nodes 0 and 1 resend (broadcast) their vote with a timeout flag + * 6. node 0 doesn't receive any of the above votes 7. nodes 1, 2 and 3 can form a valid TC out of + * the votes they received, and they switch to the next view 8. next leader (node 1) sends out a + * proposal 9. proposal (with a valid TC) is received by node 0 (which is still on previous view) + * 10. node 0 is able to move to the next view just by processing the proposal's TC (no additional + * sync requests) Expected result: node 0 is at view 2 and no sync requests have been sent */ public class SyncToTimeoutQcTest { - private static final int NUM_NODES = 4; + private static final int NUM_NODES = 4; - private final Random random = new Random(123456); + private final Random random = new Random(123456); - @Test - public void sync_to_timeout_qc_test() { - final DeterministicTest test = DeterministicTest.builder() - .numNodes(NUM_NODES) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator( - dropProposalsToNodes(ImmutableSet.of(2, 3)) - .andThen(dropVotesToNode(0)) - ) - .buildWithEpochs(View.of(10)) - .runUntil(DeterministicTest.viewUpdateOnNode(View.of(2), 0)); + @Test + public void sync_to_timeout_qc_test() { + final DeterministicTest test = + DeterministicTest.builder() + .numNodes(NUM_NODES) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator(dropProposalsToNodes(ImmutableSet.of(2, 3)).andThen(dropVotesToNode(0))) + .buildWithEpochs(View.of(10)) + .runUntil(DeterministicTest.viewUpdateOnNode(View.of(2), 0)); - for (int nodeIndex = 0; nodeIndex < NUM_NODES; ++nodeIndex) { - final var counters = test.getSystemCounters(nodeIndex); - // no bft sync requests were needed - assertEquals(0, counters.get(CounterType.BFT_SYNC_REQUESTS_SENT)); - } - } + for (int nodeIndex = 0; nodeIndex < NUM_NODES; ++nodeIndex) { + final var counters = test.getSystemCounters(nodeIndex); + // no bft sync requests were needed + assertEquals(0, counters.get(CounterType.BFT_SYNC_REQUESTS_SENT)); + } + } - private static MessageMutator dropVotesToNode(int nodeIndex) { - return (message, queue) -> { - final var msg = message.message(); - if (msg instanceof Vote) { - return message.channelId().receiverIndex() == nodeIndex; - } - return false; - }; - } + private static MessageMutator dropVotesToNode(int nodeIndex) { + return (message, queue) -> { + final var msg = message.message(); + if (msg instanceof Vote) { + return message.channelId().receiverIndex() == nodeIndex; + } + return false; + }; + } - private static MessageMutator dropProposalsToNodes(ImmutableSet nodesIndices) { - return (message, queue) -> { - final var msg = message.message(); - if (msg instanceof Proposal) { - return nodesIndices.contains(message.channelId().receiverIndex()); - } - return false; - }; - } + private static MessageMutator dropProposalsToNodes(ImmutableSet nodesIndices) { + return (message, queue) -> { + final var msg = message.message(); + if (msg instanceof Proposal) { + return nodesIndices.contains(message.channelId().receiverIndex()); + } + return false; + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/DifferentTimestampsCauseTimeoutTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/DifferentTimestampsCauseTimeoutTest.java index 0db2475796..d216e271b4 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/DifferentTimestampsCauseTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/DifferentTimestampsCauseTimeoutTest.java @@ -64,19 +64,10 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus; +import com.google.common.collect.ImmutableMap; import com.google.inject.AbstractModule; import com.radixdlt.consensus.HashSigner; import com.radixdlt.consensus.HashVerifier; -import com.radixdlt.consensus.bft.BFTInsertUpdate; -import com.radixdlt.consensus.bft.ViewUpdate; -import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest.DeterministicManualExecutor; -import java.util.Map; -import java.util.Optional; - -import org.junit.Test; - -import com.google.common.collect.ImmutableMap; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.TimestampedECDSASignature; @@ -84,164 +75,177 @@ import com.radixdlt.consensus.UnverifiedVertex; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.VoteData; +import com.radixdlt.consensus.bft.BFTInsertUpdate; import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.ViewUpdate; +import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; import com.radixdlt.crypto.ECDSASignature; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.MessageMutator; +import com.radixdlt.integration.distributed.deterministic.DeterministicTest; +import com.radixdlt.integration.distributed.deterministic.DeterministicTest.DeterministicManualExecutor; import com.radixdlt.utils.Pair; +import java.util.Map; +import java.util.Optional; +import org.junit.Test; public class DifferentTimestampsCauseTimeoutTest { - @Test - public void when_four_nodes_receive_qcs_with_same_timestamps__quorum_is_achieved() { - final int numNodes = 4; - - DeterministicManualExecutor executor = DeterministicTest.builder() - .numNodes(numNodes) - .messageMutator(mutateProposalsBy(0)) - .buildWithoutEpochs() - .createExecutor(); - - executor.start(); - executeTwoViews(executor); - - executor.processNext(3, 3, ViewUpdate.class); - executor.processNext(3, 3, Proposal.class); - executor.processNext(3, 3, BFTInsertUpdate.class); - executor.processNext(3, 0, Proposal.class); - executor.processNext(0, 0, ViewUpdate.class); - executor.processNext(0, 0, BFTInsertUpdate.class); - executor.processNext(3, 1, Proposal.class); - executor.processNext(1, 1, ViewUpdate.class); - executor.processNext(1, 1, BFTInsertUpdate.class); - executor.processNext(3, 2, Proposal.class); - executor.processNext(2, 2, ViewUpdate.class); - executor.processNext(2, 2, BFTInsertUpdate.class); - } - - @Test - public void when_four_nodes_receive_qcs_with_different_timestamps__quorum_is_not_achieved() { - final int numNodes = 4; - - // TODO: this test isn't exactly right and should be updated so that - // TODO: byzantine node sends different sets of valid QCs to each node - DeterministicManualExecutor executor = DeterministicTest.builder() - .overrideWithIncorrectModule(new AbstractModule() { - @Override - protected void configure() { - bind(HashVerifier.class).toInstance((pubKey, hash, sig) -> true); - bind(HashSigner.class).toInstance(h -> ECDSASignature.zeroSignature()); - } - }) - .numNodes(numNodes) - .messageMutator(mutateProposalsBy(1)) - .buildWithoutEpochs() - .createExecutor(); - - executor.start(); - - executeTwoViews(executor); - - // Timeouts from nodes - executor.processNext(0, 0, ScheduledLocalTimeout.class); - executor.processNext(1, 1, ScheduledLocalTimeout.class); - executor.processNext(2, 2, ScheduledLocalTimeout.class); - executor.processNext(3, 3, ScheduledLocalTimeout.class); - } - - private void executeTwoViews(DeterministicManualExecutor executor) { - // Proposal here has genesis qc, which has no timestamps - executor.processNext(1, 1, Proposal.class); - executor.processNext(1, 1, BFTInsertUpdate.class); - executor.processNext(1, 0, Proposal.class); - executor.processNext(0, 0, BFTInsertUpdate.class); - executor.processNext(1, 2, Proposal.class); - executor.processNext(2, 2, BFTInsertUpdate.class); - executor.processNext(1, 3, Proposal.class); - executor.processNext(3, 3, BFTInsertUpdate.class); - - executor.processNext(2, 2, Vote.class); // Messages to self first - executor.processNext(1, 2, Vote.class); // Leader votes early as it sees proposal early - executor.processNext(0, 2, Vote.class); - executor.processNext(3, 2, Vote.class); - - // Proposal here should have timestamps from previous view - // They are mutated as required by the test - executor.processNext(2, 2, ViewUpdate.class); - executor.processNext(2, 2, Proposal.class); - executor.processNext(2, 2, BFTInsertUpdate.class); - executor.processNext(2, 0, Proposal.class); - executor.processNext(0, 0, ViewUpdate.class); - executor.processNext(0, 0, BFTInsertUpdate.class); - executor.processNext(2, 1, Proposal.class); - executor.processNext(1, 1, ViewUpdate.class); - executor.processNext(1, 1, BFTInsertUpdate.class); - executor.processNext(2, 3, Proposal.class); - executor.processNext(3, 3, ViewUpdate.class); - executor.processNext(3, 3, BFTInsertUpdate.class); - - executor.processNext(3, 3, Vote.class); - executor.processNext(2, 3, Vote.class); - executor.processNext(0, 3, Vote.class); - executor.processNext(1, 3, Vote.class); - } - - - private MessageMutator mutateProposalsBy(int factor) { - return (message, queue) -> { - ControlledMessage messageToUse = message; - Object msg = message.message(); - if (msg instanceof Proposal) { - Proposal p = (Proposal) msg; - int receiverIndex = message.channelId().receiverIndex(); - messageToUse = new ControlledMessage( - message.origin(), - message.channelId(), - mutateProposal(p, receiverIndex * factor), - message.typeLiteral(), - message.arrivalTime() - ); - } - queue.add(messageToUse); - return true; - }; - } - - private Proposal mutateProposal(Proposal p, int destination) { - QuorumCertificate committedQC = p.highQC().highestCommittedQC(); - UnverifiedVertex vertex = p.getVertex(); - ECDSASignature signature = p.getSignature(); - - return new Proposal(mutateVertex(vertex, destination), committedQC, signature, Optional.empty()); - } - - private UnverifiedVertex mutateVertex(UnverifiedVertex v, int destination) { - var qc = v.getQC(); - var view = v.getView(); - var txns = v.getTxns(); - var proposer = v.getProposer(); - - return UnverifiedVertex.create(mutateQC(qc, destination), view, txns, proposer); - } - - private QuorumCertificate mutateQC(QuorumCertificate qc, int destination) { - TimestampedECDSASignatures signatures = qc.getTimestampedSignatures(); - VoteData voteData = qc.getVoteData(); - - return new QuorumCertificate(voteData, mutateTimestampedSignatures(signatures, destination)); - } - - private TimestampedECDSASignatures mutateTimestampedSignatures(TimestampedECDSASignatures signatures, int destination) { - Map sigs = signatures.getSignatures(); - return new TimestampedECDSASignatures(sigs.entrySet().stream() - .map(e -> Pair.of(e.getKey(), mutateTimestampedSignature(e.getValue(), destination))) - .collect(ImmutableMap.toImmutableMap(Pair::getFirst, Pair::getSecond))); - } - - private TimestampedECDSASignature mutateTimestampedSignature(TimestampedECDSASignature signature, int destination) { - long timestamp = signature.timestamp(); - ECDSASignature sig = signature.signature(); - - return TimestampedECDSASignature.from(timestamp + destination, sig); - } + @Test + public void when_four_nodes_receive_qcs_with_same_timestamps__quorum_is_achieved() { + final int numNodes = 4; + + DeterministicManualExecutor executor = + DeterministicTest.builder() + .numNodes(numNodes) + .messageMutator(mutateProposalsBy(0)) + .buildWithoutEpochs() + .createExecutor(); + + executor.start(); + executeTwoViews(executor); + + executor.processNext(3, 3, ViewUpdate.class); + executor.processNext(3, 3, Proposal.class); + executor.processNext(3, 3, BFTInsertUpdate.class); + executor.processNext(3, 0, Proposal.class); + executor.processNext(0, 0, ViewUpdate.class); + executor.processNext(0, 0, BFTInsertUpdate.class); + executor.processNext(3, 1, Proposal.class); + executor.processNext(1, 1, ViewUpdate.class); + executor.processNext(1, 1, BFTInsertUpdate.class); + executor.processNext(3, 2, Proposal.class); + executor.processNext(2, 2, ViewUpdate.class); + executor.processNext(2, 2, BFTInsertUpdate.class); + } + + @Test + public void when_four_nodes_receive_qcs_with_different_timestamps__quorum_is_not_achieved() { + final int numNodes = 4; + + // TODO: this test isn't exactly right and should be updated so that + // TODO: byzantine node sends different sets of valid QCs to each node + DeterministicManualExecutor executor = + DeterministicTest.builder() + .overrideWithIncorrectModule( + new AbstractModule() { + @Override + protected void configure() { + bind(HashVerifier.class).toInstance((pubKey, hash, sig) -> true); + bind(HashSigner.class).toInstance(h -> ECDSASignature.zeroSignature()); + } + }) + .numNodes(numNodes) + .messageMutator(mutateProposalsBy(1)) + .buildWithoutEpochs() + .createExecutor(); + + executor.start(); + + executeTwoViews(executor); + + // Timeouts from nodes + executor.processNext(0, 0, ScheduledLocalTimeout.class); + executor.processNext(1, 1, ScheduledLocalTimeout.class); + executor.processNext(2, 2, ScheduledLocalTimeout.class); + executor.processNext(3, 3, ScheduledLocalTimeout.class); + } + + private void executeTwoViews(DeterministicManualExecutor executor) { + // Proposal here has genesis qc, which has no timestamps + executor.processNext(1, 1, Proposal.class); + executor.processNext(1, 1, BFTInsertUpdate.class); + executor.processNext(1, 0, Proposal.class); + executor.processNext(0, 0, BFTInsertUpdate.class); + executor.processNext(1, 2, Proposal.class); + executor.processNext(2, 2, BFTInsertUpdate.class); + executor.processNext(1, 3, Proposal.class); + executor.processNext(3, 3, BFTInsertUpdate.class); + + executor.processNext(2, 2, Vote.class); // Messages to self first + executor.processNext(1, 2, Vote.class); // Leader votes early as it sees proposal early + executor.processNext(0, 2, Vote.class); + executor.processNext(3, 2, Vote.class); + + // Proposal here should have timestamps from previous view + // They are mutated as required by the test + executor.processNext(2, 2, ViewUpdate.class); + executor.processNext(2, 2, Proposal.class); + executor.processNext(2, 2, BFTInsertUpdate.class); + executor.processNext(2, 0, Proposal.class); + executor.processNext(0, 0, ViewUpdate.class); + executor.processNext(0, 0, BFTInsertUpdate.class); + executor.processNext(2, 1, Proposal.class); + executor.processNext(1, 1, ViewUpdate.class); + executor.processNext(1, 1, BFTInsertUpdate.class); + executor.processNext(2, 3, Proposal.class); + executor.processNext(3, 3, ViewUpdate.class); + executor.processNext(3, 3, BFTInsertUpdate.class); + + executor.processNext(3, 3, Vote.class); + executor.processNext(2, 3, Vote.class); + executor.processNext(0, 3, Vote.class); + executor.processNext(1, 3, Vote.class); + } + + private MessageMutator mutateProposalsBy(int factor) { + return (message, queue) -> { + ControlledMessage messageToUse = message; + Object msg = message.message(); + if (msg instanceof Proposal) { + Proposal p = (Proposal) msg; + int receiverIndex = message.channelId().receiverIndex(); + messageToUse = + new ControlledMessage( + message.origin(), + message.channelId(), + mutateProposal(p, receiverIndex * factor), + message.typeLiteral(), + message.arrivalTime()); + } + queue.add(messageToUse); + return true; + }; + } + + private Proposal mutateProposal(Proposal p, int destination) { + QuorumCertificate committedQC = p.highQC().highestCommittedQC(); + UnverifiedVertex vertex = p.getVertex(); + ECDSASignature signature = p.getSignature(); + + return new Proposal( + mutateVertex(vertex, destination), committedQC, signature, Optional.empty()); + } + + private UnverifiedVertex mutateVertex(UnverifiedVertex v, int destination) { + var qc = v.getQC(); + var view = v.getView(); + var txns = v.getTxns(); + var proposer = v.getProposer(); + + return UnverifiedVertex.create(mutateQC(qc, destination), view, txns, proposer); + } + + private QuorumCertificate mutateQC(QuorumCertificate qc, int destination) { + TimestampedECDSASignatures signatures = qc.getTimestampedSignatures(); + VoteData voteData = qc.getVoteData(); + + return new QuorumCertificate(voteData, mutateTimestampedSignatures(signatures, destination)); + } + + private TimestampedECDSASignatures mutateTimestampedSignatures( + TimestampedECDSASignatures signatures, int destination) { + Map sigs = signatures.getSignatures(); + return new TimestampedECDSASignatures( + sigs.entrySet().stream() + .map(e -> Pair.of(e.getKey(), mutateTimestampedSignature(e.getValue(), destination))) + .collect(ImmutableMap.toImmutableMap(Pair::getFirst, Pair::getSecond))); + } + + private TimestampedECDSASignature mutateTimestampedSignature( + TimestampedECDSASignature signature, int destination) { + long timestamp = signature.timestamp(); + ECDSASignature sig = signature.signature(); + + return TimestampedECDSASignature.from(timestamp + destination, sig); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/FProposalDropperResponsiveTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/FProposalDropperResponsiveTest.java index 86ccd2f613..207ca08fa7 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/FProposalDropperResponsiveTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/FProposalDropperResponsiveTest.java @@ -67,10 +67,9 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; - +import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -81,107 +80,118 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; - import org.assertj.core.util.Sets; import org.junit.Test; public class FProposalDropperResponsiveTest { - private final Random random = new Random(123456789); - - private void runFProposalDropperResponsiveTest(int numNodes, Function> nodesToDropFunction) { - DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator(MessageMutator.dropTimeouts().andThen(dropNodes(numNodes, nodesToDropFunction))) - .buildWithoutEpochs() - .runForCount(30_000); - } - - private static MessageMutator dropNodes(int numNodes, Function> nodesToDropFunction) { - final Map> proposalsToDrop = new HashMap<>(); - final Map proposalCount = new HashMap<>(); - return (message, queue) -> { - Object msg = message.message(); - if (msg instanceof Proposal) { - final Proposal proposal = (Proposal) msg; - final View view = proposal.getVertex().getView(); - final Set nodesToDrop = proposalsToDrop.computeIfAbsent(view, nodesToDropFunction); - if (proposalCount.merge(view, 1, Integer::sum).equals(numNodes)) { - proposalsToDrop.remove(view); - proposalCount.remove(view); - } - return nodesToDrop.contains(message.channelId().receiverIndex()); - } - return false; - }; - } - - private void runRandomMaliciousNodesTest(int numNodes) { - runFProposalDropperResponsiveTest( - numNodes, - v -> { - List nodes = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); - Collections.shuffle(nodes, random); - return Sets.newHashSet(nodes.subList(0, (numNodes - 1) / 3)); - } - ); - } - - @Test - public void when_run_4_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runRandomMaliciousNodesTest(4); - } - - @Test - public void when_run_5_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runRandomMaliciousNodesTest(5); - } - - @Test - public void when_run_10_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runRandomMaliciousNodesTest(10); - } - - @Test - public void when_run_50_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runRandomMaliciousNodesTest(50); - } - - @Test - public void when_run_100_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runRandomMaliciousNodesTest(100); - } - - private void runStaticMaliciousNodesTest(int numNodes) { - Set nodes = Stream.iterate(0, a -> a + 1).limit((numNodes - 1) / 3).collect(ImmutableSet.toImmutableSet()); - this.runFProposalDropperResponsiveTest( - numNodes, - v -> nodes - ); - } - - @Test - public void when_run_4_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runStaticMaliciousNodesTest(4); - } - - @Test - public void when_run_5_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runStaticMaliciousNodesTest(5); - } - - @Test - public void when_run_10_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runStaticMaliciousNodesTest(10); - } - - @Test - public void when_run_50_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runStaticMaliciousNodesTest(50); - } - - @Test - public void when_run_100_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runStaticMaliciousNodesTest(100); - } + private final Random random = new Random(123456789); + + private void runFProposalDropperResponsiveTest( + int numNodes, Function> nodesToDropFunction) { + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator( + MessageMutator.dropTimeouts().andThen(dropNodes(numNodes, nodesToDropFunction))) + .buildWithoutEpochs() + .runForCount(30_000); + } + + private static MessageMutator dropNodes( + int numNodes, Function> nodesToDropFunction) { + final Map> proposalsToDrop = new HashMap<>(); + final Map proposalCount = new HashMap<>(); + return (message, queue) -> { + Object msg = message.message(); + if (msg instanceof Proposal) { + final Proposal proposal = (Proposal) msg; + final View view = proposal.getVertex().getView(); + final Set nodesToDrop = proposalsToDrop.computeIfAbsent(view, nodesToDropFunction); + if (proposalCount.merge(view, 1, Integer::sum).equals(numNodes)) { + proposalsToDrop.remove(view); + proposalCount.remove(view); + } + return nodesToDrop.contains(message.channelId().receiverIndex()); + } + return false; + }; + } + + private void runRandomMaliciousNodesTest(int numNodes) { + runFProposalDropperResponsiveTest( + numNodes, + v -> { + List nodes = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); + Collections.shuffle(nodes, random); + return Sets.newHashSet(nodes.subList(0, (numNodes - 1) / 3)); + }); + } + + @Test + public void + when_run_4_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(4); + } + + @Test + public void + when_run_5_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(5); + } + + @Test + public void + when_run_10_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(10); + } + + @Test + public void + when_run_50_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(50); + } + + @Test + public void + when_run_100_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(100); + } + + private void runStaticMaliciousNodesTest(int numNodes) { + Set nodes = + Stream.iterate(0, a -> a + 1) + .limit((numNodes - 1) / 3) + .collect(ImmutableSet.toImmutableSet()); + this.runFProposalDropperResponsiveTest(numNodes, v -> nodes); + } + + @Test + public void + when_run_4_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(4); + } + + @Test + public void + when_run_5_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(5); + } + + @Test + public void + when_run_10_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(10); + } + + @Test + public void + when_run_50_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(50); + } + + @Test + public void + when_run_100_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(100); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalDropperResponsiveTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalDropperResponsiveTest.java index d11ca5c6f8..bf82e4328c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalDropperResponsiveTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalDropperResponsiveTest.java @@ -66,10 +66,9 @@ import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; - +import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -77,84 +76,95 @@ import org.junit.Test; public class OneProposalDropperResponsiveTest { - private final Random random = new Random(123456); - - private void runOneProposalDropperResponsiveTest(int numNodes, Function nodeToDropFunction) { - DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator(MessageMutator.dropTimeouts().andThen(dropNode(numNodes, nodeToDropFunction))) - .buildWithoutEpochs() - .runForCount(30_000); - } - - private MessageMutator dropNode(int numNodes, Function nodeToDropFunction) { - final Map proposalToDrop = new HashMap<>(); - final Map proposalCount = new HashMap<>(); - return (message, queue) -> { - Object msg = message.message(); - if (msg instanceof Proposal) { - final Proposal proposal = (Proposal) msg; - final View view = proposal.getVertex().getView(); - final int nodeToDrop = proposalToDrop.computeIfAbsent(view, nodeToDropFunction); - if (proposalCount.merge(view, 1, Integer::sum).equals(numNodes)) { - proposalToDrop.remove(view); - proposalCount.remove(view); - } - return message.channelId().receiverIndex() == nodeToDrop; - } - return false; - }; - } - - - @Test - public void when_run_4_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(4, v -> random.nextInt(4)); - } - - @Test - public void when_run_5_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(5, v -> random.nextInt(5)); - } - - @Test - public void when_run_10_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(10, v -> random.nextInt(10)); - } - - @Test - public void when_run_50_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(50, v -> random.nextInt(50)); - } - - @Test - public void when_run_100_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(100, v -> random.nextInt(100)); - } - - @Test - public void when_run_4_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(4, v -> 0); - } - - @Test - public void when_run_5_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(5, v -> 0); - } - - @Test - public void when_run_10_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(10, v -> 0); - } - - @Test - public void when_run_50_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(50, v -> 0); - } - - @Test - public void when_run_100_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { - this.runOneProposalDropperResponsiveTest(100, v -> 0); - } + private final Random random = new Random(123456); + + private void runOneProposalDropperResponsiveTest( + int numNodes, Function nodeToDropFunction) { + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator( + MessageMutator.dropTimeouts().andThen(dropNode(numNodes, nodeToDropFunction))) + .buildWithoutEpochs() + .runForCount(30_000); + } + + private MessageMutator dropNode(int numNodes, Function nodeToDropFunction) { + final Map proposalToDrop = new HashMap<>(); + final Map proposalCount = new HashMap<>(); + return (message, queue) -> { + Object msg = message.message(); + if (msg instanceof Proposal) { + final Proposal proposal = (Proposal) msg; + final View view = proposal.getVertex().getView(); + final int nodeToDrop = proposalToDrop.computeIfAbsent(view, nodeToDropFunction); + if (proposalCount.merge(view, 1, Integer::sum).equals(numNodes)) { + proposalToDrop.remove(view); + proposalCount.remove(view); + } + return message.channelId().receiverIndex() == nodeToDrop; + } + return false; + }; + } + + @Test + public void + when_run_4_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(4, v -> random.nextInt(4)); + } + + @Test + public void + when_run_5_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(5, v -> random.nextInt(5)); + } + + @Test + public void + when_run_10_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(10, v -> random.nextInt(10)); + } + + @Test + public void + when_run_50_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(50, v -> random.nextInt(50)); + } + + @Test + public void + when_run_100_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(100, v -> random.nextInt(100)); + } + + @Test + public void + when_run_4_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(4, v -> 0); + } + + @Test + public void + when_run_5_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(5, v -> 0); + } + + @Test + public void + when_run_10_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(10, v -> 0); + } + + @Test + public void + when_run_50_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(50, v -> 0); + } + + @Test + public void + when_run_100_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(100, v -> 0); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalTimeoutResponsiveTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalTimeoutResponsiveTest.java index ed6a5e82f2..a983d09d0f 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalTimeoutResponsiveTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/OneProposalTimeoutResponsiveTest.java @@ -64,80 +64,79 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus; -import com.radixdlt.counters.SystemCounters.CounterType; -import java.util.Random; - -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; -import com.radixdlt.counters.SystemCounters; - -import static org.assertj.core.api.Assertions.assertThat; +import com.radixdlt.integration.distributed.deterministic.DeterministicTest; +import java.util.Random; +import org.junit.Test; public class OneProposalTimeoutResponsiveTest { - private final Random random = new Random(123456); - - private void run(int numNodes, long numViews, long dropPeriod) { - DeterministicTest test = DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator(dropSomeProposals(dropPeriod)) - .buildWithoutEpochs() - .runUntil(DeterministicTest.hasReachedView(View.of(numViews))); - - long requiredIndirectParents = - numNodes <= 3 - ? 0 // there are no indirect parents for 3 nodes (QC is always formed) - : (numViews - 1) / dropPeriod; // Edge case if dropPeriod a factor of numViews - - long requiredTimeouts = numViews / dropPeriod * 2; - - long timeoutQuorums = - numNodes <= 3 - ? 0 // no timeout quorums for 3 nodes - : requiredTimeouts / 2; // otherwise, every 2nd timeout forms a TC - - for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { - SystemCounters counters = test.getSystemCounters(nodeIndex); - long numberOfIndirectParents = counters.get(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS); - long totalNumberOfTimeouts = counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); - long totalNumberOfTimeoutQuorums = counters.get(CounterType.BFT_TIMEOUT_QUORUMS); - assertThat(numberOfIndirectParents).isEqualTo(requiredIndirectParents); - assertThat(totalNumberOfTimeouts).isEqualTo(requiredTimeouts); - assertThat(totalNumberOfTimeoutQuorums).isBetween(timeoutQuorums - 1, timeoutQuorums); - } - } - - private static MessageMutator dropSomeProposals(long dropPeriod) { - return (message, queue) -> { - Object msg = message.message(); - if (msg instanceof Proposal) { - final Proposal proposal = (Proposal) msg; - final View view = proposal.getVertex().getView(); - final long viewNumber = view.number(); - - return viewNumber % dropPeriod == 0; - } - return false; - }; - } - - @Test - public void when_run_3_correct_nodes_with_1_timeout__then_bft_should_be_responsive() { - this.run(3, 50_000, 100); - } - - @Test - public void when_run_4_correct_nodes_with_1_timeout__then_bft_should_be_responsive() { - this.run(4, 50_000, 100); - } - - @Test - public void when_run_100_correct_nodes_with_1_timeout__then_bft_should_be_responsive() { - this.run(100, 1_000, 100); - } + private final Random random = new Random(123456); + + private void run(int numNodes, long numViews, long dropPeriod) { + DeterministicTest test = + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator(dropSomeProposals(dropPeriod)) + .buildWithoutEpochs() + .runUntil(DeterministicTest.hasReachedView(View.of(numViews))); + + long requiredIndirectParents = + numNodes <= 3 + ? 0 // there are no indirect parents for 3 nodes (QC is always formed) + : (numViews - 1) / dropPeriod; // Edge case if dropPeriod a factor of numViews + + long requiredTimeouts = numViews / dropPeriod * 2; + + long timeoutQuorums = + numNodes <= 3 + ? 0 // no timeout quorums for 3 nodes + : requiredTimeouts / 2; // otherwise, every 2nd timeout forms a TC + + for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { + SystemCounters counters = test.getSystemCounters(nodeIndex); + long numberOfIndirectParents = counters.get(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS); + long totalNumberOfTimeouts = counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); + long totalNumberOfTimeoutQuorums = counters.get(CounterType.BFT_TIMEOUT_QUORUMS); + assertThat(numberOfIndirectParents).isEqualTo(requiredIndirectParents); + assertThat(totalNumberOfTimeouts).isEqualTo(requiredTimeouts); + assertThat(totalNumberOfTimeoutQuorums).isBetween(timeoutQuorums - 1, timeoutQuorums); + } + } + + private static MessageMutator dropSomeProposals(long dropPeriod) { + return (message, queue) -> { + Object msg = message.message(); + if (msg instanceof Proposal) { + final Proposal proposal = (Proposal) msg; + final View view = proposal.getVertex().getView(); + final long viewNumber = view.number(); + + return viewNumber % dropPeriod == 0; + } + return false; + }; + } + + @Test + public void when_run_3_correct_nodes_with_1_timeout__then_bft_should_be_responsive() { + this.run(3, 50_000, 100); + } + + @Test + public void when_run_4_correct_nodes_with_1_timeout__then_bft_should_be_responsive() { + this.run(4, 50_000, 100); + } + + @Test + public void when_run_100_correct_nodes_with_1_timeout__then_bft_should_be_responsive() { + this.run(100, 1_000, 100); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/PacemakerViewUpdateRaceConditionTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/PacemakerViewUpdateRaceConditionTest.java index a9451da19a..4de555fd67 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/PacemakerViewUpdateRaceConditionTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/PacemakerViewUpdateRaceConditionTest.java @@ -64,6 +64,8 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.hash.HashCode; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -86,8 +88,6 @@ import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import com.radixdlt.utils.KeyComparator; import io.reactivex.rxjava3.schedulers.Timed; -import org.junit.Test; - import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -96,101 +96,109 @@ import java.util.Random; import java.util.function.Predicate; import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; /** - * This test checks that when race condition is introduced (by delaying ViewUpdate and BFTInsertUpdate messages) then - * the Pacemaker can form a valid timeout vote for an empty proposal. + * This test checks that when race condition is introduced (by delaying ViewUpdate and + * BFTInsertUpdate messages) then the Pacemaker can form a valid timeout vote for an empty proposal. * Specifically, it checks whether the vertices it inserts use a correct parent. */ public class PacemakerViewUpdateRaceConditionTest { - private static final Random random = new Random(123456); + private static final Random random = new Random(123456); - private static final int numNodes = 4; - private static final int nodeUnderTestIndex = 2; // leader for view 2 - private static final long pacemakerTimeout = 1000L; - private static final long additionalMessageDelay = pacemakerTimeout + 1000L; + private static final int numNodes = 4; + private static final int nodeUnderTestIndex = 2; // leader for view 2 + private static final long pacemakerTimeout = 1000L; + private static final long additionalMessageDelay = pacemakerTimeout + 1000L; - @Test - public void test_pacemaker_view_update_race_condition() { - final DeterministicTest test = DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator(messUpMessagesForNodeUnderTest()) - .pacemakerTimeout(pacemakerTimeout) - .overrideWithIncorrectModule(new AbstractModule() { - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor bftInsertUpdateProcessor() { - final Map insertedVertices = new HashMap<>(); - return bftInsertUpdate -> { - final PreparedVertex inserted = bftInsertUpdate.getInserted(); - insertedVertices.putIfAbsent(inserted.getId(), inserted); - final Optional maybeParent = - Optional.ofNullable(insertedVertices.get(inserted.getParentId())); + @Test + public void test_pacemaker_view_update_race_condition() { + final DeterministicTest test = + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator(messUpMessagesForNodeUnderTest()) + .pacemakerTimeout(pacemakerTimeout) + .overrideWithIncorrectModule( + new AbstractModule() { + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor bftInsertUpdateProcessor() { + final Map insertedVertices = new HashMap<>(); + return bftInsertUpdate -> { + final PreparedVertex inserted = bftInsertUpdate.getInserted(); + insertedVertices.putIfAbsent(inserted.getId(), inserted); + final Optional maybeParent = + Optional.ofNullable(insertedVertices.get(inserted.getParentId())); - maybeParent.ifPresent(parent -> { - if (parent.getView().equals(inserted.getView())) { - throw new IllegalStateException("Vertex can't have the same view as its parent."); - } - }); - }; - } + maybeParent.ifPresent( + parent -> { + if (parent.getView().equals(inserted.getView())) { + throw new IllegalStateException( + "Vertex can't have the same view as its parent."); + } + }); + }; + } - @Provides - public ProposerElection proposerElection(BFTValidatorSet validatorSet) { - final List sortedValidators = - validatorSet.getValidators().stream() - .map(BFTValidator::getNode) - .sorted(Comparator.comparing(BFTNode::getKey, KeyComparator.instance().reversed())) - .collect(Collectors.toList()); - return view -> sortedValidators.get(((int) view.number() - 1) % sortedValidators.size()); - } - }) - .buildWithoutEpochs() - .runUntil(nodeUnderTestReachesView(View.of(3))); + @Provides + public ProposerElection proposerElection(BFTValidatorSet validatorSet) { + final List sortedValidators = + validatorSet.getValidators().stream() + .map(BFTValidator::getNode) + .sorted( + Comparator.comparing( + BFTNode::getKey, KeyComparator.instance().reversed())) + .collect(Collectors.toList()); + return view -> + sortedValidators.get(((int) view.number() - 1) % sortedValidators.size()); + } + }) + .buildWithoutEpochs() + .runUntil(nodeUnderTestReachesView(View.of(3))); - final var counters = test.getSystemCounters(nodeUnderTestIndex); - assertThat(counters.get(SystemCounters.CounterType.BFT_VOTE_QUORUMS)).isEqualTo(2); // ensure that quorum was formed - assertThat(counters.get(SystemCounters.CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(2); // ensure that timeouts were processed - } + final var counters = test.getSystemCounters(nodeUnderTestIndex); + assertThat(counters.get(SystemCounters.CounterType.BFT_VOTE_QUORUMS)) + .isEqualTo(2); // ensure that quorum was formed + assertThat(counters.get(SystemCounters.CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)) + .isEqualTo(2); // ensure that timeouts were processed + } - private static Predicate> nodeUnderTestReachesView(View view) { - return timedMsg -> { - final ControlledMessage message = timedMsg.value(); - if (!(message.message() instanceof ViewUpdate)) { - return false; - } - final ViewUpdate p = (ViewUpdate) message.message(); - return message.channelId().receiverIndex() == nodeUnderTestIndex - && p.getCurrentView().gte(view); - }; - } + private static Predicate> nodeUnderTestReachesView(View view) { + return timedMsg -> { + final ControlledMessage message = timedMsg.value(); + if (!(message.message() instanceof ViewUpdate)) { + return false; + } + final ViewUpdate p = (ViewUpdate) message.message(); + return message.channelId().receiverIndex() == nodeUnderTestIndex + && p.getCurrentView().gte(view); + }; + } - private static MessageMutator messUpMessagesForNodeUnderTest() { - return (message, queue) -> { - // we only mess up messages for the test node - if (message.channelId().receiverIndex() != nodeUnderTestIndex) { - return false; - } + private static MessageMutator messUpMessagesForNodeUnderTest() { + return (message, queue) -> { + // we only mess up messages for the test node + if (message.channelId().receiverIndex() != nodeUnderTestIndex) { + return false; + } - // the unlucky node doesn't receive a Proposal and its next ViewUpdate and BFTInsertUpdate messages are delayed - // Proposal is dropped so that the node creates an empty timeout vote, and not a timeout of a previous vote - final Object msg = message.message(); - if (msg instanceof ViewUpdate - && ((ViewUpdate) msg).getCurrentView().equals(View.of(2))) { - queue.add(message.withAdditionalDelay(additionalMessageDelay)); - return true; - } else if (msg instanceof BFTInsertUpdate - && ((BFTInsertUpdate) msg).getInserted().getView().equals(View.of(1))) { - queue.add(message.withAdditionalDelay(additionalMessageDelay)); - return true; - } else { - return msg instanceof Proposal - && ((Proposal) msg).getView().equals(View.of(1)); - } - }; - } + // the unlucky node doesn't receive a Proposal and its next ViewUpdate and BFTInsertUpdate + // messages are delayed + // Proposal is dropped so that the node creates an empty timeout vote, and not a timeout of a + // previous vote + final Object msg = message.message(); + if (msg instanceof ViewUpdate && ((ViewUpdate) msg).getCurrentView().equals(View.of(2))) { + queue.add(message.withAdditionalDelay(additionalMessageDelay)); + return true; + } else if (msg instanceof BFTInsertUpdate + && ((BFTInsertUpdate) msg).getInserted().getView().equals(View.of(1))) { + queue.add(message.withAdditionalDelay(additionalMessageDelay)); + return true; + } else { + return msg instanceof Proposal && ((Proposal) msg).getView().equals(View.of(1)); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/ProposerLoadBalancedTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/ProposerLoadBalancedTest.java index b72bd1715e..ec5f11fa07 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/ProposerLoadBalancedTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/ProposerLoadBalancedTest.java @@ -64,196 +64,186 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus; +import static org.assertj.core.api.Assertions.*; + +import com.google.common.collect.ImmutableList; +import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.epoch.Epoched; import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; +import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.environment.deterministic.network.MessageMutator; +import com.radixdlt.environment.deterministic.network.MessageSelector; +import com.radixdlt.integration.distributed.deterministic.DeterministicTest; +import com.radixdlt.integration.distributed.deterministic.configuration.EpochNodeWeightMapping; +import com.radixdlt.utils.UInt256; import java.util.List; import java.util.stream.IntStream; import java.util.stream.LongStream; - import org.assertj.core.api.Condition; import org.junit.Test; -import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest; -import com.radixdlt.integration.distributed.deterministic.configuration.EpochNodeWeightMapping; -import com.radixdlt.environment.deterministic.network.MessageMutator; -import com.radixdlt.environment.deterministic.network.MessageSelector; -import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.utils.UInt256; - -import static org.assertj.core.api.Assertions.*; - public class ProposerLoadBalancedTest { - private ImmutableList run(int numNodes, long numViews, EpochNodeWeightMapping mapping) { - - DeterministicTest test = DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.firstSelector()) - .messageMutator(mutator()) - .epochNodeWeightMapping(mapping) - .buildWithoutEpochs() - .runUntil(DeterministicTest.hasReachedView(View.of(numViews))); - - return IntStream.range(0, numNodes) - .mapToObj(test::getSystemCounters) - .map(counters -> counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)) - .collect(ImmutableList.toImmutableList()); - } - - private MessageMutator mutator() { - return (message, queue) -> { - Object msg = message.message(); - if (msg instanceof ScheduledLocalTimeout || Epoched.isInstance(msg, ScheduledLocalTimeout.class)) { - return true; - } - - // Process others in submission order - queue.add(message.withArrivalTime(0L)); - return true; - }; - } - - @Test - public void when_run_2_nodes_with_very_different_weights__then_proposals_should_match() { - final int numNodes = 2; - final long proposalChunk = 20_000L; // Actually proposalChunk + 1 proposals run - ImmutableList proposals = this.run( - numNodes, - proposalChunk + 1, - EpochNodeWeightMapping.repeatingSequence(numNodes, 1, proposalChunk) - ); - assertThat(proposals).containsExactly(1L, proposalChunk); - } - - @Test - public void when_run_3_nodes_with_equal_weight__then_proposals_should_be_equal() { - final int numNodes = 3; - final long proposalsPerNode = 50_000L; - ImmutableList proposals = this.run( - numNodes, - numNodes * proposalsPerNode, - EpochNodeWeightMapping.constant(numNodes, 1L) - ); - assertThat(proposals) - .hasSize(numNodes) - .areAtLeast(numNodes - 1, new Condition<>(l -> l == proposalsPerNode, "has as many proposals as views")) - // the last view in the epoch doesn't have a proposal - .areAtMost(1, new Condition<>(l -> l == proposalsPerNode - 1, "has one less proposal")); - } - - @Test - public void when_run_100_nodes_with_equal_weight__then_proposals_should_be_equal() { - final int numNodes = 100; - final long proposalsPerNode = 10L; - ImmutableList proposals = this.run( - numNodes, - numNodes * proposalsPerNode, - EpochNodeWeightMapping.constant(100, 1L) - ); - assertThat(proposals) - .hasSize(numNodes) - .areAtLeast(numNodes - 1, new Condition<>(l -> l == proposalsPerNode, "has as many proposals as views")) - // the last view in the epoch doesn't have a proposal - .areAtMost(1, new Condition<>(l -> l == proposalsPerNode - 1, "has one less proposal")); - } - - @Test - public void when_run_3_nodes_with_linear_weights__then_proposals_should_match() { - final long proposalChunk = 20_000L; // Actually 3! * proposalChunk proposals run - List proposals = this.run( - 3, - 1 * 2 * 3 * proposalChunk, - EpochNodeWeightMapping.repeatingSequence(3, 1, 2, 3) - ); - assertThat(proposals).containsExactly(proposalChunk, 2 * proposalChunk, 3 * proposalChunk); - } - - @Test - public void when_run_100_nodes_with_two_different_weights__then_proposals_should_match() { - final int numNodes = 100; - // Nodes 0..49 have weight 1; nodes 50..99 have weight 2 - final long proposalChunk = 10L; // Actually 150 * proposalChunk proposals run - ImmutableList proposals = this.run( - 100, - 150 * proposalChunk, - EpochNodeWeightMapping.computed(numNodes, index -> UInt256.from(index / 50 + 1)) // Weights 1, 1, ..., 2, 2 - ); - - assertThat(proposals.subList(0, 50)) - .areAtLeast(49, new Condition<>(l -> l == proposalChunk, "has as many proposals as views")) - // the last view in the epoch doesn't have a proposal - .areAtMost(1, new Condition<>(l -> l == proposalChunk - 1, "has one less proposal")); - - assertThat(proposals.subList(50, 100)).allMatch(Long.valueOf(2 * proposalChunk)::equals); - } - - @Test - public void when_run_3_nodes_with_large_lcm_weighting__then_proposals_should_be_proportional() { - final int numNodes = 3; - final long numProposals = 100_000L; - ImmutableList weights = ImmutableList.of( - // Some large primes with product/LCM > 2^64 but < 2^256 - UInt256.from("941083981"), - UInt256.from("961748927"), - UInt256.from("982451653") - ); - UInt256 sum = weights.stream().reduce(UInt256.ZERO, UInt256::add); - UInt256 numViews256 = UInt256.from(numProposals); - long[] values = weights.stream() - .map(w -> w.multiply(numViews256).divide(sum)) - .mapToLong(v -> v.getLow().getLow()) - .toArray(); - ImmutableList proposals = this.run( - numNodes, - numProposals, - EpochNodeWeightMapping.computed(numNodes, weights::get) - ); - // Correct number of total proposals - assertThat(proposals.stream().mapToLong(Long::longValue).sum()).isEqualTo(numProposals); - // Same as calculated value, +/- 1 (rounding and ordering) - for (int i = 0; i < values.length; ++i) { - assertThat(proposals.get(i).longValue()).isBetween(values[i] - 1, values[i] + 1); - } - } - - @Test - public void when_run_100_nodes_with_very_large_period__then_proposals_should_be_proportional() { - final int numNodes = 100; - final long numProposals = 1_000L; - ImmutableList weights = generatePrimes(100) - .mapToObj(UInt256::from) - .collect(ImmutableList.toImmutableList()); - UInt256 sum = weights.stream().reduce(UInt256.ZERO, UInt256::add); - UInt256 numViews256 = UInt256.from(numProposals); - long[] values = weights.stream() - .map(w -> w.multiply(numViews256).divide(sum)) - .mapToLong(v -> v.getLow().getLow()) - .toArray(); - ImmutableList proposals = this.run( - numNodes, - numProposals, - EpochNodeWeightMapping.computed(numNodes, weights::get) - ); - // Correct number of total proposals - assertThat(proposals.stream().mapToLong(Long::longValue).sum()).isEqualTo(numProposals); - // Same as calculated value, +/- 1 (rounding and ordering) - for (int i = 0; i < values.length; ++i) { - assertThat(proposals.get(i).longValue()).isBetween(values[i] - 1, values[i] + 1); - } - } - - private static LongStream generatePrimes(int n) { - // Just FYI, doesn't include 2. You don't need it. - return LongStream.iterate(3L, m -> m + 2) - .filter(ProposerLoadBalancedTest::isPrime) - .limit(n); - } - - private static boolean isPrime(long number) { - return LongStream.rangeClosed(1L, (long) Math.sqrt(number) / 2L) - .map(n -> n * 2 + 1) - .noneMatch(n -> number % n == 0); - } + private ImmutableList run(int numNodes, long numViews, EpochNodeWeightMapping mapping) { + + DeterministicTest test = + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.firstSelector()) + .messageMutator(mutator()) + .epochNodeWeightMapping(mapping) + .buildWithoutEpochs() + .runUntil(DeterministicTest.hasReachedView(View.of(numViews))); + + return IntStream.range(0, numNodes) + .mapToObj(test::getSystemCounters) + .map(counters -> counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)) + .collect(ImmutableList.toImmutableList()); + } + + private MessageMutator mutator() { + return (message, queue) -> { + Object msg = message.message(); + if (msg instanceof ScheduledLocalTimeout + || Epoched.isInstance(msg, ScheduledLocalTimeout.class)) { + return true; + } + + // Process others in submission order + queue.add(message.withArrivalTime(0L)); + return true; + }; + } + + @Test + public void when_run_2_nodes_with_very_different_weights__then_proposals_should_match() { + final int numNodes = 2; + final long proposalChunk = 20_000L; // Actually proposalChunk + 1 proposals run + ImmutableList proposals = + this.run( + numNodes, + proposalChunk + 1, + EpochNodeWeightMapping.repeatingSequence(numNodes, 1, proposalChunk)); + assertThat(proposals).containsExactly(1L, proposalChunk); + } + + @Test + public void when_run_3_nodes_with_equal_weight__then_proposals_should_be_equal() { + final int numNodes = 3; + final long proposalsPerNode = 50_000L; + ImmutableList proposals = + this.run( + numNodes, numNodes * proposalsPerNode, EpochNodeWeightMapping.constant(numNodes, 1L)); + assertThat(proposals) + .hasSize(numNodes) + .areAtLeast( + numNodes - 1, + new Condition<>(l -> l == proposalsPerNode, "has as many proposals as views")) + // the last view in the epoch doesn't have a proposal + .areAtMost(1, new Condition<>(l -> l == proposalsPerNode - 1, "has one less proposal")); + } + + @Test + public void when_run_100_nodes_with_equal_weight__then_proposals_should_be_equal() { + final int numNodes = 100; + final long proposalsPerNode = 10L; + ImmutableList proposals = + this.run(numNodes, numNodes * proposalsPerNode, EpochNodeWeightMapping.constant(100, 1L)); + assertThat(proposals) + .hasSize(numNodes) + .areAtLeast( + numNodes - 1, + new Condition<>(l -> l == proposalsPerNode, "has as many proposals as views")) + // the last view in the epoch doesn't have a proposal + .areAtMost(1, new Condition<>(l -> l == proposalsPerNode - 1, "has one less proposal")); + } + + @Test + public void when_run_3_nodes_with_linear_weights__then_proposals_should_match() { + final long proposalChunk = 20_000L; // Actually 3! * proposalChunk proposals run + List proposals = + this.run( + 3, 1 * 2 * 3 * proposalChunk, EpochNodeWeightMapping.repeatingSequence(3, 1, 2, 3)); + assertThat(proposals).containsExactly(proposalChunk, 2 * proposalChunk, 3 * proposalChunk); + } + + @Test + public void when_run_100_nodes_with_two_different_weights__then_proposals_should_match() { + final int numNodes = 100; + // Nodes 0..49 have weight 1; nodes 50..99 have weight 2 + final long proposalChunk = 10L; // Actually 150 * proposalChunk proposals run + ImmutableList proposals = + this.run( + 100, + 150 * proposalChunk, + EpochNodeWeightMapping.computed( + numNodes, index -> UInt256.from(index / 50 + 1)) // Weights 1, 1, ..., 2, 2 + ); + + assertThat(proposals.subList(0, 50)) + .areAtLeast(49, new Condition<>(l -> l == proposalChunk, "has as many proposals as views")) + // the last view in the epoch doesn't have a proposal + .areAtMost(1, new Condition<>(l -> l == proposalChunk - 1, "has one less proposal")); + + assertThat(proposals.subList(50, 100)).allMatch(Long.valueOf(2 * proposalChunk)::equals); + } + + @Test + public void when_run_3_nodes_with_large_lcm_weighting__then_proposals_should_be_proportional() { + final int numNodes = 3; + final long numProposals = 100_000L; + ImmutableList weights = + ImmutableList.of( + // Some large primes with product/LCM > 2^64 but < 2^256 + UInt256.from("941083981"), UInt256.from("961748927"), UInt256.from("982451653")); + UInt256 sum = weights.stream().reduce(UInt256.ZERO, UInt256::add); + UInt256 numViews256 = UInt256.from(numProposals); + long[] values = + weights.stream() + .map(w -> w.multiply(numViews256).divide(sum)) + .mapToLong(v -> v.getLow().getLow()) + .toArray(); + ImmutableList proposals = + this.run(numNodes, numProposals, EpochNodeWeightMapping.computed(numNodes, weights::get)); + // Correct number of total proposals + assertThat(proposals.stream().mapToLong(Long::longValue).sum()).isEqualTo(numProposals); + // Same as calculated value, +/- 1 (rounding and ordering) + for (int i = 0; i < values.length; ++i) { + assertThat(proposals.get(i).longValue()).isBetween(values[i] - 1, values[i] + 1); + } + } + + @Test + public void when_run_100_nodes_with_very_large_period__then_proposals_should_be_proportional() { + final int numNodes = 100; + final long numProposals = 1_000L; + ImmutableList weights = + generatePrimes(100).mapToObj(UInt256::from).collect(ImmutableList.toImmutableList()); + UInt256 sum = weights.stream().reduce(UInt256.ZERO, UInt256::add); + UInt256 numViews256 = UInt256.from(numProposals); + long[] values = + weights.stream() + .map(w -> w.multiply(numViews256).divide(sum)) + .mapToLong(v -> v.getLow().getLow()) + .toArray(); + ImmutableList proposals = + this.run(numNodes, numProposals, EpochNodeWeightMapping.computed(numNodes, weights::get)); + // Correct number of total proposals + assertThat(proposals.stream().mapToLong(Long::longValue).sum()).isEqualTo(numProposals); + // Same as calculated value, +/- 1 (rounding and ordering) + for (int i = 0; i < values.length; ++i) { + assertThat(proposals.get(i).longValue()).isBetween(values[i] - 1, values[i] + 1); + } + } + + private static LongStream generatePrimes(int n) { + // Just FYI, doesn't include 2. You don't need it. + return LongStream.iterate(3L, m -> m + 2).filter(ProposerLoadBalancedTest::isPrime).limit(n); + } + + private static boolean isPrime(long number) { + return LongStream.rangeClosed(1L, (long) Math.sqrt(number) / 2L) + .map(n -> n * 2 + 1) + .noneMatch(n -> number % n == 0); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/QuorumWithoutALeaderWithTimeoutsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/QuorumWithoutALeaderWithTimeoutsTest.java index eccff987f1..5dad4ec06e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/QuorumWithoutALeaderWithTimeoutsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/QuorumWithoutALeaderWithTimeoutsTest.java @@ -64,6 +64,8 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.View; import com.radixdlt.counters.SystemCounters; @@ -71,66 +73,65 @@ import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; import com.radixdlt.integration.distributed.deterministic.DeterministicTest; -import org.junit.Test; - import java.util.Random; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; /** - * When original votes (to next view leader, non timed out) are dropped, - * nodes should be able to resend those votes to each other (with timeout) - * and form the quorum themselves. - * As a result, there should be no timeout (non-QC) quorums and no indirect parents. + * When original votes (to next view leader, non timed out) are dropped, nodes should be able to + * resend those votes to each other (with timeout) and form the quorum themselves. As a result, + * there should be no timeout (non-QC) quorums and no indirect parents. */ public class QuorumWithoutALeaderWithTimeoutsTest { - private final Random random = new Random(123456); + private final Random random = new Random(123456); - private void run(int numNodes, long numViews) { - final DeterministicTest test = DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator(dropAllNonTimeoutVotes()) - .buildWithoutEpochs() - .runUntil(DeterministicTest.hasReachedView(View.of(numViews))); + private void run(int numNodes, long numViews) { + final DeterministicTest test = + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator(dropAllNonTimeoutVotes()) + .buildWithoutEpochs() + .runUntil(DeterministicTest.hasReachedView(View.of(numViews))); - for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { - final SystemCounters counters = test.getSystemCounters(nodeIndex); - final long numberOfIndirectParents = counters.get(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS); - final long totalNumberOfTimeouts = counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); - final long totalNumberOfTimeoutQuorums = counters.get(CounterType.BFT_TIMEOUT_QUORUMS); - final long totalNumberOfVoteQuorums = counters.get(CounterType.BFT_VOTE_QUORUMS); - assertThat(totalNumberOfTimeoutQuorums).isEqualTo(0); // no TCs - assertThat(numberOfIndirectParents).isEqualTo(0); // no indirect parents - assertThat(totalNumberOfTimeouts).isEqualTo(numViews - 1); // a timeout for each view - assertThat(totalNumberOfVoteQuorums).isBetween(numViews - 2, numViews); // quorum count matches views - } - } + for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { + final SystemCounters counters = test.getSystemCounters(nodeIndex); + final long numberOfIndirectParents = + counters.get(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS); + final long totalNumberOfTimeouts = counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); + final long totalNumberOfTimeoutQuorums = counters.get(CounterType.BFT_TIMEOUT_QUORUMS); + final long totalNumberOfVoteQuorums = counters.get(CounterType.BFT_VOTE_QUORUMS); + assertThat(totalNumberOfTimeoutQuorums).isEqualTo(0); // no TCs + assertThat(numberOfIndirectParents).isEqualTo(0); // no indirect parents + assertThat(totalNumberOfTimeouts).isEqualTo(numViews - 1); // a timeout for each view + assertThat(totalNumberOfVoteQuorums) + .isBetween(numViews - 2, numViews); // quorum count matches views + } + } - private static MessageMutator dropAllNonTimeoutVotes() { - return (message, queue) -> { - final Object msg = message.message(); - if (msg instanceof Vote) { - final Vote vote = (Vote) msg; - return vote.getTimeoutSignature().isEmpty(); - } - return false; - }; - } + private static MessageMutator dropAllNonTimeoutVotes() { + return (message, queue) -> { + final Object msg = message.message(); + if (msg instanceof Vote) { + final Vote vote = (Vote) msg; + return vote.getTimeoutSignature().isEmpty(); + } + return false; + }; + } - @Test - public void when_run_3_correct_nodes_for_50k_views__then_bft_should_be_responsive() { - this.run(3, 50_000); - } + @Test + public void when_run_3_correct_nodes_for_50k_views__then_bft_should_be_responsive() { + this.run(3, 50_000); + } - @Test - public void when_run_10_correct_nodes_with_for_2k_views__then_bft_should_be_responsive() { - this.run(10, 2000); - } + @Test + public void when_run_10_correct_nodes_with_for_2k_views__then_bft_should_be_responsive() { + this.run(10, 2000); + } - @Test - public void when_run_100_correct_nodes_with_for_50_views__then_bft_should_be_responsive() { - this.run(100, 50); - } + @Test + public void when_run_100_correct_nodes_with_for_50_views__then_bft_should_be_responsive() { + this.run(100, 50); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/RandomChannelOrderResponsiveTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/RandomChannelOrderResponsiveTest.java index bef9829c20..3db79c3622 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/RandomChannelOrderResponsiveTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus/RandomChannelOrderResponsiveTest.java @@ -64,58 +64,61 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus; +import static org.assertj.core.api.Assertions.*; +import static org.junit.Assert.assertEquals; + import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.bft.View; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; - +import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import java.util.List; import java.util.Random; import java.util.stream.IntStream; - import org.assertj.core.api.Condition; import org.junit.Test; -import static org.assertj.core.api.Assertions.*; -import static org.junit.Assert.assertEquals; - public class RandomChannelOrderResponsiveTest { - private void run(int numNodes, long viewsToRun) { - assertEquals(0, viewsToRun % numNodes); + private void run(int numNodes, long viewsToRun) { + assertEquals(0, viewsToRun % numNodes); - final Random random = new Random(12345); + final Random random = new Random(12345); - DeterministicTest test = DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutator(MessageMutator.dropTimeouts()) - .buildWithoutEpochs() - .runUntil(DeterministicTest.hasReachedView(View.of(viewsToRun))); + DeterministicTest test = + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutator(MessageMutator.dropTimeouts()) + .buildWithoutEpochs() + .runUntil(DeterministicTest.hasReachedView(View.of(viewsToRun))); - List proposalsMade = IntStream.range(0, numNodes) - .mapToObj(test::getSystemCounters) - .map(counters -> counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)) - .collect(ImmutableList.toImmutableList()); + List proposalsMade = + IntStream.range(0, numNodes) + .mapToObj(test::getSystemCounters) + .map(counters -> counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)) + .collect(ImmutableList.toImmutableList()); - final long numViews = viewsToRun / numNodes; + final long numViews = viewsToRun / numNodes; - assertThat(proposalsMade) - .hasSize(numNodes) - .areAtLeast(numNodes - 1, new Condition<>(l -> l == numViews, "has as many proposals as views")) - // the last view in the epoch doesn't have a proposal - .areAtMost(1, new Condition<>(l -> l == numViews - 1, "has one less proposal")); - } + assertThat(proposalsMade) + .hasSize(numNodes) + .areAtLeast( + numNodes - 1, new Condition<>(l -> l == numViews, "has as many proposals as views")) + // the last view in the epoch doesn't have a proposal + .areAtMost(1, new Condition<>(l -> l == numViews - 1, "has one less proposal")); + } - @Test - public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { - run(4, 4 * 25000L); - } + @Test + public void + when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { + run(4, 4 * 25000L); + } - @Test - public void when_run_100_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { - run(100, 100 * 5L); - } + @Test + public void + when_run_100_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { + run(100, 100 * 5L); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java index a018ae715d..cdd2c01f78 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java @@ -64,109 +64,120 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus_ledger_epochs; +import static com.radixdlt.environment.deterministic.network.MessageSelector.*; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.epoch.EpochView; import com.radixdlt.consensus.epoch.Epoched; import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.environment.deterministic.network.ChannelId; +import com.radixdlt.environment.deterministic.network.ControlledMessage; +import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import java.util.LinkedList; import java.util.function.LongFunction; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; - import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - -import com.radixdlt.environment.deterministic.network.ChannelId; -import com.radixdlt.environment.deterministic.network.ControlledMessage; -import com.radixdlt.environment.deterministic.network.MessageMutator; - -import static com.radixdlt.environment.deterministic.network.MessageSelector.*; - public class MovingWindowValidatorsTest { - private static LongFunction windowedEpochToNodesMapper(int windowSize, int totalValidatorCount) { - // Epoch starts at 1, and we want base 0, so subtract 1 - return epoch -> IntStream.range(0, windowSize).map(index -> (int) (epoch - 1 + index) % totalValidatorCount); - } - - private void run(int numNodes, int windowSize, long maxEpoch, View highView) { - DeterministicTest bftTest = DeterministicTest.builder() - .numNodes(numNodes) - .messageMutator(mutator()) - .messageSelector(firstSelector()) - .epochNodeIndexesMapping(windowedEpochToNodesMapper(windowSize, numNodes)) - .buildWithEpochs(highView) - .runUntil(DeterministicTest.hasReachedEpochView(EpochView.of(maxEpoch, highView))); - - LinkedList testCounters = systemCounters(bftTest); - assertThat(testCounters).extracting(sc -> sc.get(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS)).containsOnly(0L); - assertThat(testCounters).extracting(sc -> sc.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).containsOnly(0L); - - long maxCount = maxProcessedFor(numNodes, windowSize, maxEpoch, highView.number()); - - assertThat(testCounters) - .extracting(sc -> sc.get(CounterType.BFT_COMMITTED_VERTICES)) - .allMatch(between(maxCount - maxEpoch, maxCount)); - } - - private MessageMutator mutator() { - return (message, queue) -> { - if (Epoched.isInstance(message.message(), ScheduledLocalTimeout.class)) { - // Discard - return true; - } - // Process others in arrival order, local first. - // Need to make sure EpochsLedgerUpdate is processed before consensus messages for the new epoch - if (nonLocalMessage(message)) { - queue.add(message.withArrivalTime(0)); - } else { - queue.addBefore(message.withArrivalTime(0), this::nonLocalMessage); - } - return true; - }; - } - - private boolean nonLocalMessage(ControlledMessage msg) { - ChannelId channelId = msg.channelId(); - return channelId.senderIndex() != channelId.receiverIndex(); - } - - @Test - public void given_correct_1_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_postconditions() { - run(4, 1, 100L, View.of(100)); - } - - @Test - public void given_correct_3_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_postconditions() { - run(4, 3, 120L, View.of(100)); - } - - @Test - public void given_correct_25_node_bft_with_50_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_postconditions() { - run(50, 25, 100L, View.of(100)); - } - - @Test - public void given_correct_25_node_bft_with_100_total_nodes_with_changing_epochs_per_1_view__then_should_pass_bft_and_postconditions() { - run(100, 25, 100L, View.of(100)); - } - - private static long maxProcessedFor(int numNodes, int numValidators, long epochs, long epochHighView) { - return epochHighView * epochs * numValidators / numNodes; - } - - private static LinkedList systemCounters(DeterministicTest bftTest) { - return IntStream.range(0, bftTest.numNodes()) - .mapToObj(bftTest::getSystemCounters) - .collect(Collectors.toCollection(LinkedList::new)); - } - - private static Predicate between(long lower, long upper) { - return value -> value >= lower && value <= upper; - } + private static LongFunction windowedEpochToNodesMapper( + int windowSize, int totalValidatorCount) { + // Epoch starts at 1, and we want base 0, so subtract 1 + return epoch -> + IntStream.range(0, windowSize) + .map(index -> (int) (epoch - 1 + index) % totalValidatorCount); + } + + private void run(int numNodes, int windowSize, long maxEpoch, View highView) { + DeterministicTest bftTest = + DeterministicTest.builder() + .numNodes(numNodes) + .messageMutator(mutator()) + .messageSelector(firstSelector()) + .epochNodeIndexesMapping(windowedEpochToNodesMapper(windowSize, numNodes)) + .buildWithEpochs(highView) + .runUntil(DeterministicTest.hasReachedEpochView(EpochView.of(maxEpoch, highView))); + + LinkedList testCounters = systemCounters(bftTest); + assertThat(testCounters) + .extracting(sc -> sc.get(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS)) + .containsOnly(0L); + assertThat(testCounters) + .extracting(sc -> sc.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)) + .containsOnly(0L); + + long maxCount = maxProcessedFor(numNodes, windowSize, maxEpoch, highView.number()); + + assertThat(testCounters) + .extracting(sc -> sc.get(CounterType.BFT_COMMITTED_VERTICES)) + .allMatch(between(maxCount - maxEpoch, maxCount)); + } + + private MessageMutator mutator() { + return (message, queue) -> { + if (Epoched.isInstance(message.message(), ScheduledLocalTimeout.class)) { + // Discard + return true; + } + // Process others in arrival order, local first. + // Need to make sure EpochsLedgerUpdate is processed before consensus messages for the new + // epoch + if (nonLocalMessage(message)) { + queue.add(message.withArrivalTime(0)); + } else { + queue.addBefore(message.withArrivalTime(0), this::nonLocalMessage); + } + return true; + }; + } + + private boolean nonLocalMessage(ControlledMessage msg) { + ChannelId channelId = msg.channelId(); + return channelId.senderIndex() != channelId.receiverIndex(); + } + + @Test + public void + given_correct_1_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_postconditions() { + run(4, 1, 100L, View.of(100)); + } + + @Test + public void + given_correct_3_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_postconditions() { + run(4, 3, 120L, View.of(100)); + } + + @Test + public void + given_correct_25_node_bft_with_50_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_postconditions() { + run(50, 25, 100L, View.of(100)); + } + + @Test + public void + given_correct_25_node_bft_with_100_total_nodes_with_changing_epochs_per_1_view__then_should_pass_bft_and_postconditions() { + run(100, 25, 100L, View.of(100)); + } + + private static long maxProcessedFor( + int numNodes, int numValidators, long epochs, long epochHighView) { + return epochHighView * epochs * numValidators / numNodes; + } + + private static LinkedList systemCounters(DeterministicTest bftTest) { + return IntStream.range(0, bftTest.numNodes()) + .mapToObj(bftTest::getSystemCounters) + .collect(Collectors.toCollection(LinkedList::new)); + } + + private static Predicate between(long lower, long upper) { + return value -> value >= lower && value <= upper; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/ProcessCachedEventsWithTimeoutCertTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/ProcessCachedEventsWithTimeoutCertTest.java index 44c17432ec..ca7b8b276d 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/ProcessCachedEventsWithTimeoutCertTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/consensus_ledger_epochs/ProcessCachedEventsWithTimeoutCertTest.java @@ -64,6 +64,8 @@ package com.radixdlt.integration.distributed.deterministic.tests.consensus_ledger_epochs; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; @@ -74,69 +76,64 @@ import com.radixdlt.environment.deterministic.network.MessageSelector; import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import io.reactivex.rxjava3.schedulers.Timed; -import org.junit.Test; - import java.util.Random; import java.util.function.Predicate; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class ProcessCachedEventsWithTimeoutCertTest { - private static final int TEST_NODE = 4; - private final Random random = new Random(123456); - - @Test - public void process_cached_sync_event_with_tc_test() { - final var test = DeterministicTest.builder() - .numNodes(5) - .messageSelector(MessageSelector.randomSelector(random)) - .messageMutators( - dropProposalToNodes(View.of(1), ImmutableList.of(TEST_NODE)), - dropProposalToNodes(View.of(2), ImmutableList.of(2, 3, TEST_NODE)), - dropVotesForNode(TEST_NODE) - ) - .buildWithEpochs(View.of(100)) - .runUntil(nodeVotesForView(View.of(3), TEST_NODE)); + private static final int TEST_NODE = 4; + private final Random random = new Random(123456); - // just to check if the node indeed needed to sync - final var counters = test.getSystemCounters(TEST_NODE); - assertThat(counters.get(SystemCounters.CounterType.BFT_TIMEOUT_QUORUMS)).isEqualTo(0); - assertThat(counters.get(SystemCounters.CounterType.BFT_VOTE_QUORUMS)).isEqualTo(0); - } + @Test + public void process_cached_sync_event_with_tc_test() { + final var test = + DeterministicTest.builder() + .numNodes(5) + .messageSelector(MessageSelector.randomSelector(random)) + .messageMutators( + dropProposalToNodes(View.of(1), ImmutableList.of(TEST_NODE)), + dropProposalToNodes(View.of(2), ImmutableList.of(2, 3, TEST_NODE)), + dropVotesForNode(TEST_NODE)) + .buildWithEpochs(View.of(100)) + .runUntil(nodeVotesForView(View.of(3), TEST_NODE)); - private static MessageMutator dropProposalToNodes(View view, ImmutableList nodes) { - return (message, queue) -> { - final var msg = message.message(); - if (msg instanceof Proposal) { - final Proposal proposal = (Proposal) msg; - return proposal.getView().equals(view) - && nodes.contains(message.channelId().receiverIndex()); - } - return false; - }; - } + // just to check if the node indeed needed to sync + final var counters = test.getSystemCounters(TEST_NODE); + assertThat(counters.get(SystemCounters.CounterType.BFT_TIMEOUT_QUORUMS)).isEqualTo(0); + assertThat(counters.get(SystemCounters.CounterType.BFT_VOTE_QUORUMS)).isEqualTo(0); + } - private static MessageMutator dropVotesForNode(int node) { - return (message, queue) -> { - final var msg = message.message(); - if (msg instanceof Vote) { - return message.channelId().receiverIndex() == node; - } - return false; - }; - } + private static MessageMutator dropProposalToNodes(View view, ImmutableList nodes) { + return (message, queue) -> { + final var msg = message.message(); + if (msg instanceof Proposal) { + final Proposal proposal = (Proposal) msg; + return proposal.getView().equals(view) + && nodes.contains(message.channelId().receiverIndex()); + } + return false; + }; + } - public static Predicate> nodeVotesForView(View view, int node) { - return timedMsg -> { - final var message = timedMsg.value(); - if (!(message.message() instanceof Vote)) { - return false; - } - final var vote = (Vote) message.message(); - return vote.getView().equals(view) - && message.channelId().senderIndex() == node; - }; - } + private static MessageMutator dropVotesForNode(int node) { + return (message, queue) -> { + final var msg = message.message(); + if (msg instanceof Vote) { + return message.channelId().receiverIndex() == node; + } + return false; + }; + } + public static Predicate> nodeVotesForView(View view, int node) { + return timedMsg -> { + final var message = timedMsg.value(); + if (!(message.message() instanceof Vote)) { + return false; + } + final var vote = (Vote) message.message(); + return vote.getView().equals(view) && message.channelId().senderIndex() == node; + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/ledger_sync/FullNodeSyncTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/ledger_sync/FullNodeSyncTest.java index 62e17894c1..a28762d30e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/ledger_sync/FullNodeSyncTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/deterministic/tests/ledger_sync/FullNodeSyncTest.java @@ -64,70 +64,70 @@ package com.radixdlt.integration.distributed.deterministic.tests.ledger_sync; +import static com.radixdlt.environment.deterministic.network.MessageSelector.firstSelector; +import static org.junit.Assert.assertTrue; + import com.radixdlt.consensus.bft.View; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.integration.distributed.deterministic.DeterministicTest; import com.radixdlt.sync.SyncConfig; -import org.junit.Test; - import java.util.stream.Collectors; import java.util.stream.IntStream; - -import static com.radixdlt.environment.deterministic.network.MessageSelector.firstSelector; -import static org.junit.Assert.assertTrue; +import org.junit.Test; public class FullNodeSyncTest { - /* maximum state lag is a single command */ - private static final int FULL_NODE_MAX_BEHIND_STATE_VER = 1; + /* maximum state lag is a single command */ + private static final int FULL_NODE_MAX_BEHIND_STATE_VER = 1; - private void run(int numNodes, int numValidators, View highView, long targetStateVersion) { - final var syncConfig = - SyncConfig.of( - 500L, - 0 /* unused */, - 0L /* unused */, - numNodes, /* send ledger status update to all nodes */ - Integer.MAX_VALUE /* no rate limiting */ - ); + private void run(int numNodes, int numValidators, View highView, long targetStateVersion) { + final var syncConfig = + SyncConfig.of( + 500L, + 0 /* unused */, + 0L /* unused */, + numNodes, /* send ledger status update to all nodes */ + Integer.MAX_VALUE /* no rate limiting */); - final var bftTest = DeterministicTest.builder() - .numNodes(numNodes) - .messageSelector(firstSelector()) - .epochNodeIndexesMapping(epoch -> IntStream.range(0, numValidators)) - .buildWithEpochsAndSync(highView, syncConfig) - .runUntil(DeterministicTest.ledgerStateVersionOnNode(targetStateVersion, numNodes - 1)); + final var bftTest = + DeterministicTest.builder() + .numNodes(numNodes) + .messageSelector(firstSelector()) + .epochNodeIndexesMapping(epoch -> IntStream.range(0, numValidators)) + .buildWithEpochsAndSync(highView, syncConfig) + .runUntil(DeterministicTest.ledgerStateVersionOnNode(targetStateVersion, numNodes - 1)); - final var validatorsCounters = IntStream.range(0, numValidators) - .mapToObj(bftTest::getSystemCounters); + final var validatorsCounters = + IntStream.range(0, numValidators).mapToObj(bftTest::getSystemCounters); - final var validatorsMaxStateVersion = validatorsCounters - .map(sc -> sc.get(CounterType.LEDGER_STATE_VERSION)) - .max(Long::compareTo) - .get(); + final var validatorsMaxStateVersion = + validatorsCounters + .map(sc -> sc.get(CounterType.LEDGER_STATE_VERSION)) + .max(Long::compareTo) + .get(); - final var nonValidatorsStateVersions = IntStream - .range(numValidators, numNodes - numValidators) - .mapToObj(bftTest::getSystemCounters) - .map(sc -> sc.get(CounterType.LEDGER_STATE_VERSION)) - .collect(Collectors.toList()); + final var nonValidatorsStateVersions = + IntStream.range(numValidators, numNodes - numValidators) + .mapToObj(bftTest::getSystemCounters) + .map(sc -> sc.get(CounterType.LEDGER_STATE_VERSION)) + .collect(Collectors.toList()); - nonValidatorsStateVersions.forEach(stateVersion -> - assertTrue(stateVersion + FULL_NODE_MAX_BEHIND_STATE_VER >= validatorsMaxStateVersion) - ); - } + nonValidatorsStateVersions.forEach( + stateVersion -> + assertTrue(stateVersion + FULL_NODE_MAX_BEHIND_STATE_VER >= validatorsMaxStateVersion)); + } - @Test - public void total_five_nodes_and_a_single_full_node() { - this.run(5, 4, View.of(100), 1000L); - } + @Test + public void total_five_nodes_and_a_single_full_node() { + this.run(5, 4, View.of(100), 1000L); + } - @Test - public void total_50_nodes_and_just_4_validators_two_views_per_epoch() { - this.run(50, 4, View.of(2), 500L); - } + @Test + public void total_50_nodes_and_just_4_validators_two_views_per_epoch() { + this.run(50, 4, View.of(2), 500L); + } - @Test - public void total_three_nodes_and_a_single_full_node_10k_views_per_epoch() { - this.run(3, 2, View.of(10000), 1000L); - } + @Test + public void total_three_nodes_and_a_single_full_node_10k_views_per_epoch() { + this.run(3, 2, View.of(10000), 1000L); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MockedSystemModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MockedSystemModule.java index db7c895549..f410bb470c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MockedSystemModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MockedSystemModule.java @@ -69,10 +69,11 @@ import java.util.Random; public class MockedSystemModule extends AbstractModule { - final Random sharedRandom = new Random(); - @Override - public void configure() { - bind(TimeSupplier.class).toInstance(System::currentTimeMillis); - bind(Random.class).toInstance(sharedRandom); - } + final Random sharedRandom = new Random(); + + @Override + public void configure() { + bind(TimeSupplier.class).toInstance(System::currentTimeMillis); + bind(Random.class).toInstance(sharedRandom); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/Monitor.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/Monitor.java index 246f768437..c1586160bd 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/Monitor.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/Monitor.java @@ -64,23 +64,21 @@ package com.radixdlt.integration.distributed.simulation; -/** - * Keys for different monitor checks - */ +/** Keys for different monitor checks */ public enum Monitor { - CONSENSUS_SAFETY, - CONSENSUS_LIVENESS, - CONSENSUS_NO_TIMEOUTS, - CONSENSUS_NO_EPOCH_TIMEOUTS, - CONSENSUS_DIRECT_PARENTS, - CONSENSUS_NONE_COMMITTED, - CONSENSUS_TO_LEDGER_PROCESSED, - CONSENSUS_TIMESTAMP_CHECK, - CONSENSUS_VERTEX_REQUEST_RATE, - EPOCH_CEILING_VIEW, - LEDGER_IN_ORDER, - MEMPOOL_COMMITTED, - VALIDATOR_REGISTERED, - RADIX_ENGINE_NO_INVALID_PROPOSED_COMMANDS, - SYNC_MAX_LAG + CONSENSUS_SAFETY, + CONSENSUS_LIVENESS, + CONSENSUS_NO_TIMEOUTS, + CONSENSUS_NO_EPOCH_TIMEOUTS, + CONSENSUS_DIRECT_PARENTS, + CONSENSUS_NONE_COMMITTED, + CONSENSUS_TO_LEDGER_PROCESSED, + CONSENSUS_TIMESTAMP_CHECK, + CONSENSUS_VERTEX_REQUEST_RATE, + EPOCH_CEILING_VIEW, + LEDGER_IN_ORDER, + MEMPOOL_COMMITTED, + VALIDATOR_REGISTERED, + RADIX_ENGINE_NO_INVALID_PROPOSED_COMMANDS, + SYNC_MAX_LAG } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MonitorKey.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MonitorKey.java index ad9b500cb4..97323e63b9 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MonitorKey.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/MonitorKey.java @@ -65,15 +65,12 @@ package com.radixdlt.integration.distributed.simulation; import com.google.inject.multibindings.MapKey; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -/** - * Monitor key used for Guice bindings - */ +/** Monitor key used for Guice bindings */ @MapKey(unwrapValue = true) @Retention(RetentionPolicy.RUNTIME) public @interface MonitorKey { - Monitor value(); + Monitor value(); } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkDroppers.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkDroppers.java index 0b4293e7f5..7d836bc045 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkDroppers.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkDroppers.java @@ -72,114 +72,108 @@ import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.sync.GetVerticesErrorResponse; -import com.radixdlt.consensus.sync.GetVerticesResponse; import com.radixdlt.consensus.sync.GetVerticesRequest; -import com.radixdlt.integration.distributed.simulation.network.MessageDropper; +import com.radixdlt.consensus.sync.GetVerticesResponse; import com.radixdlt.integration.distributed.simulation.network.FProposalsPerViewDropper; +import com.radixdlt.integration.distributed.simulation.network.MessageDropper; import com.radixdlt.integration.distributed.simulation.network.OneNodePerEpochLedgerStatusUpdateDropper; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.MessageInTransit; import java.util.Random; import java.util.function.Predicate; public final class NetworkDroppers { - // TODO: This doesn't work with epochs yet - public static Module fRandomProposalsPerViewDropped() { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper(ImmutableList nodes, Random random) { - return new FProposalsPerViewDropper(nodes, random); - } - }; - } + // TODO: This doesn't work with epochs yet + public static Module fRandomProposalsPerViewDropped() { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper(ImmutableList nodes, Random random) { + return new FProposalsPerViewDropper(nodes, random); + } + }; + } - // TODO: This doesn't work with epochs yet - public static Module fNodesAllReceivedProposalsDropped() { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper(ImmutableList nodes) { - return new FProposalsPerViewDropper(nodes); - } - }; - } + // TODO: This doesn't work with epochs yet + public static Module fNodesAllReceivedProposalsDropped() { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper(ImmutableList nodes) { + return new FProposalsPerViewDropper(nodes); + } + }; + } - public static Module dropAllMessagesForOneNode(long durationMillis, long timeBetweenMillis) { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper(ImmutableList nodes) { - return msg -> { - if (msg.getSender().equals(msg.getReceiver())) { - return false; - } + public static Module dropAllMessagesForOneNode(long durationMillis, long timeBetweenMillis) { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper(ImmutableList nodes) { + return msg -> { + if (msg.getSender().equals(msg.getReceiver())) { + return false; + } - if (!msg.getSender().equals(nodes.get(0)) && !msg.getReceiver().equals(nodes.get(0))) { - return false; - } + if (!msg.getSender().equals(nodes.get(0)) && !msg.getReceiver().equals(nodes.get(0))) { + return false; + } - long current = System.currentTimeMillis() % (durationMillis + timeBetweenMillis); - return current < durationMillis; - }; - } - }; - } + long current = System.currentTimeMillis() % (durationMillis + timeBetweenMillis); + return current < durationMillis; + }; + } + }; + } - public static Module randomVotesAndViewTimeoutsDropped(double drops) { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper(Random random) { - return new MessageDropper(random, drops, Vote.class); - } - }; - } + public static Module randomVotesAndViewTimeoutsDropped(double drops) { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper(Random random) { + return new MessageDropper(random, drops, Vote.class); + } + }; + } - public static Module oneNodePerEpochLedgerStatusUpdateDropped() { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper() { - return new OneNodePerEpochLedgerStatusUpdateDropper(); - } - }; - } + public static Module oneNodePerEpochLedgerStatusUpdateDropped() { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper() { + return new OneNodePerEpochLedgerStatusUpdateDropper(); + } + }; + } - public static Module bftSyncMessagesDropped(double dropRate) { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper(Random random) { - return new MessageDropper( - random, - dropRate, - GetVerticesResponse.class, - GetVerticesErrorResponse.class, - GetVerticesRequest.class - ); - } - }; - } + public static Module bftSyncMessagesDropped(double dropRate) { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper(Random random) { + return new MessageDropper( + random, + dropRate, + GetVerticesResponse.class, + GetVerticesErrorResponse.class, + GetVerticesRequest.class); + } + }; + } - public static Module bftSyncMessagesDropped() { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper() { - return new MessageDropper( - GetVerticesResponse.class, - GetVerticesErrorResponse.class, - GetVerticesRequest.class - ); - } - }; - } + public static Module bftSyncMessagesDropped() { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper() { + return new MessageDropper( + GetVerticesResponse.class, GetVerticesErrorResponse.class, GetVerticesRequest.class); + } + }; + } - public static Module dropAllProposals() { - return new AbstractModule() { - @ProvidesIntoSet - Predicate dropper() { - return new MessageDropper( - Proposal.class - ); - } - }; - } + public static Module dropAllProposals() { + return new AbstractModule() { + @ProvidesIntoSet + Predicate dropper() { + return new MessageDropper(Proposal.class); + } + }; + } - private NetworkDroppers() { - throw new UnsupportedOperationException("Cannot instantiate."); - } + private NetworkDroppers() { + throw new UnsupportedOperationException("Cannot instantiate."); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkLatencies.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkLatencies.java index 981bb7222d..a544190a95 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkLatencies.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkLatencies.java @@ -77,72 +77,75 @@ import com.radixdlt.integration.distributed.simulation.network.RandomLatencyProvider; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork; import com.radixdlt.qualifier.LatencyProviderBase; - import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; public final class NetworkLatencies { - public static Module fixed(int latency) { - return new AbstractModule() { - @Provides - @LatencyProviderBase - LatencyProvider base() { - return msg -> latency; - } - }; - } + public static Module fixed(int latency) { + return new AbstractModule() { + @Provides + @LatencyProviderBase + LatencyProvider base() { + return msg -> latency; + } + }; + } - public static Module fixed() { - return fixed(SimulationNetwork.DEFAULT_LATENCY); - } + public static Module fixed() { + return fixed(SimulationNetwork.DEFAULT_LATENCY); + } - public static Module random(int minLatency, int maxLatency) { - return new AbstractModule() { - @Provides - @Singleton - @LatencyProviderBase - LatencyProvider base() { - return new RandomLatencyProvider(minLatency, maxLatency); - } - }; - } + public static Module random(int minLatency, int maxLatency) { + return new AbstractModule() { + @Provides + @Singleton + @LatencyProviderBase + LatencyProvider base() { + return new RandomLatencyProvider(minLatency, maxLatency); + } + }; + } - public static Module oneSlowProposalSender(int inBoundsLatency, int outOfBoundsLatency) { - return new AbstractModule() { - @Provides - @Singleton - @LatencyProviderBase - LatencyProvider base(ImmutableList nodes) { - return msg -> { - if ((msg.getSender().equals(nodes.get(0)) || msg.getReceiver().equals(nodes.get(0))) - && (msg.getContent() instanceof Proposal || msg.getContent() instanceof Vote - || msg.getContent() instanceof GetVerticesResponse) - ) { - return outOfBoundsLatency; - } else { - return inBoundsLatency; - } - }; - } - }; - } + public static Module oneSlowProposalSender(int inBoundsLatency, int outOfBoundsLatency) { + return new AbstractModule() { + @Provides + @Singleton + @LatencyProviderBase + LatencyProvider base(ImmutableList nodes) { + return msg -> { + if ((msg.getSender().equals(nodes.get(0)) || msg.getReceiver().equals(nodes.get(0))) + && (msg.getContent() instanceof Proposal + || msg.getContent() instanceof Vote + || msg.getContent() instanceof GetVerticesResponse)) { + return outOfBoundsLatency; + } else { + return inBoundsLatency; + } + }; + } + }; + } - public static Module oneOutOfBounds(int inBoundsLatency, int outOfBoundsLatency) { - return new AbstractModule() { - @Provides - @Singleton - @LatencyProviderBase - LatencyProvider base(ImmutableList nodes) { - Map nodeLatencies = IntStream.range(0, nodes.size()) - .boxed() - .collect(Collectors.toMap(nodes::get, i -> i == 0 ? outOfBoundsLatency : inBoundsLatency)); - return msg -> Math.max(nodeLatencies.get(msg.getSender()), nodeLatencies.get(msg.getReceiver())); - } - }; - } + public static Module oneOutOfBounds(int inBoundsLatency, int outOfBoundsLatency) { + return new AbstractModule() { + @Provides + @Singleton + @LatencyProviderBase + LatencyProvider base(ImmutableList nodes) { + Map nodeLatencies = + IntStream.range(0, nodes.size()) + .boxed() + .collect( + Collectors.toMap( + nodes::get, i -> i == 0 ? outOfBoundsLatency : inBoundsLatency)); + return msg -> + Math.max(nodeLatencies.get(msg.getSender()), nodeLatencies.get(msg.getReceiver())); + } + }; + } - private NetworkLatencies() { - throw new UnsupportedOperationException("Cannot instantiate."); - } + private NetworkLatencies() { + throw new UnsupportedOperationException("Cannot instantiate."); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkOrdering.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkOrdering.java index 03b9b34e14..a0693791d4 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkOrdering.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NetworkOrdering.java @@ -71,29 +71,27 @@ import com.radixdlt.integration.distributed.simulation.network.OutOfOrderChannels; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.ChannelCommunication; -/** - * Modules which provide networking ordering properties - */ +/** Modules which provide networking ordering properties */ public final class NetworkOrdering { - public static Module inOrder() { - return new AbstractModule() { - @Override - protected void configure() { - bind(ChannelCommunication.class).to(InOrderChannels.class).in(Scopes.SINGLETON); - } - }; - } + public static Module inOrder() { + return new AbstractModule() { + @Override + protected void configure() { + bind(ChannelCommunication.class).to(InOrderChannels.class).in(Scopes.SINGLETON); + } + }; + } - public static Module outOfOrder() { - return new AbstractModule() { - @Override - protected void configure() { - bind(ChannelCommunication.class).to(OutOfOrderChannels.class).in(Scopes.SINGLETON); - } - }; - } + public static Module outOfOrder() { + return new AbstractModule() { + @Override + protected void configure() { + bind(ChannelCommunication.class).to(OutOfOrderChannels.class).in(Scopes.SINGLETON); + } + }; + } - private NetworkOrdering() { - throw new UnsupportedOperationException("Cannot instantiate."); - } + private NetworkOrdering() { + throw new UnsupportedOperationException("Cannot instantiate."); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NodeNetworkMessagesModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NodeNetworkMessagesModule.java index aac22c4ba4..cd8b6f0583 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NodeNetworkMessagesModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/NodeNetworkMessagesModule.java @@ -79,82 +79,92 @@ import com.radixdlt.environment.rx.RxRemoteEnvironment; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.SimulatedNetworkImpl; +import com.radixdlt.mempool.MempoolAdd; import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; import com.radixdlt.sync.messages.remote.StatusRequest; import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; -import com.radixdlt.mempool.MempoolAdd; public class NodeNetworkMessagesModule extends AbstractModule { - private final SimulationNetwork simulationNetwork; - - public NodeNetworkMessagesModule(SimulationNetwork simulationNetwork) { - this.simulationNetwork = simulationNetwork; - } - - @Override - protected void configure() { - bind(RxRemoteEnvironment.class).to(SimulatedNetworkImpl.class).in(Scopes.SINGLETON); - } - - @Provides - private SimulatedNetworkImpl network(@Self BFTNode node) { - return simulationNetwork.getNetwork(node); - } - - @ProvidesIntoSet - private RxRemoteDispatcher mempoolAdd(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(MempoolAdd.class, network.remoteEventDispatcher(MempoolAdd.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher vertexRequestDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(GetVerticesRequest.class, network.remoteEventDispatcher(GetVerticesRequest.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher vertexResponseDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(GetVerticesResponse.class, network.remoteEventDispatcher(GetVerticesResponse.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher bftSyncErrorDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(GetVerticesErrorResponse.class, network.remoteEventDispatcher(GetVerticesErrorResponse.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher proposalDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(Proposal.class, network.remoteEventDispatcher(Proposal.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher voteDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(Vote.class, network.remoteEventDispatcher(Vote.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher syncRequestDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(SyncRequest.class, network.remoteEventDispatcher(SyncRequest.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher syncResponseDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(SyncResponse.class, network.remoteEventDispatcher(SyncResponse.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher statusRequestDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(StatusRequest.class, network.remoteEventDispatcher(StatusRequest.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher statusResponseDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(StatusResponse.class, network.remoteEventDispatcher(StatusResponse.class)); - } - - @ProvidesIntoSet - private RxRemoteDispatcher ledgerStatusUpdateDispatcher(SimulatedNetworkImpl network) { - return RxRemoteDispatcher.create(LedgerStatusUpdate.class, network.remoteEventDispatcher(LedgerStatusUpdate.class)); - } + private final SimulationNetwork simulationNetwork; + + public NodeNetworkMessagesModule(SimulationNetwork simulationNetwork) { + this.simulationNetwork = simulationNetwork; + } + + @Override + protected void configure() { + bind(RxRemoteEnvironment.class).to(SimulatedNetworkImpl.class).in(Scopes.SINGLETON); + } + + @Provides + private SimulatedNetworkImpl network(@Self BFTNode node) { + return simulationNetwork.getNetwork(node); + } + + @ProvidesIntoSet + private RxRemoteDispatcher mempoolAdd(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + MempoolAdd.class, network.remoteEventDispatcher(MempoolAdd.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher vertexRequestDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + GetVerticesRequest.class, network.remoteEventDispatcher(GetVerticesRequest.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher vertexResponseDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + GetVerticesResponse.class, network.remoteEventDispatcher(GetVerticesResponse.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher bftSyncErrorDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + GetVerticesErrorResponse.class, + network.remoteEventDispatcher(GetVerticesErrorResponse.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher proposalDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create(Proposal.class, network.remoteEventDispatcher(Proposal.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher voteDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create(Vote.class, network.remoteEventDispatcher(Vote.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher syncRequestDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + SyncRequest.class, network.remoteEventDispatcher(SyncRequest.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher syncResponseDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + SyncResponse.class, network.remoteEventDispatcher(SyncResponse.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher statusRequestDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + StatusRequest.class, network.remoteEventDispatcher(StatusRequest.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher statusResponseDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + StatusResponse.class, network.remoteEventDispatcher(StatusResponse.class)); + } + + @ProvidesIntoSet + private RxRemoteDispatcher ledgerStatusUpdateDispatcher(SimulatedNetworkImpl network) { + return RxRemoteDispatcher.create( + LedgerStatusUpdate.class, network.remoteEventDispatcher(LedgerStatusUpdate.class)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationNetworkModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationNetworkModule.java index 6560599960..3cc1600cdf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationNetworkModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationNetworkModule.java @@ -74,29 +74,26 @@ import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.MessageInTransit; import com.radixdlt.qualifier.LatencyProviderBase; - import java.util.Set; import java.util.function.Predicate; public class SimulationNetworkModule extends AbstractModule { - @Override - protected void configure() { - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }); - bind(SimulationNetwork.class).in(Scopes.SINGLETON); - } + @Override + protected void configure() { + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}); + bind(SimulationNetwork.class).in(Scopes.SINGLETON); + } - @Provides - @Singleton - private LatencyProvider latencyProvider( - @LatencyProviderBase LatencyProvider base, - Set> droppers - ) { - return msg -> { - if (droppers.stream().anyMatch(f -> f.test(msg))) { - return -1; // -1 Drops the message - } + @Provides + @Singleton + private LatencyProvider latencyProvider( + @LatencyProviderBase LatencyProvider base, Set> droppers) { + return msg -> { + if (droppers.stream().anyMatch(f -> f.test(msg))) { + return -1; // -1 Drops the message + } - return base.nextLatency(msg); - }; - } + return base.nextLatency(msg); + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationTest.java index e71619f909..678ec72707 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/SimulationTest.java @@ -73,8 +73,8 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; -import com.google.inject.Scopes; import com.google.inject.Provides; +import com.google.inject.Scopes; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.Multibinder; @@ -82,64 +82,63 @@ import com.radixdlt.ConsensusRecoveryModule; import com.radixdlt.FunctionalNodeModule; import com.radixdlt.LedgerRecoveryModule; +import com.radixdlt.MockedCryptoModule; import com.radixdlt.MockedKeyModule; +import com.radixdlt.MockedPersistenceStoreModule; import com.radixdlt.application.tokens.Amount; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.BFTValidator; +import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.PacemakerMaxExponent; +import com.radixdlt.consensus.bft.PacemakerRate; +import com.radixdlt.consensus.bft.PacemakerTimeout; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCountersImpl; +import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.environment.rx.RxEnvironmentModule; +import com.radixdlt.integration.distributed.MockedPeersViewModule; +import com.radixdlt.integration.distributed.simulation.TestInvariant.TestInvariantError; +import com.radixdlt.integration.distributed.simulation.application.BFTValidatorSetNodeSelector; +import com.radixdlt.integration.distributed.simulation.application.EpochsNodeSelector; +import com.radixdlt.integration.distributed.simulation.application.LocalMempoolPeriodicSubmitter; +import com.radixdlt.integration.distributed.simulation.application.NodeSelector; +import com.radixdlt.integration.distributed.simulation.application.TxnGenerator; +import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; import com.radixdlt.integration.distributed.simulation.monitors.SimulationNodeEventsModule; +import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork; +import com.radixdlt.integration.distributed.simulation.network.SimulationNodes; +import com.radixdlt.integration.distributed.simulation.network.SimulationNodes.RunningNetwork; import com.radixdlt.ledger.DtoLedgerProof; import com.radixdlt.ledger.LedgerAccumulator; import com.radixdlt.ledger.SimpleLedgerAccumulatorAndVerifier; import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.mempool.MempoolConfig; +import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; import com.radixdlt.network.p2p.NoOpPeerControl; import com.radixdlt.network.p2p.PeerControl; import com.radixdlt.networks.Addressing; import com.radixdlt.networks.Network; +import com.radixdlt.recovery.MockedRecoveryModule; +import com.radixdlt.statecomputer.EpochCeilingView; import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.RadixEngineModule; import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; -import com.radixdlt.MockedCryptoModule; -import com.radixdlt.MockedPersistenceStoreModule; -import com.radixdlt.environment.rx.RxEnvironmentModule; -import com.radixdlt.integration.distributed.MockedPeersViewModule; import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.store.MockedRadixEngineStoreModule; import com.radixdlt.sync.CommittedReader; import com.radixdlt.sync.MockedCommittedReaderModule; -import com.radixdlt.consensus.bft.PacemakerMaxExponent; -import com.radixdlt.consensus.bft.PacemakerRate; -import com.radixdlt.consensus.bft.PacemakerTimeout; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.counters.SystemCountersImpl; -import com.radixdlt.recovery.MockedRecoveryModule; -import com.radixdlt.integration.distributed.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.integration.distributed.simulation.application.BFTValidatorSetNodeSelector; -import com.radixdlt.integration.distributed.simulation.application.TxnGenerator; -import com.radixdlt.integration.distributed.simulation.application.EpochsNodeSelector; -import com.radixdlt.integration.distributed.simulation.application.NodeSelector; -import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; -import com.radixdlt.integration.distributed.simulation.application.LocalMempoolPeriodicSubmitter; -import com.radixdlt.integration.distributed.simulation.network.SimulationNodes; -import com.radixdlt.integration.distributed.simulation.network.SimulationNodes.RunningNetwork; -import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; -import com.radixdlt.consensus.bft.BFTValidator; -import com.radixdlt.consensus.bft.BFTValidatorSet; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork; -import com.radixdlt.statecomputer.EpochCeilingView; import com.radixdlt.sync.SyncConfig; import com.radixdlt.utils.DurationParser; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; - import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Iterator; @@ -153,609 +152,654 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -/** - * High level BFT Simulation Test Runner - */ +/** High level BFT Simulation Test Runner */ public class SimulationTest { - private static final String ENVIRONMENT_VAR_NAME = "TEST_DURATION"; // Same as used by regression test suite - private static final Duration DEFAULT_TEST_DURATION = Duration.ofSeconds(30); - - public interface SimulationNetworkActor { - void start(RunningNetwork network); - void stop(); - } - - private final ImmutableList nodes; - private final SimulationNetwork simulationNetwork; - private final Module testModule; - private final Module baseNodeModule; - private final Module overrideModule; - private final Map byzantineNodeModules; - - private SimulationTest( - ImmutableList nodes, - SimulationNetwork simulationNetwork, - Module baseNodeModule, - Module overrideModule, - Map byzantineNodeModules, - Module testModule - ) { - this.nodes = nodes; - this.simulationNetwork = simulationNetwork; - this.baseNodeModule = baseNodeModule; - this.overrideModule = overrideModule; - this.byzantineNodeModules = byzantineNodeModules; - this.testModule = testModule; - } - - public static class Builder { - private enum LedgerType { - MOCKED_LEDGER(false, true, false, false, false, false, false), - LEDGER(false, true, true, false, false, false, false), - LEDGER_AND_SYNC(false, true, true, false, false, false, true), - LEDGER_AND_LOCALMEMPOOL(false, true, true, true, false, false, false), - LEDGER_AND_EPOCHS(false, true, true, false, false, true, false), - LEDGER_AND_EPOCHS_AND_SYNC(false, true, true, false, false, true, true), - LEDGER_AND_LOCALMEMPOOL_AND_EPOCHS_AND_RADIXENGINE(true, true, true, true, true, true, false), - FULL_FUNCTION(true, true, true, true, true, true, true); - - private final boolean hasSharedMempool; - private final boolean hasConsensus; - private final boolean hasSync; - - // State manager - private final boolean hasLedger; - private final boolean hasMempool; - private final boolean hasRadixEngine; - - private final boolean hasEpochs; - - LedgerType( - boolean hasSharedMempool, - boolean hasConsensus, - boolean hasLedger, - boolean hasMempool, - boolean hasRadixEngine, - boolean hasEpochs, - boolean hasSync - ) { - this.hasSharedMempool = hasSharedMempool; - this.hasConsensus = hasConsensus; - this.hasLedger = hasLedger; - this.hasMempool = hasMempool; - this.hasRadixEngine = hasRadixEngine; - this.hasEpochs = hasEpochs; - this.hasSync = hasSync; - } - } - - private ImmutableList nodes = ImmutableList.of(ECKeyPair.generateNew()); - private long pacemakerTimeout = 12 * SimulationNetwork.DEFAULT_LATENCY; - private LedgerType ledgerType = LedgerType.MOCKED_LEDGER; - - private Module initialNodesModule; - private final ImmutableList.Builder testModules = ImmutableList.builder(); - private final ImmutableList.Builder modules = ImmutableList.builder(); - private Module networkModule; - private Module overrideModule = null; - private Function, ImmutableMap> byzantineModuleCreator = i -> ImmutableMap.of(); - private ImmutableMap> addressBookNodes; - - // TODO: Fix pacemaker so can Default 1 so can debug in IDE, possibly from properties at some point - // TODO: Specifically, simulation test with engine, epochs and mempool gets stuck on a single validator - private final int minValidators = 2; - - private Builder() { - } - - public Builder addSingleByzantineModule(Module byzantineModule) { - this.byzantineModuleCreator = nodes -> ImmutableMap.of(nodes.get(0), byzantineModule); - return this; - } - - public Builder addByzantineModuleToAll(Module byzantineModule) { - this.byzantineModuleCreator = nodes -> nodes.stream() - .collect(ImmutableMap.toImmutableMap(n -> n, n -> byzantineModule)); - return this; - } - - public Builder overrideWithIncorrectModule(Module module) { - this.overrideModule = module; - return this; - } - - public Builder networkModules(Module... networkModules) { - this.networkModule = Modules.combine(networkModules); - return this; - } - - public Builder addNetworkModule(Module networkModule) { - this.networkModule = Modules.combine(this.networkModule, networkModule); - return this; - } - - public Builder pacemakerTimeout(long pacemakerTimeout) { - this.pacemakerTimeout = pacemakerTimeout; - return this; - } - - /** - * A mapping from a node index to a list of other nodes indices. - * If key is not present, then address book for that node contains all other nodes. - */ - public Builder addressBook(ImmutableMap> addressBookNodes) { - this.addressBookNodes = addressBookNodes; - return this; - } - - public Builder numNodes(int numNodes, int numInitialValidators, Iterable initialStakes) { - this.nodes = Stream.generate(ECKeyPair::generateNew) - .limit(numNodes) - .collect(ImmutableList.toImmutableList()); - - final var stakesIterator = repeatLast(initialStakes); - final var initialStakesMap = nodes.stream() - .collect(ImmutableMap.toImmutableMap(ECKeyPair::getPublicKey, k -> stakesIterator.next())); - - var initialVset = BFTValidatorSet.from(initialStakesMap.entrySet().stream() - .map(e -> BFTValidator.from(BFTNode.create(e.getKey()), e.getValue()))); - - final var bftNodes = initialStakesMap.keySet().stream() - .map(BFTNode::create) - .collect(ImmutableList.toImmutableList()); - final var validators = initialStakesMap.entrySet().stream() - .map(e -> BFTValidator.from(BFTNode.create(e.getKey()), e.getValue())) - .collect(ImmutableList.toImmutableList()); - - this.initialNodesModule = new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { }).toInstance(bftNodes); - bind(new TypeLiteral>() { }).toInstance(validators); - } - }; - - this.modules.add(new AbstractModule() { - @Override - public void configure() { - bind(new TypeLiteral>() { }).toInstance(bftNodes); - bind(new TypeLiteral>() { }).toInstance(validators); - } - }); - - return this; - } - - public Builder numNodes(int numNodes, int numInitialValidators) { - return numNodes(numNodes, numInitialValidators, ImmutableList.of(UInt256.ONE)); - } - - public Builder numNodes(int numNodes) { - return numNodes(numNodes, numNodes); - } - - public Builder ledgerAndEpochs(View epochHighView, Function epochToNodeIndexMapper) { - this.ledgerType = LedgerType.LEDGER_AND_EPOCHS; - this.modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(View.class).annotatedWith(EpochCeilingView.class).toInstance(epochHighView); - } - - @Provides - public Function epochToNodeMapper() { - return epochToNodeIndexMapper.andThen(indices -> BFTValidatorSet.from( - indices.mapToObj(nodes::get) - .map(node -> BFTNode.create(node.getPublicKey())) - .map(node -> BFTValidator.from(node, UInt256.ONE)) - .collect(Collectors.toList()))); - } - }); - - return this; - } - - public Builder ledger() { - this.ledgerType = LedgerType.LEDGER; - return this; - } - - public Builder ledgerAndSync(SyncConfig syncConfig) { - this.ledgerType = LedgerType.LEDGER_AND_SYNC; - modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(SyncConfig.class).toInstance(syncConfig); - } - }); - return this; - } - - public Builder fullFunctionNodes(SyncConfig syncConfig) { - this.ledgerType = LedgerType.FULL_FUNCTION; - modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(SyncConfig.class).toInstance(syncConfig); - bind(new TypeLiteral>() { }).toInstance(List.of()); - } - }); - - return this; - } - - public Builder ledgerAndEpochsAndSync( - View epochHighView, - Function epochToNodeIndexMapper, - SyncConfig syncConfig - ) { - this.ledgerType = LedgerType.LEDGER_AND_EPOCHS_AND_SYNC; - modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(View.class).annotatedWith(EpochCeilingView.class).toInstance(epochHighView); - bind(SyncConfig.class).toInstance(syncConfig); - } - - @Provides - public Function epochToNodeMapper() { - return epochToNodeIndexMapper.andThen(indices -> BFTValidatorSet.from( - indices.mapToObj(nodes::get) - .map(node -> BFTNode.create(node.getPublicKey())) - .map(node -> BFTValidator.from(node, UInt256.ONE)) - .collect(Collectors.toList()))); - } - }); - return this; - } - - public Builder ledgerAndMempool() { - this.ledgerType = LedgerType.LEDGER_AND_LOCALMEMPOOL; - this.modules.add(MempoolConfig.asModule(10, 10)); - return this; - } - - public Builder ledgerAndRadixEngineWithEpochHighView() { - this.ledgerType = LedgerType.LEDGER_AND_LOCALMEMPOOL_AND_EPOCHS_AND_RADIXENGINE; - this.modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { }).toInstance(List.of()); - install(MempoolConfig.asModule(100, 10)); - } - - @Provides - CommittedReader committedReader() { - return new CommittedReader() { - @Override - public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { - return null; - } - - @Override - public Optional getEpochProof(long epoch) { - return Optional.empty(); - } - - @Override - public Optional getLastProof() { - return Optional.empty(); - } - }; - } - }); - - return this; - } - - public Builder addRadixEngineConfigModules(Module... modules) { - this.modules.add(modules); - this.testModules.add(modules); - return this; - } - - public Builder addNodeModule(Module module) { - this.modules.add(module); - return this; - } - - public Builder addTestModules(Module... modules) { - this.testModules.add(modules); - return this; - } - - public Builder addMempoolSubmissionsSteadyState(Class txnGeneratorClass) { - NodeSelector nodeSelector = this.ledgerType.hasEpochs ? new EpochsNodeSelector() : new BFTValidatorSetNodeSelector(); - this.testModules.add(new AbstractModule() { - @Override - public void configure() { - var multibinder = Multibinder.newSetBinder(binder(), SimulationNetworkActor.class); - multibinder.addBinding().to(LocalMempoolPeriodicSubmitter.class); - bind(TxnGenerator.class).to(txnGeneratorClass); - } - - @Provides - @Singleton - LocalMempoolPeriodicSubmitter mempoolSubmittor(TxnGenerator txnGenerator) { - return new LocalMempoolPeriodicSubmitter( - txnGenerator, - nodeSelector - ); - } - }); - - return this; - } - - public Builder addActor(Class c) { - this.testModules.add(new AbstractModule() { - @Override - public void configure() { - Multibinder.newSetBinder(binder(), SimulationNetworkActor.class).addBinding().to(c); - } - }); - return this; - }; - - public SimulationTest build() { - final NodeEvents nodeEvents = new NodeEvents(); - - // Config - modules.add(new AbstractModule() { - @Override - public void configure() { - bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); - bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(200); - bindConstant().annotatedWith(PacemakerTimeout.class).to(pacemakerTimeout); - bindConstant().annotatedWith(PacemakerRate.class).to(2.0); - bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); // Use constant timeout for now - bind(RateLimiter.class).annotatedWith(GetVerticesRequestRateLimit.class).toInstance(RateLimiter.create(50.0)); - bind(NodeEvents.class).toInstance(nodeEvents); - bind(PeerControl.class).toInstance(new NoOpPeerControl()); - } - }); - modules.add(new MockedSystemModule()); - modules.add(new MockedKeyModule()); - modules.add(new MockedCryptoModule()); - modules.add(new MockedPeersViewModule(this.addressBookNodes)); - - // Functional - modules.add(new FunctionalNodeModule( - ledgerType.hasConsensus, - ledgerType.hasLedger, - ledgerType.hasMempool, - ledgerType.hasSharedMempool, - ledgerType.hasRadixEngine, - ledgerType.hasEpochs, - ledgerType.hasSync - )); - - // Persistence - if (ledgerType.hasRadixEngine) { - modules.add(new MockedRadixEngineStoreModule()); - modules.add(new MockedGenesisModule( - nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), - Amount.ofTokens(1000000), - Amount.ofTokens(10000) - )); - modules.add(new LedgerRecoveryModule()); - modules.add(new ConsensusRecoveryModule()); - - // FIXME: A bit of a hack - testModules.add(new AbstractModule() { - public void configure() { - install(new MockedCryptoModule()); - install(new RadixEngineModule()); - install(new MockedGenesisModule( - nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), - Amount.ofTokens(1000000), - Amount.ofTokens(10000) - )); - bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); - bind(new TypeLiteral>() { }).toInstance(new InMemoryEngineStore<>()); - bind(SystemCounters.class).toInstance(new SystemCountersImpl()); - bind(CommittedReader.class).toInstance(CommittedReader.mocked()); - } - - @Genesis - @Provides - Txn genesis(@Genesis VerifiedTxnsAndProof txnsAndProof) { - return txnsAndProof.getTxns().get(0); - } - }); - } else { - modules.add(new MockedRecoveryModule()); - var initialVset = BFTValidatorSet.from(nodes.stream() - .map(e -> BFTValidator.from(BFTNode.create(e.getPublicKey()), UInt256.ONE))); - modules.add(new AbstractModule() { - public void configure() { - bind(BFTValidatorSet.class).toInstance(initialVset); - } - }); - } - - modules.add(new MockedPersistenceStoreModule()); - - // Testing - modules.add(new SimulationNodeEventsModule()); - testModules.add(new AbstractModule() { - @Override - protected void configure() { - Multibinder.newSetBinder(binder(), SimulationNetworkActor.class); - bind(Key.get(new TypeLiteral>() { })).toInstance(nodes); - bind(NodeEvents.class).toInstance(nodeEvents); - } - }); - - // Nodes - final SimulationNetwork simulationNetwork = Guice.createInjector( - initialNodesModule, - new SimulationNetworkModule(), - networkModule - ).getInstance(SimulationNetwork.class); - - // Runners - modules.add(new RxEnvironmentModule()); - if (ledgerType.hasLedger && ledgerType.hasSync) { - modules.add(new MockedCommittedReaderModule()); - } - - return new SimulationTest( - nodes, - simulationNetwork, - Modules.combine(modules.build()), - overrideModule, - byzantineModuleCreator.apply(this.nodes), - Modules.combine(testModules.build()) - ); - } - } - - public static Builder builder() { - return new Builder(); - } - - private Observable>> runChecks( - Set runners, - Map checkers, - RunningNetwork runningNetwork, - Duration duration - ) { - List>>> assertions = checkers.keySet().stream() - .map(name -> { - TestInvariant check = checkers.get(name); - return - Pair.of( - name, - check.check(runningNetwork).map(e -> Pair.of(name, e)).publish().autoConnect(2) - ); - }) - .collect(Collectors.toList()); - - Single firstErrorSignal = Observable.merge(assertions.stream().map(Pair::getSecond).collect(Collectors.toList())) - .firstOrError() - .map(Pair::getFirst); - - List>>> results = assertions.stream() - .map(assertion -> assertion.getSecond() - .takeUntil(firstErrorSignal.flatMapObservable(name -> - !assertion.getFirst().equals(name) ? Observable.just(name) : Observable.never())) - .takeUntil(Observable.timer(duration.get(ChronoUnit.SECONDS), TimeUnit.SECONDS)) - .map(e -> Optional.of(e.getSecond())) - .first(Optional.empty()) - .map(result -> Pair.of(assertion.getFirst(), result)) - ) - .collect(Collectors.toList()); - - return Single.merge(results).toObservable() - .doOnSubscribe(d -> runners.forEach(r -> r.start(runningNetwork))); - } - - /** - * Runs the test for time configured via environment variable. If environment variable is missing then - * default duration is used. Returns either once the duration has passed or if a check has failed. - * Returns a map from the check name to the result. - * - * @return map of check results - */ - public RunningSimulationTest run() { - return run(getConfiguredDuration(), ImmutableMap.of()); - } - - public RunningSimulationTest run(Duration duration) { - return run(duration, ImmutableMap.of()); - } - - /** - * Get test duration. - * - * @return configured test duration. - */ - public static Duration getConfiguredDuration() { - return Optional.ofNullable(System.getenv(ENVIRONMENT_VAR_NAME)) - .flatMap(DurationParser::parse) - .orElse(DEFAULT_TEST_DURATION); - } - - /** - * Runs the test for a given time. Returns either once the duration has passed or if a check has failed. - * Returns a map from the check name to the result. - * - * @param duration duration to run test for - * @param disabledModuleRunners a list of disabled module runners by node index - * @return test results - */ - public RunningSimulationTest run( - Duration duration, - ImmutableMap> disabledModuleRunners - ) { - Injector testInjector = Guice.createInjector(testModule); - var runners = testInjector.getInstance(Key.get(new TypeLiteral>() { })); - var checkers = testInjector.getInstance(Key.get(new TypeLiteral>() { })); - - SimulationNodes bftNetwork = new SimulationNodes( - nodes, - simulationNetwork, - baseNodeModule, - overrideModule, - byzantineNodeModules - ); - RunningNetwork runningNetwork = bftNetwork.start(disabledModuleRunners); - - final var resultObservable = runChecks(runners, checkers, runningNetwork, duration) - .doFinally(() -> { - runners.forEach(SimulationNetworkActor::stop); - bftNetwork.stop(); - }); - - return new RunningSimulationTest(resultObservable, runningNetwork); - } - - private static Iterator repeatLast(Iterable iterable) { - final var iterator = iterable.iterator(); - if (!iterator.hasNext()) { - throw new IllegalArgumentException("Can't repeat an empty iterable"); - } - return new Iterator<>() { - T lastValue = null; - - @Override - public boolean hasNext() { - return true; - } - - @Override - public T next() { - if (iterator.hasNext()) { - this.lastValue = iterator.next(); - } - return this.lastValue; - } - }; - } - - public static final class RunningSimulationTest { - - private final Observable>> resultObservable; - private final RunningNetwork network; - - private RunningSimulationTest( - Observable>> resultObservable, - RunningNetwork network - ) { - this.resultObservable = resultObservable; - this.network = network; - } - - public RunningNetwork getNetwork() { - return network; - } - - public Map> awaitCompletion() { - return this.resultObservable - .blockingStream() - .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); - } - } + private static final String ENVIRONMENT_VAR_NAME = + "TEST_DURATION"; // Same as used by regression test suite + private static final Duration DEFAULT_TEST_DURATION = Duration.ofSeconds(30); + + public interface SimulationNetworkActor { + void start(RunningNetwork network); + + void stop(); + } + + private final ImmutableList nodes; + private final SimulationNetwork simulationNetwork; + private final Module testModule; + private final Module baseNodeModule; + private final Module overrideModule; + private final Map byzantineNodeModules; + + private SimulationTest( + ImmutableList nodes, + SimulationNetwork simulationNetwork, + Module baseNodeModule, + Module overrideModule, + Map byzantineNodeModules, + Module testModule) { + this.nodes = nodes; + this.simulationNetwork = simulationNetwork; + this.baseNodeModule = baseNodeModule; + this.overrideModule = overrideModule; + this.byzantineNodeModules = byzantineNodeModules; + this.testModule = testModule; + } + + public static class Builder { + private enum LedgerType { + MOCKED_LEDGER(false, true, false, false, false, false, false), + LEDGER(false, true, true, false, false, false, false), + LEDGER_AND_SYNC(false, true, true, false, false, false, true), + LEDGER_AND_LOCALMEMPOOL(false, true, true, true, false, false, false), + LEDGER_AND_EPOCHS(false, true, true, false, false, true, false), + LEDGER_AND_EPOCHS_AND_SYNC(false, true, true, false, false, true, true), + LEDGER_AND_LOCALMEMPOOL_AND_EPOCHS_AND_RADIXENGINE(true, true, true, true, true, true, false), + FULL_FUNCTION(true, true, true, true, true, true, true); + + private final boolean hasSharedMempool; + private final boolean hasConsensus; + private final boolean hasSync; + + // State manager + private final boolean hasLedger; + private final boolean hasMempool; + private final boolean hasRadixEngine; + + private final boolean hasEpochs; + + LedgerType( + boolean hasSharedMempool, + boolean hasConsensus, + boolean hasLedger, + boolean hasMempool, + boolean hasRadixEngine, + boolean hasEpochs, + boolean hasSync) { + this.hasSharedMempool = hasSharedMempool; + this.hasConsensus = hasConsensus; + this.hasLedger = hasLedger; + this.hasMempool = hasMempool; + this.hasRadixEngine = hasRadixEngine; + this.hasEpochs = hasEpochs; + this.hasSync = hasSync; + } + } + + private ImmutableList nodes = ImmutableList.of(ECKeyPair.generateNew()); + private long pacemakerTimeout = 12 * SimulationNetwork.DEFAULT_LATENCY; + private LedgerType ledgerType = LedgerType.MOCKED_LEDGER; + + private Module initialNodesModule; + private final ImmutableList.Builder testModules = ImmutableList.builder(); + private final ImmutableList.Builder modules = ImmutableList.builder(); + private Module networkModule; + private Module overrideModule = null; + private Function, ImmutableMap> + byzantineModuleCreator = i -> ImmutableMap.of(); + private ImmutableMap> addressBookNodes; + + // TODO: Fix pacemaker so can Default 1 so can debug in IDE, possibly from properties at some + // point + // TODO: Specifically, simulation test with engine, epochs and mempool gets stuck on a single + // validator + private final int minValidators = 2; + + private Builder() {} + + public Builder addSingleByzantineModule(Module byzantineModule) { + this.byzantineModuleCreator = nodes -> ImmutableMap.of(nodes.get(0), byzantineModule); + return this; + } + + public Builder addByzantineModuleToAll(Module byzantineModule) { + this.byzantineModuleCreator = + nodes -> + nodes.stream() + .collect( + ImmutableMap.toImmutableMap( + n -> n, n -> byzantineModule)); + return this; + } + + public Builder overrideWithIncorrectModule(Module module) { + this.overrideModule = module; + return this; + } + + public Builder networkModules(Module... networkModules) { + this.networkModule = Modules.combine(networkModules); + return this; + } + + public Builder addNetworkModule(Module networkModule) { + this.networkModule = Modules.combine(this.networkModule, networkModule); + return this; + } + + public Builder pacemakerTimeout(long pacemakerTimeout) { + this.pacemakerTimeout = pacemakerTimeout; + return this; + } + + /** + * A mapping from a node index to a list of other nodes indices. If key is not present, then + * address book for that node contains all other nodes. + */ + public Builder addressBook(ImmutableMap> addressBookNodes) { + this.addressBookNodes = addressBookNodes; + return this; + } + + public Builder numNodes( + int numNodes, int numInitialValidators, Iterable initialStakes) { + this.nodes = + Stream.generate(ECKeyPair::generateNew) + .limit(numNodes) + .collect(ImmutableList.toImmutableList()); + + final var stakesIterator = repeatLast(initialStakes); + final var initialStakesMap = + nodes.stream() + .collect( + ImmutableMap.toImmutableMap(ECKeyPair::getPublicKey, k -> stakesIterator.next())); + + var initialVset = + BFTValidatorSet.from( + initialStakesMap.entrySet().stream() + .map(e -> BFTValidator.from(BFTNode.create(e.getKey()), e.getValue()))); + + final var bftNodes = + initialStakesMap.keySet().stream() + .map(BFTNode::create) + .collect(ImmutableList.toImmutableList()); + final var validators = + initialStakesMap.entrySet().stream() + .map(e -> BFTValidator.from(BFTNode.create(e.getKey()), e.getValue())) + .collect(ImmutableList.toImmutableList()); + + this.initialNodesModule = + new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}).toInstance(bftNodes); + bind(new TypeLiteral>() {}).toInstance(validators); + } + }; + + this.modules.add( + new AbstractModule() { + @Override + public void configure() { + bind(new TypeLiteral>() {}).toInstance(bftNodes); + bind(new TypeLiteral>() {}).toInstance(validators); + } + }); + + return this; + } + + public Builder numNodes(int numNodes, int numInitialValidators) { + return numNodes(numNodes, numInitialValidators, ImmutableList.of(UInt256.ONE)); + } + + public Builder numNodes(int numNodes) { + return numNodes(numNodes, numNodes); + } + + public Builder ledgerAndEpochs( + View epochHighView, Function epochToNodeIndexMapper) { + this.ledgerType = LedgerType.LEDGER_AND_EPOCHS; + this.modules.add( + new AbstractModule() { + @Override + protected void configure() { + bind(View.class).annotatedWith(EpochCeilingView.class).toInstance(epochHighView); + } + + @Provides + public Function epochToNodeMapper() { + return epochToNodeIndexMapper.andThen( + indices -> + BFTValidatorSet.from( + indices + .mapToObj(nodes::get) + .map(node -> BFTNode.create(node.getPublicKey())) + .map(node -> BFTValidator.from(node, UInt256.ONE)) + .collect(Collectors.toList()))); + } + }); + + return this; + } + + public Builder ledger() { + this.ledgerType = LedgerType.LEDGER; + return this; + } + + public Builder ledgerAndSync(SyncConfig syncConfig) { + this.ledgerType = LedgerType.LEDGER_AND_SYNC; + modules.add( + new AbstractModule() { + @Override + protected void configure() { + bind(SyncConfig.class).toInstance(syncConfig); + } + }); + return this; + } + + public Builder fullFunctionNodes(SyncConfig syncConfig) { + this.ledgerType = LedgerType.FULL_FUNCTION; + modules.add( + new AbstractModule() { + @Override + protected void configure() { + bind(SyncConfig.class).toInstance(syncConfig); + bind(new TypeLiteral>() {}).toInstance(List.of()); + } + }); + + return this; + } + + public Builder ledgerAndEpochsAndSync( + View epochHighView, + Function epochToNodeIndexMapper, + SyncConfig syncConfig) { + this.ledgerType = LedgerType.LEDGER_AND_EPOCHS_AND_SYNC; + modules.add( + new AbstractModule() { + @Override + protected void configure() { + bind(View.class).annotatedWith(EpochCeilingView.class).toInstance(epochHighView); + bind(SyncConfig.class).toInstance(syncConfig); + } + + @Provides + public Function epochToNodeMapper() { + return epochToNodeIndexMapper.andThen( + indices -> + BFTValidatorSet.from( + indices + .mapToObj(nodes::get) + .map(node -> BFTNode.create(node.getPublicKey())) + .map(node -> BFTValidator.from(node, UInt256.ONE)) + .collect(Collectors.toList()))); + } + }); + return this; + } + + public Builder ledgerAndMempool() { + this.ledgerType = LedgerType.LEDGER_AND_LOCALMEMPOOL; + this.modules.add(MempoolConfig.asModule(10, 10)); + return this; + } + + public Builder ledgerAndRadixEngineWithEpochHighView() { + this.ledgerType = LedgerType.LEDGER_AND_LOCALMEMPOOL_AND_EPOCHS_AND_RADIXENGINE; + this.modules.add( + new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}).toInstance(List.of()); + install(MempoolConfig.asModule(100, 10)); + } + + @Provides + CommittedReader committedReader() { + return new CommittedReader() { + @Override + public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { + return null; + } + + @Override + public Optional getEpochProof(long epoch) { + return Optional.empty(); + } + + @Override + public Optional getLastProof() { + return Optional.empty(); + } + }; + } + }); + + return this; + } + + public Builder addRadixEngineConfigModules(Module... modules) { + this.modules.add(modules); + this.testModules.add(modules); + return this; + } + + public Builder addNodeModule(Module module) { + this.modules.add(module); + return this; + } + + public Builder addTestModules(Module... modules) { + this.testModules.add(modules); + return this; + } + + public Builder addMempoolSubmissionsSteadyState( + Class txnGeneratorClass) { + NodeSelector nodeSelector = + this.ledgerType.hasEpochs ? new EpochsNodeSelector() : new BFTValidatorSetNodeSelector(); + this.testModules.add( + new AbstractModule() { + @Override + public void configure() { + var multibinder = Multibinder.newSetBinder(binder(), SimulationNetworkActor.class); + multibinder.addBinding().to(LocalMempoolPeriodicSubmitter.class); + bind(TxnGenerator.class).to(txnGeneratorClass); + } + + @Provides + @Singleton + LocalMempoolPeriodicSubmitter mempoolSubmittor(TxnGenerator txnGenerator) { + return new LocalMempoolPeriodicSubmitter(txnGenerator, nodeSelector); + } + }); + + return this; + } + + public Builder addActor(Class c) { + this.testModules.add( + new AbstractModule() { + @Override + public void configure() { + Multibinder.newSetBinder(binder(), SimulationNetworkActor.class).addBinding().to(c); + } + }); + return this; + } + ; + + public SimulationTest build() { + final NodeEvents nodeEvents = new NodeEvents(); + + // Config + modules.add( + new AbstractModule() { + @Override + public void configure() { + bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); + bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); + bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(200); + bindConstant().annotatedWith(PacemakerTimeout.class).to(pacemakerTimeout); + bindConstant().annotatedWith(PacemakerRate.class).to(2.0); + bindConstant() + .annotatedWith(PacemakerMaxExponent.class) + .to(0); // Use constant timeout for now + bind(RateLimiter.class) + .annotatedWith(GetVerticesRequestRateLimit.class) + .toInstance(RateLimiter.create(50.0)); + bind(NodeEvents.class).toInstance(nodeEvents); + bind(PeerControl.class).toInstance(new NoOpPeerControl()); + } + }); + modules.add(new MockedSystemModule()); + modules.add(new MockedKeyModule()); + modules.add(new MockedCryptoModule()); + modules.add(new MockedPeersViewModule(this.addressBookNodes)); + + // Functional + modules.add( + new FunctionalNodeModule( + ledgerType.hasConsensus, + ledgerType.hasLedger, + ledgerType.hasMempool, + ledgerType.hasSharedMempool, + ledgerType.hasRadixEngine, + ledgerType.hasEpochs, + ledgerType.hasSync)); + + // Persistence + if (ledgerType.hasRadixEngine) { + modules.add(new MockedRadixEngineStoreModule()); + modules.add( + new MockedGenesisModule( + nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), + Amount.ofTokens(1000000), + Amount.ofTokens(10000))); + modules.add(new LedgerRecoveryModule()); + modules.add(new ConsensusRecoveryModule()); + + // FIXME: A bit of a hack + testModules.add( + new AbstractModule() { + public void configure() { + install(new MockedCryptoModule()); + install(new RadixEngineModule()); + install( + new MockedGenesisModule( + nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), + Amount.ofTokens(1000000), + Amount.ofTokens(10000))); + bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); + bind(new TypeLiteral>() {}) + .toInstance(new InMemoryEngineStore<>()); + bind(SystemCounters.class).toInstance(new SystemCountersImpl()); + bind(CommittedReader.class).toInstance(CommittedReader.mocked()); + } + + @Genesis + @Provides + Txn genesis(@Genesis VerifiedTxnsAndProof txnsAndProof) { + return txnsAndProof.getTxns().get(0); + } + }); + } else { + modules.add(new MockedRecoveryModule()); + var initialVset = + BFTValidatorSet.from( + nodes.stream() + .map(e -> BFTValidator.from(BFTNode.create(e.getPublicKey()), UInt256.ONE))); + modules.add( + new AbstractModule() { + public void configure() { + bind(BFTValidatorSet.class).toInstance(initialVset); + } + }); + } + + modules.add(new MockedPersistenceStoreModule()); + + // Testing + modules.add(new SimulationNodeEventsModule()); + testModules.add( + new AbstractModule() { + @Override + protected void configure() { + Multibinder.newSetBinder(binder(), SimulationNetworkActor.class); + bind(Key.get(new TypeLiteral>() {})).toInstance(nodes); + bind(NodeEvents.class).toInstance(nodeEvents); + } + }); + + // Nodes + final SimulationNetwork simulationNetwork = + Guice.createInjector(initialNodesModule, new SimulationNetworkModule(), networkModule) + .getInstance(SimulationNetwork.class); + + // Runners + modules.add(new RxEnvironmentModule()); + if (ledgerType.hasLedger && ledgerType.hasSync) { + modules.add(new MockedCommittedReaderModule()); + } + + return new SimulationTest( + nodes, + simulationNetwork, + Modules.combine(modules.build()), + overrideModule, + byzantineModuleCreator.apply(this.nodes), + Modules.combine(testModules.build())); + } + } + + public static Builder builder() { + return new Builder(); + } + + private Observable>> runChecks( + Set runners, + Map checkers, + RunningNetwork runningNetwork, + Duration duration) { + List>>> assertions = + checkers.keySet().stream() + .map( + name -> { + TestInvariant check = checkers.get(name); + return Pair.of( + name, + check + .check(runningNetwork) + .map(e -> Pair.of(name, e)) + .publish() + .autoConnect(2)); + }) + .collect(Collectors.toList()); + + Single firstErrorSignal = + Observable.merge(assertions.stream().map(Pair::getSecond).collect(Collectors.toList())) + .firstOrError() + .map(Pair::getFirst); + + List>>> results = + assertions.stream() + .map( + assertion -> + assertion + .getSecond() + .takeUntil( + firstErrorSignal.flatMapObservable( + name -> + !assertion.getFirst().equals(name) + ? Observable.just(name) + : Observable.never())) + .takeUntil( + Observable.timer(duration.get(ChronoUnit.SECONDS), TimeUnit.SECONDS)) + .map(e -> Optional.of(e.getSecond())) + .first(Optional.empty()) + .map(result -> Pair.of(assertion.getFirst(), result))) + .collect(Collectors.toList()); + + return Single.merge(results) + .toObservable() + .doOnSubscribe(d -> runners.forEach(r -> r.start(runningNetwork))); + } + + /** + * Runs the test for time configured via environment variable. If environment variable is missing + * then default duration is used. Returns either once the duration has passed or if a check has + * failed. Returns a map from the check name to the result. + * + * @return map of check results + */ + public RunningSimulationTest run() { + return run(getConfiguredDuration(), ImmutableMap.of()); + } + + public RunningSimulationTest run(Duration duration) { + return run(duration, ImmutableMap.of()); + } + + /** + * Get test duration. + * + * @return configured test duration. + */ + public static Duration getConfiguredDuration() { + return Optional.ofNullable(System.getenv(ENVIRONMENT_VAR_NAME)) + .flatMap(DurationParser::parse) + .orElse(DEFAULT_TEST_DURATION); + } + + /** + * Runs the test for a given time. Returns either once the duration has passed or if a check has + * failed. Returns a map from the check name to the result. + * + * @param duration duration to run test for + * @param disabledModuleRunners a list of disabled module runners by node index + * @return test results + */ + public RunningSimulationTest run( + Duration duration, ImmutableMap> disabledModuleRunners) { + Injector testInjector = Guice.createInjector(testModule); + var runners = + testInjector.getInstance(Key.get(new TypeLiteral>() {})); + var checkers = + testInjector.getInstance(Key.get(new TypeLiteral>() {})); + + SimulationNodes bftNetwork = + new SimulationNodes( + nodes, simulationNetwork, baseNodeModule, overrideModule, byzantineNodeModules); + RunningNetwork runningNetwork = bftNetwork.start(disabledModuleRunners); + + final var resultObservable = + runChecks(runners, checkers, runningNetwork, duration) + .doFinally( + () -> { + runners.forEach(SimulationNetworkActor::stop); + bftNetwork.stop(); + }); + + return new RunningSimulationTest(resultObservable, runningNetwork); + } + + private static Iterator repeatLast(Iterable iterable) { + final var iterator = iterable.iterator(); + if (!iterator.hasNext()) { + throw new IllegalArgumentException("Can't repeat an empty iterable"); + } + return new Iterator<>() { + T lastValue = null; + + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + if (iterator.hasNext()) { + this.lastValue = iterator.next(); + } + return this.lastValue; + } + }; + } + + public static final class RunningSimulationTest { + + private final Observable>> resultObservable; + private final RunningNetwork network; + + private RunningSimulationTest( + Observable>> resultObservable, + RunningNetwork network) { + this.resultObservable = resultObservable; + this.network = network; + } + + public RunningNetwork getNetwork() { + return network; + } + + public Map> awaitCompletion() { + return this.resultObservable + .blockingStream() + .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); + } + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/TestInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/TestInvariant.java index 5e133dcb9b..5945a334cd 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/TestInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/TestInvariant.java @@ -67,33 +67,31 @@ import com.radixdlt.integration.distributed.simulation.network.SimulationNodes.RunningNetwork; import io.reactivex.rxjava3.core.Observable; -/** - * A running BFT check given access to network - */ +/** A running BFT check given access to network */ public interface TestInvariant { - class TestInvariantError { - private final String description; - public TestInvariantError(String description) { - this.description = description; - } + class TestInvariantError { + private final String description; + + public TestInvariantError(String description) { + this.description = description; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - @Override - public String toString() { - return String.format("%s{desc=%s}", this.getClass().getSimpleName(), description); - } - } + @Override + public String toString() { + return String.format("%s{desc=%s}", this.getClass().getSimpleName(), description); + } + } - /** - * Creates an observable which runs assertions against a bft network. - * Assertions errors are expected to propagate down the observable. - * TODO: Cleanup interface a bit - * - * @param network network to check - * @return completable to subscribe to enable checking - */ - Observable check(RunningNetwork network); + /** + * Creates an observable which runs assertions against a bft network. Assertions errors are + * expected to propagate down the observable. TODO: Cleanup interface a bit + * + * @param network network to check + * @return completable to subscribe to enable checking + */ + Observable check(RunningNetwork network); } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/BFTValidatorSetNodeSelector.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/BFTValidatorSetNodeSelector.java index ac36c0b59c..08abd91be1 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/BFTValidatorSetNodeSelector.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/BFTValidatorSetNodeSelector.java @@ -71,18 +71,16 @@ import io.reactivex.rxjava3.core.Single; import java.util.Random; -/** - * Selects nodes from an initial bft configuration (no epochs) - */ +/** Selects nodes from an initial bft configuration (no epochs) */ public class BFTValidatorSetNodeSelector implements NodeSelector { - private final Random random = new Random(); + private final Random random = new Random(); - @Override - public Single nextNode(RunningNetwork network) { - BFTConfiguration config = network.bftConfiguration(); - ImmutableList validators = config.getValidatorSet().nodes().asList(); - int validatorSetSize = validators.size(); - BFTNode node = validators.get(random.nextInt(validatorSetSize)); - return Single.just(node); - } + @Override + public Single nextNode(RunningNetwork network) { + BFTConfiguration config = network.bftConfiguration(); + ImmutableList validators = config.getValidatorSet().nodes().asList(); + int validatorSetSize = validators.size(); + BFTNode node = validators.get(random.nextInt(validatorSetSize)); + return Single.just(node); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/EpochsNodeSelector.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/EpochsNodeSelector.java index 7cfb00aecf..1bb3a785e0 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/EpochsNodeSelector.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/EpochsNodeSelector.java @@ -70,19 +70,21 @@ import io.reactivex.rxjava3.core.Single; import java.util.Random; -/** - * Selects random nodes from changing validator sets given by epochs - */ +/** Selects random nodes from changing validator sets given by epochs */ public class EpochsNodeSelector implements NodeSelector { - private final Random random = new Random(); + private final Random random = new Random(); - @Override - public Single nextNode(RunningNetwork network) { - return network.latestEpochChanges() - .map(e -> { - ImmutableList validators = e.getBFTConfiguration().getValidatorSet().nodes().asList(); - int validatorSetSize = validators.size(); - return validators.get(random.nextInt(validatorSetSize)); - }).firstOrError(); - } + @Override + public Single nextNode(RunningNetwork network) { + return network + .latestEpochChanges() + .map( + e -> { + ImmutableList validators = + e.getBFTConfiguration().getValidatorSet().nodes().asList(); + int validatorSetSize = validators.size(); + return validators.get(random.nextInt(validatorSetSize)); + }) + .firstOrError(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/IncrementalBytes.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/IncrementalBytes.java index e7c91cff17..a93e634cc6 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/IncrementalBytes.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/IncrementalBytes.java @@ -67,14 +67,12 @@ import com.google.common.primitives.Longs; import com.radixdlt.atom.Txn; -/** - * Submits unique bytes (incrementally) to a network - */ +/** Submits unique bytes (incrementally) to a network */ public class IncrementalBytes implements TxnGenerator { - private long commandId = 0; + private long commandId = 0; - @Override - public Txn nextTxn() { - return Txn.create(Longs.toByteArray(commandId++)); - } + @Override + public Txn nextTxn() { + return Txn.create(Longs.toByteArray(commandId++)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/LocalMempoolPeriodicSubmitter.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/LocalMempoolPeriodicSubmitter.java index 48d78d8f19..1ee0fea8f1 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/LocalMempoolPeriodicSubmitter.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/LocalMempoolPeriodicSubmitter.java @@ -76,47 +76,46 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import java.util.concurrent.TimeUnit; -/** - * Contributes to steady state by submitting commands to the mempool every few seconds - */ +/** Contributes to steady state by submitting commands to the mempool every few seconds */ public class LocalMempoolPeriodicSubmitter implements SimulationNetworkActor { - private final PublishSubject> txns; - private final TxnGenerator txnGenerator; - private final NodeSelector nodeSelector; + private final PublishSubject> txns; + private final TxnGenerator txnGenerator; + private final NodeSelector nodeSelector; - private Disposable commandsDisposable; + private Disposable commandsDisposable; - public LocalMempoolPeriodicSubmitter(TxnGenerator txnGenerator, NodeSelector nodeSelector) { - this.txns = PublishSubject.create(); - this.txnGenerator = txnGenerator; - this.nodeSelector = nodeSelector; - } + public LocalMempoolPeriodicSubmitter(TxnGenerator txnGenerator, NodeSelector nodeSelector) { + this.txns = PublishSubject.create(); + this.txnGenerator = txnGenerator; + this.nodeSelector = nodeSelector; + } - private void act(RunningNetwork network, Txn txn, BFTNode node) { - network.getDispatcher(MempoolAdd.class, node).dispatch(MempoolAdd.create(txn)); - } + private void act(RunningNetwork network, Txn txn, BFTNode node) { + network.getDispatcher(MempoolAdd.class, node).dispatch(MempoolAdd.create(txn)); + } - public Observable> issuedTxns() { - return txns.observeOn(Schedulers.io()); - } + public Observable> issuedTxns() { + return txns.observeOn(Schedulers.io()); + } - @Override - public void start(RunningNetwork network) { - if (commandsDisposable != null) { - return; - } + @Override + public void start(RunningNetwork network) { + if (commandsDisposable != null) { + return; + } - commandsDisposable = Observable.interval(1, 10, TimeUnit.SECONDS) - .map(i -> txnGenerator.nextTxn()) - .flatMapSingle(cmd -> nodeSelector.nextNode(network).map(node -> Pair.of(cmd, node))) - .doOnNext(p -> this.act(network, p.getFirst(), p.getSecond())) - .subscribe(txns::onNext); - } + commandsDisposable = + Observable.interval(1, 10, TimeUnit.SECONDS) + .map(i -> txnGenerator.nextTxn()) + .flatMapSingle(cmd -> nodeSelector.nextNode(network).map(node -> Pair.of(cmd, node))) + .doOnNext(p -> this.act(network, p.getFirst(), p.getSecond())) + .subscribe(txns::onNext); + } - @Override - public void stop() { - commandsDisposable.dispose(); - txns.onComplete(); - } + @Override + public void stop() { + commandsDisposable.dispose(); + txns.onComplete(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/MempoolFillerStarter.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/MempoolFillerStarter.java index 157ff94aef..0132c85628 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/MempoolFillerStarter.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/MempoolFillerStarter.java @@ -64,23 +64,20 @@ package com.radixdlt.integration.distributed.simulation.application; -import com.radixdlt.mempoolfiller.MempoolFillerUpdate; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.network.SimulationNodes; +import com.radixdlt.mempoolfiller.MempoolFillerUpdate; -/** - * Starts a mempool filler - */ +/** Starts a mempool filler */ public final class MempoolFillerStarter implements SimulationTest.SimulationNetworkActor { - @Override - public void start(SimulationNodes.RunningNetwork network) { - EventDispatcher dispatcher = network - .getDispatcher(MempoolFillerUpdate.class, network.getNodes().get(0)); - dispatcher.dispatch(MempoolFillerUpdate.enable(15, true)); - } + @Override + public void start(SimulationNodes.RunningNetwork network) { + EventDispatcher dispatcher = + network.getDispatcher(MempoolFillerUpdate.class, network.getNodes().get(0)); + dispatcher.dispatch(MempoolFillerUpdate.enable(15, true)); + } - @Override - public void stop() { - } + @Override + public void stop() {} } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeSelector.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeSelector.java index 02cb81654e..f625fbe7ce 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeSelector.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeSelector.java @@ -68,9 +68,7 @@ import com.radixdlt.integration.distributed.simulation.network.SimulationNodes.RunningNetwork; import io.reactivex.rxjava3.core.Single; -/** - * Node generator for a given network - */ +/** Node generator for a given network */ public interface NodeSelector { - Single nextNode(RunningNetwork network); + Single nextNode(RunningNetwork network); } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRandomRegistrator.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRandomRegistrator.java index 5afcbe7e52..2d4811a2d8 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRandomRegistrator.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRandomRegistrator.java @@ -79,54 +79,58 @@ import com.radixdlt.statecomputer.RadixEngineStateComputer; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.Disposable; - import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; -/** - * Register and unregisters nodes as validators randomly - */ +/** Register and unregisters nodes as validators randomly */ public final class NodeValidatorRandomRegistrator implements SimulationTest.SimulationNetworkActor { - private Disposable disposable; - private final Random random; + private Disposable disposable; + private final Random random; - @Inject - public NodeValidatorRandomRegistrator(Random random) { - this.random = random; - } + @Inject + public NodeValidatorRandomRegistrator(Random random) { + this.random = random; + } - @Override - public void start(SimulationNodes.RunningNetwork network) { - List nodes = network.getNodes(); - this.disposable = Observable.interval(1, 1, TimeUnit.SECONDS) - // Don't unregister node0/node1 so we are assured validatorSet never becomes empty - .map(i -> nodes.get(random.nextInt(nodes.size() - 2) + 2)) - .subscribe(node -> { - var txnConstructionRequest = TxnConstructionRequest.create(); - if (random.nextBoolean()) { - txnConstructionRequest.registerAsValidator(node.getKey()); - } else { - txnConstructionRequest.unregisterAsValidator(node.getKey()); - } + @Override + public void start(SimulationNodes.RunningNetwork network) { + List nodes = network.getNodes(); + this.disposable = + Observable.interval(1, 1, TimeUnit.SECONDS) + // Don't unregister node0/node1 so we are assured validatorSet never becomes empty + .map(i -> nodes.get(random.nextInt(nodes.size() - 2) + 2)) + .subscribe( + node -> { + var txnConstructionRequest = TxnConstructionRequest.create(); + if (random.nextBoolean()) { + txnConstructionRequest.registerAsValidator(node.getKey()); + } else { + txnConstructionRequest.unregisterAsValidator(node.getKey()); + } - var radixEngine = network.getNodeInjector(node) - .getInstance(Key.get(new TypeLiteral>() { })); - var radixEngineStateComputer = network.getNodeInjector(node) - .getInstance(RadixEngineStateComputer.class); - var hashSigner = network.getNodeInjector(node).getInstance(HashSigner.class); + var radixEngine = + network + .getNodeInjector(node) + .getInstance( + Key.get(new TypeLiteral>() {})); + var radixEngineStateComputer = + network.getNodeInjector(node).getInstance(RadixEngineStateComputer.class); + var hashSigner = network.getNodeInjector(node).getInstance(HashSigner.class); - var txBuilder = radixEngine.construct(txnConstructionRequest.feePayer(REAddr.ofPubKeyAccount(node.getKey()))); - var txn = txBuilder.signAndBuild(hashSigner::sign); - try { - radixEngineStateComputer.addToMempool(txn); - } catch (MempoolRejectedException ignored) { - } - }); - } + var txBuilder = + radixEngine.construct( + txnConstructionRequest.feePayer(REAddr.ofPubKeyAccount(node.getKey()))); + var txn = txBuilder.signAndBuild(hashSigner::sign); + try { + radixEngineStateComputer.addToMempool(txn); + } catch (MempoolRejectedException ignored) { + } + }); + } - @Override - public void stop() { - this.disposable.dispose(); - } + @Override + public void stop() { + this.disposable.dispose(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRegistrator.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRegistrator.java index c283ffe25c..1eb83f1067 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRegistrator.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/NodeValidatorRegistrator.java @@ -81,47 +81,50 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.subjects.BehaviorSubject; import io.reactivex.rxjava3.subjects.Subject; - import java.util.List; import java.util.concurrent.TimeUnit; -/** - * Registers nodes in order as validators - */ +/** Registers nodes in order as validators */ public final class NodeValidatorRegistrator implements SimulationTest.SimulationNetworkActor { - private Disposable disposable; - private final Subject validationRegistrations = BehaviorSubject.create(); + private Disposable disposable; + private final Subject validationRegistrations = BehaviorSubject.create(); - @Override - public void start(SimulationNodes.RunningNetwork network) { - List nodes = network.getNodes(); - this.disposable = Observable.fromIterable(nodes) + @Override + public void start(SimulationNodes.RunningNetwork network) { + List nodes = network.getNodes(); + this.disposable = + Observable.fromIterable(nodes) .concatMap(i -> Observable.timer(3, TimeUnit.SECONDS).map(l -> i)) .doOnNext(validationRegistrations::onNext) - .subscribe(node -> { - var radixEngine = network.getNodeInjector(node) - .getInstance(Key.get(new TypeLiteral>() { })); - var radixEngineStateComputer = network.getNodeInjector(node) - .getInstance(RadixEngineStateComputer.class); - var hashSigner = network.getNodeInjector(node).getInstance(HashSigner.class); - var request = TxnConstructionRequest.create() - .action(new RegisterValidator(node.getKey())) - .feePayer(REAddr.ofPubKeyAccount(node.getKey())); - var txBuilder = radixEngine.construct(request); - var txn = txBuilder.signAndBuild(hashSigner::sign); - try { + .subscribe( + node -> { + var radixEngine = + network + .getNodeInjector(node) + .getInstance( + Key.get(new TypeLiteral>() {})); + var radixEngineStateComputer = + network.getNodeInjector(node).getInstance(RadixEngineStateComputer.class); + var hashSigner = network.getNodeInjector(node).getInstance(HashSigner.class); + var request = + TxnConstructionRequest.create() + .action(new RegisterValidator(node.getKey())) + .feePayer(REAddr.ofPubKeyAccount(node.getKey())); + var txBuilder = radixEngine.construct(request); + var txn = txBuilder.signAndBuild(hashSigner::sign); + try { radixEngineStateComputer.addToMempool(txn); - } catch (MempoolRejectedException ignored) { - } - }); - } + } catch (MempoolRejectedException ignored) { + } + }); + } - @Override - public void stop() { - this.disposable.dispose(); - } + @Override + public void stop() { + this.disposable.dispose(); + } - public Observable validatorRegistrationSubmissions() { - return validationRegistrations; - } + public Observable validatorRegistrationSubmissions() { + return validationRegistrations; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/RadixEngineUniqueGenerator.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/RadixEngineUniqueGenerator.java index b3d4be62d4..c7cdc01e49 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/RadixEngineUniqueGenerator.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/RadixEngineUniqueGenerator.java @@ -71,37 +71,33 @@ import com.radixdlt.atom.Txn; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.parser.REParser; -import com.radixdlt.statecomputer.forks.RERules; import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.checkpoint.Genesis; - +import com.radixdlt.statecomputer.forks.RERules; import java.nio.charset.StandardCharsets; /** - * Generates a new unique rri consumer command. Because new addresses are used - * on every call, the command should never fail when executed on a radix engine. + * Generates a new unique rri consumer command. Because new addresses are used on every call, the + * command should never fail when executed on a radix engine. */ public class RadixEngineUniqueGenerator implements TxnGenerator { - @Inject - private REParser parser; + @Inject private REParser parser; - @Inject - private RERules rules; + @Inject private RERules rules; - @Inject - @Genesis - private Txn genesis; + @Inject @Genesis private Txn genesis; - @Override - public Txn nextTxn() { - var keyPair = ECKeyPair.generateNew(); - var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "smthng"); - var builder = TxBuilder.newBuilder(parser.getSubstateDeserialization(), rules.getSerialization()) - .toLowLevelBuilder() - .syscall(Syscall.READDR_CLAIM, "smthng".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .end(); - var sig = keyPair.sign(builder.hashToSign()); - return builder.sig(sig).build(); - } + @Override + public Txn nextTxn() { + var keyPair = ECKeyPair.generateNew(); + var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "smthng"); + var builder = + TxBuilder.newBuilder(parser.getSubstateDeserialization(), rules.getSerialization()) + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, "smthng".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .end(); + var sig = keyPair.sign(builder.hashToSign()); + return builder.sig(sig).build(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/TxnGenerator.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/TxnGenerator.java index adbdfc3748..1fee6ef4e4 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/TxnGenerator.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/application/TxnGenerator.java @@ -66,9 +66,7 @@ import com.radixdlt.atom.Txn; -/** - * Generator of commands for consensus processing - */ +/** Generator of commands for consensus processing */ public interface TxnGenerator { - Txn nextTxn(); + Txn nextTxn(); } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/EventNeverOccursInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/EventNeverOccursInvariant.java index fccc981f1b..96095e25e6 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/EventNeverOccursInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/EventNeverOccursInvariant.java @@ -70,28 +70,28 @@ /** * Checks that an event of a certain class never occurs in a system + * * @param the class of the event to check for */ public final class EventNeverOccursInvariant implements TestInvariant { - private final NodeEvents nodeEvents; - private final Class eventClass; + private final NodeEvents nodeEvents; + private final Class eventClass; - public EventNeverOccursInvariant(NodeEvents nodeEvents, Class eventClass) { - this.nodeEvents = nodeEvents; - this.eventClass = eventClass; - } + public EventNeverOccursInvariant(NodeEvents nodeEvents, Class eventClass) { + this.nodeEvents = nodeEvents; + this.eventClass = eventClass; + } - @Override - public Observable check(SimulationNodes.RunningNetwork network) { - return Observable.create( - emitter -> - this.nodeEvents.addListener( - (node, event) -> emitter.onNext(new TestInvariant.TestInvariantError( - "Event " + event + " occurred at node " + node) - ), - eventClass - ) - ) - .serialize(); - } + @Override + public Observable check(SimulationNodes.RunningNetwork network) { + return Observable.create( + emitter -> + this.nodeEvents.addListener( + (node, event) -> + emitter.onNext( + new TestInvariant.TestInvariantError( + "Event " + event + " occurred at node " + node)), + eventClass)) + .serialize(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/NodeEvents.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/NodeEvents.java index d9d37707f9..2deed1dda1 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/NodeEvents.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/NodeEvents.java @@ -69,28 +69,29 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.environment.EventProcessor; import com.radixdlt.environment.EventProcessorOnDispatch; - import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.function.BiConsumer; /** - * Temporary class for simulation tests. - * TODO: Replace use of this class with the NodeEvents class in deterministic tests. + * Temporary class for simulation tests. TODO: Replace use of this class with the NodeEvents class + * in deterministic tests. */ public final class NodeEvents { - private final ConcurrentMap, Set>> consumers = Maps.newConcurrentMap(); + private final ConcurrentMap, Set>> consumers = + Maps.newConcurrentMap(); - public void addListener(BiConsumer eventConsumer, Class eventClass) { - this.consumers.computeIfAbsent(eventClass, k -> Sets.newConcurrentHashSet()) - .add((node, e) -> eventConsumer.accept(node, eventClass.cast(e))); - } + public void addListener(BiConsumer eventConsumer, Class eventClass) { + this.consumers + .computeIfAbsent(eventClass, k -> Sets.newConcurrentHashSet()) + .add((node, e) -> eventConsumer.accept(node, eventClass.cast(e))); + } - public EventProcessor processor(BFTNode node, Class eventClass) { - return t -> this.consumers.getOrDefault(eventClass, Set.of()).forEach(c -> c.accept(node, t)); - } + public EventProcessor processor(BFTNode node, Class eventClass) { + return t -> this.consumers.getOrDefault(eventClass, Set.of()).forEach(c -> c.accept(node, t)); + } - public EventProcessorOnDispatch processorOnDispatch(BFTNode node, Class eventClass) { - return new EventProcessorOnDispatch<>(eventClass, processor(node, eventClass)); - } + public EventProcessorOnDispatch processorOnDispatch(BFTNode node, Class eventClass) { + return new EventProcessorOnDispatch<>(eventClass, processor(node, eventClass)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/SimulationNodeEventsModule.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/SimulationNodeEventsModule.java index e87db8f33f..08a730e73b 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/SimulationNodeEventsModule.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/SimulationNodeEventsModule.java @@ -78,59 +78,45 @@ import com.radixdlt.environment.ProcessOnDispatch; import com.radixdlt.statecomputer.InvalidProposedTxn; -/** - * Module which manages node testing events for simulation - */ +/** Module which manages node testing events for simulation */ public final class SimulationNodeEventsModule extends AbstractModule { - @ProvidesIntoSet - private EventProcessorOnDispatch epochTimeoutProcessor( - @Self BFTNode node, - NodeEvents nodeEvents - ) { - return nodeEvents.processorOnDispatch(node, EpochLocalTimeoutOccurrence.class); - } + @ProvidesIntoSet + private EventProcessorOnDispatch epochTimeoutProcessor( + @Self BFTNode node, NodeEvents nodeEvents) { + return nodeEvents.processorOnDispatch(node, EpochLocalTimeoutOccurrence.class); + } - @ProcessOnDispatch - @ProvidesIntoSet - private EventProcessor timeoutEventProcessor( - @Self BFTNode node, - NodeEvents nodeEvents - ) { - return nodeEvents.processor(node, LocalTimeoutOccurrence.class); - } + @ProcessOnDispatch + @ProvidesIntoSet + private EventProcessor timeoutEventProcessor( + @Self BFTNode node, NodeEvents nodeEvents) { + return nodeEvents.processor(node, LocalTimeoutOccurrence.class); + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor requestProcessor( - @Self BFTNode node, - NodeEvents nodeEvents - ) { - return nodeEvents.processor(node, GetVerticesRequest.class); - } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor requestProcessor( + @Self BFTNode node, NodeEvents nodeEvents) { + return nodeEvents.processor(node, GetVerticesRequest.class); + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor committedProcessor( - @Self BFTNode node, - NodeEvents nodeEvents - ) { - return nodeEvents.processor(node, BFTCommittedUpdate.class); - } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor committedProcessor( + @Self BFTNode node, NodeEvents nodeEvents) { + return nodeEvents.processor(node, BFTCommittedUpdate.class); + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor highQCProcessor( - @Self BFTNode node, - NodeEvents nodeEvents - ) { - return nodeEvents.processor(node, BFTHighQCUpdate.class); - } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor highQCProcessor( + @Self BFTNode node, NodeEvents nodeEvents) { + return nodeEvents.processor(node, BFTHighQCUpdate.class); + } - @ProvidesIntoSet - private EventProcessorOnDispatch invalidCommandsProcessor( - @Self BFTNode node, - NodeEvents nodeEvents - ) { - return nodeEvents.processorOnDispatch(node, InvalidProposedTxn.class); - } + @ProvidesIntoSet + private EventProcessorOnDispatch invalidCommandsProcessor( + @Self BFTNode node, NodeEvents nodeEvents) { + return nodeEvents.processorOnDispatch(node, InvalidProposedTxn.class); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/ApplicationMonitors.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/ApplicationMonitors.java index 0b5ff41072..3a35b6ccf4 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/ApplicationMonitors.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/ApplicationMonitors.java @@ -75,31 +75,31 @@ import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; import com.radixdlt.utils.Pair; -/** - * Monitors which occur at the mempool level or higher - */ +/** Monitors which occur at the mempool level or higher */ public final class ApplicationMonitors { - private ApplicationMonitors() { - throw new IllegalStateException("Cannot instantiate."); - } + private ApplicationMonitors() { + throw new IllegalStateException("Cannot instantiate."); + } - public static Module registeredNodeToEpoch() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.VALIDATOR_REGISTERED) - TestInvariant registeredValidator(NodeValidatorRegistrator validatorRegistrator) { - return new RegisteredValidatorChecker(validatorRegistrator.validatorRegistrationSubmissions()); - } - }; - } + public static Module registeredNodeToEpoch() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.VALIDATOR_REGISTERED) + TestInvariant registeredValidator(NodeValidatorRegistrator validatorRegistrator) { + return new RegisteredValidatorChecker( + validatorRegistrator.validatorRegistrationSubmissions()); + } + }; + } - public static Module mempoolCommitted() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.MEMPOOL_COMMITTED) - TestInvariant mempoolCommitted(LocalMempoolPeriodicSubmitter mempoolSubmitter, NodeEvents nodeEvents) { - return new CommittedChecker(mempoolSubmitter.issuedTxns().map(Pair::getFirst), nodeEvents); - } - }; - } + public static Module mempoolCommitted() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.MEMPOOL_COMMITTED) + TestInvariant mempoolCommitted( + LocalMempoolPeriodicSubmitter mempoolSubmitter, NodeEvents nodeEvents) { + return new CommittedChecker(mempoolSubmitter.issuedTxns().map(Pair::getFirst), nodeEvents); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/CommittedChecker.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/CommittedChecker.java index 4c8f9290e9..eb80bc4777 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/CommittedChecker.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/CommittedChecker.java @@ -71,46 +71,45 @@ import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; import com.radixdlt.integration.distributed.simulation.network.SimulationNodes.RunningNetwork; import io.reactivex.rxjava3.core.Observable; - import java.util.Arrays; import java.util.Objects; import java.util.concurrent.TimeUnit; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Checks to make sure that commands have been committed in a certain amount - * of time - */ +/** Checks to make sure that commands have been committed in a certain amount of time */ public class CommittedChecker implements TestInvariant { - private static final Logger log = LogManager.getLogger(); - private final Observable submittedTxns; - private final NodeEvents commits; - - public CommittedChecker(Observable submittedTxns, NodeEvents commits) { - this.submittedTxns = Objects.requireNonNull(submittedTxns); - this.commits = Objects.requireNonNull(commits); - } + private static final Logger log = LogManager.getLogger(); + private final Observable submittedTxns; + private final NodeEvents commits; - @Override - public Observable check(RunningNetwork network) { - return submittedTxns - .doOnNext(txn -> log.debug("Submitted txn: {}", txn)) - .flatMapMaybe(txn -> - Observable.create( - emitter -> commits.addListener((n, e) -> emitter.onNext(e), BFTCommittedUpdate.class)) + public CommittedChecker(Observable submittedTxns, NodeEvents commits) { + this.submittedTxns = Objects.requireNonNull(submittedTxns); + this.commits = Objects.requireNonNull(commits); + } - .serialize() - .filter(e -> e.getCommitted().stream() - .flatMap(PreparedVertex::getTxns) - .anyMatch(c -> Arrays.equals(c.getPayload(), txn.getPayload()))) - .timeout(10, TimeUnit.SECONDS) - .firstOrError() - .ignoreElement() - .onErrorReturn(e -> new TestInvariantError( - "Submitted txn has not been committed in 10 seconds: " + txn - )) - ); - } + @Override + public Observable check(RunningNetwork network) { + return submittedTxns + .doOnNext(txn -> log.debug("Submitted txn: {}", txn)) + .flatMapMaybe( + txn -> + Observable.create( + emitter -> + commits.addListener( + (n, e) -> emitter.onNext(e), BFTCommittedUpdate.class)) + .serialize() + .filter( + e -> + e.getCommitted().stream() + .flatMap(PreparedVertex::getTxns) + .anyMatch(c -> Arrays.equals(c.getPayload(), txn.getPayload()))) + .timeout(10, TimeUnit.SECONDS) + .firstOrError() + .ignoreElement() + .onErrorReturn( + e -> + new TestInvariantError( + "Submitted txn has not been committed in 10 seconds: " + txn))); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/RegisteredValidatorChecker.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/RegisteredValidatorChecker.java index 4c2df10659..45c787993b 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/RegisteredValidatorChecker.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/application/RegisteredValidatorChecker.java @@ -71,28 +71,29 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; -/** - * Checks to make sure that a node has been registered as a validator - * in some epoch - */ +/** Checks to make sure that a node has been registered as a validator in some epoch */ public class RegisteredValidatorChecker implements TestInvariant { - private final Observable registeringValidators; - - public RegisteredValidatorChecker(Observable registeringValidators) { - this.registeringValidators = Objects.requireNonNull(registeringValidators); - } + private final Observable registeringValidators; - @Override - public Observable check(RunningNetwork network) { - return registeringValidators - .flatMapMaybe(validator -> - network.latestEpochChanges() - .filter(epochChange -> epochChange.getBFTConfiguration().getValidatorSet().containsNode(validator)) - .timeout(20, TimeUnit.SECONDS) - .firstOrError() - .ignoreElement() - .onErrorReturn(e -> new TestInvariantError(validator + " was not included in any epoch in last 20 seconds")) - ); - } + public RegisteredValidatorChecker(Observable registeringValidators) { + this.registeringValidators = Objects.requireNonNull(registeringValidators); + } + @Override + public Observable check(RunningNetwork network) { + return registeringValidators.flatMapMaybe( + validator -> + network + .latestEpochChanges() + .filter( + epochChange -> + epochChange.getBFTConfiguration().getValidatorSet().containsNode(validator)) + .timeout(20, TimeUnit.SECONDS) + .firstOrError() + .ignoreElement() + .onErrorReturn( + e -> + new TestInvariantError( + validator + " was not included in any epoch in last 20 seconds"))); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/AllProposalsHaveDirectParentsInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/AllProposalsHaveDirectParentsInvariant.java index ea313f0514..6770c54508 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/AllProposalsHaveDirectParentsInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/AllProposalsHaveDirectParentsInvariant.java @@ -65,36 +65,39 @@ package com.radixdlt.integration.distributed.simulation.monitors.consensus; import com.radixdlt.consensus.ConsensusEvent; -import com.radixdlt.environment.rx.RemoteEvent; -import com.radixdlt.integration.distributed.simulation.TestInvariant; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.UnverifiedVertex; +import com.radixdlt.environment.rx.RemoteEvent; +import com.radixdlt.integration.distributed.simulation.TestInvariant; import com.radixdlt.integration.distributed.simulation.network.SimulationNodes.RunningNetwork; import io.reactivex.rxjava3.core.Observable; import java.util.List; import java.util.stream.Collectors; /** - * Check that every proposal on the network has a direct parent. - * This check only makes sense in networks where there are no failing nodes. + * Check that every proposal on the network has a direct parent. This check only makes sense in + * networks where there are no failing nodes. */ public class AllProposalsHaveDirectParentsInvariant implements TestInvariant { - @Override - public Observable check(RunningNetwork network) { - List> correctProposals = network.getNodes().stream() - .map(network.getUnderlyingNetwork()::getNetwork) - .map(net -> net.remoteEvents(ConsensusEvent.class).map(RemoteEvent::getEvent)) - .map(p -> p.ofType(Proposal.class).toObservable().map(Proposal::getVertex)) - .collect(Collectors.toList()); + @Override + public Observable check(RunningNetwork network) { + List> correctProposals = + network.getNodes().stream() + .map(network.getUnderlyingNetwork()::getNetwork) + .map(net -> net.remoteEvents(ConsensusEvent.class).map(RemoteEvent::getEvent)) + .map(p -> p.ofType(Proposal.class).toObservable().map(Proposal::getVertex)) + .collect(Collectors.toList()); - return Observable.merge(correctProposals) - .concatMap(v -> { - if (!v.getView().equals(v.getQC().getProposed().getView().next())) { - return Observable.just(new TestInvariantError(String.format("Vertex %s has no direct parent", v))); - } else { - return Observable.empty(); - } - }); - } + return Observable.merge(correctProposals) + .concatMap( + v -> { + if (!v.getView().equals(v.getQC().getProposed().getView().next())) { + return Observable.just( + new TestInvariantError(String.format("Vertex %s has no direct parent", v))); + } else { + return Observable.empty(); + } + }); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/ConsensusMonitors.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/ConsensusMonitors.java index 916714cf03..413849c85c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/ConsensusMonitors.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/ConsensusMonitors.java @@ -78,110 +78,107 @@ import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; import com.radixdlt.integration.distributed.simulation.monitors.epochs.EpochViewInvariant; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork; - import java.time.Duration; import java.util.concurrent.TimeUnit; -/** - * Monitors which checks things in the consensus module - */ +/** Monitors which checks things in the consensus module */ public final class ConsensusMonitors { - public static Module timestampChecker() { - return timestampChecker(Duration.ofSeconds(2)); - } - - public static Module timestampChecker(Duration maxDelay) { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_TIMESTAMP_CHECK) - public TestInvariant timestampsInvariant() { - return new TimestampChecker(maxDelay); - } - }; - } - - public static Module vertexRequestRate(int permitsPerSecond) { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_VERTEX_REQUEST_RATE) - TestInvariant vertexRequestRateInvariant(NodeEvents nodeEvents) { - return new VertexRequestRateInvariant(nodeEvents, permitsPerSecond); - } - }; - } - - public static Module liveness() { - return liveness(8 * SimulationNetwork.DEFAULT_LATENCY, TimeUnit.MILLISECONDS); - } - - public static Module liveness(long duration, TimeUnit timeUnit) { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_LIVENESS) - TestInvariant livenessInvariant(NodeEvents nodeEvents) { - return new LivenessInvariant(nodeEvents, duration, timeUnit); - } - }; - } - - public static Module safety() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_SAFETY) - TestInvariant safetyInvariant(NodeEvents nodeEvents) { - return new SafetyInvariant(nodeEvents); - } - }; - } - - public static Module noTimeouts() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_NO_TIMEOUTS) - TestInvariant noTimeoutsInvariant(NodeEvents nodeEvents) { - return new EventNeverOccursInvariant<>(nodeEvents, LocalTimeoutOccurrence.class); - } - - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_NO_EPOCH_TIMEOUTS) - TestInvariant noEpochTimeoutsInvariant(NodeEvents nodeEvents) { - return new EventNeverOccursInvariant<>(nodeEvents, EpochLocalTimeoutOccurrence.class); - } - }; - } - - public static Module directParents() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_DIRECT_PARENTS) - TestInvariant directParentsInvariant() { - return new AllProposalsHaveDirectParentsInvariant(); - } - }; - } - - public static Module noneCommitted() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_NONE_COMMITTED) - TestInvariant noneCommittedInvariant(NodeEvents nodeEvents) { - return new EventNeverOccursInvariant<>(nodeEvents, BFTCommittedUpdate.class); - } - }; - } - - public static Module epochCeilingView(View epochCeilingView) { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.EPOCH_CEILING_VIEW) - TestInvariant epochHighViewInvariant(NodeEvents nodeEvents) { - return new EpochViewInvariant(epochCeilingView, nodeEvents); - } - }; - } - - private ConsensusMonitors() { - throw new IllegalStateException("Cannot instantiate."); - } + public static Module timestampChecker() { + return timestampChecker(Duration.ofSeconds(2)); + } + + public static Module timestampChecker(Duration maxDelay) { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_TIMESTAMP_CHECK) + public TestInvariant timestampsInvariant() { + return new TimestampChecker(maxDelay); + } + }; + } + + public static Module vertexRequestRate(int permitsPerSecond) { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_VERTEX_REQUEST_RATE) + TestInvariant vertexRequestRateInvariant(NodeEvents nodeEvents) { + return new VertexRequestRateInvariant(nodeEvents, permitsPerSecond); + } + }; + } + + public static Module liveness() { + return liveness(8 * SimulationNetwork.DEFAULT_LATENCY, TimeUnit.MILLISECONDS); + } + + public static Module liveness(long duration, TimeUnit timeUnit) { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_LIVENESS) + TestInvariant livenessInvariant(NodeEvents nodeEvents) { + return new LivenessInvariant(nodeEvents, duration, timeUnit); + } + }; + } + + public static Module safety() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_SAFETY) + TestInvariant safetyInvariant(NodeEvents nodeEvents) { + return new SafetyInvariant(nodeEvents); + } + }; + } + + public static Module noTimeouts() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_NO_TIMEOUTS) + TestInvariant noTimeoutsInvariant(NodeEvents nodeEvents) { + return new EventNeverOccursInvariant<>(nodeEvents, LocalTimeoutOccurrence.class); + } + + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_NO_EPOCH_TIMEOUTS) + TestInvariant noEpochTimeoutsInvariant(NodeEvents nodeEvents) { + return new EventNeverOccursInvariant<>(nodeEvents, EpochLocalTimeoutOccurrence.class); + } + }; + } + + public static Module directParents() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_DIRECT_PARENTS) + TestInvariant directParentsInvariant() { + return new AllProposalsHaveDirectParentsInvariant(); + } + }; + } + + public static Module noneCommitted() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_NONE_COMMITTED) + TestInvariant noneCommittedInvariant(NodeEvents nodeEvents) { + return new EventNeverOccursInvariant<>(nodeEvents, BFTCommittedUpdate.class); + } + }; + } + + public static Module epochCeilingView(View epochCeilingView) { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.EPOCH_CEILING_VIEW) + TestInvariant epochHighViewInvariant(NodeEvents nodeEvents) { + return new EpochViewInvariant(epochCeilingView, nodeEvents); + } + }; + } + + private ConsensusMonitors() { + throw new IllegalStateException("Cannot instantiate."); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/LivenessInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/LivenessInvariant.java index c4fb7137d8..126d212dbf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/LivenessInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/LivenessInvariant.java @@ -77,39 +77,46 @@ import java.util.concurrent.TimeUnit; /** - * Check that the network is making progress by ensuring that new QCs and epochs - * are progressively increasing. + * Check that the network is making progress by ensuring that new QCs and epochs are progressively + * increasing. */ public class LivenessInvariant implements TestInvariant { - private final NodeEvents nodeEvents; - private final long duration; - private final TimeUnit timeUnit; + private final NodeEvents nodeEvents; + private final long duration; + private final TimeUnit timeUnit; - public LivenessInvariant(NodeEvents nodeEvents, long duration, TimeUnit timeUnit) { - this.nodeEvents = nodeEvents; - this.duration = duration; - this.timeUnit = timeUnit; - } + public LivenessInvariant(NodeEvents nodeEvents, long duration, TimeUnit timeUnit) { + this.nodeEvents = nodeEvents; + this.duration = duration; + this.timeUnit = timeUnit; + } - @Override - public Observable check(RunningNetwork network) { - return - Observable.create(emitter -> { - nodeEvents.addListener((node, highQCUpdate) -> { - emitter.onNext(highQCUpdate.getHighQC().highestQC()); - }, BFTHighQCUpdate.class); - nodeEvents.addListener((node, committed) -> { - emitter.onNext(committed.getVertexStoreState().getHighQC().highestQC()); - }, BFTCommittedUpdate.class); - }) - .serialize() - .map(QuorumCertificate::getProposed) - .map(header -> EpochView.of(header.getLedgerHeader().getEpoch(), header.getView())) - .scan(EpochView.of(0, View.genesis()), Ordering.natural()::max) - .distinctUntilChanged() - .debounce(duration, timeUnit) - .map(epochView -> new TestInvariantError( - String.format("Highest QC hasn't increased from %s after %s %s", epochView, duration, timeUnit) - )); - } + @Override + public Observable check(RunningNetwork network) { + return Observable.create( + emitter -> { + nodeEvents.addListener( + (node, highQCUpdate) -> { + emitter.onNext(highQCUpdate.getHighQC().highestQC()); + }, + BFTHighQCUpdate.class); + nodeEvents.addListener( + (node, committed) -> { + emitter.onNext(committed.getVertexStoreState().getHighQC().highestQC()); + }, + BFTCommittedUpdate.class); + }) + .serialize() + .map(QuorumCertificate::getProposed) + .map(header -> EpochView.of(header.getLedgerHeader().getEpoch(), header.getView())) + .scan(EpochView.of(0, View.genesis()), Ordering.natural()::max) + .distinctUntilChanged() + .debounce(duration, timeUnit) + .map( + epochView -> + new TestInvariantError( + String.format( + "Highest QC hasn't increased from %s after %s %s", + epochView, duration, timeUnit))); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/MaxLedgerSyncLagInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/MaxLedgerSyncLagInvariant.java index 057799d8e1..0a808fddd3 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/MaxLedgerSyncLagInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/MaxLedgerSyncLagInvariant.java @@ -71,35 +71,47 @@ import io.reactivex.rxjava3.core.Observable; /** - * Checks that at all times ledger sync lag is no more than a specified maximum number of state versions - * (compared to the highest state version in the network) for any node. + * Checks that at all times ledger sync lag is no more than a specified maximum number of state + * versions (compared to the highest state version in the network) for any node. */ public final class MaxLedgerSyncLagInvariant implements TestInvariant { - private final long maxLag; + private final long maxLag; - public MaxLedgerSyncLagInvariant(long maxLag) { - this.maxLag = maxLag; - } + public MaxLedgerSyncLagInvariant(long maxLag) { + this.maxLag = maxLag; + } - @Override - public Observable check(RunningNetwork network) { - return network.ledgerUpdates().flatMap(unused -> { - final var maxStateVersion = network - .getSystemCounters().values().stream() - .map(sc -> sc.get(SystemCounters.CounterType.LEDGER_STATE_VERSION)) - .max(Long::compareTo) - .get(); + @Override + public Observable check(RunningNetwork network) { + return network + .ledgerUpdates() + .flatMap( + unused -> { + final var maxStateVersion = + network.getSystemCounters().values().stream() + .map(sc -> sc.get(SystemCounters.CounterType.LEDGER_STATE_VERSION)) + .max(Long::compareTo) + .get(); - final var maybeTooMuchLag = network.getSystemCounters().entrySet().stream() - .filter(e -> e.getValue().get(CounterType.LEDGER_STATE_VERSION) + maxLag < maxStateVersion) - .findAny(); + final var maybeTooMuchLag = + network.getSystemCounters().entrySet().stream() + .filter( + e -> + e.getValue().get(CounterType.LEDGER_STATE_VERSION) + maxLag + < maxStateVersion) + .findAny(); - return maybeTooMuchLag - .map(e -> Observable.just(new TestInvariantError( - String.format("Node %s ledger sync lag exceeded maximum of %s state versions", e.getKey(), maxLag)) - )) - .orElse(Observable.empty()); - }); - } + return maybeTooMuchLag + .map( + e -> + Observable.just( + new TestInvariantError( + String.format( + "Node %s ledger sync lag exceeded maximum of %s state" + + " versions", + e.getKey(), maxLag)))) + .orElse(Observable.empty()); + }); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SafetyInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SafetyInvariant.java index 642c7aabda..62f6ac008a 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SafetyInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SafetyInvariant.java @@ -73,22 +73,28 @@ import com.radixdlt.utils.Pair; import io.reactivex.rxjava3.core.Observable; -/** - * Checks that validator nodes do not commit on conflicting vertices - */ +/** Checks that validator nodes do not commit on conflicting vertices */ public class SafetyInvariant implements TestInvariant { - private final NodeEvents nodeEvents; + private final NodeEvents nodeEvents; - public SafetyInvariant(NodeEvents nodeEvents) { - this.nodeEvents = nodeEvents; - } + public SafetyInvariant(NodeEvents nodeEvents) { + this.nodeEvents = nodeEvents; + } - @Override - public Observable check(RunningNetwork network) { - final SafetyChecker safetyChecker = new SafetyChecker(network.getNodes()); - return Observable.>create(emitter -> - nodeEvents.addListener((node, update) -> emitter.onNext(Pair.of(node, update)), BFTCommittedUpdate.class) - ).serialize() - .flatMap(e -> safetyChecker.process(e.getFirst(), e.getSecond()).map(Observable::just).orElse(Observable.empty())); - } + @Override + public Observable check(RunningNetwork network) { + final SafetyChecker safetyChecker = new SafetyChecker(network.getNodes()); + return Observable.>create( + emitter -> + nodeEvents.addListener( + (node, update) -> emitter.onNext(Pair.of(node, update)), + BFTCommittedUpdate.class)) + .serialize() + .flatMap( + e -> + safetyChecker + .process(e.getFirst(), e.getSecond()) + .map(Observable::just) + .orElse(Observable.empty())); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SyncMonitors.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SyncMonitors.java index 36440a6df4..2bb7e18417 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SyncMonitors.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/SyncMonitors.java @@ -71,22 +71,20 @@ import com.radixdlt.integration.distributed.simulation.MonitorKey; import com.radixdlt.integration.distributed.simulation.TestInvariant; -/** - * Monitors which checks things in the sync module - */ +/** Monitors which checks things in the sync module */ public final class SyncMonitors { - public static Module maxLedgerSyncLag(long maxLag) { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.SYNC_MAX_LAG) - TestInvariant maxSyncLagInvariant() { - return new MaxLedgerSyncLagInvariant(maxLag); - } - }; - } + public static Module maxLedgerSyncLag(long maxLag) { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.SYNC_MAX_LAG) + TestInvariant maxSyncLagInvariant() { + return new MaxLedgerSyncLagInvariant(maxLag); + } + }; + } - private SyncMonitors() { - throw new IllegalStateException("Cannot instantiate."); - } + private SyncMonitors() { + throw new IllegalStateException("Cannot instantiate."); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/TimestampChecker.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/TimestampChecker.java index d3fede8913..b50b20ca6c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/TimestampChecker.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/TimestampChecker.java @@ -72,51 +72,49 @@ import com.radixdlt.utils.Pair; import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Observable; - import java.time.Duration; /** - * Checks that the first time a ledger update occurs on the network that it is close - * to the real wall clock time. + * Checks that the first time a ledger update occurs on the network that it is close to the real + * wall clock time. */ public final class TimestampChecker implements TestInvariant { - private final Duration acceptableTimeRange; + private final Duration acceptableTimeRange; - public TimestampChecker(Duration acceptableTimeRange) { - this.acceptableTimeRange = acceptableTimeRange; - } + public TimestampChecker(Duration acceptableTimeRange) { + this.acceptableTimeRange = acceptableTimeRange; + } - private Maybe checkCloseTimestamp(LedgerUpdate update) { - final var now = System.currentTimeMillis(); - final var proof = update.getTail(); - final var timestamp = proof.timestamp(); - final var diff = now - timestamp; - if (0 <= diff && diff < acceptableTimeRange.toMillis()) { - return Maybe.empty(); - } else { - return Maybe.just( - new TestInvariantError( - String.format( - "Expecting timestamp to be close to %s but was %s%+d at %s:%s with %s", - now, now, diff, proof.getEpoch(), proof.getView(), update - ) - ) - ); - } - } + private Maybe checkCloseTimestamp(LedgerUpdate update) { + final var now = System.currentTimeMillis(); + final var proof = update.getTail(); + final var timestamp = proof.timestamp(); + final var diff = now - timestamp; + if (0 <= diff && diff < acceptableTimeRange.toMillis()) { + return Maybe.empty(); + } else { + return Maybe.just( + new TestInvariantError( + String.format( + "Expecting timestamp to be close to %s but was %s%+d at %s:%s with %s", + now, now, diff, proof.getEpoch(), proof.getView(), update))); + } + } - private static boolean isFirstView(LedgerUpdate ledgerUpdate) { - return ledgerUpdate.getTail().getEpoch() == 1 && ledgerUpdate.getTail().getView().equals(View.of(1)); - } + private static boolean isFirstView(LedgerUpdate ledgerUpdate) { + return ledgerUpdate.getTail().getEpoch() == 1 + && ledgerUpdate.getTail().getView().equals(View.of(1)); + } - @Override - public Observable check(RunningNetwork network) { - return network.ledgerUpdates() - .map(Pair::getSecond) - // Test on only the first ledger update in the network - .distinct(update -> EpochView.of(update.getTail().getEpoch(), update.getTail().getView())) - .filter(l -> !isFirstView(l)) - .flatMapMaybe(this::checkCloseTimestamp); - } + @Override + public Observable check(RunningNetwork network) { + return network + .ledgerUpdates() + .map(Pair::getSecond) + // Test on only the first ledger update in the network + .distinct(update -> EpochView.of(update.getTail().getEpoch(), update.getTail().getView())) + .filter(l -> !isFirstView(l)) + .flatMapMaybe(this::checkCloseTimestamp); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/VertexRequestRateInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/VertexRequestRateInvariant.java index dc8ace21b1..fa1ca72e06 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/VertexRequestRateInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/consensus/VertexRequestRateInvariant.java @@ -74,34 +74,39 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -/** - * Checks that vertex request explosions don't occur - */ +/** Checks that vertex request explosions don't occur */ public final class VertexRequestRateInvariant implements TestInvariant { - private final NodeEvents nodeEvents; - private final int permitsPerSecond; - - public VertexRequestRateInvariant(NodeEvents nodeEvents, int permitsPerSecond) { - this.nodeEvents = nodeEvents; - this.permitsPerSecond = permitsPerSecond; - } + private final NodeEvents nodeEvents; + private final int permitsPerSecond; - @Override - public Observable check(RunningNetwork network) { - return Observable.>create(emitter -> - nodeEvents.addListener((node, req) -> emitter.onNext(Pair.of(node, req)), GetVerticesRequest.class)) - .serialize() - .groupBy(Pair::getFirst) - .flatMap(o -> o.buffer(1, TimeUnit.SECONDS) - .filter(l -> l.size() > permitsPerSecond) - .map(l -> new TestInvariantError( - String.format("Get Vertices over the rate limit (%s/sec) for node: %s buffer: %s", - permitsPerSecond, - o.getKey(), - l.stream().collect(Collectors.groupingBy(Pair::getSecond, Collectors.counting()) - )) - )) - ); - } + public VertexRequestRateInvariant(NodeEvents nodeEvents, int permitsPerSecond) { + this.nodeEvents = nodeEvents; + this.permitsPerSecond = permitsPerSecond; + } + @Override + public Observable check(RunningNetwork network) { + return Observable.>create( + emitter -> + nodeEvents.addListener( + (node, req) -> emitter.onNext(Pair.of(node, req)), GetVerticesRequest.class)) + .serialize() + .groupBy(Pair::getFirst) + .flatMap( + o -> + o.buffer(1, TimeUnit.SECONDS) + .filter(l -> l.size() > permitsPerSecond) + .map( + l -> + new TestInvariantError( + String.format( + "Get Vertices over the rate limit (%s/sec) for node: %s buffer:" + + " %s", + permitsPerSecond, + o.getKey(), + l.stream() + .collect( + Collectors.groupingBy( + Pair::getSecond, Collectors.counting())))))); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/epochs/EpochViewInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/epochs/EpochViewInvariant.java index bea6a47ab7..92af66dbc1 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/epochs/EpochViewInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/epochs/EpochViewInvariant.java @@ -72,36 +72,37 @@ import io.reactivex.rxjava3.core.Observable; import java.util.Objects; -/** - * Invariant which checks that a committed vertex never goes above some view - */ +/** Invariant which checks that a committed vertex never goes above some view */ public class EpochViewInvariant implements TestInvariant { - private final NodeEvents commits; - private final View epochHighView; - - public EpochViewInvariant(View epochHighView, NodeEvents commits) { - this.commits = commits; - this.epochHighView = Objects.requireNonNull(epochHighView); - } + private final NodeEvents commits; + private final View epochHighView; - @Override - public Observable check(RunningNetwork network) { - return Observable.create( - emitter -> this.commits.addListener((node, commit) -> emitter.onNext(commit), BFTCommittedUpdate.class) - ).serialize() - .concatMap(committedUpdate -> Observable.fromStream(committedUpdate.getCommitted().stream())) - .flatMap(vertex -> { - final View view = vertex.getView(); - if (view.compareTo(epochHighView) > 0) { - return Observable.just( - new TestInvariantError( - String.format("Vertex committed with view %s but epochHighView is %s", view, epochHighView) - ) - ); - } + public EpochViewInvariant(View epochHighView, NodeEvents commits) { + this.commits = commits; + this.epochHighView = Objects.requireNonNull(epochHighView); + } - return Observable.empty(); - }); - } + @Override + public Observable check(RunningNetwork network) { + return Observable.create( + emitter -> + this.commits.addListener( + (node, commit) -> emitter.onNext(commit), BFTCommittedUpdate.class)) + .serialize() + .concatMap( + committedUpdate -> Observable.fromStream(committedUpdate.getCommitted().stream())) + .flatMap( + vertex -> { + final View view = vertex.getView(); + if (view.compareTo(epochHighView) > 0) { + return Observable.just( + new TestInvariantError( + String.format( + "Vertex committed with view %s but epochHighView is %s", + view, epochHighView))); + } + return Observable.empty(); + }); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/ConsensusToLedgerCommittedInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/ConsensusToLedgerCommittedInvariant.java index 218bb94f87..7ce0a38238 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/ConsensusToLedgerCommittedInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/ConsensusToLedgerCommittedInvariant.java @@ -78,40 +78,52 @@ import java.util.concurrent.TimeUnit; /** - * Checks to make sure that everything committed by consensus eventually makes it to the ledger - * of atleast one node (TODO: test for every node) + * Checks to make sure that everything committed by consensus eventually makes it to the ledger of + * atleast one node (TODO: test for every node) */ public class ConsensusToLedgerCommittedInvariant implements TestInvariant { - private final NodeEvents commits; + private final NodeEvents commits; - public ConsensusToLedgerCommittedInvariant(NodeEvents commits) { - this.commits = commits; - } + public ConsensusToLedgerCommittedInvariant(NodeEvents commits) { + this.commits = commits; + } - @Override - public Observable check(RunningNetwork network) { - BehaviorSubject> committedTxns = BehaviorSubject.create(); - Disposable d = network.ledgerUpdates().>scan( - new HashSet<>(), - (set, next) -> { - set.addAll(next.getSecond().getNewTxns()); - return set; - } - ).subscribe(committedTxns::onNext); + @Override + public Observable check(RunningNetwork network) { + BehaviorSubject> committedTxns = BehaviorSubject.create(); + Disposable d = + network + .ledgerUpdates() + .>scan( + new HashSet<>(), + (set, next) -> { + set.addAll(next.getSecond().getNewTxns()); + return set; + }) + .subscribe(committedTxns::onNext); - return Observable.create(emitter -> - commits.addListener((node, event) -> emitter.onNext(event), BFTCommittedUpdate.class) - ).serialize() - .concatMap(committedUpdate -> Observable.fromStream(committedUpdate.getCommitted().stream() - .flatMap(PreparedVertex::successfulCommands))) - .flatMapMaybe(txn -> committedTxns - .filter(cmdSet -> cmdSet.contains(txn.txn())) - .timeout(10, TimeUnit.SECONDS) - .firstOrError() - .ignoreElement() - .onErrorReturn(e -> new TestInvariantError( - "Committed command in vertex has not been inserted into the ledger after 10 seconds") - ) - ).doFinally(d::dispose); - } + return Observable.create( + emitter -> + commits.addListener( + (node, event) -> emitter.onNext(event), BFTCommittedUpdate.class)) + .serialize() + .concatMap( + committedUpdate -> + Observable.fromStream( + committedUpdate.getCommitted().stream() + .flatMap(PreparedVertex::successfulCommands))) + .flatMapMaybe( + txn -> + committedTxns + .filter(cmdSet -> cmdSet.contains(txn.txn())) + .timeout(10, TimeUnit.SECONDS) + .firstOrError() + .ignoreElement() + .onErrorReturn( + e -> + new TestInvariantError( + "Committed command in vertex has not been inserted into the ledger" + + " after 10 seconds"))) + .doFinally(d::dispose); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerInOrderInvariant.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerInOrderInvariant.java index 910d1a92dd..8444289a3c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerInOrderInvariant.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerInOrderInvariant.java @@ -78,36 +78,41 @@ import java.util.Optional; /** - * Ledger-side safety check. Checks that commands and the order getting persisted are - * the same across all nodes. + * Ledger-side safety check. Checks that commands and the order getting persisted are the same + * across all nodes. */ public class LedgerInOrderInvariant implements TestInvariant { - @Override - public Observable check(RunningNetwork network) { - Map> commandsPerNode = new HashMap<>(); - network.getNodes().forEach(n -> commandsPerNode.put(n, new ArrayList<>())); + @Override + public Observable check(RunningNetwork network) { + Map> commandsPerNode = new HashMap<>(); + network.getNodes().forEach(n -> commandsPerNode.put(n, new ArrayList<>())); - return network.ledgerUpdates().flatMap(nodeAndCommand -> { - BFTNode node = nodeAndCommand.getFirst(); - LedgerUpdate ledgerUpdate = nodeAndCommand.getSecond(); - List nodeTxns = commandsPerNode.get(node); - nodeTxns.addAll(ledgerUpdate.getNewTxns()); + return network + .ledgerUpdates() + .flatMap( + nodeAndCommand -> { + BFTNode node = nodeAndCommand.getFirst(); + LedgerUpdate ledgerUpdate = nodeAndCommand.getSecond(); + List nodeTxns = commandsPerNode.get(node); + nodeTxns.addAll(ledgerUpdate.getNewTxns()); - return commandsPerNode.values().stream() - .filter(list -> nodeTxns != list) - .filter(list -> list.size() >= nodeTxns.size()) - .findFirst() // Only need to check one node, if passes, guaranteed to pass the others - .flatMap(list -> { - if (Collections.indexOfSubList(list, nodeTxns) != 0) { - TestInvariantError err = new TestInvariantError( - "Two nodes don't agree on commands: " + list + " " + nodeTxns - ); - return Optional.of(Observable.just(err)); - } - return Optional.empty(); - }) - .orElse(Observable.empty()); - }); - } + return commandsPerNode.values().stream() + .filter(list -> nodeTxns != list) + .filter(list -> list.size() >= nodeTxns.size()) + .findFirst() // Only need to check one node, if passes, guaranteed to pass the + // others + .flatMap( + list -> { + if (Collections.indexOfSubList(list, nodeTxns) != 0) { + TestInvariantError err = + new TestInvariantError( + "Two nodes don't agree on commands: " + list + " " + nodeTxns); + return Optional.of(Observable.just(err)); + } + return Optional.empty(); + }) + .orElse(Observable.empty()); + }); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerMonitors.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerMonitors.java index f4c4cf7eb8..0b5fa38e97 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerMonitors.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/ledger/LedgerMonitors.java @@ -72,31 +72,29 @@ import com.radixdlt.integration.distributed.simulation.TestInvariant; import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; -/** - * Monitors which checks things in the ledger module - */ +/** Monitors which checks things in the ledger module */ public final class LedgerMonitors { - public static Module consensusToLedger() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.CONSENSUS_TO_LEDGER_PROCESSED) - TestInvariant ledgerProcessedInvariant(NodeEvents nodeEvents) { - return new ConsensusToLedgerCommittedInvariant(nodeEvents); - } - }; - } + public static Module consensusToLedger() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.CONSENSUS_TO_LEDGER_PROCESSED) + TestInvariant ledgerProcessedInvariant(NodeEvents nodeEvents) { + return new ConsensusToLedgerCommittedInvariant(nodeEvents); + } + }; + } - public static Module ordered() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.LEDGER_IN_ORDER) - TestInvariant ledgerInOrderInvariant() { - return new LedgerInOrderInvariant(); - } - }; - } + public static Module ordered() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.LEDGER_IN_ORDER) + TestInvariant ledgerInOrderInvariant() { + return new LedgerInOrderInvariant(); + } + }; + } - private LedgerMonitors() { - throw new IllegalStateException("Cannot instantiate."); - } + private LedgerMonitors() { + throw new IllegalStateException("Cannot instantiate."); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/radix_engine/RadixEngineMonitors.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/radix_engine/RadixEngineMonitors.java index b217bf4e3c..ce1105d6bf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/radix_engine/RadixEngineMonitors.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/monitors/radix_engine/RadixEngineMonitors.java @@ -74,21 +74,19 @@ import com.radixdlt.integration.distributed.simulation.monitors.NodeEvents; import com.radixdlt.statecomputer.InvalidProposedTxn; -/** - * Monitors which check for radix engine related functionality - */ +/** Monitors which check for radix engine related functionality */ public final class RadixEngineMonitors { - private RadixEngineMonitors() { - throw new IllegalStateException("Cannot instantiate."); - } + private RadixEngineMonitors() { + throw new IllegalStateException("Cannot instantiate."); + } - public static Module noInvalidProposedCommands() { - return new AbstractModule() { - @ProvidesIntoMap - @MonitorKey(Monitor.RADIX_ENGINE_NO_INVALID_PROPOSED_COMMANDS) - TestInvariant registeredValidator(NodeEvents nodeEvents) { - return new EventNeverOccursInvariant<>(nodeEvents, InvalidProposedTxn.class); - } - }; - } + public static Module noInvalidProposedCommands() { + return new AbstractModule() { + @ProvidesIntoMap + @MonitorKey(Monitor.RADIX_ENGINE_NO_INVALID_PROPOSED_COMMANDS) + TestInvariant registeredValidator(NodeEvents nodeEvents) { + return new EventNeverOccursInvariant<>(nodeEvents, InvalidProposedTxn.class); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/FProposalsPerViewDropper.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/FProposalsPerViewDropper.java index c8a73b87c2..d6940f03c2 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/FProposalsPerViewDropper.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/FProposalsPerViewDropper.java @@ -68,8 +68,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.View; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.MessageInTransit; import java.util.Collections; import java.util.List; @@ -78,48 +78,49 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; -/** - * Drops one proposal per view - */ +/** Drops one proposal per view */ public class FProposalsPerViewDropper implements Predicate { - private final ConcurrentHashMap> proposalToDrop = new ConcurrentHashMap<>(); - private final ConcurrentHashMap proposalCount = new ConcurrentHashMap<>(); - private final ImmutableList validatorSet; - private final Random random; - private final int faultySize; + private final ConcurrentHashMap> proposalToDrop = new ConcurrentHashMap<>(); + private final ConcurrentHashMap proposalCount = new ConcurrentHashMap<>(); + private final ImmutableList validatorSet; + private final Random random; + private final int faultySize; - public FProposalsPerViewDropper(ImmutableList validatorSet, Random random) { - this.validatorSet = validatorSet; - this.random = random; - this.faultySize = (validatorSet.size() - 1) / 3; - } + public FProposalsPerViewDropper(ImmutableList validatorSet, Random random) { + this.validatorSet = validatorSet; + this.random = random; + this.faultySize = (validatorSet.size() - 1) / 3; + } - public FProposalsPerViewDropper(ImmutableList validatorSet) { - this.validatorSet = validatorSet; - this.random = null; - this.faultySize = (validatorSet.size() - 1) / 3; - } + public FProposalsPerViewDropper(ImmutableList validatorSet) { + this.validatorSet = validatorSet; + this.random = null; + this.faultySize = (validatorSet.size() - 1) / 3; + } - @Override - public boolean test(MessageInTransit msg) { - if (msg.getContent() instanceof Proposal) { - final Proposal proposal = (Proposal) msg.getContent(); - final View view = proposal.getVertex().getView(); - final Set nodesToDrop = proposalToDrop.computeIfAbsent(view, v -> { - final List nodes = Lists.newArrayList(validatorSet); - if (random != null) { - Collections.shuffle(nodes, random); - } - return ImmutableSet.copyOf(nodes.subList(0, faultySize)); - }); - if (proposalCount.merge(view, 1, Integer::sum).equals(validatorSet.size())) { - proposalToDrop.remove(view); - proposalCount.remove(view); - } + @Override + public boolean test(MessageInTransit msg) { + if (msg.getContent() instanceof Proposal) { + final Proposal proposal = (Proposal) msg.getContent(); + final View view = proposal.getVertex().getView(); + final Set nodesToDrop = + proposalToDrop.computeIfAbsent( + view, + v -> { + final List nodes = Lists.newArrayList(validatorSet); + if (random != null) { + Collections.shuffle(nodes, random); + } + return ImmutableSet.copyOf(nodes.subList(0, faultySize)); + }); + if (proposalCount.merge(view, 1, Integer::sum).equals(validatorSet.size())) { + proposalToDrop.remove(view); + proposalCount.remove(view); + } - return nodesToDrop.contains(msg.getReceiver()); - } + return nodesToDrop.contains(msg.getReceiver()); + } - return false; - } + return false; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/InOrderChannels.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/InOrderChannels.java index 6f35042992..a00108ae82 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/InOrderChannels.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/InOrderChannels.java @@ -74,38 +74,44 @@ import java.util.concurrent.TimeUnit; public final class InOrderChannels implements ChannelCommunication { - private final LatencyProvider latencyProvider; + private final LatencyProvider latencyProvider; - @Inject - public InOrderChannels(LatencyProvider latencyProvider) { - this.latencyProvider = Objects.requireNonNull(latencyProvider); - } + @Inject + public InOrderChannels(LatencyProvider latencyProvider) { + this.latencyProvider = Objects.requireNonNull(latencyProvider); + } - private MessageInTransit addLatencyIfNotToSelf(MessageInTransit msg, BFTNode receiver) { - if (msg.getSender().equals(receiver)) { - return msg; - } else { - return msg.delayed(latencyProvider.nextLatency(msg)); - } - } + private MessageInTransit addLatencyIfNotToSelf(MessageInTransit msg, BFTNode receiver) { + if (msg.getSender().equals(receiver)) { + return msg; + } else { + return msg.delayed(latencyProvider.nextLatency(msg)); + } + } - public static Timed delayCarryover(Timed prev, Timed next) { - int delayCarryover = (int) Math.max(prev.time() + prev.value().getDelay() - next.time(), 0); - int additionalDelay = (int) (next.value().getDelay() - delayCarryover); - if (additionalDelay > 0) { - return new Timed<>(next.value().delayAfterPrevious(additionalDelay), next.time(), next.unit()); - } else { - return next; - } - } + public static Timed delayCarryover( + Timed prev, Timed next) { + int delayCarryover = (int) Math.max(prev.time() + prev.value().getDelay() - next.time(), 0); + int additionalDelay = (int) (next.value().getDelay() - delayCarryover); + if (additionalDelay > 0) { + return new Timed<>( + next.value().delayAfterPrevious(additionalDelay), next.time(), next.unit()); + } else { + return next; + } + } - @Override - public Observable transform(BFTNode sender, BFTNode receiver, Observable messages) { - return messages - .map(msg -> addLatencyIfNotToSelf(msg, receiver)) - .filter(msg -> msg.getDelay() >= 0) - .timestamp(TimeUnit.MILLISECONDS) - .scan(InOrderChannels::delayCarryover) - .concatMap(p -> Observable.just(p.value()).delay(p.value().getDelayAfterPrevious(), TimeUnit.MILLISECONDS)); - } + @Override + public Observable transform( + BFTNode sender, BFTNode receiver, Observable messages) { + return messages + .map(msg -> addLatencyIfNotToSelf(msg, receiver)) + .filter(msg -> msg.getDelay() >= 0) + .timestamp(TimeUnit.MILLISECONDS) + .scan(InOrderChannels::delayCarryover) + .concatMap( + p -> + Observable.just(p.value()) + .delay(p.value().getDelayAfterPrevious(), TimeUnit.MILLISECONDS)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/LatencyProvider.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/LatencyProvider.java index c47fb47b49..6a0bdcfd97 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/LatencyProvider.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/LatencyProvider.java @@ -66,16 +66,15 @@ import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.MessageInTransit; -/** - * The latency configuration for a network - */ +/** The latency configuration for a network */ public interface LatencyProvider { - /** - * If >= 0, returns the latency in milliseconds of the next message. If < 0, signifies to drop the next message. - * - * @param msg the next message - * @return the latency in milliseconds if >= 0, otherwise a negative number signifies a drop - */ - int nextLatency(MessageInTransit msg); + /** + * If >= 0, returns the latency in milliseconds of the next message. If < 0, signifies to drop the + * next message. + * + * @param msg the next message + * @return the latency in milliseconds if >= 0, otherwise a negative number signifies a drop + */ + int nextLatency(MessageInTransit msg); } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/MessageDropper.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/MessageDropper.java index 73da9daf94..1b92b84e7a 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/MessageDropper.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/MessageDropper.java @@ -72,27 +72,26 @@ import java.util.stream.Collectors; public class MessageDropper implements Predicate { - private final Set> msgClassesToDrop; - private final Random random; - private final double dropRate; + private final Set> msgClassesToDrop; + private final Random random; + private final double dropRate; + public MessageDropper(Class... msgClasses) { + this(null, 0.0, msgClasses); + } - public MessageDropper(Class... msgClasses) { - this(null, 0.0, msgClasses); - } + public MessageDropper(Random random, double dropRate, Class... msgClasses) { + this.msgClassesToDrop = Arrays.stream(msgClasses).collect(Collectors.toSet()); + this.random = random; + this.dropRate = dropRate; + } - public MessageDropper(Random random, double dropRate, Class... msgClasses) { - this.msgClassesToDrop = Arrays.stream(msgClasses).collect(Collectors.toSet()); - this.random = random; - this.dropRate = dropRate; - } + @Override + public boolean test(MessageInTransit messageInTransit) { + if (msgClassesToDrop.stream().noneMatch(c -> c.isInstance(messageInTransit.getContent()))) { + return false; + } - @Override - public boolean test(MessageInTransit messageInTransit) { - if (msgClassesToDrop.stream().noneMatch(c -> c.isInstance(messageInTransit.getContent()))) { - return false; - } - - return random == null || random.nextDouble() < dropRate; - } + return random == null || random.nextDouble() < dropRate; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OneNodePerEpochLedgerStatusUpdateDropper.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OneNodePerEpochLedgerStatusUpdateDropper.java index c1c8e19b3f..bc807113cf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OneNodePerEpochLedgerStatusUpdateDropper.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OneNodePerEpochLedgerStatusUpdateDropper.java @@ -67,31 +67,32 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.integration.distributed.simulation.network.SimulationNetwork.MessageInTransit; import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; - import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; /** - * Drops all epoch ledger status update messages from the first node to send an epoch response - * for a given epoch + * Drops all epoch ledger status update messages from the first node to send an epoch response for a + * given epoch */ public class OneNodePerEpochLedgerStatusUpdateDropper implements Predicate { - private final Map nodeToDrop = new HashMap<>(); + private final Map nodeToDrop = new HashMap<>(); - @Override - public boolean test(MessageInTransit messageInTransit) { - if (!(messageInTransit.getContent() instanceof LedgerStatusUpdate)) { - return false; - } + @Override + public boolean test(MessageInTransit messageInTransit) { + if (!(messageInTransit.getContent() instanceof LedgerStatusUpdate)) { + return false; + } - final var ledgerStatusUpdate = (LedgerStatusUpdate) messageInTransit.getContent(); + final var ledgerStatusUpdate = (LedgerStatusUpdate) messageInTransit.getContent(); - final var node = nodeToDrop.putIfAbsent(ledgerStatusUpdate.getHeader().getEpoch(), messageInTransit.getSender()); - if (node == null) { - return true; - } + final var node = + nodeToDrop.putIfAbsent( + ledgerStatusUpdate.getHeader().getEpoch(), messageInTransit.getSender()); + if (node == null) { + return true; + } - return messageInTransit.getSender().equals(node); - } + return messageInTransit.getSender().equals(node); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OutOfOrderChannels.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OutOfOrderChannels.java index 78b753e39d..df90b9b36f 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OutOfOrderChannels.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/OutOfOrderChannels.java @@ -73,22 +73,25 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; -/** - * Provides message ordering in a channel which does not - * necessarily need to be in order. - */ +/** Provides message ordering in a channel which does not necessarily need to be in order. */ public class OutOfOrderChannels implements ChannelCommunication { - private final LatencyProvider latencyProvider; + private final LatencyProvider latencyProvider; - @Inject - public OutOfOrderChannels(LatencyProvider latencyProvider) { - this.latencyProvider = Objects.requireNonNull(latencyProvider); - } + @Inject + public OutOfOrderChannels(LatencyProvider latencyProvider) { + this.latencyProvider = Objects.requireNonNull(latencyProvider); + } - @Override - public Observable transform(BFTNode sender, BFTNode receiver, Observable messages) { - return messages.map(msg -> msg.delayed(latencyProvider.nextLatency(msg))) - .filter(msg -> msg.getDelay() >= 0) - .flatMap(msg -> Observable.just(msg).delay(msg.getDelay(), TimeUnit.MILLISECONDS).observeOn(Schedulers.io())); - } + @Override + public Observable transform( + BFTNode sender, BFTNode receiver, Observable messages) { + return messages + .map(msg -> msg.delayed(latencyProvider.nextLatency(msg))) + .filter(msg -> msg.getDelay() >= 0) + .flatMap( + msg -> + Observable.just(msg) + .delay(msg.getDelay(), TimeUnit.MILLISECONDS) + .observeOn(Schedulers.io())); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomLatencyProvider.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomLatencyProvider.java index 4b4590fc2b..9aea56bfb9 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomLatencyProvider.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomLatencyProvider.java @@ -69,34 +69,32 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Latency Provider which uniformly distributes latency across a minimum and maximum - */ +/** Latency Provider which uniformly distributes latency across a minimum and maximum */ public final class RandomLatencyProvider implements LatencyProvider { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - private final int minLatency; - private final int maxLatency; - private final Random rng; + private final int minLatency; + private final int maxLatency; + private final Random rng; - public RandomLatencyProvider(int minLatency, int maxLatency) { - if (minLatency < 0) { - throw new IllegalArgumentException("minimumLatency must be >= 0 but was " + minLatency); - } - if (maxLatency < 0) { - throw new IllegalArgumentException("maximumLatency must be >= 0 but was " + maxLatency); - } + public RandomLatencyProvider(int minLatency, int maxLatency) { + if (minLatency < 0) { + throw new IllegalArgumentException("minimumLatency must be >= 0 but was " + minLatency); + } + if (maxLatency < 0) { + throw new IllegalArgumentException("maximumLatency must be >= 0 but was " + maxLatency); + } - final long seed = System.currentTimeMillis(); - log.info("{} using seed {}", this.getClass().getSimpleName(), seed); + final long seed = System.currentTimeMillis(); + log.info("{} using seed {}", this.getClass().getSimpleName(), seed); - this.rng = new Random(seed); - this.minLatency = minLatency; - this.maxLatency = maxLatency; - } + this.rng = new Random(seed); + this.minLatency = minLatency; + this.maxLatency = maxLatency; + } - @Override - public int nextLatency(MessageInTransit msg) { - return minLatency + rng.nextInt(maxLatency - minLatency + 1); - } + @Override + public int nextLatency(MessageInTransit msg) { + return minLatency + rng.nextInt(maxLatency - minLatency + 1); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomVoteDropper.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomVoteDropper.java index bd26acb4b4..7842f48d47 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomVoteDropper.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/RandomVoteDropper.java @@ -70,25 +70,23 @@ import java.util.Random; import java.util.function.Predicate; -/** - * Drops random vote messages - */ +/** Drops random vote messages */ public class RandomVoteDropper implements Predicate { - private final Random random; - private final double drops; + private final Random random; + private final double drops; - public RandomVoteDropper(Random random, double drops) { - this.random = Objects.requireNonNull(random); - this.drops = drops; - } + public RandomVoteDropper(Random random, double drops) { + this.random = Objects.requireNonNull(random); + this.drops = drops; + } - @Override - public boolean test(MessageInTransit msg) { - Object content = msg.getContent(); - if (content instanceof Vote) { - return random.nextDouble() < drops; - } + @Override + public boolean test(MessageInTransit msg) { + Object content = msg.getContent(); + if (content instanceof Vote) { + return random.nextDouble() < drops; + } - return false; - } + return false; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetwork.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetwork.java index 7bbaeebbc8..f5da2b67a8 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetwork.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetwork.java @@ -73,7 +73,6 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Observable; - import io.reactivex.rxjava3.subjects.ReplaySubject; import io.reactivex.rxjava3.subjects.Subject; import java.util.Map; @@ -81,144 +80,147 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Simple simulated network implementation that just sends messages to itself with a configurable latency. + * Simple simulated network implementation that just sends messages to itself with a configurable + * latency. */ public class SimulationNetwork { - public static final int DEFAULT_LATENCY = 50; - - public static final class MessageInTransit { - private final Object content; - private final BFTNode sender; - private final BFTNode receiver; - private final long delay; - private final long delayAfterPrevious; - - private MessageInTransit(Object content, BFTNode sender, BFTNode receiver, long delay, long delayAfterPrevious) { - if (content instanceof RemoteEvent) { - throw new IllegalArgumentException("Message in transit should not be RemoteEvent"); - } - - this.content = Objects.requireNonNull(content); - this.sender = sender; - this.receiver = receiver; - this.delay = delay; - this.delayAfterPrevious = delayAfterPrevious; - } - - private static MessageInTransit newMessage(Object content, BFTNode sender, BFTNode receiver) { - return new MessageInTransit(content, sender, receiver, 0, 0); - } - - public Maybe localEvent(Class eventClass) { - if (sender.equals(receiver) && eventClass.isInstance(content)) { - return Maybe.just(eventClass.cast(content)); - } - - return Maybe.empty(); - } - - public Maybe> remoteEvent(Class eventClass) { - if (!sender.equals(receiver) && eventClass.isInstance(content)) { - return Maybe.just(RemoteEvent.create(sender, eventClass.cast(content))); - } - - return Maybe.empty(); - } - - MessageInTransit delayed(long delay) { - return new MessageInTransit(content, sender, receiver, delay, delay); - } - - MessageInTransit delayAfterPrevious(long delayAfterPrevious) { - return new MessageInTransit(content, sender, receiver, delay, delayAfterPrevious); - } - - public long getDelayAfterPrevious() { - return delayAfterPrevious; - } - - public long getDelay() { - return delay; - } - - public Object getContent() { - return this.content; - } - - public BFTNode getSender() { - return sender; - } - - public BFTNode getReceiver() { - return receiver; - } - - @Override - public String toString() { - return String.format("%s %s -> %s %d %d", - content, - sender.getSimpleName(), - receiver.getSimpleName(), - delay, - delayAfterPrevious - ); - } - } - - public interface ChannelCommunication { - Observable transform(BFTNode sender, BFTNode receiver, Observable messages); - } - - private final Subject receivedMessages; - private final Map receivers = new ConcurrentHashMap<>(); - private final ChannelCommunication channelCommunication; - - @Inject - public SimulationNetwork(ChannelCommunication channelCommunication) { - this.channelCommunication = Objects.requireNonNull(channelCommunication); - this.receivedMessages = ReplaySubject.createWithSize(128) // To catch startup timing issues - .toSerialized(); - } - - public class SimulatedNetworkImpl implements RxRemoteEnvironment { - private final Flowable myMessages; - private final BFTNode thisNode; - - private SimulatedNetworkImpl(BFTNode node) { - this.thisNode = node; - // filter only relevant messages (appropriate target and if receiving is allowed) - this.myMessages = Flowable.fromObservable(receivedMessages - .filter(msg -> msg.receiver.equals(node)) - .groupBy(MessageInTransit::getSender) - .serialize() - .flatMap(groupedObservable -> - channelCommunication - .transform(groupedObservable.getKey(), node, groupedObservable) - ) - .publish() - .refCount(), BackpressureStrategy.BUFFER) - .onBackpressureBuffer(255, false, true /* unbounded */); - } - - public Observable localEvents(Class eventClass) { - return myMessages.flatMapMaybe(m -> m.localEvent(eventClass)).toObservable(); - } - - @Override - public Flowable> remoteEvents(Class eventClass) { - return myMessages.flatMapMaybe(m -> m.remoteEvent(eventClass)); - } - - public RemoteEventDispatcher remoteEventDispatcher(Class eventClass) { - return this::sendRemoteEvent; - } - - private void sendRemoteEvent(BFTNode node, T event) { - receivedMessages.onNext(MessageInTransit.newMessage(event, thisNode, node)); - } - } - - public SimulatedNetworkImpl getNetwork(BFTNode forNode) { - return receivers.computeIfAbsent(forNode, SimulatedNetworkImpl::new); - } + public static final int DEFAULT_LATENCY = 50; + + public static final class MessageInTransit { + private final Object content; + private final BFTNode sender; + private final BFTNode receiver; + private final long delay; + private final long delayAfterPrevious; + + private MessageInTransit( + Object content, BFTNode sender, BFTNode receiver, long delay, long delayAfterPrevious) { + if (content instanceof RemoteEvent) { + throw new IllegalArgumentException("Message in transit should not be RemoteEvent"); + } + + this.content = Objects.requireNonNull(content); + this.sender = sender; + this.receiver = receiver; + this.delay = delay; + this.delayAfterPrevious = delayAfterPrevious; + } + + private static MessageInTransit newMessage(Object content, BFTNode sender, BFTNode receiver) { + return new MessageInTransit(content, sender, receiver, 0, 0); + } + + public Maybe localEvent(Class eventClass) { + if (sender.equals(receiver) && eventClass.isInstance(content)) { + return Maybe.just(eventClass.cast(content)); + } + + return Maybe.empty(); + } + + public Maybe> remoteEvent(Class eventClass) { + if (!sender.equals(receiver) && eventClass.isInstance(content)) { + return Maybe.just(RemoteEvent.create(sender, eventClass.cast(content))); + } + + return Maybe.empty(); + } + + MessageInTransit delayed(long delay) { + return new MessageInTransit(content, sender, receiver, delay, delay); + } + + MessageInTransit delayAfterPrevious(long delayAfterPrevious) { + return new MessageInTransit(content, sender, receiver, delay, delayAfterPrevious); + } + + public long getDelayAfterPrevious() { + return delayAfterPrevious; + } + + public long getDelay() { + return delay; + } + + public Object getContent() { + return this.content; + } + + public BFTNode getSender() { + return sender; + } + + public BFTNode getReceiver() { + return receiver; + } + + @Override + public String toString() { + return String.format( + "%s %s -> %s %d %d", + content, sender.getSimpleName(), receiver.getSimpleName(), delay, delayAfterPrevious); + } + } + + public interface ChannelCommunication { + Observable transform( + BFTNode sender, BFTNode receiver, Observable messages); + } + + private final Subject receivedMessages; + private final Map receivers = new ConcurrentHashMap<>(); + private final ChannelCommunication channelCommunication; + + @Inject + public SimulationNetwork(ChannelCommunication channelCommunication) { + this.channelCommunication = Objects.requireNonNull(channelCommunication); + this.receivedMessages = + ReplaySubject.createWithSize(128) // To catch startup timing issues + .toSerialized(); + } + + public class SimulatedNetworkImpl implements RxRemoteEnvironment { + private final Flowable myMessages; + private final BFTNode thisNode; + + private SimulatedNetworkImpl(BFTNode node) { + this.thisNode = node; + // filter only relevant messages (appropriate target and if receiving is allowed) + this.myMessages = + Flowable.fromObservable( + receivedMessages + .filter(msg -> msg.receiver.equals(node)) + .groupBy(MessageInTransit::getSender) + .serialize() + .flatMap( + groupedObservable -> + channelCommunication.transform( + groupedObservable.getKey(), node, groupedObservable)) + .publish() + .refCount(), + BackpressureStrategy.BUFFER) + .onBackpressureBuffer(255, false, true /* unbounded */); + } + + public Observable localEvents(Class eventClass) { + return myMessages.flatMapMaybe(m -> m.localEvent(eventClass)).toObservable(); + } + + @Override + public Flowable> remoteEvents(Class eventClass) { + return myMessages.flatMapMaybe(m -> m.remoteEvent(eventClass)); + } + + public RemoteEventDispatcher remoteEventDispatcher(Class eventClass) { + return this::sendRemoteEvent; + } + + private void sendRemoteEvent(BFTNode node, T event) { + receivedMessages.onNext(MessageInTransit.newMessage(event, thisNode, node)); + } + } + + public SimulatedNetworkImpl getNetwork(BFTNode forNode) { + return receivers.computeIfAbsent(forNode, SimulatedNetworkImpl::new); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetworkTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetworkTest.java index f90a536ae4..9b034e923e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetworkTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNetworkTest.java @@ -76,34 +76,32 @@ import org.junit.Test; public class SimulationNetworkTest { - private BFTNode node1; - private BFTNode node2; - private ChannelCommunication channelCommunication; - private SimulationNetwork network; + private BFTNode node1; + private BFTNode node2; + private ChannelCommunication channelCommunication; + private SimulationNetwork network; - @Before - public void setup() { - node1 = mock(BFTNode.class); - node2 = mock(BFTNode.class); - this.channelCommunication = new InOrderChannels(msg -> 50); - this.network = new SimulationNetwork(channelCommunication); - } + @Before + public void setup() { + node1 = mock(BFTNode.class); + node2 = mock(BFTNode.class); + this.channelCommunication = new InOrderChannels(msg -> 50); + this.network = new SimulationNetwork(channelCommunication); + } - @Test - public void when_send_get_vertex_request_to_another_node__then_should_receive_it() { - HashCode vertexId = mock(HashCode.class); + @Test + public void when_send_get_vertex_request_to_another_node__then_should_receive_it() { + HashCode vertexId = mock(HashCode.class); - TestObserver> rpcRequestListener = - network.getNetwork(node2).remoteEvents(GetVerticesRequest.class) - .toObservable().test(); + TestObserver> rpcRequestListener = + network.getNetwork(node2).remoteEvents(GetVerticesRequest.class).toObservable().test(); - network - .getNetwork(node1) - .remoteEventDispatcher(GetVerticesRequest.class) - .dispatch(node2, new GetVerticesRequest(vertexId, 1)); - - rpcRequestListener.awaitCount(1); - rpcRequestListener.assertValueAt(0, r -> r.getEvent().getVertexId().equals(vertexId)); - } + network + .getNetwork(node1) + .remoteEventDispatcher(GetVerticesRequest.class) + .dispatch(node2, new GetVerticesRequest(vertexId, 1)); + rpcRequestListener.awaitCount(1); + rpcRequestListener.assertValueAt(0, r -> r.getEvent().getVertexId().equals(vertexId)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNodes.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNodes.java index ed15851b7e..e670f0dfa0 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNodes.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/network/SimulationNodes.java @@ -64,6 +64,8 @@ package com.radixdlt.integration.distributed.simulation.network; +import static java.util.function.Predicate.not; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -90,7 +92,7 @@ import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.qualifier.LocalSigner; import com.radixdlt.utils.Pair; - +import io.reactivex.rxjava3.core.Observable; import java.util.List; import java.util.Map; import java.util.Objects; @@ -98,207 +100,218 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import io.reactivex.rxjava3.core.Observable; - -import static java.util.function.Predicate.not; - -/** - * A multi-node bft test network where the network and latencies of each message is simulated. - */ +/** A multi-node bft test network where the network and latencies of each message is simulated. */ public class SimulationNodes { - private final SimulationNetwork underlyingNetwork; - private final ImmutableList nodeInstances; - private final Module baseModule; - private final Module overrideModule; - private final Map byzantineNodeModules; - - /** - * Create a BFT test network with an underlying simulated network. - * @param nodes The nodes on the network - * @param underlyingNetwork the network simulator - */ - public SimulationNodes( - List nodes, - SimulationNetwork underlyingNetwork, - Module baseModule, - Module overrideModule, - Map byzantineNodeModules - ) { - this.baseModule = baseModule; - this.overrideModule = overrideModule; - this.byzantineNodeModules = byzantineNodeModules; - this.underlyingNetwork = Objects.requireNonNull(underlyingNetwork); - this.nodeInstances = nodes.stream().map(this::createBFTInstance).collect(ImmutableList.toImmutableList()); - } - - private Injector createBFTInstance(ECKeyPair self) { - Module module = Modules.combine( - new AbstractModule() { - @Provides - @Self - private BFTNode self() { - return BFTNode.create(self.getPublicKey()); - } - - @Provides - @Self - private ECPublicKey key() { - return self.getPublicKey(); - } - - @Provides - @LocalSigner - HashSigner hashSigner() { - return self::sign; - } - - }, - new NodeNetworkMessagesModule(underlyingNetwork), - baseModule - ); - - // Override modules can be used to prove that certain adversaries - // can break network behavior if incorrect modules are used - if (overrideModule != null) { - module = Modules.override(module).with(overrideModule); - } - - Module byzantineModule = byzantineNodeModules.get(self); - if (byzantineModule != null) { - module = Modules.override(module).with(byzantineModule); - } - - return Guice.createInjector(module); - } - - // TODO: Add support for epoch changes - public interface RunningNetwork { - List getNodes(); - - BFTConfiguration bftConfiguration(); - - Observable latestEpochChanges(); - - Observable> ledgerUpdates(); - - Injector getNodeInjector(BFTNode node); - - EventDispatcher getDispatcher(Class eventClass, BFTNode node); - - SimulationNetwork getUnderlyingNetwork(); - - Map getSystemCounters(); - - void runModule(int nodeIndex, String name); - } - - public RunningNetwork start(ImmutableMap> disabledModuleRunners) { - final var moduleRunnersPerNode = - IntStream.range(0, this.nodeInstances.size()) - .mapToObj(i -> { - final var injector = this.nodeInstances.get(i); - final var moduleRunners = - injector.getInstance(Key.get(new TypeLiteral>() { })); - return Pair.of(i, moduleRunners); - }) - .collect(ImmutableList.toImmutableList()); - - for (var pair : moduleRunnersPerNode) { - final var nodeDisabledModuleRunners = - disabledModuleRunners.getOrDefault(pair.getFirst(), ImmutableSet.of()); - - pair.getSecond().entrySet().stream() - .filter(not(e -> nodeDisabledModuleRunners.contains(e.getKey()))) - .forEach(e -> e.getValue().start()); - } - - final List bftNodes = this.nodeInstances.stream() - .map(i -> i.getInstance(Key.get(BFTNode.class, Self.class))) - .collect(Collectors.toList()); - - return new RunningNetwork() { - @Override - public List getNodes() { - return bftNodes; - } - - @Override - public BFTConfiguration bftConfiguration() { - return nodeInstances.get(0).getInstance(BFTConfiguration.class); - } - - @Override - public Observable latestEpochChanges() { - // Just do first instance for now - EpochChange initialEpoch = nodeInstances.get(0).getInstance(EpochChange.class); - - Set> epochChanges = nodeInstances.stream() - .map(i -> i.getInstance(Key.get(new TypeLiteral>() { }))) - .map(o -> o - .map(u -> u.getStateComputerOutput().getInstance(EpochChange.class)) - .filter(Objects::nonNull) - ) - .collect(Collectors.toSet()); - - return Observable.just(initialEpoch).concatWith( - Observable.merge(epochChanges) - .scan((cur, next) -> next.getProof().getEpoch() > cur.getProof().getEpoch() ? next : cur) - .distinctUntilChanged() - ); - } - - @Override - public Observable> ledgerUpdates() { - Set>> committedCommands = nodeInstances.stream() - .map(i -> { - BFTNode node = i.getInstance(Key.get(BFTNode.class, Self.class)); - return i.getInstance(Key.get(new TypeLiteral>() { })) - .map(v -> Pair.of(node, v)); - }) - .collect(Collectors.toSet()); - - return Observable.merge(committedCommands); - } - - @Override - public Injector getNodeInjector(BFTNode node) { - int index = getNodes().indexOf(node); - return nodeInstances.get(index); - } - - @Override - public EventDispatcher getDispatcher(Class eventClass, BFTNode node) { - int index = getNodes().indexOf(node); - return nodeInstances.get(index).getInstance(Environment.class).getDispatcher(eventClass); - } - - @Override - public SimulationNetwork getUnderlyingNetwork() { - return underlyingNetwork; - } - - @Override - public Map getSystemCounters() { - return bftNodes.stream() - .collect(Collectors.toMap( - node -> node, - node -> nodeInstances.get(bftNodes.indexOf(node)).getInstance(SystemCounters.class) - )); - } - - @Override - public void runModule(int nodeIndex, String name) { - nodeInstances.get(nodeIndex) - .getInstance(Key.get(new TypeLiteral>() { })) - .get(name) - .start(); - } - }; - } - - public void stop() { - this.nodeInstances.stream() - .flatMap(i -> i.getInstance(Key.get(new TypeLiteral>() { })).values().stream()) - .forEach(ModuleRunner::stop); - } + private final SimulationNetwork underlyingNetwork; + private final ImmutableList nodeInstances; + private final Module baseModule; + private final Module overrideModule; + private final Map byzantineNodeModules; + + /** + * Create a BFT test network with an underlying simulated network. + * + * @param nodes The nodes on the network + * @param underlyingNetwork the network simulator + */ + public SimulationNodes( + List nodes, + SimulationNetwork underlyingNetwork, + Module baseModule, + Module overrideModule, + Map byzantineNodeModules) { + this.baseModule = baseModule; + this.overrideModule = overrideModule; + this.byzantineNodeModules = byzantineNodeModules; + this.underlyingNetwork = Objects.requireNonNull(underlyingNetwork); + this.nodeInstances = + nodes.stream().map(this::createBFTInstance).collect(ImmutableList.toImmutableList()); + } + + private Injector createBFTInstance(ECKeyPair self) { + Module module = + Modules.combine( + new AbstractModule() { + @Provides + @Self + private BFTNode self() { + return BFTNode.create(self.getPublicKey()); + } + + @Provides + @Self + private ECPublicKey key() { + return self.getPublicKey(); + } + + @Provides + @LocalSigner + HashSigner hashSigner() { + return self::sign; + } + }, + new NodeNetworkMessagesModule(underlyingNetwork), + baseModule); + + // Override modules can be used to prove that certain adversaries + // can break network behavior if incorrect modules are used + if (overrideModule != null) { + module = Modules.override(module).with(overrideModule); + } + + Module byzantineModule = byzantineNodeModules.get(self); + if (byzantineModule != null) { + module = Modules.override(module).with(byzantineModule); + } + + return Guice.createInjector(module); + } + + // TODO: Add support for epoch changes + public interface RunningNetwork { + List getNodes(); + + BFTConfiguration bftConfiguration(); + + Observable latestEpochChanges(); + + Observable> ledgerUpdates(); + + Injector getNodeInjector(BFTNode node); + + EventDispatcher getDispatcher(Class eventClass, BFTNode node); + + SimulationNetwork getUnderlyingNetwork(); + + Map getSystemCounters(); + + void runModule(int nodeIndex, String name); + } + + public RunningNetwork start(ImmutableMap> disabledModuleRunners) { + final var moduleRunnersPerNode = + IntStream.range(0, this.nodeInstances.size()) + .mapToObj( + i -> { + final var injector = this.nodeInstances.get(i); + final var moduleRunners = + injector.getInstance( + Key.get(new TypeLiteral>() {})); + return Pair.of(i, moduleRunners); + }) + .collect(ImmutableList.toImmutableList()); + + for (var pair : moduleRunnersPerNode) { + final var nodeDisabledModuleRunners = + disabledModuleRunners.getOrDefault(pair.getFirst(), ImmutableSet.of()); + + pair.getSecond().entrySet().stream() + .filter(not(e -> nodeDisabledModuleRunners.contains(e.getKey()))) + .forEach(e -> e.getValue().start()); + } + + final List bftNodes = + this.nodeInstances.stream() + .map(i -> i.getInstance(Key.get(BFTNode.class, Self.class))) + .collect(Collectors.toList()); + + return new RunningNetwork() { + @Override + public List getNodes() { + return bftNodes; + } + + @Override + public BFTConfiguration bftConfiguration() { + return nodeInstances.get(0).getInstance(BFTConfiguration.class); + } + + @Override + public Observable latestEpochChanges() { + // Just do first instance for now + EpochChange initialEpoch = nodeInstances.get(0).getInstance(EpochChange.class); + + Set> epochChanges = + nodeInstances.stream() + .map(i -> i.getInstance(Key.get(new TypeLiteral>() {}))) + .map( + o -> + o.map(u -> u.getStateComputerOutput().getInstance(EpochChange.class)) + .filter(Objects::nonNull)) + .collect(Collectors.toSet()); + + return Observable.just(initialEpoch) + .concatWith( + Observable.merge(epochChanges) + .scan( + (cur, next) -> + next.getProof().getEpoch() > cur.getProof().getEpoch() ? next : cur) + .distinctUntilChanged()); + } + + @Override + public Observable> ledgerUpdates() { + Set>> committedCommands = + nodeInstances.stream() + .map( + i -> { + BFTNode node = i.getInstance(Key.get(BFTNode.class, Self.class)); + return i.getInstance(Key.get(new TypeLiteral>() {})) + .map(v -> Pair.of(node, v)); + }) + .collect(Collectors.toSet()); + + return Observable.merge(committedCommands); + } + + @Override + public Injector getNodeInjector(BFTNode node) { + int index = getNodes().indexOf(node); + return nodeInstances.get(index); + } + + @Override + public EventDispatcher getDispatcher(Class eventClass, BFTNode node) { + int index = getNodes().indexOf(node); + return nodeInstances.get(index).getInstance(Environment.class).getDispatcher(eventClass); + } + + @Override + public SimulationNetwork getUnderlyingNetwork() { + return underlyingNetwork; + } + + @Override + public Map getSystemCounters() { + return bftNodes.stream() + .collect( + Collectors.toMap( + node -> node, + node -> + nodeInstances + .get(bftNodes.indexOf(node)) + .getInstance(SystemCounters.class))); + } + + @Override + public void runModule(int nodeIndex, String name) { + nodeInstances + .get(nodeIndex) + .getInstance(Key.get(new TypeLiteral>() {})) + .get(name) + .start(); + } + }; + } + + public void stop() { + this.nodeInstances.stream() + .flatMap( + i -> + i + .getInstance(Key.get(new TypeLiteral>() {})) + .values() + .stream()) + .forEach(ModuleRunner::stop); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FPlusOneOutOfBoundsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FPlusOneOutOfBoundsTest.java index 9682906cc8..e76a129ce2 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FPlusOneOutOfBoundsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FPlusOneOutOfBoundsTest.java @@ -66,65 +66,57 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.Monitor; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; - +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import java.util.concurrent.TimeUnit; import org.junit.Test; public class FPlusOneOutOfBoundsTest { - private final int latency = 50; - private final int synchronousTimeout = 8 * latency; - private final int outOfBoundsLatency = synchronousTimeout; - private final Builder bftTestBuilder = SimulationTest.builder() - .pacemakerTimeout(synchronousTimeout) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(synchronousTimeout, TimeUnit.MILLISECONDS) - ); + private final int latency = 50; + private final int synchronousTimeout = 8 * latency; + private final int outOfBoundsLatency = synchronousTimeout; + private final Builder bftTestBuilder = + SimulationTest.builder() + .pacemakerTimeout(synchronousTimeout) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(synchronousTimeout, TimeUnit.MILLISECONDS)); - /** - * Tests a configuration of 0 out of 3 nodes out of synchrony bounds - */ - @Test - public void given_0_out_of_3_nodes_out_of_synchrony_bounds() { - SimulationTest test = bftTestBuilder - .numNodes(3) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(latency) - ) - .build(); + /** Tests a configuration of 0 out of 3 nodes out of synchrony bounds */ + @Test + public void given_0_out_of_3_nodes_out_of_synchrony_bounds() { + SimulationTest test = + bftTestBuilder + .numNodes(3) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed(latency)) + .build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults) - .allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } - /** - * Tests a configuration of 1 out of 3 nodes out of synchrony bounds - */ - @Test - public void given_1_out_of_3_nodes_out_of_synchrony_bounds() { - SimulationTest test = bftTestBuilder - .numNodes(3) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.oneOutOfBounds(latency, outOfBoundsLatency) - ) - .build(); + /** Tests a configuration of 1 out of 3 nodes out of synchrony bounds */ + @Test + public void given_1_out_of_3_nodes_out_of_synchrony_bounds() { + SimulationTest test = + bftTestBuilder + .numNodes(3) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.oneOutOfBounds(latency, outOfBoundsLatency)) + .build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults) - .hasEntrySatisfying(Monitor.CONSENSUS_LIVENESS, error -> assertThat(error).isPresent()) - .hasEntrySatisfying(Monitor.CONSENSUS_SAFETY, error -> assertThat(error).isNotPresent()); - } + assertThat(checkResults) + .hasEntrySatisfying(Monitor.CONSENSUS_LIVENESS, error -> assertThat(error).isPresent()) + .hasEntrySatisfying(Monitor.CONSENSUS_SAFETY, error -> assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FProposalsPerViewDropperTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FProposalsPerViewDropperTest.java index 0d19a8924f..f5cfba5ec0 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FProposalsPerViewDropperTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/FProposalsPerViewDropperTest.java @@ -72,13 +72,13 @@ import com.radixdlt.consensus.sync.VertexRequestTimeout; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.ScheduledEventDispatcher; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.Monitor; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import java.util.Arrays; import java.util.Collection; import org.junit.Test; @@ -87,99 +87,104 @@ import org.junit.runners.Parameterized.Parameters; /** - * Simulation with a communication adversary which drops a random proposal message in every - * round. + * Simulation with a communication adversary which drops a random proposal message in every round. * - * Dropped proposals implies that validators will need to retrieve the information - * originally in this proposals via syncing with other nodes. + *

Dropped proposals implies that validators will need to retrieve the information originally in + * this proposals via syncing with other nodes. */ @RunWith(Parameterized.class) public class FProposalsPerViewDropperTest { - @Parameters - public static Collection testParameters() { - return Arrays.asList(new Object[][] { - {4}, {5} // TODO: Investigate why 5 still failing on Travis and 20 still failing on Jenkins - }); - } + @Parameters + public static Collection testParameters() { + return Arrays.asList( + new Object[][] { + {4}, + {5} // TODO: Investigate why 5 still failing on Travis and 20 still failing on Jenkins + }); + } - private final Builder bftTestBuilder; + private final Builder bftTestBuilder; - public FProposalsPerViewDropperTest(int numNodes) { - bftTestBuilder = SimulationTest.builder() - .numNodes(numNodes) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(10), - NetworkDroppers.fRandomProposalsPerViewDropped() - ) - .pacemakerTimeout(5000) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.vertexRequestRate(50), // Conservative check - ConsensusMonitors.noTimeouts() - ); - } + public FProposalsPerViewDropperTest(int numNodes) { + bftTestBuilder = + SimulationTest.builder() + .numNodes(numNodes) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(10), + NetworkDroppers.fRandomProposalsPerViewDropped()) + .pacemakerTimeout(5000) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.vertexRequestRate(50), // Conservative check + ConsensusMonitors.noTimeouts()); + } - /** - * Tests a configuration of 4 nodes with a dropping proposal adversary - * Test should fail with GetVertices RPC disabled - */ - @Test - public void given_incorrect_module_where_vertex_sync_is_disabled__then_test_should_fail_against_drop_proposal_adversary() { - SimulationTest test = bftTestBuilder - .overrideWithIncorrectModule(new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { }).toInstance((node, request) -> { }); - } - }) - .build(); + /** + * Tests a configuration of 4 nodes with a dropping proposal adversary Test should fail with + * GetVertices RPC disabled + */ + @Test + public void + given_incorrect_module_where_vertex_sync_is_disabled__then_test_should_fail_against_drop_proposal_adversary() { + SimulationTest test = + bftTestBuilder + .overrideWithIncorrectModule( + new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}) + .toInstance((node, request) -> {}); + } + }) + .build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).hasEntrySatisfying( - Monitor.CONSENSUS_NO_TIMEOUTS, - error -> assertThat(error).isPresent() - ); - } + assertThat(checkResults) + .hasEntrySatisfying(Monitor.CONSENSUS_NO_TIMEOUTS, error -> assertThat(error).isPresent()); + } - /** - * Tests a configuration of 4 nodes with a dropping proposal adversary - * Test should fail with GetVertices RPC disabled - */ - @Test - public void given_get_vertices_enabled__then_test_should_succeed_against_drop_proposal_adversary() { - SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + /** + * Tests a configuration of 4 nodes with a dropping proposal adversary Test should fail with + * GetVertices RPC disabled + */ + @Test + public void + given_get_vertices_enabled__then_test_should_succeed_against_drop_proposal_adversary() { + SimulationTest test = bftTestBuilder.build(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } - @Test - public void dropping_sync_adversary_should_cause_no_timeouts_because_of_sync_retries() { - SimulationTest test = bftTestBuilder - .addNetworkModule(NetworkDroppers.bftSyncMessagesDropped(0.1)) - .build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + @Test + public void dropping_sync_adversary_should_cause_no_timeouts_because_of_sync_retries() { + SimulationTest test = + bftTestBuilder.addNetworkModule(NetworkDroppers.bftSyncMessagesDropped(0.1)).build(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } - @Test - public void dropping_sync_adversary_with_no_timeout_scheduler_should_cause_timeouts() { - SimulationTest test = bftTestBuilder - .addNetworkModule(NetworkDroppers.bftSyncMessagesDropped(0.1)) - .overrideWithIncorrectModule(new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { }) - .toInstance((request, millis) -> { }); - } - }) - .build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).hasEntrySatisfying(Monitor.CONSENSUS_NO_TIMEOUTS, error -> assertThat(error).isPresent()); - } + @Test + public void dropping_sync_adversary_with_no_timeout_scheduler_should_cause_timeouts() { + SimulationTest test = + bftTestBuilder + .addNetworkModule(NetworkDroppers.bftSyncMessagesDropped(0.1)) + .overrideWithIncorrectModule( + new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}) + .toInstance((request, millis) -> {}); + } + }) + .build(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); + assertThat(checkResults) + .hasEntrySatisfying(Monitor.CONSENSUS_NO_TIMEOUTS, error -> assertThat(error).isPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneByzantineGenesisTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneByzantineGenesisTest.java index 48180977c4..1392c9073f 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneByzantineGenesisTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneByzantineGenesisTest.java @@ -67,62 +67,59 @@ import static org.assertj.core.api.Assertions.assertThat; import com.radixdlt.crypto.HashUtils; -import com.radixdlt.recovery.MockedRecoveryModule; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.Monitor; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.recovery.MockedRecoveryModule; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; -/** - * Tests that progress cannot be made if nodes do not form a quorum on the - * genesis hash. - */ +/** Tests that progress cannot be made if nodes do not form a quorum on the genesis hash. */ public class OneByzantineGenesisTest { - SimulationTest.Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .pacemakerTimeout(1000) - .addTestModules(ConsensusMonitors.safety()); - - @Test - public void given_2_correct_bfts_and_1_byzantine__then_should_never_make_progress() { - SimulationTest bftTest = bftTestBuilder - .numNodes(3) - .addSingleByzantineModule(new MockedRecoveryModule(HashUtils.random256())) - .addTestModules(ConsensusMonitors.noneCommitted()) - .build(); + SimulationTest.Builder bftTestBuilder = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .pacemakerTimeout(1000) + .addTestModules(ConsensusMonitors.safety()); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + @Test + public void given_2_correct_bfts_and_1_byzantine__then_should_never_make_progress() { + SimulationTest bftTest = + bftTestBuilder + .numNodes(3) + .addSingleByzantineModule(new MockedRecoveryModule(HashUtils.random256())) + .addTestModules(ConsensusMonitors.noneCommitted()) + .build(); - @Test - public void given_3_correct_bfts__then_none_committed_invariant_should_fail() { - SimulationTest bftTest = bftTestBuilder - .numNodes(3) - .addTestModules(ConsensusMonitors.noneCommitted()) - .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).hasEntrySatisfying(Monitor.CONSENSUS_NONE_COMMITTED, error -> assertThat(error).isPresent()); - } + @Test + public void given_3_correct_bfts__then_none_committed_invariant_should_fail() { + SimulationTest bftTest = + bftTestBuilder.numNodes(3).addTestModules(ConsensusMonitors.noneCommitted()).build(); - @Test - public void given_3_correct_bfts_and_1_byzantine__then_should_make_progress() { - SimulationTest bftTest = bftTestBuilder - .numNodes(4) - .addSingleByzantineModule(new MockedRecoveryModule(HashUtils.random256())) - .addTestModules(ConsensusMonitors.liveness(5, TimeUnit.SECONDS)) - .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults) + .hasEntrySatisfying( + Monitor.CONSENSUS_NONE_COMMITTED, error -> assertThat(error).isPresent()); + } - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + @Test + public void given_3_correct_bfts_and_1_byzantine__then_should_make_progress() { + SimulationTest bftTest = + bftTestBuilder + .numNodes(4) + .addSingleByzantineModule(new MockedRecoveryModule(HashUtils.random256())) + .addTestModules(ConsensusMonitors.liveness(5, TimeUnit.SECONDS)) + .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneOutOfBoundsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneOutOfBoundsTest.java index fe45b626b3..da6005c2d3 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneOutOfBoundsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneOutOfBoundsTest.java @@ -66,45 +66,42 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; public class OneOutOfBoundsTest { - private static final int latency = 50; - private static final int synchronousTimeout = 8 * latency; - private static final int outOfBoundsLatency = synchronousTimeout; + private static final int latency = 50; + private static final int synchronousTimeout = 8 * latency; + private static final int outOfBoundsLatency = synchronousTimeout; - // TODO: Add 1 timeout check - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.oneOutOfBounds(latency, outOfBoundsLatency) - ) - .pacemakerTimeout(synchronousTimeout) - .addTestModules( - ConsensusMonitors.safety(), - // FIXME: Should be 2 * synchronousTimeout, and can be set back to that once message scheduling improved - ConsensusMonitors.liveness(4 * synchronousTimeout, TimeUnit.MILLISECONDS) - ); + // TODO: Add 1 timeout check + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.oneOutOfBounds(latency, outOfBoundsLatency)) + .pacemakerTimeout(synchronousTimeout) + .addTestModules( + ConsensusMonitors.safety(), + // FIXME: Should be 2 * synchronousTimeout, and can be set back to that once message + // scheduling improved + ConsensusMonitors.liveness(4 * synchronousTimeout, TimeUnit.MILLISECONDS)); - /** - * Tests a configuration of 1 out of 4 nodes out of synchrony bounds - */ - @Test - public void given_1_out_of_4_nodes_out_of_synchrony_bounds() { - SimulationTest test = bftTestBuilder - .numNodes(4) - .build(); + /** Tests a configuration of 1 out of 4 nodes out of synchrony bounds */ + @Test + public void given_1_out_of_4_nodes_out_of_synchrony_bounds() { + SimulationTest test = bftTestBuilder.numNodes(4).build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); - } + assertThat(checkResults) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneSlowNodeTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneSlowNodeTest.java index 20d746744b..d837581624 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneSlowNodeTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OneSlowNodeTest.java @@ -66,46 +66,44 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; /** - * Simulation which tests for bft correctness if one node is significantly slower than - * the others but is still within bounds of synchrony. Correctness depends on whether syncing is - * enabled or not. Both cases are verified in this test. + * Simulation which tests for bft correctness if one node is significantly slower than the others + * but is still within bounds of synchrony. Correctness depends on whether syncing is enabled or + * not. Both cases are verified in this test. */ public class OneSlowNodeTest { - private final int minLatency = 10; - private final int maxLatency = 200; - private final int trips = 8; - private final int synchronousTimeout = maxLatency * trips; - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.oneSlowProposalSender(minLatency, maxLatency) - ) - .pacemakerTimeout(synchronousTimeout) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.directParents() - ); + private final int minLatency = 10; + private final int maxLatency = 200; + private final int trips = 8; + private final int synchronousTimeout = maxLatency * trips; + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.oneSlowProposalSender(minLatency, maxLatency)) + .pacemakerTimeout(synchronousTimeout) + .addTestModules(ConsensusMonitors.safety(), ConsensusMonitors.directParents()); - /** - * Tests a static configuration of 3 fast, equal nodes and 1 slow node. - * Test should pass even with GetVertices RPC disabled - */ - @Test - public void given_4_nodes_3_fast_and_1_slow_node_and_sync_disabled__then_a_timeout_wont_occur() { - SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + /** + * Tests a static configuration of 3 fast, equal nodes and 1 slow node. Test should pass even with + * GetVertices RPC disabled + */ + @Test + public void given_4_nodes_3_fast_and_1_slow_node_and_sync_disabled__then_a_timeout_wont_occur() { + SimulationTest test = bftTestBuilder.build(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); - } + assertThat(checkResults) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OutOfOrderTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OutOfOrderTest.java index fc380039bc..38dad92234 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OutOfOrderTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/OutOfOrderTest.java @@ -67,12 +67,12 @@ import static org.assertj.core.api.Assertions.assertThat; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import java.util.Collection; import java.util.List; import java.util.LongSummaryStatistics; @@ -85,54 +85,50 @@ import org.junit.runners.Parameterized.Parameters; /** - * Test where network does not guarantee ordering of messages. - * BFT logic including BFT-sync should not be dependent on - * message ordering so all properties of the system should hold intact. + * Test where network does not guarantee ordering of messages. BFT logic including BFT-sync should + * not be dependent on message ordering so all properties of the system should hold intact. */ @RunWith(Parameterized.class) public final class OutOfOrderTest { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - @Parameters - public static Collection numNodes() { - return List.of(new Object[][] { - {4}, {10} - }); - } + @Parameters + public static Collection numNodes() { + return List.of(new Object[][] {{4}, {10}}); + } - private final int minLatency = 10; - private final int maxLatency = 200; - private final Builder bftTestBuilder; + private final int minLatency = 10; + private final int maxLatency = 200; + private final Builder bftTestBuilder; - public OutOfOrderTest(int numNodes) { - this.bftTestBuilder = SimulationTest.builder() - .numNodes(numNodes) - .networkModules( - NetworkOrdering.outOfOrder(), - NetworkLatencies.random(minLatency, maxLatency), - NetworkDroppers.fRandomProposalsPerViewDropped() - ) - .pacemakerTimeout(5000) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5000, TimeUnit.MILLISECONDS), - ConsensusMonitors.directParents() - ); - } + public OutOfOrderTest(int numNodes) { + this.bftTestBuilder = + SimulationTest.builder() + .numNodes(numNodes) + .networkModules( + NetworkOrdering.outOfOrder(), + NetworkLatencies.random(minLatency, maxLatency), + NetworkDroppers.fRandomProposalsPerViewDropped()) + .pacemakerTimeout(5000) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5000, TimeUnit.MILLISECONDS), + ConsensusMonitors.directParents()); + } - @Test - public void out_of_order_messaging_should_not_affect_properties_of_system() { - SimulationTest test = bftTestBuilder - .build(); + @Test + public void out_of_order_messaging_should_not_affect_properties_of_system() { + SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.BFT_SYNC_REQUESTS_SENT)) - .mapToLong(l -> l) - .summaryStatistics(); - logger.info(statistics); - assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.BFT_SYNC_REQUESTS_SENT)) + .mapToLong(l -> l) + .summaryStatistics(); + logger.info(statistics); + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomLatencyTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomLatencyTest.java index 692cc36c56..3d6c5e0c19 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomLatencyTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomLatencyTest.java @@ -66,72 +66,63 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; -import org.junit.Test; - +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import java.util.concurrent.TimeUnit; +import org.junit.Test; /** - * Simulation which randomly selects (uniform distribution) latencies for each message as - * long as its still within the bounds of synchrony, the idea being that random edge cases - * may be found. + * Simulation which randomly selects (uniform distribution) latencies for each message as long as + * its still within the bounds of synchrony, the idea being that random edge cases may be found. */ public class RandomLatencyTest { - // use a maxLatency of 20x the min latency since we know a round can take up to - // atleast 6x transmission time. 20x so that we can hit these cases more often - private final int minLatency = 10; - private final int maxLatency = 200; - // the minimum latency per round is determined using the network latency - // a round can consist of 6 * max_transmission_time (MTT) - private final int trips = 6; - private final int synchronousTimeout = maxLatency * trips; + // use a maxLatency of 20x the min latency since we know a round can take up to + // atleast 6x transmission time. 20x so that we can hit these cases more often + private final int minLatency = 10; + private final int maxLatency = 200; + // the minimum latency per round is determined using the network latency + // a round can consist of 6 * max_transmission_time (MTT) + private final int trips = 6; + private final int synchronousTimeout = maxLatency * trips; - private Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.random(minLatency, maxLatency), - NetworkDroppers.bftSyncMessagesDropped() - ) - .pacemakerTimeout(synchronousTimeout) // Since no syncing needed 6*MTT required - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(synchronousTimeout, TimeUnit.MILLISECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents() - ); + private Builder bftTestBuilder = + SimulationTest.builder() + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.random(minLatency, maxLatency), + NetworkDroppers.bftSyncMessagesDropped()) + .pacemakerTimeout(synchronousTimeout) // Since no syncing needed 6*MTT required + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(synchronousTimeout, TimeUnit.MILLISECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents()); - /** - * Tests a static configuration of 3 nodes with random, high variance in latency - */ - @Test - public void given_3_correct_nodes_in_random_network_and_no_sync__then_all_synchronous_checks_should_pass() { - SimulationTest test = bftTestBuilder - .numNodes(3) - .build(); + /** Tests a static configuration of 3 nodes with random, high variance in latency */ + @Test + public void + given_3_correct_nodes_in_random_network_and_no_sync__then_all_synchronous_checks_should_pass() { + SimulationTest test = bftTestBuilder.numNodes(3).build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } - /** - * Tests a static configuration of 4 nodes with random, high variance in latency - */ - @Test - public void given_4_correct_bfts_in_random_network_and_no_sync__then_all_synchronous_checks_should_pass() { - SimulationTest test = bftTestBuilder - .numNodes(4) - .build(); + /** Tests a static configuration of 4 nodes with random, high variance in latency */ + @Test + public void + given_4_correct_bfts_in_random_network_and_no_sync__then_all_synchronous_checks_should_pass() { + SimulationTest test = bftTestBuilder.numNodes(4).build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomVoteAndViewTimeoutDropperTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomVoteAndViewTimeoutDropperTest.java index 036b0c815e..fd6d665c21 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomVoteAndViewTimeoutDropperTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/RandomVoteAndViewTimeoutDropperTest.java @@ -67,51 +67,51 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import java.util.LongSummaryStatistics; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; /** - * Dropping random vote and view-timeout messages should cause consensus to fork quite a bit. - * This is to test that safety should always be preserved even in multiple forking situations. + * Dropping random vote and view-timeout messages should cause consensus to fork quite a bit. This + * is to test that safety should always be preserved even in multiple forking situations. */ public class RandomVoteAndViewTimeoutDropperTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(), - NetworkDroppers.randomVotesAndViewTimeoutsDropped(0.4) - ) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(20, TimeUnit.SECONDS) - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(), + NetworkDroppers.randomVotesAndViewTimeoutsDropped(0.4)) + .addTestModules( + ConsensusMonitors.safety(), ConsensusMonitors.liveness(20, TimeUnit.SECONDS)); - /** - * Tests a configuration of 4 nodes with a dropping proposal adversary - * Test should fail with GetVertices RPC disabled - */ - @Test - public void sanity_test() { - SimulationTest test = bftTestBuilder.build(); + /** + * Tests a configuration of 4 nodes with a dropping proposal adversary Test should fail with + * GetVertices RPC disabled + */ + @Test + public void sanity_test() { + SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.BFT_VERTEX_STORE_FORKS)) - .mapToLong(l -> l) - .summaryStatistics(); - System.out.println(statistics); + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.BFT_VERTEX_STORE_FORKS)) + .mapToLong(l -> l) + .summaryStatistics(); + System.out.println(statistics); - assertThat(checkResults).allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); - } + assertThat(checkResults) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/UniformLatencyTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/UniformLatencyTest.java index 9b2204f002..5c9b3ceaeb 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/UniformLatencyTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus/UniformLatencyTest.java @@ -66,43 +66,38 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import org.junit.Test; -/** - * Simulation test which has the same latency for every message - */ +/** Simulation test which has the same latency for every message */ public class UniformLatencyTest { - /** - * Sanity test check for a perfect network. 4 is the size used because it is - * the smallest network size where quorum size (3) != network size. The sanity checks - * done are: - *

    - *
  1. Committed vertices are the same across nodes - *
  2. The size of vertex store does not increase for any node - *
  3. A timeout never occurs for any node - *
  4. Every proposal has a direct parent - *
- */ - @Test - public void given_4_correct_bfts__then_should_pass_sanity_tests_over_1_minute() { - SimulationTest bftTest = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .numNodes(4) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents() - ) - .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + /** + * Sanity test check for a perfect network. 4 is the size used because it is the smallest network + * size where quorum size (3) != network size. The sanity checks done are: + * + *
    + *
  1. Committed vertices are the same across nodes + *
  2. The size of vertex store does not increase for any node + *
  3. A timeout never occurs for any node + *
  4. Every proposal has a direct parent + *
+ */ + @Test + public void given_4_correct_bfts__then_should_pass_sanity_tests_over_1_minute() { + SimulationTest bftTest = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .numNodes(4) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents()) + .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/OneOutOfBoundsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/OneOutOfBoundsTest.java index 63364536c7..629cebd12d 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/OneOutOfBoundsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/OneOutOfBoundsTest.java @@ -66,50 +66,45 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.util.concurrent.TimeUnit; import org.junit.Test; /** - * Runs checks with a consensus and ledger module across 4 nodes with a single - * node out of bounds and verifies sanity checks are maintained + * Runs checks with a consensus and ledger module across 4 nodes with a single node out of bounds + * and verifies sanity checks are maintained */ public class OneOutOfBoundsTest { - private static final int latency = 50; - private static final int outOfBoundsLatency = 1500; + private static final int latency = 50; + private static final int outOfBoundsLatency = 1500; - // TODO: Add 1 timeout check - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.oneOutOfBounds(latency, outOfBoundsLatency) - ) - .ledger() - .pacemakerTimeout(1000) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(4, TimeUnit.SECONDS), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + // TODO: Add 1 timeout check + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.oneOutOfBounds(latency, outOfBoundsLatency)) + .ledger() + .pacemakerTimeout(1000) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(4, TimeUnit.SECONDS), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - /** - * Tests a configuration of 1 out of 4 nodes out of synchrony bounds - */ - @Test - public void given_1_out_of_4_nodes_out_of_synchrony_bounds() { - SimulationTest test = bftTestBuilder - .numNodes(4) - .build(); + /** Tests a configuration of 1 out of 4 nodes out of synchrony bounds */ + @Test + public void given_1_out_of_4_nodes_out_of_synchrony_bounds() { + SimulationTest test = bftTestBuilder.numNodes(4).build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); - } + assertThat(checkResults).allSatisfy((name, error) -> assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/RandomVoteAndViewTimeoutDropperTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/RandomVoteAndViewTimeoutDropperTest.java index 6dacb2879f..ecffb81e21 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/RandomVoteAndViewTimeoutDropperTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/RandomVoteAndViewTimeoutDropperTest.java @@ -67,56 +67,56 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.util.LongSummaryStatistics; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; /** - * Dropping random vote and view-timeout messages should cause consensus to fork quite a bit. - * This is to test that safety should always be preserved even in multiple forking situations. + * Dropping random vote and view-timeout messages should cause consensus to fork quite a bit. This + * is to test that safety should always be preserved even in multiple forking situations. */ public class RandomVoteAndViewTimeoutDropperTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(), - NetworkDroppers.randomVotesAndViewTimeoutsDropped(0.4) - ) - .ledger() - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(20, TimeUnit.SECONDS), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); - - /** - * Tests a configuration of 4 nodes with a dropping proposal adversary - * Test should fail with GetVertices RPC disabled - */ - @Test - public void sanity_test() { - SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(); - final var checkResults = runningTest.awaitCompletion(); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(), + NetworkDroppers.randomVotesAndViewTimeoutsDropped(0.4)) + .ledger() + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(20, TimeUnit.SECONDS), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); + /** + * Tests a configuration of 4 nodes with a dropping proposal adversary Test should fail with + * GetVertices RPC disabled + */ + @Test + public void sanity_test() { + SimulationTest test = bftTestBuilder.build(); + final var runningTest = test.run(); + final var checkResults = runningTest.awaitCompletion(); - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.BFT_VERTEX_STORE_FORKS)) - .mapToLong(l -> l) - .summaryStatistics(); + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.BFT_VERTEX_STORE_FORKS)) + .mapToLong(l -> l) + .summaryStatistics(); - System.out.println(statistics); + System.out.println(statistics); - assertThat(checkResults).allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); - } + assertThat(checkResults) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/SanityTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/SanityTest.java index ff90212407..006c159061 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/SanityTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/SanityTest.java @@ -66,42 +66,38 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; /** - * Runs a consensus and ledger module across 4 nodes and verifies the base - * case required conditions + * Runs a consensus and ledger module across 4 nodes and verifies the base case required conditions */ public class SanityTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .ledger() - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .ledger() + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - @Test - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); - final var checkResults = simulationTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + @Test + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); + final var checkResults = simulationTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/TimeoutPreviousVoteWithDroppedProposalsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/TimeoutPreviousVoteWithDroppedProposalsTest.java index 62bf219962..6bad68fc0e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/TimeoutPreviousVoteWithDroppedProposalsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger/TimeoutPreviousVoteWithDroppedProposalsTest.java @@ -64,72 +64,75 @@ package com.radixdlt.integration.distributed.simulation.tests.consensus_ledger; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableList; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.Test; - +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.time.Duration; import java.util.stream.LongStream; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.Test; /** - * When running a network with 3 nodes, where all proposals are dropped, - * leader should be able to re-send his vote with a timeout flag and the vote - * should be accepted by all other nodes, resulting in moving to a next view (once remaining two nodes also timeout). - * If the timeout replacement votes are not accepted, then we loose a round because a node can only - * move to the next view when it hasn't received the original non-timeout vote. - * This test checks that all nodes only need a single timeout event to proceed to next view, even the node that - * initially received a non-timeout vote (next leader), meaning that it must have successfully replaced - * a non-timeout vote with a timeout vote in the same view. + * When running a network with 3 nodes, where all proposals are dropped, leader should be able to + * re-send his vote with a timeout flag and the vote should be accepted by all other nodes, + * resulting in moving to a next view (once remaining two nodes also timeout). If the timeout + * replacement votes are not accepted, then we loose a round because a node can only move to the + * next view when it hasn't received the original non-timeout vote. This test checks that all nodes + * only need a single timeout event to proceed to next view, even the node that initially received a + * non-timeout vote (next leader), meaning that it must have successfully replaced a non-timeout + * vote with a timeout vote in the same view. */ public class TimeoutPreviousVoteWithDroppedProposalsTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(3) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(), - NetworkDroppers.dropAllProposals() - ) - .ledger() - .addTestModules( - ConsensusMonitors.safety(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(3) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(), + NetworkDroppers.dropAllProposals()) + .ledger() + .addTestModules( + ConsensusMonitors.safety(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - @Test - public void sanity_test() { - SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(Duration.ofSeconds(10)); - final var results = runningTest.awaitCompletion(); + @Test + public void sanity_test() { + SimulationTest test = bftTestBuilder.build(); + final var runningTest = test.run(Duration.ofSeconds(10)); + final var results = runningTest.awaitCompletion(); - final var statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> LongStream.of(s.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT), s.get(CounterType.BFT_PACEMAKER_TIMED_OUT_ROUNDS))) - .map(LongStream::summaryStatistics) - .collect(ImmutableList.toImmutableList()); + final var statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map( + s -> + LongStream.of( + s.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT), + s.get(CounterType.BFT_PACEMAKER_TIMED_OUT_ROUNDS))) + .map(LongStream::summaryStatistics) + .collect(ImmutableList.toImmutableList()); - statistics.forEach(s -> { - // to make sure we've processed some views - assertTrue(s.getMin() > 2); + statistics.forEach( + s -> { + // to make sure we've processed some views + assertTrue(s.getMin() > 2); - // this ensures that we only need a single timeout per view - // BFT_TIMEOUT equal to BFT_TIMED_OUT_VIEWS - assertEquals(s.getMin(), s.getMax()); - }); + // this ensures that we only need a single timeout per view + // BFT_TIMEOUT equal to BFT_TIMED_OUT_VIEWS + assertEquals(s.getMin(), s.getMax()); + }); - assertThat(results).allSatisfy((name, error) -> - AssertionsForClassTypes.assertThat(error).isNotPresent() - ); - } + assertThat(results) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java index 321943f1f2..106b90d294 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/MovingWindowValidatorsTest.java @@ -67,97 +67,102 @@ import static org.assertj.core.api.Assertions.assertThat; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.IntStream; import org.junit.Test; public class MovingWindowValidatorsTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - private static Function windowedEpochToNodesMapper(int windowSize, int totalValidatorCount) { - return epoch -> IntStream.range(0, windowSize).map(index -> (int) (epoch + index) % totalValidatorCount); - } + private static Function windowedEpochToNodesMapper( + int windowSize, int totalValidatorCount) { + return epoch -> + IntStream.range(0, windowSize).map(index -> (int) (epoch + index) % totalValidatorCount); + } - @Test - public void given_correct_1_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .numNodes(4, 2) - .ledgerAndEpochs(View.of(100), windowedEpochToNodesMapper(1, 4)) - .pacemakerTimeout(5000) - .addTestModules( - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.timestampChecker(), - ConsensusMonitors.epochCeilingView(View.of(100)) - ) - .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + @Test + public void + given_correct_1_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder + .numNodes(4, 2) + .ledgerAndEpochs(View.of(100), windowedEpochToNodesMapper(1, 4)) + .pacemakerTimeout(5000) + .addTestModules( + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.timestampChecker(), + ConsensusMonitors.epochCeilingView(View.of(100))) + .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } - @Test - public void given_correct_3_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .numNodes(4, 2) - .ledgerAndEpochs(View.of(100), windowedEpochToNodesMapper(3, 4)) - .pacemakerTimeout(1000) - .addTestModules( - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.timestampChecker(), - ConsensusMonitors.epochCeilingView(View.of(100)) - ) - .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + @Test + public void + given_correct_3_node_bft_with_4_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder + .numNodes(4, 2) + .ledgerAndEpochs(View.of(100), windowedEpochToNodesMapper(3, 4)) + .pacemakerTimeout(1000) + .addTestModules( + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.timestampChecker(), + ConsensusMonitors.epochCeilingView(View.of(100))) + .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } - @Test - public void given_correct_25_node_bft_with_50_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .numNodes(100, 2) - .ledgerAndEpochs(View.of(100), windowedEpochToNodesMapper(25, 50)) - .pacemakerTimeout(5000) - .addTestModules( - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), // High timeout to make Travis happy - ConsensusMonitors.timestampChecker(), - ConsensusMonitors.epochCeilingView(View.of(100)) - ) - .build(); + @Test + public void + given_correct_25_node_bft_with_50_total_nodes_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder + .numNodes(100, 2) + .ledgerAndEpochs(View.of(100), windowedEpochToNodesMapper(25, 50)) + .pacemakerTimeout(5000) + .addTestModules( + ConsensusMonitors.liveness( + 5, TimeUnit.SECONDS), // High timeout to make Travis happy + ConsensusMonitors.timestampChecker(), + ConsensusMonitors.epochCeilingView(View.of(100))) + .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } - @Test - public void given_correct_25_node_bft_with_50_total_nodes_with_changing_epochs_per_1_view__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .numNodes(100, 2) - .ledgerAndEpochs(View.of(1), windowedEpochToNodesMapper(25, 50)) - .pacemakerTimeout(5000) - .addTestModules( - ConsensusMonitors.epochCeilingView(View.of(1)), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS) // High timeout to make Travis happy - ) - .build(); + @Test + public void + given_correct_25_node_bft_with_50_total_nodes_with_changing_epochs_per_1_view__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder + .numNodes(100, 2) + .ledgerAndEpochs(View.of(1), windowedEpochToNodesMapper(25, 50)) + .pacemakerTimeout(5000) + .addTestModules( + ConsensusMonitors.epochCeilingView(View.of(1)), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS) // High timeout to make Travis happy + ) + .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/OneNodeNeverSendEpochResponseTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/OneNodeNeverSendEpochResponseTest.java index e82fd938f3..2a85b11f1e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/OneNodeNeverSendEpochResponseTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/OneNodeNeverSendEpochResponseTest.java @@ -67,13 +67,13 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.time.Duration; import java.util.Collections; import java.util.Random; @@ -85,46 +85,46 @@ import org.junit.Test; /** - * Drops all epoch responses from the first node to send the epoch response (effectively a down node). - * Tests to make sure that epoch changes are still smooth even with an epoch dropper. + * Drops all epoch responses from the first node to send the epoch response (effectively a down + * node). Tests to make sure that epoch changes are still smooth even with an epoch dropper. */ public class OneNodeNeverSendEpochResponseTest { - private static final int numNodes = 10; - private static final int minValidators = 4; // need at least f=1 for this test + private static final int numNodes = 10; + private static final int minValidators = 4; // need at least f=1 for this test - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(), - NetworkDroppers.oneNodePerEpochLedgerStatusUpdateDropped() - ) - .pacemakerTimeout(1000) - .numNodes(numNodes, 4) - .ledgerAndEpochs(View.of(4), randomEpochToNodesMapper()) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.timestampChecker(Duration.ofSeconds(2)), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(), + NetworkDroppers.oneNodePerEpochLedgerStatusUpdateDropped()) + .pacemakerTimeout(1000) + .numNodes(numNodes, 4) + .ledgerAndEpochs(View.of(4), randomEpochToNodesMapper()) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.timestampChecker(Duration.ofSeconds(2)), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - private static Function randomEpochToNodesMapper() { - return epoch -> { - final var indices = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); - final var random = new Random(epoch); - Collections.shuffle(indices, random); - final var numValidators = minValidators + random.nextInt(numNodes - minValidators + 1); - return indices.subList(0, numValidators).stream().mapToInt(Integer::intValue); - }; - } + private static Function randomEpochToNodesMapper() { + return epoch -> { + final var indices = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); + final var random = new Random(epoch); + Collections.shuffle(indices, random); + final var numValidators = minValidators + random.nextInt(numNodes - minValidators + 1); + return indices.subList(0, numValidators).stream().mapToInt(Integer::intValue); + }; + } - @Test - public void given_deterministic_randomized_validator_sets__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .build(); + @Test + public void + given_deterministic_randomized_validator_sets__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = bftTestBuilder.build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/RandomValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/RandomValidatorsTest.java index 23433a5bff..7ff00b26ad 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/RandomValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/RandomValidatorsTest.java @@ -67,12 +67,12 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.util.List; import java.util.Optional; import java.util.Random; @@ -85,66 +85,63 @@ import org.junit.Test; public class RandomValidatorsTest { - private static final int numNodes = 50; - - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .pacemakerTimeout(5000) - .numNodes(numNodes, 2) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5000, TimeUnit.MILLISECONDS), - ConsensusMonitors.timestampChecker(), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - ConsensusMonitors.epochCeilingView(View.of(100)), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private static final int numNodes = 50; - private static Function randomEpochToNodesMapper(Function randomSupplier) { - return epoch -> { - List indices = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); - Random random = randomSupplier.apply(epoch); - for (long i = 0; i < epoch; i++) { - random.nextInt(numNodes); - } - return IntStream.range(0, random.nextInt(numNodes) + 1) - .map(i -> indices.remove(random.nextInt(indices.size()))); - }; - } + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .pacemakerTimeout(5000) + .numNodes(numNodes, 2) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5000, TimeUnit.MILLISECONDS), + ConsensusMonitors.timestampChecker(), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + ConsensusMonitors.epochCeilingView(View.of(100)), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - private static Function goodRandomEpochToNodesMapper() { - return randomEpochToNodesMapper(Random::new); - } + private static Function randomEpochToNodesMapper( + Function randomSupplier) { + return epoch -> { + List indices = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); + Random random = randomSupplier.apply(epoch); + for (long i = 0; i < epoch; i++) { + random.nextInt(numNodes); + } + return IntStream.range(0, random.nextInt(numNodes) + 1) + .map(i -> indices.remove(random.nextInt(indices.size()))); + }; + } - private static Function badRandomEpochToNodesMapper() { - // random number generator which gives a different number per node - Random random = new Random(); - return randomEpochToNodesMapper(l -> random); - } + private static Function goodRandomEpochToNodesMapper() { + return randomEpochToNodesMapper(Random::new); + } - @Test - public void given_deterministic_randomized_validator_sets__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .ledgerAndEpochs(View.of(100), goodRandomEpochToNodesMapper()) - .build(); + private static Function badRandomEpochToNodesMapper() { + // random number generator which gives a different number per node + Random random = new Random(); + return randomEpochToNodesMapper(l -> random); + } - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + @Test + public void + given_deterministic_randomized_validator_sets__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder.ledgerAndEpochs(View.of(100), goodRandomEpochToNodesMapper()).build(); - @Test - public void given_nondeterministic_randomized_validator_sets__then_should_fail() { - SimulationTest bftTest = bftTestBuilder - .ledgerAndEpochs(View.of(100), badRandomEpochToNodesMapper()) - .build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).hasValueSatisfying(new Condition<>(Optional::isPresent, "Has error")); - } + @Test + public void given_nondeterministic_randomized_validator_sets__then_should_fail() { + SimulationTest bftTest = + bftTestBuilder.ledgerAndEpochs(View.of(100), badRandomEpochToNodesMapper()).build(); + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).hasValueSatisfying(new Condition<>(Optional::isPresent, "Has error")); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/StaticValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/StaticValidatorsTest.java index 6eb0e4c778..1b74bd3fe1 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/StaticValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs/StaticValidatorsTest.java @@ -67,73 +67,72 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.Monitor; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; -import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; import com.radixdlt.integration.distributed.simulation.SimulationTest; +import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import org.junit.Test; public class StaticValidatorsTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .numNodes(4, 2) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .numNodes(4, 2) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - @Test - public void given_correct_bft_with_changing_epochs_every_view__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .pacemakerTimeout(1000) - .ledgerAndEpochs(View.of(1), e -> IntStream.range(0, 4)) - .addTestModules(ConsensusMonitors.epochCeilingView(View.of(1))) - .build(); + @Test + public void + given_correct_bft_with_changing_epochs_every_view__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder + .pacemakerTimeout(1000) + .ledgerAndEpochs(View.of(1), e -> IntStream.range(0, 4)) + .addTestModules(ConsensusMonitors.epochCeilingView(View.of(1))) + .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } - @Test - public void given_correct_bft_with_changing_epochs_per_100_views__then_should_fail_incorrect_epoch_invariant() { - SimulationTest bftTest = bftTestBuilder - .ledgerAndEpochs(View.of(100), e -> IntStream.range(0, 4)) - .addTestModules( - ConsensusMonitors.epochCeilingView(View.of(99)), - ConsensusMonitors.timestampChecker() - ) - .build(); + @Test + public void + given_correct_bft_with_changing_epochs_per_100_views__then_should_fail_incorrect_epoch_invariant() { + SimulationTest bftTest = + bftTestBuilder + .ledgerAndEpochs(View.of(100), e -> IntStream.range(0, 4)) + .addTestModules( + ConsensusMonitors.epochCeilingView(View.of(99)), + ConsensusMonitors.timestampChecker()) + .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).hasEntrySatisfying( - Monitor.EPOCH_CEILING_VIEW, - error -> assertThat(error).isPresent() - ); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults) + .hasEntrySatisfying(Monitor.EPOCH_CEILING_VIEW, error -> assertThat(error).isPresent()); + } - @Test - public void given_correct_bft_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .ledgerAndEpochs(View.of(100), e -> IntStream.range(0, 4)) - .addTestModules( - ConsensusMonitors.epochCeilingView(View.of(100)), - ConsensusMonitors.timestampChecker() - ) - .build(); + @Test + public void + given_correct_bft_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = + bftTestBuilder + .ledgerAndEpochs(View.of(100), e -> IntStream.range(0, 4)) + .addTestModules( + ConsensusMonitors.epochCeilingView(View.of(100)), + ConsensusMonitors.timestampChecker()) + .build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/IncreasingValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/IncreasingValidatorsTest.java index 267d157640..a5b4c92f82 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/IncreasingValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/IncreasingValidatorsTest.java @@ -66,60 +66,55 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; -import java.util.concurrent.TimeUnit; - import com.radixdlt.integration.distributed.simulation.application.NodeValidatorRegistrator; +import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; +import java.util.concurrent.TimeUnit; import org.junit.Test; -/** - * Slowly registers more and more validators to the network - */ +/** Slowly registers more and more validators to the network */ public class IncreasingValidatorsTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .pacemakerTimeout(3000) - .numNodes(50, 2) // Can't be 1 otherwise epochs move too fast, TODO: Fix with mempool-aware pacemaker - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault().overrideMaxSigsPerRound(5)), - new ForksModule() - ) - .ledgerAndRadixEngineWithEpochHighView() - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands(), - ApplicationMonitors.registeredNodeToEpoch() - ) - .addActor(NodeValidatorRegistrator.class); + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .pacemakerTimeout(3000) + .numNodes( + 50, 2) // Can't be 1 otherwise epochs move too fast, TODO: Fix with mempool-aware + // pacemaker + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + RERulesConfig.testingDefault().overrideMaxSigsPerRound(5)), + new ForksModule()) + .ledgerAndRadixEngineWithEpochHighView() + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands(), + ApplicationMonitors.registeredNodeToEpoch()) + .addActor(NodeValidatorRegistrator.class); - @Test - public void when_increasing_validators__then_they_should_be_getting_registered() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + public void when_increasing_validators__then_they_should_be_getting_registered() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var runningTest = simulationTest.run(); - final var checkResults = runningTest.awaitCompletion(); + final var runningTest = simulationTest.run(); + final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - } + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomValidatorsTest.java index fb008ef549..61510c8c36 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomValidatorsTest.java @@ -66,77 +66,67 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import com.radixdlt.application.system.FeeTable; import com.radixdlt.application.tokens.Amount; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; - -import java.util.OptionalInt; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; - import com.radixdlt.integration.distributed.simulation.application.NodeValidatorRandomRegistrator; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; -import com.radixdlt.application.system.FeeTable; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; +import java.util.OptionalInt; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; -/** - * Randomly registers and unregisters nodes as validators - */ +/** Randomly registers and unregisters nodes as validators */ public class RandomValidatorsTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(10, 2) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.noFees(), - 1024 * 1024, - OptionalInt.of(5), - 100, - 2, - Amount.ofTokens(10), - 1, - Amount.ofTokens(10), - 9800, - 50 - )), - new ForksModule() - ) - .ledgerAndRadixEngineWithEpochHighView() - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands() - ) - .addActor(NodeValidatorRandomRegistrator.class); - - @Test - public void when_random_validators__then_sanity_checks_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(10, 2) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.noFees(), + 1024 * 1024, + OptionalInt.of(5), + 100, + 2, + Amount.ofTokens(10), + 1, + Amount.ofTokens(10), + 9800, + 50)), + new ForksModule()) + .ledgerAndRadixEngineWithEpochHighView() + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands()) + .addActor(NodeValidatorRandomRegistrator.class); - final var checkResults = simulationTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + @Test + public void when_random_validators__then_sanity_checks_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); + final var checkResults = simulationTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomVoteAndViewTimeoutDropperTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomVoteAndViewTimeoutDropperTest.java index fa5631be32..c3d444eb2c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomVoteAndViewTimeoutDropperTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/RandomVoteAndViewTimeoutDropperTest.java @@ -64,8 +64,7 @@ package com.radixdlt.integration.distributed.simulation.tests.consensus_ledger_epochs_radixengine; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.Test; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; @@ -81,7 +80,6 @@ import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; - import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.List; @@ -90,63 +88,66 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.Test; public class RandomVoteAndViewTimeoutDropperTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(8, 4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(), - NetworkDroppers.randomVotesAndViewTimeoutsDropped(0.2) - ) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault().overrideMaxSigsPerRound(5)), - new ForksModule() - ) - .ledgerAndRadixEngineWithEpochHighView() - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(20, TimeUnit.SECONDS), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands() - ) - .addActor(NodeValidatorRandomRegistrator.class); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(8, 4) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(), + NetworkDroppers.randomVotesAndViewTimeoutsDropped(0.2)) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + RERulesConfig.testingDefault().overrideMaxSigsPerRound(5)), + new ForksModule()) + .ledgerAndRadixEngineWithEpochHighView() + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(20, TimeUnit.SECONDS), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands()) + .addActor(NodeValidatorRandomRegistrator.class); - @Test - public void when_random_validators__then_sanity_checks_should_pass() { - SimulationTest simulationTest = bftTestBuilder.build(); - final var runningTest = simulationTest.run(Duration.of(2, ChronoUnit.MINUTES)); - final var checkResults = runningTest.awaitCompletion(); + @Test + public void when_random_validators__then_sanity_checks_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); + final var runningTest = simulationTest.run(Duration.of(2, ChronoUnit.MINUTES)); + final var checkResults = runningTest.awaitCompletion(); - List counterTypes = List.of( - CounterType.BFT_VERTEX_STORE_FORKS, - CounterType.BFT_COMMITTED_VERTICES, - CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, - CounterType.LEDGER_STATE_VERSION - ); + List counterTypes = + List.of( + CounterType.BFT_VERTEX_STORE_FORKS, + CounterType.BFT_COMMITTED_VERTICES, + CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, + CounterType.LEDGER_STATE_VERSION); - Map statistics = counterTypes.stream() - .collect(Collectors.toMap( - counterType -> counterType, - counterType -> runningTest.getNetwork().getSystemCounters().values() - .stream() - .mapToLong(s -> s.get(counterType)).summaryStatistics()) - ); + Map statistics = + counterTypes.stream() + .collect( + Collectors.toMap( + counterType -> counterType, + counterType -> + runningTest.getNetwork().getSystemCounters().values().stream() + .mapToLong(s -> s.get(counterType)) + .summaryStatistics())); - System.out.println("statistics:\n" + print(statistics.entrySet())); + System.out.println("statistics:\n" + print(statistics.entrySet())); - assertThat(checkResults).allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); - } + assertThat(checkResults) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } - private String print(Set> entrySet) { - var builder = new StringBuilder(); + private String print(Set> entrySet) { + var builder = new StringBuilder(); - entrySet.forEach(e -> builder.append(e.getKey()).append(" = ").append(e.getValue().toString()).append('\n')); + entrySet.forEach( + e -> builder.append(e.getKey()).append(" = ").append(e.getValue().toString()).append('\n')); - return builder.toString(); - } + return builder.toString(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/SanityTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/SanityTest.java index 16242c9c2d..ce8c22eb5a 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/SanityTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_epochs_radixengine/SanityTest.java @@ -66,55 +66,50 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; -import java.util.concurrent.TimeUnit; - import com.radixdlt.integration.distributed.simulation.application.RadixEngineUniqueGenerator; +import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; +import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; public class SanityTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new ForksModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault()) - ) - .ledgerAndRadixEngineWithEpochHighView() - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands(), - ApplicationMonitors.mempoolCommitted() - ) - .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new ForksModule(), + new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault())) + .ledgerAndRadixEngineWithEpochHighView() + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands(), + ApplicationMonitors.mempoolCommitted()) + .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); - @Test - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var checkResults = simulationTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var checkResults = simulationTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_localmempool/MempoolSanityTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_localmempool/MempoolSanityTest.java index bc658c3562..a2fa2c6f79 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_localmempool/MempoolSanityTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_localmempool/MempoolSanityTest.java @@ -69,71 +69,64 @@ import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.radixdlt.atom.Txn; -import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.Monitor; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; import com.radixdlt.integration.distributed.simulation.application.IncrementalBytes; -import com.radixdlt.mempool.Mempools; +import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.mempool.Mempool; +import com.radixdlt.mempool.Mempools; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; -/** - * Simple mempool sanity test which runs the mempool submit and commit invariant. - */ +/** Simple mempool sanity test which runs the mempool submit and commit invariant. */ public class MempoolSanityTest { - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .ledgerAndMempool() - .pacemakerTimeout(3000) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - ApplicationMonitors.mempoolCommitted() - ) - .addMempoolSubmissionsSteadyState(IncrementalBytes.class); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .ledgerAndMempool() + .pacemakerTimeout(3000) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + ApplicationMonitors.mempoolCommitted()) + .addMempoolSubmissionsSteadyState(IncrementalBytes.class); - /** - * TODO: This is more of a test for mempoolSubmissionSteadyState, should move somewhere else - */ - @Test - public void when_submitting_items_to_null_mempool__then_test_should_fail() { - SimulationTest simulationTest = bftTestBuilder - .overrideWithIncorrectModule(new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { }).toInstance(Mempools.empty()); - } - }) - .build(); + /** TODO: This is more of a test for mempoolSubmissionSteadyState, should move somewhere else */ + @Test + public void when_submitting_items_to_null_mempool__then_test_should_fail() { + SimulationTest simulationTest = + bftTestBuilder + .overrideWithIncorrectModule( + new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}).toInstance(Mempools.empty()); + } + }) + .build(); - final var checkResults = simulationTest.run().awaitCompletion(); - assertThat(checkResults).hasEntrySatisfying( - Monitor.MEMPOOL_COMMITTED, - error -> assertThat(error).isPresent() - ); - } + final var checkResults = simulationTest.run().awaitCompletion(); + assertThat(checkResults) + .hasEntrySatisfying(Monitor.MEMPOOL_COMMITTED, error -> assertThat(error).isPresent()); + } - @Test - public void when_submitting_items_to_mempool__then_they_should_get_executed() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + public void when_submitting_items_to_mempool__then_they_should_get_executed() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var checkResults = simulationTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var checkResults = simulationTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/ByzantineSyncTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/ByzantineSyncTest.java index d92e4534e6..17efa7b3b2 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/ByzantineSyncTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/ByzantineSyncTest.java @@ -72,103 +72,101 @@ import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.environment.EventProcessorOnDispatch; -import com.radixdlt.ledger.LedgerUpdate; -import com.radixdlt.ledger.IncorrectAlwaysAcceptingAccumulatorVerifierModule; -import com.radixdlt.sync.SometimesByzantineCommittedReader; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.Monitor; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import com.radixdlt.ledger.IncorrectAlwaysAcceptingAccumulatorVerifierModule; +import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.sync.CommittedReader; +import com.radixdlt.sync.SometimesByzantineCommittedReader; +import com.radixdlt.sync.SyncConfig; import java.util.LongSummaryStatistics; import java.util.concurrent.TimeUnit; - -import com.radixdlt.sync.SyncConfig; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; -/** - * Any number/sort of byzantine sync modules should never be able to cause - * a safety failure. - */ +/** Any number/sort of byzantine sync modules should never be able to cause a safety failure. */ public class ByzantineSyncTest { - private static final Logger logger = LogManager.getLogger(); - private final Builder bftTestBuilder; + private static final Logger logger = LogManager.getLogger(); + private final Builder bftTestBuilder; - public ByzantineSyncTest() { - this.bftTestBuilder = SimulationTest.builder() - .numNodes(5) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(10), - NetworkDroppers.fNodesAllReceivedProposalsDropped() - ) - .addByzantineModuleToAll(new AbstractModule() { - @Override - protected void configure() { - bind(CommittedReader.class).to(SometimesByzantineCommittedReader.class).in(Scopes.SINGLETON); - bind(SometimesByzantineCommittedReader.class).in(Scopes.SINGLETON); - } + public ByzantineSyncTest() { + this.bftTestBuilder = + SimulationTest.builder() + .numNodes(5) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(10), + NetworkDroppers.fNodesAllReceivedProposalsDropped()) + .addByzantineModuleToAll( + new AbstractModule() { + @Override + protected void configure() { + bind(CommittedReader.class) + .to(SometimesByzantineCommittedReader.class) + .in(Scopes.SINGLETON); + bind(SometimesByzantineCommittedReader.class).in(Scopes.SINGLETON); + } - @ProvidesIntoSet - @Singleton - private EventProcessorOnDispatch eventProcessor(SometimesByzantineCommittedReader reader) { - return new EventProcessorOnDispatch<>( - LedgerUpdate.class, - reader.ledgerUpdateEventProcessor() - ); - } - }) - .pacemakerTimeout(3000) - .ledgerAndSync(SyncConfig.of(200L, 10, 2000L)) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); - } + @ProvidesIntoSet + @Singleton + private EventProcessorOnDispatch eventProcessor( + SometimesByzantineCommittedReader reader) { + return new EventProcessorOnDispatch<>( + LedgerUpdate.class, reader.ledgerUpdateEventProcessor()); + } + }) + .pacemakerTimeout(3000) + .ledgerAndSync(SyncConfig.of(200L, 10, 2000L)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); + } - @Test - public void given_a_sometimes_byzantine_sync_layer__sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); - final var runningTest = simulationTest.run(); - final var results = runningTest.awaitCompletion(); - assertThat(results).allSatisfy((name, err) -> assertThat(err).isEmpty()); + @Test + public void given_a_sometimes_byzantine_sync_layer__sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); + final var runningTest = simulationTest.run(); + final var results = runningTest.awaitCompletion(); + assertThat(results).allSatisfy((name, err) -> assertThat(err).isEmpty()); - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) - .mapToLong(l -> l) - .summaryStatistics(); + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) + .mapToLong(l -> l) + .summaryStatistics(); - logger.info("{}", statistics); - assertThat(statistics.getSum()).isGreaterThan(0L); - } + logger.info("{}", statistics); + assertThat(statistics.getSum()).isGreaterThan(0L); + } - @Test - public void given_a_sometimes_byzantine_sync_layer_with_incorrect_accumulator_verifier__sanity_tests_should_not_pass() { - SimulationTest simulationTest = bftTestBuilder - .overrideWithIncorrectModule(new IncorrectAlwaysAcceptingAccumulatorVerifierModule()) - .build(); - final var runningTest = simulationTest.run(); - final var checkResults = runningTest.awaitCompletion(); + @Test + public void + given_a_sometimes_byzantine_sync_layer_with_incorrect_accumulator_verifier__sanity_tests_should_not_pass() { + SimulationTest simulationTest = + bftTestBuilder + .overrideWithIncorrectModule(new IncorrectAlwaysAcceptingAccumulatorVerifierModule()) + .build(); + final var runningTest = simulationTest.run(); + final var checkResults = runningTest.awaitCompletion(); - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) - .mapToLong(l -> l) - .summaryStatistics(); + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) + .mapToLong(l -> l) + .summaryStatistics(); - logger.info("{}", statistics); - assertThat(checkResults).hasEntrySatisfying( - Monitor.LEDGER_IN_ORDER, - error -> assertThat(error).isPresent() - ); - } + logger.info("{}", statistics); + assertThat(checkResults) + .hasEntrySatisfying(Monitor.LEDGER_IN_ORDER, error -> assertThat(error).isPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FNodesNeverReceiveProposalDropperTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FNodesNeverReceiveProposalDropperTest.java index 170376b645..326939bdeb 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FNodesNeverReceiveProposalDropperTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FNodesNeverReceiveProposalDropperTest.java @@ -67,19 +67,18 @@ import static org.assertj.core.api.Assertions.assertThat; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import com.radixdlt.sync.SyncConfig; import java.util.Collection; import java.util.List; import java.util.LongSummaryStatistics; import java.util.concurrent.TimeUnit; - -import com.radixdlt.sync.SyncConfig; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; @@ -89,50 +88,50 @@ @RunWith(Parameterized.class) public class FNodesNeverReceiveProposalDropperTest { - private static final Logger logger = LogManager.getLogger(); - - @Parameters - public static Collection testParameters() { - // Need at least five nodes to ensure that remote sync occurs, otherwise just vertex sync is required - return List.of(new Object[][]{{5}, {20}}); - } + private static final Logger logger = LogManager.getLogger(); - private final Builder bftTestBuilder; + @Parameters + public static Collection testParameters() { + // Need at least five nodes to ensure that remote sync occurs, otherwise just vertex sync is + // required + return List.of(new Object[][] {{5}, {20}}); + } - public FNodesNeverReceiveProposalDropperTest(int numNodes) { - this.bftTestBuilder = SimulationTest.builder() - .numNodes(numNodes) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(10), - NetworkDroppers.fNodesAllReceivedProposalsDropped() - ) - .pacemakerTimeout(3000) - .ledgerAndSync(SyncConfig.of(200L, 10, 200L)) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.directParents(), - //ConsensusMonitors.vertexRequestRate(50), // FIXME: add back check - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); - } + private final Builder bftTestBuilder; - @Test - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder.build(); - final var runningTest = simulationTest.run(); - final var checkResults = runningTest.awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); + public FNodesNeverReceiveProposalDropperTest(int numNodes) { + this.bftTestBuilder = + SimulationTest.builder() + .numNodes(numNodes) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(10), + NetworkDroppers.fNodesAllReceivedProposalsDropped()) + .pacemakerTimeout(3000) + .ledgerAndSync(SyncConfig.of(200L, 10, 200L)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.directParents(), + // ConsensusMonitors.vertexRequestRate(50), // FIXME: add back check + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); + } - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) - .mapToLong(l -> l) - .summaryStatistics(); + @Test + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); + final var runningTest = simulationTest.run(); + final var checkResults = runningTest.awaitCompletion(); + assertThat(checkResults).allSatisfy((name, err) -> assertThat(err).isEmpty()); - logger.info("{}", statistics); - assertThat(statistics.getSum()).isGreaterThan(0L); - } + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) + .mapToLong(l -> l) + .summaryStatistics(); + logger.info("{}", statistics); + assertThat(statistics.getSum()).isGreaterThan(0L); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FallBehindMultipleEpochsLedgerSyncTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FallBehindMultipleEpochsLedgerSyncTest.java index a6a664842e..a7855dcb6c 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FallBehindMultipleEpochsLedgerSyncTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync/FallBehindMultipleEpochsLedgerSyncTest.java @@ -64,6 +64,10 @@ package com.radixdlt.integration.distributed.simulation.tests.consensus_ledger_sync; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.inject.AbstractModule; @@ -75,84 +79,87 @@ import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; +import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; -import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; import com.radixdlt.sync.SyncConfig; -import org.junit.Test; - import java.time.Duration; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; /** - * A node that falls behind multiple epochs should be able to successfully sync its ledger - * and keep up once synced. + * A node that falls behind multiple epochs should be able to successfully sync its ledger and keep + * up once synced. */ public class FallBehindMultipleEpochsLedgerSyncTest { - private static final int NODE_UNDER_TEST_INDEX = 2; - private static final long SYNC_DELAY = 5000L; + private static final int NODE_UNDER_TEST_INDEX = 2; + private static final long SYNC_DELAY = 5000L; - private final Builder testBuilder; + private final Builder testBuilder; - public FallBehindMultipleEpochsLedgerSyncTest() { - this.testBuilder = SimulationTest.builder() - .numNodes(3) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(10) - ) - .overrideWithIncorrectModule(new AbstractModule() { - @Provides - public BFTValidatorSet genesisValidatorSet(Function mapper) { - return mapper.apply(0L); - } - }) - .pacemakerTimeout(3000) - .ledgerAndEpochsAndSync(View.of(10), (unused) -> IntStream.of(0, 1), SyncConfig.of(200L, 10, 2000L)) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - ConsensusMonitors.epochCeilingView(View.of(10)) - ); - } + public FallBehindMultipleEpochsLedgerSyncTest() { + this.testBuilder = + SimulationTest.builder() + .numNodes(3) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed(10)) + .overrideWithIncorrectModule( + new AbstractModule() { + @Provides + public BFTValidatorSet genesisValidatorSet( + Function mapper) { + return mapper.apply(0L); + } + }) + .pacemakerTimeout(3000) + .ledgerAndEpochsAndSync( + View.of(10), (unused) -> IntStream.of(0, 1), SyncConfig.of(200L, 10, 2000L)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + ConsensusMonitors.epochCeilingView(View.of(10))); + } - @Test - public void given_a_node_that_falls_behind_multiple_epochs__it_should_sync_up() { - final var simulationTest = testBuilder.build(); + @Test + public void given_a_node_that_falls_behind_multiple_epochs__it_should_sync_up() { + final var simulationTest = testBuilder.build(); - final var runningTest = simulationTest.run( - Duration.ofSeconds(15), - ImmutableMap.of(NODE_UNDER_TEST_INDEX, ImmutableSet.of(Runners.SYNC)) - ); + final var runningTest = + simulationTest.run( + Duration.ofSeconds(15), + ImmutableMap.of(NODE_UNDER_TEST_INDEX, ImmutableSet.of(Runners.SYNC))); - Executors.newSingleThreadScheduledExecutor() - .schedule(() -> runningTest.getNetwork().runModule(NODE_UNDER_TEST_INDEX, Runners.SYNC), SYNC_DELAY, TimeUnit.MILLISECONDS); + Executors.newSingleThreadScheduledExecutor() + .schedule( + () -> runningTest.getNetwork().runModule(NODE_UNDER_TEST_INDEX, Runners.SYNC), + SYNC_DELAY, + TimeUnit.MILLISECONDS); - final var results = runningTest.awaitCompletion(); + final var results = runningTest.awaitCompletion(); - final var nodeCounters = runningTest.getNetwork() - .getSystemCounters().get(runningTest.getNetwork().getNodes().get(NODE_UNDER_TEST_INDEX)); + final var nodeCounters = + runningTest + .getNetwork() + .getSystemCounters() + .get(runningTest.getNetwork().getNodes().get(NODE_UNDER_TEST_INDEX)); - assertThat(results).allSatisfy((name, err) -> assertThat(err).isEmpty()); + assertThat(results).allSatisfy((name, err) -> assertThat(err).isEmpty()); - // node must be synced up to some state after the first epoch - // and must not fall behind too much - var diff = nodeCounters.get(CounterType.SYNC_TARGET_STATE_VERSION) - nodeCounters.get(CounterType.SYNC_CURRENT_STATE_VERSION); - assertThat(diff).isLessThan(200); - assertTrue(nodeCounters.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED) > 200); - assertTrue(nodeCounters.get(CounterType.LEDGER_STATE_VERSION) > 200); - // just to be sure that node wasn't a validator - assertEquals(0, nodeCounters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)); - assertEquals(0, nodeCounters.get(CounterType.BFT_COMMITTED_VERTICES)); - } + // node must be synced up to some state after the first epoch + // and must not fall behind too much + var diff = + nodeCounters.get(CounterType.SYNC_TARGET_STATE_VERSION) + - nodeCounters.get(CounterType.SYNC_CURRENT_STATE_VERSION); + assertThat(diff).isLessThan(200); + assertTrue(nodeCounters.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED) > 200); + assertTrue(nodeCounters.get(CounterType.LEDGER_STATE_VERSION) > 200); + // just to be sure that node wasn't a validator + assertEquals(0, nodeCounters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)); + assertEquals(0, nodeCounters.get(CounterType.BFT_COMMITTED_VERTICES)); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/FullNodeSyncingWithAnotherFullNodeTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/FullNodeSyncingWithAnotherFullNodeTest.java index 39caaf759c..606a400b74 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/FullNodeSyncingWithAnotherFullNodeTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/FullNodeSyncingWithAnotherFullNodeTest.java @@ -74,89 +74,98 @@ import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.View; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.SyncMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.SyncMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import com.radixdlt.sync.SyncConfig; import java.util.concurrent.TimeUnit; import java.util.function.Function; -import com.radixdlt.sync.SyncConfig; import org.junit.Test; /** - * Full (non-validator) node should be able to sync with another non-validator node that's connected to a validator. + * Full (non-validator) node should be able to sync with another non-validator node that's connected + * to a validator. */ public class FullNodeSyncingWithAnotherFullNodeTest { - private static final ImmutableList VALIDATORS = ImmutableList.of(0, 1); - private static final int NON_VALIDATOR_SYNC_NODE = 2; - private static final int NODE_UNDER_TEST = 3; - private static final int MAX_LEDGER_SYNC_LAG = 300; - - private final Builder testBuilder; + private static final ImmutableList VALIDATORS = ImmutableList.of(0, 1); + private static final int NON_VALIDATOR_SYNC_NODE = 2; + private static final int NODE_UNDER_TEST = 3; + private static final int MAX_LEDGER_SYNC_LAG = 300; - public FullNodeSyncingWithAnotherFullNodeTest() { - this.testBuilder = SimulationTest.builder() - .numNodes(4) - .addressBook(ImmutableMap.of( - 0, VALIDATORS, - 1, VALIDATORS, - 2, VALIDATORS, - // node 3 only knows of a (non-validator) node 2 - NODE_UNDER_TEST, ImmutableList.of(NON_VALIDATOR_SYNC_NODE) - )) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(10) - ) - .overrideWithIncorrectModule(new AbstractModule() { // TODO: remove this hack - @Provides - public BFTValidatorSet genesisValidatorSet(Function mapper) { - return mapper.apply(0L); - } - }) - .pacemakerTimeout(3000) - .ledgerAndEpochsAndSync( - View.of(100), - (unused) -> VALIDATORS.stream().mapToInt(i -> i), - SyncConfig.of(1000L, 10, 500L, 10, Long.MAX_VALUE) - ) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - ConsensusMonitors.epochCeilingView(View.of(100)), - SyncMonitors.maxLedgerSyncLag(MAX_LEDGER_SYNC_LAG) - ); - } + private final Builder testBuilder; - @Test - public void given_a_full_node_that_is_only_conencted_to_another_full_node__it_should_sync() { - final var simulationTest = testBuilder.build(); - final var runningTest = simulationTest.run(); - final var results = runningTest.awaitCompletion(); - assertThat(results).allSatisfy((name, err) -> assertThat(err).isEmpty()); + public FullNodeSyncingWithAnotherFullNodeTest() { + this.testBuilder = + SimulationTest.builder() + .numNodes(4) + .addressBook( + ImmutableMap.of( + 0, + VALIDATORS, + 1, + VALIDATORS, + 2, + VALIDATORS, + // node 3 only knows of a (non-validator) node 2 + NODE_UNDER_TEST, + ImmutableList.of(NON_VALIDATOR_SYNC_NODE))) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed(10)) + .overrideWithIncorrectModule( + new AbstractModule() { // TODO: remove this hack + @Provides + public BFTValidatorSet genesisValidatorSet( + Function mapper) { + return mapper.apply(0L); + } + }) + .pacemakerTimeout(3000) + .ledgerAndEpochsAndSync( + View.of(100), + (unused) -> VALIDATORS.stream().mapToInt(i -> i), + SyncConfig.of(1000L, 10, 500L, 10, Long.MAX_VALUE)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + ConsensusMonitors.epochCeilingView(View.of(100)), + SyncMonitors.maxLedgerSyncLag(MAX_LEDGER_SYNC_LAG)); + } - final var testNodeCounters = runningTest.getNetwork() - .getSystemCounters().get(runningTest.getNetwork().getNodes().get(NODE_UNDER_TEST)); + @Test + public void given_a_full_node_that_is_only_conencted_to_another_full_node__it_should_sync() { + final var simulationTest = testBuilder.build(); + final var runningTest = simulationTest.run(); + final var results = runningTest.awaitCompletion(); + assertThat(results).allSatisfy((name, err) -> assertThat(err).isEmpty()); - // just to be sure that node wasn't a validator - assertEquals(0, testNodeCounters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)); - assertEquals(0, testNodeCounters.get(CounterType.BFT_COMMITTED_VERTICES)); + final var testNodeCounters = + runningTest + .getNetwork() + .getSystemCounters() + .get(runningTest.getNetwork().getNodes().get(NODE_UNDER_TEST)); - final var syncNodeCounters = runningTest.getNetwork() - .getSystemCounters().get(runningTest.getNetwork().getNodes().get(NON_VALIDATOR_SYNC_NODE)); + // just to be sure that node wasn't a validator + assertEquals(0, testNodeCounters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)); + assertEquals(0, testNodeCounters.get(CounterType.BFT_COMMITTED_VERTICES)); - // make sure that the sync target node actually processed all the requests from test node - // and test node didn't communicate directly with a validator - assertThat( - syncNodeCounters.get(CounterType.SYNC_REMOTE_REQUESTS_RECEIVED) - testNodeCounters.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED) - ).isBetween(-4L, 4L); // small discrepancies are fine - } + final var syncNodeCounters = + runningTest + .getNetwork() + .getSystemCounters() + .get(runningTest.getNetwork().getNodes().get(NON_VALIDATOR_SYNC_NODE)); + // make sure that the sync target node actually processed all the requests from test node + // and test node didn't communicate directly with a validator + assertThat( + syncNodeCounters.get(CounterType.SYNC_REMOTE_REQUESTS_RECEIVED) + - testNodeCounters.get(CounterType.SYNC_VALID_RESPONSES_RECEIVED)) + .isBetween(-4L, 4L); // small discrepancies are fine + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/OneNodeFallingBehindTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/OneNodeFallingBehindTest.java index 2b16d37c8c..20676d2155 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/OneNodeFallingBehindTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/OneNodeFallingBehindTest.java @@ -68,61 +68,61 @@ import com.radixdlt.consensus.bft.View; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkDroppers; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import com.radixdlt.sync.SyncConfig; import java.time.Duration; import java.util.LongSummaryStatistics; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; - -import com.radixdlt.sync.SyncConfig; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; /** - * Get the system into a configuration where one node needs to catch up to - * BFT but is slowed down by Ledger sync. + * Get the system into a configuration where one node needs to catch up to BFT but is slowed down by + * Ledger sync. */ public class OneNodeFallingBehindTest { - private final SyncConfig syncConfig = SyncConfig.of(200L, 10, 200L); - - private final Builder bftTestBuilder = SimulationTest.builder() - .numNodes(10) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed(), - NetworkDroppers.dropAllMessagesForOneNode(10000, 10000) - ) - .pacemakerTimeout(3000) - .ledgerAndEpochsAndSync(View.of(100), epoch -> IntStream.range(0, 10), syncConfig) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(30, TimeUnit.SECONDS), - ConsensusMonitors.vertexRequestRate(100), // Conservative check, TODO: too conservative - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final SyncConfig syncConfig = SyncConfig.of(200L, 10, 200L); - @Test - public void sanity_test() { - SimulationTest test = bftTestBuilder.build(); - final var runningTest = test.run(Duration.ofSeconds(60)); - final var checkResults = runningTest.awaitCompletion(); + private final Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(10) + .networkModules( + NetworkOrdering.inOrder(), + NetworkLatencies.fixed(), + NetworkDroppers.dropAllMessagesForOneNode(10000, 10000)) + .pacemakerTimeout(3000) + .ledgerAndEpochsAndSync(View.of(100), epoch -> IntStream.range(0, 10), syncConfig) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(30, TimeUnit.SECONDS), + ConsensusMonitors.vertexRequestRate( + 100), // Conservative check, TODO: too conservative + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - LongSummaryStatistics statistics = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(CounterType.BFT_SYNC_REQUESTS_SENT)) - .mapToLong(l -> l) - .summaryStatistics(); + @Test + public void sanity_test() { + SimulationTest test = bftTestBuilder.build(); + final var runningTest = test.run(Duration.ofSeconds(60)); + final var checkResults = runningTest.awaitCompletion(); - System.out.println(statistics); + LongSummaryStatistics statistics = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(CounterType.BFT_SYNC_REQUESTS_SENT)) + .mapToLong(l -> l) + .summaryStatistics(); - assertThat(checkResults).allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); - } + System.out.println(statistics); + assertThat(checkResults) + .allSatisfy((name, error) -> AssertionsForClassTypes.assertThat(error).isNotPresent()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/RandomValidatorsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/RandomValidatorsTest.java index b4ce6a8411..fd42600e55 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/RandomValidatorsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/consensus_ledger_sync_epochs/RandomValidatorsTest.java @@ -67,69 +67,70 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.bft.View; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import com.radixdlt.sync.SyncConfig; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; - -import com.radixdlt.sync.SyncConfig; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; public class RandomValidatorsTest { - private static final int numNodes = 10; + private static final int numNodes = 10; - private final SyncConfig syncConfig = SyncConfig.of(200L, 10, 200L); + private final SyncConfig syncConfig = SyncConfig.of(200L, 10, 200L); - private final Builder bftTestBuilder = SimulationTest.builder() - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .ledgerAndEpochsAndSync(View.of(3), goodRandomEpochToNodesMapper(), syncConfig) // TODO: investigate why this fails with View.of(10) - .pacemakerTimeout(5000) - .numNodes(numNodes, 2) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(5, TimeUnit.SECONDS), - ConsensusMonitors.vertexRequestRate(50), // Conservative check - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - ConsensusMonitors.epochCeilingView(View.of(100)), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered() - ); + private final Builder bftTestBuilder = + SimulationTest.builder() + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .ledgerAndEpochsAndSync( + View.of(3), + goodRandomEpochToNodesMapper(), + syncConfig) // TODO: investigate why this fails with View.of(10) + .pacemakerTimeout(5000) + .numNodes(numNodes, 2) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(5, TimeUnit.SECONDS), + ConsensusMonitors.vertexRequestRate(50), // Conservative check + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + ConsensusMonitors.epochCeilingView(View.of(100)), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered()); - private static Function randomEpochToNodesMapper(Function randomSupplier) { - return epoch -> { - List indices = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); - Random random = randomSupplier.apply(epoch); - for (long i = 0; i < epoch; i++) { - random.nextInt(numNodes); - } - return IntStream.range(0, random.nextInt(numNodes) + 1) - .map(i -> indices.remove(random.nextInt(indices.size()))); - }; - } + private static Function randomEpochToNodesMapper( + Function randomSupplier) { + return epoch -> { + List indices = IntStream.range(0, numNodes).boxed().collect(Collectors.toList()); + Random random = randomSupplier.apply(epoch); + for (long i = 0; i < epoch; i++) { + random.nextInt(numNodes); + } + return IntStream.range(0, random.nextInt(numNodes) + 1) + .map(i -> indices.remove(random.nextInt(indices.size()))); + }; + } - private static Function goodRandomEpochToNodesMapper() { - return randomEpochToNodesMapper(Random::new); - } + private static Function goodRandomEpochToNodesMapper() { + return randomEpochToNodesMapper(Random::new); + } - @Test - public void given_deterministic_randomized_validator_sets__then_should_pass_bft_and_epoch_invariants() { - SimulationTest bftTest = bftTestBuilder - .build(); + @Test + public void + given_deterministic_randomized_validator_sets__then_should_pass_bft_and_epoch_invariants() { + SimulationTest bftTest = bftTestBuilder.build(); - final var checkResults = bftTest.run().awaitCompletion(); - assertThat(checkResults).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var checkResults = bftTest.run().awaitCompletion(); + assertThat(checkResults) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/MempoolFillTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/MempoolFillTest.java index f3eca65c65..19f4ff46c6 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/MempoolFillTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/MempoolFillTest.java @@ -64,102 +64,98 @@ package com.radixdlt.integration.distributed.simulation.tests.full_function; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.application.TokenUnitConversions; -import com.radixdlt.mempoolfiller.MempoolFillerModule; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECPublicKey; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.application.MempoolFillerStarter; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; import com.radixdlt.mempool.MempoolConfig; +import com.radixdlt.mempoolfiller.MempoolFillerModule; import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.sync.SyncConfig; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.assertj.core.api.Condition; import org.junit.Ignore; import org.junit.Test; import org.radix.TokenIssuance; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; - -/** - * Runs the chaos mempool filler and verifies that all operations are working normally - */ +/** Runs the chaos mempool filler and verifies that all operations are working normally */ public class MempoolFillTest { - private final SimulationTest.Builder bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .fullFunctionNodes(SyncConfig.of(800L, 10, 5000L)) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(), - new ForksModule() - ) - .addNodeModule(new AbstractModule() { - @Override - protected void configure() { - install(MempoolConfig.asModule(1000, 200)); - install(new MempoolFillerModule()); - } + private final SimulationTest.Builder bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .fullFunctionNodes(SyncConfig.of(800L, 10, 5000L)) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule(), + new ForksModule()) + .addNodeModule( + new AbstractModule() { + @Override + protected void configure() { + install(MempoolConfig.asModule(1000, 200)); + install(new MempoolFillerModule()); + } - @ProvidesIntoSet - private TokenIssuance mempoolFillerIssuance(@Genesis ImmutableList validators) { - return TokenIssuance.of(validators.get(0), TokenUnitConversions.unitsToSubunits(10000000000L)); - } - }) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - //ConsensusMonitors.noTimeouts(), // Removed for now to appease Jenkins - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands() - ) - .addActor(MempoolFillerStarter.class); + @ProvidesIntoSet + private TokenIssuance mempoolFillerIssuance( + @Genesis ImmutableList validators) { + return TokenIssuance.of( + validators.get(0), TokenUnitConversions.unitsToSubunits(10000000000L)); + } + }) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + // ConsensusMonitors.noTimeouts(), // Removed for now to appease Jenkins + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands()) + .addActor(MempoolFillerStarter.class); - @Test - @Ignore("Travis not playing nice") - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + @Ignore("Travis not playing nice") + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var runningTest = simulationTest.run(); - final var results = runningTest.awaitCompletion(); + final var runningTest = simulationTest.run(); + final var results = runningTest.awaitCompletion(); - // Post conditions - assertThat(results).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - long invalidCommandsCount = runningTest.getNetwork().getSystemCounters().values().stream() - .map(s -> s.get(SystemCounters.CounterType.RADIX_ENGINE_INVALID_PROPOSED_COMMANDS)) - .mapToLong(l -> l) - .sum(); - assertThat(invalidCommandsCount).isZero(); - } + // Post conditions + assertThat(results) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + long invalidCommandsCount = + runningTest.getNetwork().getSystemCounters().values().stream() + .map(s -> s.get(SystemCounters.CounterType.RADIX_ENGINE_INVALID_PROPOSED_COMMANDS)) + .mapToLong(l -> l) + .sum(); + assertThat(invalidCommandsCount).isZero(); + } - @Test - @Ignore("Travis not playing nicely with timeouts so disable for now until fixed.") - public void filler_should_overwhelm_unratelimited_mempool() { - SimulationTest simulationTest = bftTestBuilder - .overrideWithIncorrectModule(MempoolConfig.asModule(100, 0)) - .build(); + @Test + @Ignore("Travis not playing nicely with timeouts so disable for now until fixed.") + public void filler_should_overwhelm_unratelimited_mempool() { + SimulationTest simulationTest = + bftTestBuilder.overrideWithIncorrectModule(MempoolConfig.asModule(100, 0)).build(); - final var results = simulationTest.run().awaitCompletion(); - assertThat(results).hasValueSatisfying(new Condition<>(Optional::isPresent, "Error exists")); - } + final var results = simulationTest.run().awaitCompletion(); + assertThat(results).hasValueSatisfying(new Condition<>(Optional::isPresent, "Error exists")); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/OneOutOfBoundsTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/OneOutOfBoundsTest.java index ef629215b2..a2017af058 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/OneOutOfBoundsTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/OneOutOfBoundsTest.java @@ -64,6 +64,9 @@ package com.radixdlt.integration.distributed.simulation.tests.full_function; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import com.radixdlt.application.system.FeeTable; import com.radixdlt.application.tokens.Amount; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; @@ -73,18 +76,12 @@ import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; import com.radixdlt.mempool.MempoolConfig; -import com.radixdlt.application.system.FeeTable; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.sync.SyncConfig; import com.radixdlt.utils.UInt256; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.time.Duration; import java.util.Collection; import java.util.List; @@ -93,69 +90,63 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class OneOutOfBoundsTest { - @Parameterized.Parameters - public static Collection fees() { - return List.of(new Object[][] { - {UInt256.ONE}, {UInt256.ZERO}, - }); - } - - private final SimulationTest.Builder bftTestBuilder; + @Parameterized.Parameters + public static Collection fees() { + return List.of( + new Object[][] { + {UInt256.ONE}, {UInt256.ZERO}, + }); + } - public OneOutOfBoundsTest(UInt256 perByteFee) { - bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .pacemakerTimeout(3000) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.oneOutOfBounds(50, 10000) - ) - .fullFunctionNodes(SyncConfig.of(400L, 10, 2000L)) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.create( - Amount.ofSubunits(perByteFee), - Map.of() - ), - 1024 * 1024, - OptionalInt.of(5), - 20L, - 2, - Amount.ofTokens(10), - 1, - Amount.ofTokens(10), - 9800, - 10 - )), - new ForksModule() - ) - .addNodeModule(MempoolConfig.asModule(1000, 10)) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(10000, TimeUnit.SECONDS), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands() - ) - .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); - } + private final SimulationTest.Builder bftTestBuilder; + public OneOutOfBoundsTest(UInt256 perByteFee) { + bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .pacemakerTimeout(3000) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.oneOutOfBounds(50, 10000)) + .fullFunctionNodes(SyncConfig.of(400L, 10, 2000L)) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.create(Amount.ofSubunits(perByteFee), Map.of()), + 1024 * 1024, + OptionalInt.of(5), + 20L, + 2, + Amount.ofTokens(10), + 1, + Amount.ofTokens(10), + 9800, + 10)), + new ForksModule()) + .addNodeModule(MempoolConfig.asModule(1000, 10)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(10000, TimeUnit.SECONDS), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands()) + .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); + } - @Test - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var results = simulationTest.run(Duration.ofMinutes(2)).awaitCompletion(); - assertThat(results).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var results = simulationTest.run(Duration.ofMinutes(2)).awaitCompletion(); + assertThat(results) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/SanityTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/SanityTest.java index ae1d83a98c..b6e518e6f0 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/SanityTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function/SanityTest.java @@ -64,80 +64,75 @@ package com.radixdlt.integration.distributed.simulation.tests.full_function; -import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; import com.radixdlt.integration.distributed.simulation.application.RadixEngineUniqueGenerator; +import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.sync.SyncConfig; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; - @RunWith(Parameterized.class) public class SanityTest { - @Parameterized.Parameters - public static Collection fees() { - return List.of(new Object[][] { - {true}, {false}, - }); - } - - private final Builder bftTestBuilder; + @Parameterized.Parameters + public static Collection fees() { + return List.of( + new Object[][] { + {true}, {false}, + }); + } - public SanityTest(boolean fees) { - bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .pacemakerTimeout(3000) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .fullFunctionNodes(SyncConfig.of(400L, 10, 2000L)) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(), - new ForksModule() - ) - .addNodeModule(MempoolConfig.asModule(1000, 10)) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(1, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands() - ) - .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); + private final Builder bftTestBuilder; - if (!fees) { - bftTestBuilder.addTestModules(ApplicationMonitors.mempoolCommitted()); - } - } + public SanityTest(boolean fees) { + bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .pacemakerTimeout(3000) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .fullFunctionNodes(SyncConfig.of(400L, 10, 2000L)) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule(), + new ForksModule()) + .addNodeModule(MempoolConfig.asModule(1000, 10)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(1, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands()) + .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); + if (!fees) { + bftTestBuilder.addTestModules(ApplicationMonitors.mempoolCommitted()); + } + } - @Test - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var results = simulationTest.run().awaitCompletion(); - assertThat(results).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var results = simulationTest.run().awaitCompletion(); + assertThat(results) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function_forks/SanityTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function_forks/SanityTest.java index ef33b1af3a..d1c5778977 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function_forks/SanityTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/distributed/simulation/tests/full_function_forks/SanityTest.java @@ -64,31 +64,26 @@ package com.radixdlt.integration.distributed.simulation.tests.full_function_forks; -import com.radixdlt.application.tokens.Amount; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + import com.radixdlt.application.system.FeeTable; -import com.radixdlt.statecomputer.forks.ForkOverwritesWithShorterEpochsModule; -import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; -import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.integration.distributed.simulation.NetworkLatencies; import com.radixdlt.integration.distributed.simulation.NetworkOrdering; import com.radixdlt.integration.distributed.simulation.SimulationTest; import com.radixdlt.integration.distributed.simulation.SimulationTest.Builder; import com.radixdlt.integration.distributed.simulation.application.RadixEngineUniqueGenerator; +import com.radixdlt.integration.distributed.simulation.monitors.application.ApplicationMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.consensus.ConsensusMonitors; +import com.radixdlt.integration.distributed.simulation.monitors.ledger.LedgerMonitors; import com.radixdlt.integration.distributed.simulation.monitors.radix_engine.RadixEngineMonitors; import com.radixdlt.mempool.MempoolConfig; +import com.radixdlt.statecomputer.forks.ForkOverwritesWithShorterEpochsModule; import com.radixdlt.statecomputer.forks.ForksModule; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.sync.SyncConfig; import com.radixdlt.utils.UInt256; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.time.Duration; import java.util.Collection; import java.util.List; @@ -96,74 +91,73 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class SanityTest { - @Parameterized.Parameters - public static Collection fees() { - return List.of(new Object[][] { - {UInt256.ZERO}, {UInt256.ONE}, - }); - } + @Parameterized.Parameters + public static Collection fees() { + return List.of( + new Object[][] { + {UInt256.ZERO}, {UInt256.ONE}, + }); + } - private static final Logger logger = LogManager.getLogger(); - private final Builder bftTestBuilder; + private static final Logger logger = LogManager.getLogger(); + private final Builder bftTestBuilder; - public SanityTest(UInt256 perByteFee) { - logger.info("Test fees={}", perByteFee); - bftTestBuilder = SimulationTest.builder() - .numNodes(4) - .pacemakerTimeout(3000) - .networkModules( - NetworkOrdering.inOrder(), - NetworkLatencies.fixed() - ) - .fullFunctionNodes(SyncConfig.of(400L, 10, 2000L)) - .addRadixEngineConfigModules( - new MainnetForkConfigsModule(), - new ForkOverwritesWithShorterEpochsModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.noFees(), - 1024 * 1024, - OptionalInt.of(5), - 10, - 2, - Amount.ofTokens(10), - 1, - Amount.ofTokens(10), - 9800, - 10 - )), - new ForksModule() - ) - .addNodeModule(MempoolConfig.asModule(1000, 10)) - .addTestModules( - ConsensusMonitors.safety(), - ConsensusMonitors.liveness(3, TimeUnit.SECONDS), - ConsensusMonitors.noTimeouts(), - ConsensusMonitors.directParents(), - LedgerMonitors.consensusToLedger(), - LedgerMonitors.ordered(), - RadixEngineMonitors.noInvalidProposedCommands() - ) - .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); + public SanityTest(UInt256 perByteFee) { + logger.info("Test fees={}", perByteFee); + bftTestBuilder = + SimulationTest.builder() + .numNodes(4) + .pacemakerTimeout(3000) + .networkModules(NetworkOrdering.inOrder(), NetworkLatencies.fixed()) + .fullFunctionNodes(SyncConfig.of(400L, 10, 2000L)) + .addRadixEngineConfigModules( + new MainnetForkConfigsModule(), + new ForkOverwritesWithShorterEpochsModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.noFees(), + 1024 * 1024, + OptionalInt.of(5), + 10, + 2, + Amount.ofTokens(10), + 1, + Amount.ofTokens(10), + 9800, + 10)), + new ForksModule()) + .addNodeModule(MempoolConfig.asModule(1000, 10)) + .addTestModules( + ConsensusMonitors.safety(), + ConsensusMonitors.liveness(3, TimeUnit.SECONDS), + ConsensusMonitors.noTimeouts(), + ConsensusMonitors.directParents(), + LedgerMonitors.consensusToLedger(), + LedgerMonitors.ordered(), + RadixEngineMonitors.noInvalidProposedCommands()) + .addMempoolSubmissionsSteadyState(RadixEngineUniqueGenerator.class); - if (perByteFee.isZero()) { - bftTestBuilder.addTestModules(ApplicationMonitors.mempoolCommitted()); - } - } + if (perByteFee.isZero()) { + bftTestBuilder.addTestModules(ApplicationMonitors.mempoolCommitted()); + } + } - @Test - public void sanity_tests_should_pass() { - SimulationTest simulationTest = bftTestBuilder - .build(); + @Test + public void sanity_tests_should_pass() { + SimulationTest simulationTest = bftTestBuilder.build(); - final var results = simulationTest - .run(Duration.ofMinutes(1)).awaitCompletion(); - assertThat(results).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); - } + final var results = simulationTest.run(Duration.ofMinutes(1)).awaitCompletion(); + assertThat(results) + .allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/invariants/SafetyChecker.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/invariants/SafetyChecker.java index 440c5ec961..7b391ef0cf 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/invariants/SafetyChecker.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/invariants/SafetyChecker.java @@ -83,98 +83,86 @@ import java.util.TreeMap; import javax.annotation.concurrent.NotThreadSafe; -/** - * Processes committed vertices and verifies that it forms a single - * chain without any forks. - */ +/** Processes committed vertices and verifies that it forms a single chain without any forks. */ @NotThreadSafe public final class SafetyChecker { - private final TreeMap committedVertices = new TreeMap<>(); - private final Map lastCommittedByNode = new HashMap<>(); - private final List nodes; + private final TreeMap committedVertices = new TreeMap<>(); + private final Map lastCommittedByNode = new HashMap<>(); + private final List nodes; - @Inject - public SafetyChecker(List nodes) { - this.nodes = Objects.requireNonNull(nodes); - } + @Inject + public SafetyChecker(List nodes) { + this.nodes = Objects.requireNonNull(nodes); + } - private static Optional conflictingVerticesError(VerifiedVertex vertex, VerifiedVertex currentVertex) { - return Optional.of( - new TestInvariantError( - String.format("Conflicting vertices [%s, %s] committed at same view: %s", - vertex, - currentVertex, - vertex.getView() - ) - ) - ); - } + private static Optional conflictingVerticesError( + VerifiedVertex vertex, VerifiedVertex currentVertex) { + return Optional.of( + new TestInvariantError( + String.format( + "Conflicting vertices [%s, %s] committed at same view: %s", + vertex, currentVertex, vertex.getView()))); + } - private static Optional brokenChainError(VerifiedVertex vertex, VerifiedVertex closeVertex) { - return Optional.of( - new TestInvariantError( - String.format("Broken Chain [%s, %s]", - vertex, - closeVertex - ) - ) - ); - } + private static Optional brokenChainError( + VerifiedVertex vertex, VerifiedVertex closeVertex) { + return Optional.of( + new TestInvariantError(String.format("Broken Chain [%s, %s]", vertex, closeVertex))); + } - private Optional process(BFTNode node, VerifiedVertex vertex) { - final EpochView epochView = EpochView.of( - vertex.getParentHeader().getLedgerHeader().getEpoch(), - vertex.getView() - ); + private Optional process(BFTNode node, VerifiedVertex vertex) { + final EpochView epochView = + EpochView.of(vertex.getParentHeader().getLedgerHeader().getEpoch(), vertex.getView()); - final VerifiedVertex currentVertexAtView = committedVertices.get(epochView); - if (currentVertexAtView != null) { - if (!currentVertexAtView.getId().equals(vertex.getId())) { - return conflictingVerticesError(vertex, currentVertexAtView); - } - } else { - EpochView parentEpochView = EpochView.of( - vertex.getParentHeader().getLedgerHeader().getEpoch(), - vertex.getParentHeader().getView() - ); - VerifiedVertex parent = committedVertices.get(parentEpochView); - if (parent == null) { - Entry higherCommitted = committedVertices.higherEntry(parentEpochView); - if (higherCommitted != null) { - BFTHeader higherParentHeader = higherCommitted.getValue().getParentHeader(); - EpochView higherCommittedParentEpochView = EpochView.of( - higherParentHeader.getLedgerHeader().getEpoch(), - higherParentHeader.getView() - ); - if (epochView.compareTo(higherCommittedParentEpochView) > 0) { - return brokenChainError(vertex, higherCommitted.getValue()); - } - } - } + final VerifiedVertex currentVertexAtView = committedVertices.get(epochView); + if (currentVertexAtView != null) { + if (!currentVertexAtView.getId().equals(vertex.getId())) { + return conflictingVerticesError(vertex, currentVertexAtView); + } + } else { + EpochView parentEpochView = + EpochView.of( + vertex.getParentHeader().getLedgerHeader().getEpoch(), + vertex.getParentHeader().getView()); + VerifiedVertex parent = committedVertices.get(parentEpochView); + if (parent == null) { + Entry higherCommitted = + committedVertices.higherEntry(parentEpochView); + if (higherCommitted != null) { + BFTHeader higherParentHeader = higherCommitted.getValue().getParentHeader(); + EpochView higherCommittedParentEpochView = + EpochView.of( + higherParentHeader.getLedgerHeader().getEpoch(), higherParentHeader.getView()); + if (epochView.compareTo(higherCommittedParentEpochView) > 0) { + return brokenChainError(vertex, higherCommitted.getValue()); + } + } + } - committedVertices.put(epochView, vertex); - } + committedVertices.put(epochView, vertex); + } - // Clean up old vertices so that we avoid consuming too much memory - lastCommittedByNode.put(node, epochView); - final EpochView lowest = nodes.stream() - .map(n -> lastCommittedByNode.getOrDefault(n, EpochView.of(0, View.genesis()))) - .reduce((v0, v1) -> v0.compareTo(v1) < 0 ? v0 : v1) - .orElse(EpochView.of(0, View.genesis())); - committedVertices.headMap(lowest).clear(); + // Clean up old vertices so that we avoid consuming too much memory + lastCommittedByNode.put(node, epochView); + final EpochView lowest = + nodes.stream() + .map(n -> lastCommittedByNode.getOrDefault(n, EpochView.of(0, View.genesis()))) + .reduce((v0, v1) -> v0.compareTo(v1) < 0 ? v0 : v1) + .orElse(EpochView.of(0, View.genesis())); + committedVertices.headMap(lowest).clear(); - return Optional.empty(); - } + return Optional.empty(); + } - public Optional process(BFTNode node, BFTCommittedUpdate committedUpdate) { - ImmutableList vertices = committedUpdate.getCommitted(); - for (PreparedVertex vertex : vertices) { - Optional maybeError = process(node, vertex.getVertex()); - if (maybeError.isPresent()) { - return maybeError; - } - } + public Optional process(BFTNode node, BFTCommittedUpdate committedUpdate) { + ImmutableList vertices = committedUpdate.getCommitted(); + for (PreparedVertex vertex : vertices) { + Optional maybeError = process(node, vertex.getVertex()); + if (maybeError.isPresent()) { + return maybeError; + } + } - return Optional.empty(); - } + return Optional.empty(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolFillAndEmptyTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolFillAndEmptyTest.java index 4214e4a552..bea0ee0c93 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolFillAndEmptyTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolFillAndEmptyTest.java @@ -64,107 +64,104 @@ package com.radixdlt.integration.mempool; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.utils.PrivateKeys; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; -import com.radixdlt.mempoolfiller.MempoolFillerModule; -import com.radixdlt.mempoolfiller.MempoolFillerUpdate; -import com.radixdlt.mempoolfiller.ScheduledMempoolFill; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.epoch.EpochViewUpdate; import com.radixdlt.counters.SystemCounters; +import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.environment.deterministic.DeterministicProcessor; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.mempool.MempoolConfig; +import com.radixdlt.mempoolfiller.MempoolFillerModule; +import com.radixdlt.mempoolfiller.MempoolFillerUpdate; +import com.radixdlt.mempoolfiller.ScheduledMempoolFill; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; - +import com.radixdlt.utils.PrivateKeys; import java.util.Set; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; /** - * Test which fills a mempool and then empties it checking to make sure there are no - * stragglers left behind. + * Test which fills a mempool and then empties it checking to make sure there are no stragglers left + * behind. */ public final class MempoolFillAndEmptyTest { - private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); + @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Inject private DeterministicProcessor processor; - @Inject private DeterministicNetwork network; - @Inject private EventDispatcher mempoolFillerUpdateEventDispatcher; - @Inject private EventDispatcher scheduledMempoolFillEventDispatcher; - @Inject private SystemCounters systemCounters; + @Inject private DeterministicProcessor processor; + @Inject private DeterministicNetwork network; + @Inject private EventDispatcher mempoolFillerUpdateEventDispatcher; + @Inject private EventDispatcher scheduledMempoolFillEventDispatcher; + @Inject private SystemCounters systemCounters; - private Injector createInjector() { - return Guice.createInjector( - MempoolConfig.asModule(1000, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault()), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), - new MockedGenesisModule( - Set.of(TEST_KEY.getPublicKey()), - Amount.ofTokens(10000000000L), - Amount.ofTokens(1000) - ), - new MempoolFillerModule(), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } + private Injector createInjector() { + return Guice.createInjector( + MempoolConfig.asModule(1000, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault()), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), + new MockedGenesisModule( + Set.of(TEST_KEY.getPublicKey()), Amount.ofTokens(10000000000L), Amount.ofTokens(1000)), + new MempoolFillerModule(), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } - private void fillAndEmptyMempool() { - while (systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE) < 1000) { - ControlledMessage msg = network.nextMessage().value(); - processor.handleMessage(msg.origin(), msg.message(), msg.typeLiteral()); - if (msg.message() instanceof EpochViewUpdate) { - scheduledMempoolFillEventDispatcher.dispatch(ScheduledMempoolFill.create()); - } - } - - for (int i = 0; i < 10000; i++) { - ControlledMessage msg = network.nextMessage().value(); - processor.handleMessage(msg.origin(), msg.message(), msg.typeLiteral()); - if (systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE) == 0) { - break; - } - } + private void fillAndEmptyMempool() { + while (systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE) < 1000) { + ControlledMessage msg = network.nextMessage().value(); + processor.handleMessage(msg.origin(), msg.message(), msg.typeLiteral()); + if (msg.message() instanceof EpochViewUpdate) { + scheduledMempoolFillEventDispatcher.dispatch(ScheduledMempoolFill.create()); + } + } - assertThat(systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); + for (int i = 0; i < 10000; i++) { + ControlledMessage msg = network.nextMessage().value(); + processor.handleMessage(msg.origin(), msg.message(), msg.typeLiteral()); + if (systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE) == 0) { + break; + } } - @Test - public void check_that_full_mempool_empties_itself() { - createInjector().injectMembers(this); - processor.start(); + assertThat(systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); + } - mempoolFillerUpdateEventDispatcher.dispatch(MempoolFillerUpdate.enable(50, true)); + @Test + public void check_that_full_mempool_empties_itself() { + createInjector().injectMembers(this); + processor.start(); - for (int i = 0; i < 10; i++) { - fillAndEmptyMempool(); - } + mempoolFillerUpdateEventDispatcher.dispatch(MempoolFillerUpdate.enable(50, true)); - assertThat(systemCounters.get(SystemCounters.CounterType.RADIX_ENGINE_INVALID_PROPOSED_COMMANDS)).isZero(); + for (int i = 0; i < 10; i++) { + fillAndEmptyMempool(); } + + assertThat( + systemCounters.get(SystemCounters.CounterType.RADIX_ENGINE_INVALID_PROPOSED_COMMANDS)) + .isZero(); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolRelayTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolRelayTest.java index 47fb6dc162..3d81370190 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolRelayTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/mempool/MempoolRelayTest.java @@ -64,53 +64,50 @@ package com.radixdlt.integration.mempool; -import com.google.inject.Provides; -import com.radixdlt.mempoolfiller.MempoolFillerModule; -import com.radixdlt.mempoolfiller.MempoolFillerUpdate; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.atom.TxAction; -import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.crypto.ECPublicKey; -import com.radixdlt.environment.Environment; -import com.radixdlt.environment.deterministic.DeterministicProcessor; -import com.radixdlt.identifiers.REAddr; -import com.radixdlt.mempool.MempoolConfig; -import com.radixdlt.mempool.MempoolRelayTrigger; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; -import com.radixdlt.utils.KeyComparator; -import org.apache.logging.log4j.ThreadContext; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; +import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.radixdlt.PersistedNodeForTestingModule; +import com.radixdlt.application.tokens.Amount; +import com.radixdlt.atom.TxAction; +import com.radixdlt.atom.actions.MintToken; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.crypto.ECPublicKey; +import com.radixdlt.environment.Environment; +import com.radixdlt.environment.deterministic.DeterministicProcessor; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; +import com.radixdlt.identifiers.REAddr; +import com.radixdlt.mempool.MempoolConfig; +import com.radixdlt.mempool.MempoolRelayTrigger; +import com.radixdlt.mempoolfiller.MempoolFillerModule; +import com.radixdlt.mempoolfiller.MempoolFillerUpdate; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; +import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; - +import com.radixdlt.utils.KeyComparator; +import io.reactivex.rxjava3.schedulers.Timed; import java.util.Collection; import java.util.Comparator; import java.util.List; @@ -118,181 +115,184 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; - -import io.reactivex.rxjava3.schedulers.Timed; +import org.apache.logging.log4j.ThreadContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - * Mempool should periodically relay its unprocessed messages to other nodes. - */ +/** Mempool should periodically relay its unprocessed messages to other nodes. */ @RunWith(Parameterized.class) public class MempoolRelayTest { - private static final int MEMPOOL_FILLER_NODE = 0; + private static final int MEMPOOL_FILLER_NODE = 0; - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][]{ - {2, 1}, // 2 validators, 1 full node - {5, 2} // 5 validators, 2 full nodes - }); - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + {2, 1}, // 2 validators, 1 full node + {5, 2} // 5 validators, 2 full nodes + }); + } - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + @Rule public TemporaryFolder folder = new TemporaryFolder(); - private final ImmutableList validators; - private final ImmutableList fullNodes; - private DeterministicNetwork network; - private ImmutableList nodeKeys; - private ImmutableList nodes; + private final ImmutableList validators; + private final ImmutableList fullNodes; + private DeterministicNetwork network; + private ImmutableList nodeKeys; + private ImmutableList nodes; - public MempoolRelayTest(int numValidators, int numFullNodes) { - this.validators = IntStream.range(0, numValidators) - .boxed().collect(ImmutableList.toImmutableList()); - this.fullNodes = IntStream.range(numValidators, numValidators + numFullNodes) - .boxed().collect(ImmutableList.toImmutableList()); - } + public MempoolRelayTest(int numValidators, int numFullNodes) { + this.validators = + IntStream.range(0, numValidators).boxed().collect(ImmutableList.toImmutableList()); + this.fullNodes = + IntStream.range(numValidators, numValidators + numFullNodes) + .boxed() + .collect(ImmutableList.toImmutableList()); + } - @Before - public void setup() { - final var numNodes = this.validators.size() + this.fullNodes.size(); + @Before + public void setup() { + final var numNodes = this.validators.size() + this.fullNodes.size(); - this.nodeKeys = Stream.generate(ECKeyPair::generateNew) - .limit(numNodes) - .sorted(Comparator.comparing(ECKeyPair::getPublicKey, KeyComparator.instance())) - .collect(ImmutableList.toImmutableList()); + this.nodeKeys = + Stream.generate(ECKeyPair::generateNew) + .limit(numNodes) + .sorted(Comparator.comparing(ECKeyPair::getPublicKey, KeyComparator.instance())) + .collect(ImmutableList.toImmutableList()); - final var bftNodes = nodeKeys.stream() - .map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); + final var bftNodes = + nodeKeys.stream().map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); - this.network = new DeterministicNetwork( - bftNodes, - MessageSelector.firstSelector(), - MessageMutator.nothing() - ); + this.network = + new DeterministicNetwork( + bftNodes, MessageSelector.firstSelector(), MessageMutator.nothing()); - this.nodes = nodeKeys.stream() - .>map(k -> () -> createRunner(k, bftNodes)) - .map(Supplier::get) - .collect(ImmutableList.toImmutableList()); + this.nodes = + nodeKeys.stream() + .>map(k -> () -> createRunner(k, bftNodes)) + .map(Supplier::get) + .collect(ImmutableList.toImmutableList()); - this.nodes.forEach(i -> i.getInstance(DeterministicProcessor.class).start()); - } + this.nodes.forEach(i -> i.getInstance(DeterministicProcessor.class).start()); + } - private Injector createRunner(ECKeyPair ecKeyPair, List allNodes) { - final var validatorsKeys = this.validators.stream() - .map(nodeKeys::get) - .map(ECKeyPair::getPublicKey) - .collect(Collectors.toSet()); + private Injector createRunner(ECKeyPair ecKeyPair, List allNodes) { + final var validatorsKeys = + this.validators.stream() + .map(nodeKeys::get) + .map(ECKeyPair::getPublicKey) + .collect(Collectors.toSet()); - return Guice.createInjector( - new MockedGenesisModule( - validatorsKeys, - Amount.ofTokens(1000), - Amount.ofTokens(1000) - ), - MempoolConfig.asModule(500, 100, 10, 10, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault().overrideMaxSigsPerRound(50)), - new ForksModule(), - new PersistedNodeForTestingModule(), - new MempoolFillerModule(), - new AbstractModule() { - @Override - protected void configure() { - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); - bind(new TypeLiteral>() { }).toInstance(allNodes); - bind(Environment.class).toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); - bindConstant().annotatedWith(DatabaseLocation.class) - .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); - } + return Guice.createInjector( + new MockedGenesisModule(validatorsKeys, Amount.ofTokens(1000), Amount.ofTokens(1000)), + MempoolConfig.asModule(500, 100, 10, 10, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + RERulesConfig.testingDefault().overrideMaxSigsPerRound(50)), + new ForksModule(), + new PersistedNodeForTestingModule(), + new MempoolFillerModule(), + new AbstractModule() { + @Override + protected void configure() { + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); + bind(new TypeLiteral>() {}).toInstance(allNodes); + bind(Environment.class) + .toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); + } - @Provides - private PeersView peersView(@Self BFTNode self) { - return () -> allNodes.stream() - .filter(n -> !self.equals(n)) - .map(PeersView.PeerInfo::fromBftNode); - } + @Provides + private PeersView peersView(@Self BFTNode self) { + return () -> + allNodes.stream().filter(n -> !self.equals(n)).map(PeersView.PeerInfo::fromBftNode); + } - @Provides - @Genesis - private List mempoolFillerIssuance(@Self ECPublicKey self) { - return List.of(new MintToken( - REAddr.ofNativeToken(), - REAddr.ofPubKeyAccount(nodeKeys.get(MEMPOOL_FILLER_NODE).getPublicKey()), - Amount.ofTokens(10000000000L).toSubunits() - )); - } - } - ); - } + @Provides + @Genesis + private List mempoolFillerIssuance(@Self ECPublicKey self) { + return List.of( + new MintToken( + REAddr.ofNativeToken(), + REAddr.ofPubKeyAccount(nodeKeys.get(MEMPOOL_FILLER_NODE).getPublicKey()), + Amount.ofTokens(10000000000L).toSubunits())); + } + }); + } - @After - public void teardown() { - this.nodes.forEach(this::stopDatabase); - } + @After + public void teardown() { + this.nodes.forEach(this::stopDatabase); + } - private void stopDatabase(Injector injector) { - injector.getInstance(BerkeleyLedgerEntryStore.class).close(); - injector.getInstance(PersistentSafetyStateStore.class).close(); - injector.getInstance(DatabaseEnvironment.class).stop(); - } + private void stopDatabase(Injector injector) { + injector.getInstance(BerkeleyLedgerEntryStore.class).close(); + injector.getInstance(PersistentSafetyStateStore.class).close(); + injector.getInstance(DatabaseEnvironment.class).stop(); + } - private void processForCount(int messageCount) { - for (int i = 0; i < messageCount; i++) { - processNext(); - } - } + private void processForCount(int messageCount) { + for (int i = 0; i < messageCount; i++) { + processNext(); + } + } - private Timed processNext() { - final var msg = this.network.nextMessage(); - final var nodeIndex = msg.value().channelId().receiverIndex(); - final var injector = this.nodes.get(nodeIndex); - withThreadCtx(injector, () -> - injector.getInstance(DeterministicProcessor.class) - .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()) - ); - return msg; - } + private Timed processNext() { + final var msg = this.network.nextMessage(); + final var nodeIndex = msg.value().channelId().receiverIndex(); + final var injector = this.nodes.get(nodeIndex); + withThreadCtx( + injector, + () -> + injector + .getInstance(DeterministicProcessor.class) + .handleMessage( + msg.value().origin(), msg.value().message(), msg.value().typeLiteral())); + return msg; + } - private void withThreadCtx(Injector injector, Runnable r) { - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - r.run(); - } finally { - ThreadContext.remove("self"); - } - } + private void withThreadCtx(Injector injector, Runnable r) { + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + r.run(); + } finally { + ThreadContext.remove("self"); + } + } - private long getCounter(int nodeIndex, CounterType counterType) { - return this.nodes.get(nodeIndex).getInstance(SystemCounters.class).get(counterType); - } + private long getCounter(int nodeIndex, CounterType counterType) { + return this.nodes.get(nodeIndex).getInstance(SystemCounters.class).get(counterType); + } - private void dispatch(int nodeIndex, Class clazz, T event) { - network.createSender(nodeIndex).getDispatcher(clazz).dispatch(event); - } + private void dispatch(int nodeIndex, Class clazz, T event) { + network.createSender(nodeIndex).getDispatcher(clazz).dispatch(event); + } - @Test - public void full_node_should_relay_mempool_messages_so_they_can_be_processed_by_validator() { - dispatch(MEMPOOL_FILLER_NODE, MempoolFillerUpdate.class, MempoolFillerUpdate.enable(500, true)); - processForCount(100000); - dispatch(MEMPOOL_FILLER_NODE, MempoolFillerUpdate.class, MempoolFillerUpdate.disable()); - processForCount(100000); + @Test + public void full_node_should_relay_mempool_messages_so_they_can_be_processed_by_validator() { + dispatch(MEMPOOL_FILLER_NODE, MempoolFillerUpdate.class, MempoolFillerUpdate.enable(500, true)); + processForCount(100000); + dispatch(MEMPOOL_FILLER_NODE, MempoolFillerUpdate.class, MempoolFillerUpdate.disable()); + processForCount(100000); - // assert that validators have an empty mempool, but not the full nodes - this.validators.forEach(n -> assertEquals(0L, getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE))); - this.fullNodes.forEach(n -> assertTrue(getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE) >= 1L)); + // assert that validators have an empty mempool, but not the full nodes + this.validators.forEach(n -> assertEquals(0L, getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE))); + this.fullNodes.forEach(n -> assertTrue(getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE) >= 1L)); - // trigger mempool relay on the full nodes and process some more messages - this.fullNodes.forEach(n -> dispatch(n, MempoolRelayTrigger.class, MempoolRelayTrigger.create())); - processForCount(10000); + // trigger mempool relay on the full nodes and process some more messages + this.fullNodes.forEach( + n -> dispatch(n, MempoolRelayTrigger.class, MempoolRelayTrigger.create())); + processForCount(10000); - // at this point all mempools should be empty - this.validators.forEach(n -> assertEquals(0L, getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE))); - this.fullNodes.forEach(n -> assertEquals(0L, getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE))); - } + // at this point all mempools should be empty + this.validators.forEach(n -> assertEquals(0L, getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE))); + this.fullNodes.forEach(n -> assertEquals(0L, getCounter(n, CounterType.MEMPOOL_CURRENT_SIZE))); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/OneNodeAlwaysAliveSafetyTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/OneNodeAlwaysAliveSafetyTest.java index c62441b902..ff655935ca 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/OneNodeAlwaysAliveSafetyTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/OneNodeAlwaysAliveSafetyTest.java @@ -64,29 +64,6 @@ package com.radixdlt.integration.recovery; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.environment.Environment; -import com.radixdlt.environment.EventProcessorOnDispatch; -import com.radixdlt.environment.deterministic.DeterministicProcessor; -import com.radixdlt.mempool.MempoolConfig; -import com.radixdlt.application.system.FeeTable; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; -import com.radixdlt.utils.KeyComparator; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.ThreadContext; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; @@ -95,6 +72,8 @@ import com.google.inject.TypeLiteral; import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.PersistedNodeForTestingModule; +import com.radixdlt.application.system.FeeTable; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.bft.BFTCommittedUpdate; import com.radixdlt.consensus.bft.BFTNode; @@ -104,8 +83,11 @@ import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.consensus.sync.GetVerticesRequest; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.environment.Environment; import com.radixdlt.environment.EventProcessor; +import com.radixdlt.environment.EventProcessorOnDispatch; import com.radixdlt.environment.ProcessOnDispatch; +import com.radixdlt.environment.deterministic.DeterministicProcessor; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.environment.deterministic.network.MessageSelector; @@ -113,13 +95,19 @@ import com.radixdlt.integration.distributed.deterministic.NodeEvents.NodeEventProcessor; import com.radixdlt.integration.distributed.deterministic.NodeEventsModule; import com.radixdlt.integration.distributed.deterministic.SafetyCheckerModule; +import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; +import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; import com.radixdlt.sync.messages.local.LocalSyncRequest; - +import com.radixdlt.utils.KeyComparator; +import io.reactivex.rxjava3.schedulers.Timed; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -130,219 +118,231 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; - -import io.reactivex.rxjava3.schedulers.Timed; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.ThreadContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class OneNodeAlwaysAliveSafetyTest { - private static final Logger logger = LogManager.getLogger(); - - @Parameters - public static Collection numNodes() { - return List.of(new Object[][]{ - {5} - }); - } - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - private DeterministicNetwork network; - private List> nodeCreators; - private List nodes = new ArrayList<>(); - private final List nodeKeys; - - @Inject - private NodeEvents nodeEvents; - - private int lastNodeToCommit; - - public OneNodeAlwaysAliveSafetyTest(int numNodes) { - this.nodeKeys = Stream.generate(ECKeyPair::generateNew).limit(numNodes) - .sorted(Comparator.comparing(ECKeyPair::getPublicKey, KeyComparator.instance())) - .collect(Collectors.toList()); - } - - @Before - public void setup() { - List allNodes = nodeKeys.stream() - .map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); - - this.network = new DeterministicNetwork( - allNodes, - MessageSelector.firstSelector(), - (message, queue) -> message.message() instanceof GetVerticesRequest - || message.message() instanceof LocalSyncRequest - ); - - Guice.createInjector( - new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { }).toInstance(allNodes); - } - - @ProvidesIntoSet - public NodeEventProcessor updateChecker() { - return new NodeEventProcessor<>( - ViewQuorumReached.class, - (node, viewQuorumReached) -> { - if (viewQuorumReached.votingResult() instanceof FormedQC - && ((FormedQC) viewQuorumReached.votingResult()).getQC().getCommitted().isPresent()) { - lastNodeToCommit = network.lookup(node); - } - } - ); - } - }, - new SafetyCheckerModule(), - new NodeEventsModule() - ).injectMembers(this); - - this.nodeCreators = nodeKeys.stream() - .>map(k -> () -> createRunner(k, allNodes)) - .collect(Collectors.toList()); - - for (Supplier nodeCreator : nodeCreators) { - this.nodes.add(nodeCreator.get()); - } - } - - private void stopDatabase(Injector injector) { - injector.getInstance(BerkeleyLedgerEntryStore.class).close(); - injector.getInstance(PersistentSafetyStateStore.class).close(); - injector.getInstance(DatabaseEnvironment.class).stop(); - } - - @After - public void teardown() { - this.nodes.forEach(this::stopDatabase); - } - - private Injector createRunner(ECKeyPair ecKeyPair, List allNodes) { - return Guice.createInjector( - new MockedGenesisModule( - nodeKeys.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), - Amount.ofTokens(1000000), - Amount.ofTokens(10000) - ), - MempoolConfig.asModule(10, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.noFees(), - 1024 * 1024, - OptionalInt.of(50), - 88, - 2, - Amount.ofTokens(10), - 1, - Amount.ofTokens(10), - 9800, - 10 - )), - new ForksModule(), - new PersistedNodeForTestingModule(), - new AbstractModule() { - @Override - protected void configure() { - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); - bind(new TypeLiteral>() { }).toInstance(allNodes); - bind(PeersView.class).toInstance(Stream::of); - bind(Environment.class).toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); - bindConstant().annotatedWith(DatabaseLocation.class) - .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); - } - - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor committedUpdateEventProcessor(@Self BFTNode node) { - return nodeEvents.processor(node, BFTCommittedUpdate.class); - } - - @ProvidesIntoSet - private EventProcessorOnDispatch viewQuorumReachedEventProcessor(@Self BFTNode node) { - return nodeEvents.processorOnDispatch(node, ViewQuorumReached.class); - } - } - ); - } - - private void restartNode(int index) { - this.network.dropMessages(m -> m.channelId().receiverIndex() == index); - Injector injector = nodeCreators.get(index).get(); - this.nodes.set(index, injector); - } - - private void startNode(int index) { - Injector injector = nodes.get(index); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - injector.getInstance(DeterministicProcessor.class).start(); - } finally { - ThreadContext.remove("self"); - } - } - - private void processNext() { - Timed msg = this.network.nextMessage(); - logger.debug("Processing message {}", msg); - - int nodeIndex = msg.value().channelId().receiverIndex(); - Injector injector = this.nodes.get(nodeIndex); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - injector.getInstance(DeterministicProcessor.class) - .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); - } finally { - ThreadContext.remove("self"); - } - } - - private void processUntilNextCommittedUpdate() { - lastNodeToCommit = -1; - - while (lastNodeToCommit == -1) { - processNext(); - } - } - - private void processForCount(int messageCount) { - for (int i = 0; i < messageCount; i++) { - processNext(); - } - } - - @Test - public void dropper_and_crasher_adversares_should_not_cause_safety_failures() { - // Start - for (int i = 0; i < nodes.size(); i++) { - this.startNode(i); - } - - // Drop first proposal so view 2 will be committed - this.network.dropMessages(m -> m.message() instanceof Proposal); - - // process until view 2 committed - this.processUntilNextCommittedUpdate(); - - // Restart all except last committed - logger.info("Restarting..."); - for (int i = 0; i < nodes.size(); i++) { - if (i != this.lastNodeToCommit) { - this.restartNode(i); - } - } - for (int i = 0; i < nodes.size(); i++) { - if (i != this.lastNodeToCommit) { - this.startNode(i); - } - } - - // If nodes restart with correct safety precautions then view 1 should be skipped - // otherwise, this will cause failure - this.processForCount(5000); - } + private static final Logger logger = LogManager.getLogger(); + + @Parameters + public static Collection numNodes() { + return List.of(new Object[][] {{5}}); + } + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + private DeterministicNetwork network; + private List> nodeCreators; + private List nodes = new ArrayList<>(); + private final List nodeKeys; + + @Inject private NodeEvents nodeEvents; + + private int lastNodeToCommit; + + public OneNodeAlwaysAliveSafetyTest(int numNodes) { + this.nodeKeys = + Stream.generate(ECKeyPair::generateNew) + .limit(numNodes) + .sorted(Comparator.comparing(ECKeyPair::getPublicKey, KeyComparator.instance())) + .collect(Collectors.toList()); + } + + @Before + public void setup() { + List allNodes = + nodeKeys.stream().map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); + + this.network = + new DeterministicNetwork( + allNodes, + MessageSelector.firstSelector(), + (message, queue) -> + message.message() instanceof GetVerticesRequest + || message.message() instanceof LocalSyncRequest); + + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}).toInstance(allNodes); + } + + @ProvidesIntoSet + public NodeEventProcessor updateChecker() { + return new NodeEventProcessor<>( + ViewQuorumReached.class, + (node, viewQuorumReached) -> { + if (viewQuorumReached.votingResult() instanceof FormedQC + && ((FormedQC) viewQuorumReached.votingResult()) + .getQC() + .getCommitted() + .isPresent()) { + lastNodeToCommit = network.lookup(node); + } + }); + } + }, + new SafetyCheckerModule(), + new NodeEventsModule()) + .injectMembers(this); + + this.nodeCreators = + nodeKeys.stream() + .>map(k -> () -> createRunner(k, allNodes)) + .collect(Collectors.toList()); + + for (Supplier nodeCreator : nodeCreators) { + this.nodes.add(nodeCreator.get()); + } + } + + private void stopDatabase(Injector injector) { + injector.getInstance(BerkeleyLedgerEntryStore.class).close(); + injector.getInstance(PersistentSafetyStateStore.class).close(); + injector.getInstance(DatabaseEnvironment.class).stop(); + } + + @After + public void teardown() { + this.nodes.forEach(this::stopDatabase); + } + + private Injector createRunner(ECKeyPair ecKeyPair, List allNodes) { + return Guice.createInjector( + new MockedGenesisModule( + nodeKeys.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), + Amount.ofTokens(1000000), + Amount.ofTokens(10000)), + MempoolConfig.asModule(10, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.noFees(), + 1024 * 1024, + OptionalInt.of(50), + 88, + 2, + Amount.ofTokens(10), + 1, + Amount.ofTokens(10), + 9800, + 10)), + new ForksModule(), + new PersistedNodeForTestingModule(), + new AbstractModule() { + @Override + protected void configure() { + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); + bind(new TypeLiteral>() {}).toInstance(allNodes); + bind(PeersView.class).toInstance(Stream::of); + bind(Environment.class) + .toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); + } + + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor committedUpdateEventProcessor( + @Self BFTNode node) { + return nodeEvents.processor(node, BFTCommittedUpdate.class); + } + + @ProvidesIntoSet + private EventProcessorOnDispatch viewQuorumReachedEventProcessor(@Self BFTNode node) { + return nodeEvents.processorOnDispatch(node, ViewQuorumReached.class); + } + }); + } + + private void restartNode(int index) { + this.network.dropMessages(m -> m.channelId().receiverIndex() == index); + Injector injector = nodeCreators.get(index).get(); + this.nodes.set(index, injector); + } + + private void startNode(int index) { + Injector injector = nodes.get(index); + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + injector.getInstance(DeterministicProcessor.class).start(); + } finally { + ThreadContext.remove("self"); + } + } + + private void processNext() { + Timed msg = this.network.nextMessage(); + logger.debug("Processing message {}", msg); + + int nodeIndex = msg.value().channelId().receiverIndex(); + Injector injector = this.nodes.get(nodeIndex); + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + injector + .getInstance(DeterministicProcessor.class) + .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); + } finally { + ThreadContext.remove("self"); + } + } + + private void processUntilNextCommittedUpdate() { + lastNodeToCommit = -1; + + while (lastNodeToCommit == -1) { + processNext(); + } + } + + private void processForCount(int messageCount) { + for (int i = 0; i < messageCount; i++) { + processNext(); + } + } + + @Test + public void dropper_and_crasher_adversares_should_not_cause_safety_failures() { + // Start + for (int i = 0; i < nodes.size(); i++) { + this.startNode(i); + } + + // Drop first proposal so view 2 will be committed + this.network.dropMessages(m -> m.message() instanceof Proposal); + + // process until view 2 committed + this.processUntilNextCommittedUpdate(); + + // Restart all except last committed + logger.info("Restarting..."); + for (int i = 0; i < nodes.size(); i++) { + if (i != this.lastNodeToCommit) { + this.restartNode(i); + } + } + for (int i = 0; i < nodes.size(); i++) { + if (i != this.lastNodeToCommit) { + this.startNode(i); + } + } + + // If nodes restart with correct safety precautions then view 1 should be skipped + // otherwise, this will cause failure + this.processForCount(5000); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/RecoveryLivenessTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/RecoveryLivenessTest.java index 719d2a0821..ad6a07c4c0 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/RecoveryLivenessTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/recovery/RecoveryLivenessTest.java @@ -64,38 +64,19 @@ package com.radixdlt.integration.recovery; -import com.google.common.collect.ClassToInstanceMap; -import com.google.inject.Provides; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.environment.Environment; -import com.radixdlt.environment.deterministic.DeterministicProcessor; -import com.radixdlt.environment.deterministic.LastEventsModule; -import com.radixdlt.mempool.MempoolConfig; -import com.radixdlt.application.system.FeeTable; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; -import com.radixdlt.utils.KeyComparator; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.ThreadContext; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; +import static org.assertj.core.api.Assertions.assertThat; +import com.google.common.collect.ClassToInstanceMap; import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; +import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.radixdlt.PersistedNodeForTestingModule; +import com.radixdlt.application.system.FeeTable; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; import com.radixdlt.consensus.bft.View; @@ -103,19 +84,28 @@ import com.radixdlt.consensus.epoch.EpochViewUpdate; import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.environment.Environment; import com.radixdlt.environment.EventDispatcher; +import com.radixdlt.environment.deterministic.DeterministicProcessor; +import com.radixdlt.environment.deterministic.LastEventsModule; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageQueue; import com.radixdlt.environment.deterministic.network.MessageSelector; +import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; +import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; import com.radixdlt.sync.messages.local.SyncCheckTrigger; - +import com.radixdlt.utils.KeyComparator; +import io.reactivex.rxjava3.schedulers.Timed; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -127,310 +117,326 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.ThreadContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; -import io.reactivex.rxjava3.schedulers.Timed; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Various liveness+recovery tests - */ +/** Various liveness+recovery tests */ @RunWith(Parameterized.class) public class RecoveryLivenessTest { - private static final Logger logger = LogManager.getLogger(); - - @Parameters - public static Collection numNodes() { - return List.of(new Object[][] { - {1, 88L}, {2, 88L}, {3, 88L}, {4, 88L}, - {2, 1L}, {10, 100L} - }); - } - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - private DeterministicNetwork network; - private List> nodeCreators; - private List nodes = new ArrayList<>(); - private final ImmutableList nodeKeys; - private final long epochCeilingView; - private MessageMutator messageMutator; - - public RecoveryLivenessTest(int numNodes, long epochCeilingView) { - this.nodeKeys = Stream.generate(ECKeyPair::generateNew) - .limit(numNodes) - .sorted(Comparator.comparing(ECKeyPair::getPublicKey, KeyComparator.instance())) - .collect(ImmutableList.toImmutableList()); - this.epochCeilingView = epochCeilingView; - } - - @Before - public void setup() { - this.messageMutator = MessageMutator.nothing(); - this.network = new DeterministicNetwork( - nodeKeys.stream().map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()), - MessageSelector.firstSelector(), - this::mutate - ); - - List allNodes = nodeKeys.stream() - .map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); - - this.nodeCreators = nodeKeys.stream() - .>map(k -> () -> createRunner(k, allNodes)) - .collect(Collectors.toList()); - - for (Supplier nodeCreator : nodeCreators) { - this.nodes.add(nodeCreator.get()); - } - this.nodes.forEach(i -> i.getInstance(DeterministicProcessor.class).start()); - } - - boolean mutate(ControlledMessage message, MessageQueue queue) { - return messageMutator.mutate(message, queue); - } - - private void stopDatabase(Injector injector) { - injector.getInstance(BerkeleyLedgerEntryStore.class).close(); - injector.getInstance(PersistentSafetyStateStore.class).close(); - injector.getInstance(DatabaseEnvironment.class).stop(); - } - - @After - public void teardown() { - this.nodes.forEach(this::stopDatabase); - } - - private Injector createRunner(ECKeyPair ecKeyPair, List allNodes) { - return Guice.createInjector( - new MockedGenesisModule( - nodeKeys.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), - Amount.ofTokens(100000), - Amount.ofTokens(1000) - ), - MempoolConfig.asModule(10, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.noFees(), - 1024 * 1024, - OptionalInt.of(50), - epochCeilingView, - 2, - Amount.ofTokens(10), - 1, - Amount.ofTokens(10), - 9800, - 10 - )), - new ForksModule(), - new PersistedNodeForTestingModule(), - new LastEventsModule(EpochViewUpdate.class), - new AbstractModule() { - @Override - protected void configure() { - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); - bind(new TypeLiteral>() { }).toInstance(allNodes); - bind(Environment.class).toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); - bindConstant().annotatedWith(DatabaseLocation.class) - .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); - } - - @Provides - private PeersView peersView(@Self BFTNode self) { - return () -> allNodes.stream() - .filter(n -> !self.equals(n)) - .map(PeersView.PeerInfo::fromBftNode); - } - } - ); - } - - private void restartNode(int index) { - this.network.dropMessages(m -> m.channelId().receiverIndex() == index); - Injector injector = nodeCreators.get(index).get(); - stopDatabase(this.nodes.set(index, injector)); - withThreadCtx(injector, () -> injector.getInstance(DeterministicProcessor.class).start()); - } - - private void initSync() { - for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { - final var injector = nodeCreators.get(nodeIndex).get(); - withThreadCtx(injector, () -> { - // need to manually init sync check, normally sync runner schedules it periodically - injector.getInstance(new Key>() { }).dispatch(SyncCheckTrigger.create()); - }); - } - } - - private void withThreadCtx(Injector injector, Runnable r) { - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - r.run(); - } finally { - ThreadContext.remove("self"); - } - } - - private Timed processNext() { - Timed msg = this.network.nextMessage(); - logger.debug("Processing message {}", msg); - - int nodeIndex = msg.value().channelId().receiverIndex(); - Injector injector = this.nodes.get(nodeIndex); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - injector.getInstance(DeterministicProcessor.class) - .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); - } finally { - ThreadContext.remove("self"); - } - - return msg; - } - - private Optional lastCommitViewEmitted() { - return network.allMessages().stream() - .filter(msg -> msg.message() instanceof EpochViewUpdate) - .map(msg -> (EpochViewUpdate) msg.message()) - .map(e -> new EpochView(e.getEpoch(), e.getViewUpdate().getHighQC().highestCommittedQC().getView())) - .max(Comparator.naturalOrder()); - } - - private EpochView latestEpochView() { - return this.nodes.stream() - .map(i -> i.getInstance(Key.get(new TypeLiteral>() { })).getInstance(EpochViewUpdate.class)) - .map(e -> e == null ? new EpochView(0, View.genesis()) : e.getEpochView()) - .max(Comparator.naturalOrder()).orElse(new EpochView(0, View.genesis())); - } - - private int processUntilNextCommittedEmitted(int maxSteps) { - EpochView lastCommitted = this.lastCommitViewEmitted().orElse(new EpochView(0, View.genesis())); - int count = 0; - int senderIndex; - do { - if (count > maxSteps) { - throw new IllegalStateException("Already lost liveness"); - } - - Timed msg = processNext(); - senderIndex = msg.value().channelId().senderIndex(); - count++; - } while (this.lastCommitViewEmitted().stream().noneMatch(v -> v.compareTo(lastCommitted) > 0)); - - return senderIndex; - } - - private void processForCount(int messageCount) { - for (int i = 0; i < messageCount; i++) { - processNext(); - } - } - - /** - * Given that one validator is always alive means that that validator will - * always have the latest committed vertex which the validator can sync - * others with. - */ - @Test - public void liveness_check_when_restart_all_but_one_node() { - EpochView epochView = this.latestEpochView(); - - for (int restart = 0; restart < 5; restart++) { - processForCount(5000); - - EpochView nextEpochView = latestEpochView(); - assertThat(nextEpochView).isGreaterThan(epochView); - epochView = nextEpochView; - - logger.info("Restarting " + restart); - for (int nodeIndex = 1; nodeIndex < nodes.size(); nodeIndex++) { - restartNode(nodeIndex); - } - initSync(); - } - - assertThat(epochView.getEpoch()).isGreaterThan(1); - } - - @Test - public void liveness_check_when_restart_node_on_view_update_with_commit() { - EpochView epochView = this.latestEpochView(); - - for (int restart = 0; restart < 5; restart++) { - processForCount(5000); - - EpochView nextEpochView = latestEpochView(); - assertThat(nextEpochView).isGreaterThan(epochView); - epochView = nextEpochView; - - int nodeToRestart = processUntilNextCommittedEmitted(5000); - - logger.info("Restarting " + restart); - for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { - if (nodeIndex != (nodeToRestart + 1) % nodes.size()) { - restartNode(nodeIndex); - } - } - initSync(); - } - - assertThat(epochView.getEpoch()).isGreaterThan(1); - } - - @Test - public void liveness_check_when_restart_all_nodes() { - EpochView epochView = this.latestEpochView(); - - for (int restart = 0; restart < 5; restart++) { - processForCount(5000); - - EpochView nextEpochView = latestEpochView(); - assertThat(nextEpochView).isGreaterThan(epochView); - epochView = nextEpochView; - - logger.info("Restarting " + restart); - for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { - restartNode(nodeIndex); - } - initSync(); - } - - assertThat(epochView.getEpoch()).isGreaterThan(1); - } - - /** - * This test tests for recovery when there is a vertex chain > 3 due to timeouts. - * Probably won't be an issue once timeout certificates implemented. - */ - @Test - public void liveness_check_when_restart_all_nodes_and_f_nodes_down() { - int f = (nodes.size() - 1) / 3; - if (f <= 0) { - // if f <= 0, this is equivalent to liveness_check_when_restart_all_nodes(); - return; - } - - this.messageMutator = (message, queue) -> message.channelId().receiverIndex() < f || message.channelId().senderIndex() < f; - - EpochView epochView = this.latestEpochView(); - - for (int restart = 0; restart < 5; restart++) { - processForCount(5000); - - EpochView nextEpochView = latestEpochView(); - assertThat(nextEpochView).isGreaterThan(epochView); - epochView = nextEpochView; - - logger.info("Restarting " + restart); - for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { - restartNode(nodeIndex); - } - initSync(); - } - - assertThat(epochView.getEpoch()).isGreaterThan(1); - } + private static final Logger logger = LogManager.getLogger(); + + @Parameters + public static Collection numNodes() { + return List.of(new Object[][] {{1, 88L}, {2, 88L}, {3, 88L}, {4, 88L}, {2, 1L}, {10, 100L}}); + } + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + private DeterministicNetwork network; + private List> nodeCreators; + private List nodes = new ArrayList<>(); + private final ImmutableList nodeKeys; + private final long epochCeilingView; + private MessageMutator messageMutator; + + public RecoveryLivenessTest(int numNodes, long epochCeilingView) { + this.nodeKeys = + Stream.generate(ECKeyPair::generateNew) + .limit(numNodes) + .sorted(Comparator.comparing(ECKeyPair::getPublicKey, KeyComparator.instance())) + .collect(ImmutableList.toImmutableList()); + this.epochCeilingView = epochCeilingView; + } + + @Before + public void setup() { + this.messageMutator = MessageMutator.nothing(); + this.network = + new DeterministicNetwork( + nodeKeys.stream() + .map(k -> BFTNode.create(k.getPublicKey())) + .collect(Collectors.toList()), + MessageSelector.firstSelector(), + this::mutate); + + List allNodes = + nodeKeys.stream().map(k -> BFTNode.create(k.getPublicKey())).collect(Collectors.toList()); + + this.nodeCreators = + nodeKeys.stream() + .>map(k -> () -> createRunner(k, allNodes)) + .collect(Collectors.toList()); + + for (Supplier nodeCreator : nodeCreators) { + this.nodes.add(nodeCreator.get()); + } + this.nodes.forEach(i -> i.getInstance(DeterministicProcessor.class).start()); + } + + boolean mutate(ControlledMessage message, MessageQueue queue) { + return messageMutator.mutate(message, queue); + } + + private void stopDatabase(Injector injector) { + injector.getInstance(BerkeleyLedgerEntryStore.class).close(); + injector.getInstance(PersistentSafetyStateStore.class).close(); + injector.getInstance(DatabaseEnvironment.class).stop(); + } + + @After + public void teardown() { + this.nodes.forEach(this::stopDatabase); + } + + private Injector createRunner(ECKeyPair ecKeyPair, List allNodes) { + return Guice.createInjector( + new MockedGenesisModule( + nodeKeys.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), + Amount.ofTokens(100000), + Amount.ofTokens(1000)), + MempoolConfig.asModule(10, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.noFees(), + 1024 * 1024, + OptionalInt.of(50), + epochCeilingView, + 2, + Amount.ofTokens(10), + 1, + Amount.ofTokens(10), + 9800, + 10)), + new ForksModule(), + new PersistedNodeForTestingModule(), + new LastEventsModule(EpochViewUpdate.class), + new AbstractModule() { + @Override + protected void configure() { + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); + bind(new TypeLiteral>() {}).toInstance(allNodes); + bind(Environment.class) + .toInstance(network.createSender(BFTNode.create(ecKeyPair.getPublicKey()))); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath() + "/" + ecKeyPair.getPublicKey().toHex()); + } + + @Provides + private PeersView peersView(@Self BFTNode self) { + return () -> + allNodes.stream().filter(n -> !self.equals(n)).map(PeersView.PeerInfo::fromBftNode); + } + }); + } + + private void restartNode(int index) { + this.network.dropMessages(m -> m.channelId().receiverIndex() == index); + Injector injector = nodeCreators.get(index).get(); + stopDatabase(this.nodes.set(index, injector)); + withThreadCtx(injector, () -> injector.getInstance(DeterministicProcessor.class).start()); + } + + private void initSync() { + for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { + final var injector = nodeCreators.get(nodeIndex).get(); + withThreadCtx( + injector, + () -> { + // need to manually init sync check, normally sync runner schedules it periodically + injector + .getInstance(new Key>() {}) + .dispatch(SyncCheckTrigger.create()); + }); + } + } + + private void withThreadCtx(Injector injector, Runnable r) { + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + r.run(); + } finally { + ThreadContext.remove("self"); + } + } + + private Timed processNext() { + Timed msg = this.network.nextMessage(); + logger.debug("Processing message {}", msg); + + int nodeIndex = msg.value().channelId().receiverIndex(); + Injector injector = this.nodes.get(nodeIndex); + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + injector + .getInstance(DeterministicProcessor.class) + .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); + } finally { + ThreadContext.remove("self"); + } + + return msg; + } + + private Optional lastCommitViewEmitted() { + return network.allMessages().stream() + .filter(msg -> msg.message() instanceof EpochViewUpdate) + .map(msg -> (EpochViewUpdate) msg.message()) + .map( + e -> + new EpochView( + e.getEpoch(), e.getViewUpdate().getHighQC().highestCommittedQC().getView())) + .max(Comparator.naturalOrder()); + } + + private EpochView latestEpochView() { + return this.nodes.stream() + .map( + i -> + i.getInstance(Key.get(new TypeLiteral>() {})) + .getInstance(EpochViewUpdate.class)) + .map(e -> e == null ? new EpochView(0, View.genesis()) : e.getEpochView()) + .max(Comparator.naturalOrder()) + .orElse(new EpochView(0, View.genesis())); + } + + private int processUntilNextCommittedEmitted(int maxSteps) { + EpochView lastCommitted = this.lastCommitViewEmitted().orElse(new EpochView(0, View.genesis())); + int count = 0; + int senderIndex; + do { + if (count > maxSteps) { + throw new IllegalStateException("Already lost liveness"); + } + + Timed msg = processNext(); + senderIndex = msg.value().channelId().senderIndex(); + count++; + } while (this.lastCommitViewEmitted().stream().noneMatch(v -> v.compareTo(lastCommitted) > 0)); + + return senderIndex; + } + + private void processForCount(int messageCount) { + for (int i = 0; i < messageCount; i++) { + processNext(); + } + } + + /** + * Given that one validator is always alive means that that validator will always have the latest + * committed vertex which the validator can sync others with. + */ + @Test + public void liveness_check_when_restart_all_but_one_node() { + EpochView epochView = this.latestEpochView(); + + for (int restart = 0; restart < 5; restart++) { + processForCount(5000); + + EpochView nextEpochView = latestEpochView(); + assertThat(nextEpochView).isGreaterThan(epochView); + epochView = nextEpochView; + + logger.info("Restarting " + restart); + for (int nodeIndex = 1; nodeIndex < nodes.size(); nodeIndex++) { + restartNode(nodeIndex); + } + initSync(); + } + + assertThat(epochView.getEpoch()).isGreaterThan(1); + } + + @Test + public void liveness_check_when_restart_node_on_view_update_with_commit() { + EpochView epochView = this.latestEpochView(); + + for (int restart = 0; restart < 5; restart++) { + processForCount(5000); + + EpochView nextEpochView = latestEpochView(); + assertThat(nextEpochView).isGreaterThan(epochView); + epochView = nextEpochView; + + int nodeToRestart = processUntilNextCommittedEmitted(5000); + + logger.info("Restarting " + restart); + for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { + if (nodeIndex != (nodeToRestart + 1) % nodes.size()) { + restartNode(nodeIndex); + } + } + initSync(); + } + + assertThat(epochView.getEpoch()).isGreaterThan(1); + } + + @Test + public void liveness_check_when_restart_all_nodes() { + EpochView epochView = this.latestEpochView(); + + for (int restart = 0; restart < 5; restart++) { + processForCount(5000); + + EpochView nextEpochView = latestEpochView(); + assertThat(nextEpochView).isGreaterThan(epochView); + epochView = nextEpochView; + + logger.info("Restarting " + restart); + for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { + restartNode(nodeIndex); + } + initSync(); + } + + assertThat(epochView.getEpoch()).isGreaterThan(1); + } + + /** + * This test tests for recovery when there is a vertex chain > 3 due to timeouts. Probably won't + * be an issue once timeout certificates implemented. + */ + @Test + public void liveness_check_when_restart_all_nodes_and_f_nodes_down() { + int f = (nodes.size() - 1) / 3; + if (f <= 0) { + // if f <= 0, this is equivalent to liveness_check_when_restart_all_nodes(); + return; + } + + this.messageMutator = + (message, queue) -> + message.channelId().receiverIndex() < f || message.channelId().senderIndex() < f; + + EpochView epochView = this.latestEpochView(); + + for (int restart = 0; restart < 5; restart++) { + processForCount(5000); + + EpochView nextEpochView = latestEpochView(); + assertThat(nextEpochView).isGreaterThan(epochView); + epochView = nextEpochView; + + logger.info("Restarting " + restart); + for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { + restartNode(nodeIndex); + } + initSync(); + } + + assertThat(epochView.getEpoch()).isGreaterThan(1); + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/serialization/MessageCentralFuzzyTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/serialization/MessageCentralFuzzyTest.java index a89e8cbbc4..e87ec6b9ef 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/serialization/MessageCentralFuzzyTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/serialization/MessageCentralFuzzyTest.java @@ -1,9 +1,76 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.integration.serialization; -import org.junit.Test; -import org.radix.network.messages.PeerPingMessage; -import org.radix.network.messaging.Message; -import org.radix.time.Time; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.DefaultSerialization; import com.radixdlt.counters.SystemCountersImpl; @@ -20,113 +87,110 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.Serialization; import com.radixdlt.utils.Compress; - +import io.reactivex.rxjava3.subjects.PublishSubject; import java.security.SecureRandom; import java.util.Comparator; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; - -import io.reactivex.rxjava3.subjects.PublishSubject; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; +import org.radix.network.messages.PeerPingMessage; +import org.radix.network.messaging.Message; +import org.radix.time.Time; public class MessageCentralFuzzyTest { - private static final int MIN_MESSAGE_LEN = 1; - private static final int MAX_MESSAGE_LEN = 1024 * 1024; - private static final int NUM_TEST_MESSAGES = 1000; - - private final Random random = new SecureRandom(); - private final Serialization serialization = DefaultSerialization.getInstance(); - - @Test - @SuppressWarnings("unchecked") - public void fuzzy_messaged_are_not_accepted() throws Exception { - var inboundMessages = PublishSubject.create(); - var config = mock(MessageCentralConfiguration.class); - var peerControl = mock(PeerControl.class); - var peerManager = mock(PeerManager.class); - var queueFactory = mock(EventQueueFactory.class); - - when(config.messagingOutboundQueueMax(anyInt())).thenReturn(1); - when(config.messagingTimeToLive(anyLong())).thenReturn(30_000L); - when(peerManager.messages()).thenReturn(inboundMessages); - - when(queueFactory.createEventQueue(anyInt(), any(Comparator.class))) - .thenReturn(new SimplePriorityBlockingQueue<>(1, OutboundMessageEvent.comparator())); - - var messageCentral = new MessageCentralImpl( - config, - serialization, - peerManager, - Time::currentTimestamp, - queueFactory, - new SystemCountersImpl(), - () -> peerControl - ); - - var counter = new AtomicLong(0); - - var disposable = messageCentral.messagesOf(Message.class) - .subscribe(nextItem -> counter.incrementAndGet(), error -> fail(error.getMessage())); - - //Insert single valid message to ensure whole pipeline is working properly - emitSingleValidMessage(inboundMessages); - // Insert batch of randomly generated messages - emitFuzzyMessages(inboundMessages); - - disposable.dispose(); - - // Ensure that only one (valid) message passed through - assertEquals(1L, counter.get()); - } - - private void emitSingleValidMessage(PublishSubject subject) { - try { - var bytes = Compress.compress(serialization.toDson(new PeerPingMessage(), DsonOutput.Output.WIRE)); - var valid = new InboundMessage(Time.currentTimestamp(), randomNodeId(), bytes); - subject.onNext(valid); - } catch (Exception e) { - // Ignore - } - } - - private void emitFuzzyMessages(PublishSubject subject) { - for (int i = 0; i < NUM_TEST_MESSAGES; i++) { - subject.onNext(generateFuzzyMessage()); - } - - subject.onComplete(); - } - - private InboundMessage generateFuzzyMessage() { - while (true) { - try { - var compressedMessage = Compress.compress(generateRandomBytes()); - return new InboundMessage(Time.currentTimestamp(), randomNodeId(), compressedMessage); - } catch (Exception e) { - // Ignore exception and generate new message - } - } - } - - private NodeId randomNodeId() { - return NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); - } - - private byte[] generateRandomBytes() { - var len = random.nextInt(MIN_MESSAGE_LEN, MAX_MESSAGE_LEN); - var result = new byte[len]; - - for (int i = 0; i < len; i++) { - result[i] = (byte) random.nextInt(0, 255); - } - - return result; - } + private static final int MIN_MESSAGE_LEN = 1; + private static final int MAX_MESSAGE_LEN = 1024 * 1024; + private static final int NUM_TEST_MESSAGES = 1000; + + private final Random random = new SecureRandom(); + private final Serialization serialization = DefaultSerialization.getInstance(); + + @Test + @SuppressWarnings("unchecked") + public void fuzzy_messaged_are_not_accepted() throws Exception { + var inboundMessages = PublishSubject.create(); + var config = mock(MessageCentralConfiguration.class); + var peerControl = mock(PeerControl.class); + var peerManager = mock(PeerManager.class); + var queueFactory = mock(EventQueueFactory.class); + + when(config.messagingOutboundQueueMax(anyInt())).thenReturn(1); + when(config.messagingTimeToLive(anyLong())).thenReturn(30_000L); + when(peerManager.messages()).thenReturn(inboundMessages); + + when(queueFactory.createEventQueue(anyInt(), any(Comparator.class))) + .thenReturn(new SimplePriorityBlockingQueue<>(1, OutboundMessageEvent.comparator())); + + var messageCentral = + new MessageCentralImpl( + config, + serialization, + peerManager, + Time::currentTimestamp, + queueFactory, + new SystemCountersImpl(), + () -> peerControl); + + var counter = new AtomicLong(0); + + var disposable = + messageCentral + .messagesOf(Message.class) + .subscribe(nextItem -> counter.incrementAndGet(), error -> fail(error.getMessage())); + + // Insert single valid message to ensure whole pipeline is working properly + emitSingleValidMessage(inboundMessages); + // Insert batch of randomly generated messages + emitFuzzyMessages(inboundMessages); + + disposable.dispose(); + + // Ensure that only one (valid) message passed through + assertEquals(1L, counter.get()); + } + + private void emitSingleValidMessage(PublishSubject subject) { + try { + var bytes = + Compress.compress(serialization.toDson(new PeerPingMessage(), DsonOutput.Output.WIRE)); + var valid = new InboundMessage(Time.currentTimestamp(), randomNodeId(), bytes); + subject.onNext(valid); + } catch (Exception e) { + // Ignore + } + } + + private void emitFuzzyMessages(PublishSubject subject) { + for (int i = 0; i < NUM_TEST_MESSAGES; i++) { + subject.onNext(generateFuzzyMessage()); + } + + subject.onComplete(); + } + + private InboundMessage generateFuzzyMessage() { + while (true) { + try { + var compressedMessage = Compress.compress(generateRandomBytes()); + return new InboundMessage(Time.currentTimestamp(), randomNodeId(), compressedMessage); + } catch (Exception e) { + // Ignore exception and generate new message + } + } + } + + private NodeId randomNodeId() { + return NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); + } + + private byte[] generateRandomBytes() { + var len = random.nextInt(MIN_MESSAGE_LEN, MAX_MESSAGE_LEN); + var result = new byte[len]; + + for (int i = 0; i < len; i++) { + result[i] = (byte) random.nextInt(0, 255); + } + + return result; + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/staking/UnstakingLockedTokensTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/staking/UnstakingLockedTokensTest.java index c7433a167f..0284e76ee1 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/staking/UnstakingLockedTokensTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/staking/UnstakingLockedTokensTest.java @@ -64,28 +64,17 @@ package com.radixdlt.integration.staking; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.atom.TxnConstructionRequest; -import com.radixdlt.environment.deterministic.SingleNodeDeterministicRunner; -import com.radixdlt.identifiers.AID; -import com.radixdlt.statecomputer.RadixEngineStateComputer; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.utils.PrivateKeys; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.atom.TxAction; import com.radixdlt.atom.TxBuilderException; +import com.radixdlt.atom.TxnConstructionRequest; import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atom.actions.TransferToken; import com.radixdlt.atom.actions.UnstakeTokens; @@ -96,151 +85,166 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.engine.RadixEngine; +import com.radixdlt.environment.deterministic.SingleNodeDeterministicRunner; +import com.radixdlt.identifiers.AID; import com.radixdlt.identifiers.REAddr; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.qualifier.LocalSigner; import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.REOutput; +import com.radixdlt.statecomputer.RadixEngineStateComputer; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; - +import com.radixdlt.utils.PrivateKeys; import java.util.Collection; import java.util.List; import java.util.Set; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class UnstakingLockedTokensTest { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][]{ - {1, 2, 3, false}, - {1, 2, 4, true}, - {3, 4, 5, false}, - {3, 4, 6, true}, - }); - } - - private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Inject - @LocalSigner - private HashSigner hashSigner; - @Inject @Self private ECPublicKey self; - @Inject private SingleNodeDeterministicRunner runner; - @Inject private RadixEngine radixEngine; - @Inject private RadixEngineStateComputer radixEngineStateComputer; - private final long stakingEpoch; - private final long unstakingEpoch; - private final long transferEpoch; - private final boolean shouldSucceed; - - public UnstakingLockedTokensTest(long stakingEpoch, long unstakingEpoch, long transferEpoch, boolean shouldSucceed) { - if (stakingEpoch < 1) { - throw new IllegalArgumentException(); - } - - this.stakingEpoch = stakingEpoch; - this.unstakingEpoch = unstakingEpoch; - this.transferEpoch = transferEpoch; - this.shouldSucceed = shouldSucceed; - } - - private Injector createInjector() { - return Guice.createInjector( - MempoolConfig.asModule(1000, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault()), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), - new MockedGenesisModule( - Set.of(TEST_KEY.getPublicKey()), - Amount.ofTokens(110), - Amount.ofTokens(100) - ), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } - - public REProcessedTxn waitForCommit(AID txnId) { - var committed = runner.runNextEventsThrough( - LedgerUpdate.class, - u -> { - var output = u.getStateComputerOutput().getInstance(REOutput.class); - return output.getProcessedTxns().stream().anyMatch(txn -> txn.getTxn().getId().equals(txnId)); - } - ); - - return committed.getStateComputerOutput().getInstance(REOutput.class).getProcessedTxns().stream() - .filter(t -> t.getTxn().getId().equals(txnId)) - .findFirst() - .orElseThrow(); - } - - public REProcessedTxn dispatchAndWaitForCommit(TxAction action) throws Exception { - var request = TxnConstructionRequest.create().action(action); - var txBuilder = radixEngine.construct(request.feePayer(REAddr.ofPubKeyAccount(self))); - var txn = txBuilder.signAndBuild(hashSigner::sign); - radixEngineStateComputer.addToMempool(txn); - return waitForCommit(txn.getId()); - } - - @Test - public void test_stake_unlocking() throws Exception { - createInjector().injectMembers(this); - - runner.start(); - - if (stakingEpoch > 1) { - runner.runNextEventsThrough( - LedgerUpdate.class, - e -> { - var epochChange = e.getStateComputerOutput().getInstance(EpochChange.class); - return epochChange != null && epochChange.getEpoch() == stakingEpoch; - } - ); - } - - var accountAddr = REAddr.ofPubKeyAccount(self); - var stakeTxn = dispatchAndWaitForCommit(new StakeTokens(accountAddr, self, Amount.ofTokens(10).toSubunits())); - runner.runNextEventsThrough( - LedgerUpdate.class, - e -> { - var epochChange = e.getStateComputerOutput().getInstance(EpochChange.class); - return epochChange != null && epochChange.getEpoch() == unstakingEpoch; - } - ); - var unstakeTxn = dispatchAndWaitForCommit(new UnstakeTokens(self, accountAddr, Amount.ofTokens(10).toSubunits())); - - if (transferEpoch > unstakingEpoch) { - runner.runNextEventsThrough( - LedgerUpdate.class, - e -> { - var epochChange = e.getStateComputerOutput().getInstance(EpochChange.class); - return epochChange != null && epochChange.getEpoch() == transferEpoch; - } - ); - } - - var otherAddr = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); - var transferAction = new TransferToken(REAddr.ofNativeToken(), accountAddr, otherAddr, Amount.ofTokens(10).toSubunits()); - - // Build transaction through radix engine - if (shouldSucceed) { - radixEngine.construct(transferAction); - } else { - assertThatThrownBy(() -> radixEngine.construct(transferAction)).isInstanceOf(TxBuilderException.class); - } - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + {1, 2, 3, false}, + {1, 2, 4, true}, + {3, 4, 5, false}, + {3, 4, 6, true}, + }); + } + + private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Inject @LocalSigner private HashSigner hashSigner; + @Inject @Self private ECPublicKey self; + @Inject private SingleNodeDeterministicRunner runner; + @Inject private RadixEngine radixEngine; + @Inject private RadixEngineStateComputer radixEngineStateComputer; + private final long stakingEpoch; + private final long unstakingEpoch; + private final long transferEpoch; + private final boolean shouldSucceed; + + public UnstakingLockedTokensTest( + long stakingEpoch, long unstakingEpoch, long transferEpoch, boolean shouldSucceed) { + if (stakingEpoch < 1) { + throw new IllegalArgumentException(); + } + + this.stakingEpoch = stakingEpoch; + this.unstakingEpoch = unstakingEpoch; + this.transferEpoch = transferEpoch; + this.shouldSucceed = shouldSucceed; + } + + private Injector createInjector() { + return Guice.createInjector( + MempoolConfig.asModule(1000, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault()), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), + new MockedGenesisModule( + Set.of(TEST_KEY.getPublicKey()), Amount.ofTokens(110), Amount.ofTokens(100)), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } + + public REProcessedTxn waitForCommit(AID txnId) { + var committed = + runner.runNextEventsThrough( + LedgerUpdate.class, + u -> { + var output = u.getStateComputerOutput().getInstance(REOutput.class); + return output.getProcessedTxns().stream() + .anyMatch(txn -> txn.getTxn().getId().equals(txnId)); + }); + + return committed + .getStateComputerOutput() + .getInstance(REOutput.class) + .getProcessedTxns() + .stream() + .filter(t -> t.getTxn().getId().equals(txnId)) + .findFirst() + .orElseThrow(); + } + + public REProcessedTxn dispatchAndWaitForCommit(TxAction action) throws Exception { + var request = TxnConstructionRequest.create().action(action); + var txBuilder = radixEngine.construct(request.feePayer(REAddr.ofPubKeyAccount(self))); + var txn = txBuilder.signAndBuild(hashSigner::sign); + radixEngineStateComputer.addToMempool(txn); + return waitForCommit(txn.getId()); + } + + @Test + public void test_stake_unlocking() throws Exception { + createInjector().injectMembers(this); + + runner.start(); + + if (stakingEpoch > 1) { + runner.runNextEventsThrough( + LedgerUpdate.class, + e -> { + var epochChange = e.getStateComputerOutput().getInstance(EpochChange.class); + return epochChange != null && epochChange.getEpoch() == stakingEpoch; + }); + } + + var accountAddr = REAddr.ofPubKeyAccount(self); + var stakeTxn = + dispatchAndWaitForCommit( + new StakeTokens(accountAddr, self, Amount.ofTokens(10).toSubunits())); + runner.runNextEventsThrough( + LedgerUpdate.class, + e -> { + var epochChange = e.getStateComputerOutput().getInstance(EpochChange.class); + return epochChange != null && epochChange.getEpoch() == unstakingEpoch; + }); + var unstakeTxn = + dispatchAndWaitForCommit( + new UnstakeTokens(self, accountAddr, Amount.ofTokens(10).toSubunits())); + + if (transferEpoch > unstakingEpoch) { + runner.runNextEventsThrough( + LedgerUpdate.class, + e -> { + var epochChange = e.getStateComputerOutput().getInstance(EpochChange.class); + return epochChange != null && epochChange.getEpoch() == transferEpoch; + }); + } + + var otherAddr = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); + var transferAction = + new TransferToken( + REAddr.ofNativeToken(), accountAddr, otherAddr, Amount.ofTokens(10).toSubunits()); + + // Build transaction through radix engine + if (shouldSucceed) { + radixEngine.construct(transferAction); + } else { + assertThatThrownBy(() -> radixEngine.construct(transferAction)) + .isInstanceOf(TxBuilderException.class); + } + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/LargeEpochChangeTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/LargeEpochChangeTest.java index 9851f86f52..bddb50385e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/LargeEpochChangeTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/LargeEpochChangeTest.java @@ -77,9 +77,9 @@ import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.validators.state.AllowDelegationFlag; +import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.application.validators.state.ValidatorMetaData; import com.radixdlt.application.validators.state.ValidatorOwnerCopy; -import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; import com.radixdlt.atom.Txn; import com.radixdlt.atom.TxnConstructionRequest; @@ -105,19 +105,13 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.LastStoredProof; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -126,193 +120,214 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.IntStream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class LargeEpochChangeTest { - private static final int NUM_ROUNDS = 10000; - private static final Logger logger = LogManager.getLogger(); - private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + private static final int NUM_ROUNDS = 10000; + private static final Logger logger = LogManager.getLogger(); + private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); + @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Inject - private RadixEngine sut; + @Inject private RadixEngine sut; - // FIXME: Hack, need this in order to cause provider for genesis to be stored - @Inject - @LastStoredProof - private LedgerProof ledgerProof; + // FIXME: Hack, need this in order to cause provider for genesis to be stored + @Inject @LastStoredProof private LedgerProof ledgerProof; - private Injector createInjector() { - return Guice.createInjector( - MempoolConfig.asModule(1000, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.create( - Amount.ofMicroTokens(200), // 0.0002XRD per byte fee - Map.of( - TokenResource.class, Amount.ofTokens(1000), // 1000XRD per resource - ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update - ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update - AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update - PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake - PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake - ) - ), - 1024 * 1024, - OptionalInt.of(50), // 50 Txns per round - NUM_ROUNDS, - 1, - Amount.ofTokens(100), // Minimum stake - 150, // Two weeks worth of epochs - Amount.ofTokens(10), // Rewards per proposal - 9800, // 98.00% threshold for completed proposals to get any rewards - 100 // 100 max validators - ) - ), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), - new MockedGenesisModule( - Set.of(TEST_KEY.getPublicKey()), - Amount.ofTokens(100000), - Amount.ofTokens(1000) - ), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } + private Injector createInjector() { + return Guice.createInjector( + MempoolConfig.asModule(1000, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.create( + Amount.ofMicroTokens(200), // 0.0002XRD per byte fee + Map.of( + TokenResource.class, Amount.ofTokens(1000), // 1000XRD per resource + ValidatorRegisteredCopy.class, + Amount.ofTokens(5), // 5XRD per validator update + ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update + AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update + PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake + PreparedUnstakeOwnership.class, + Amount.ofMilliTokens(500) // 0.5XRD per unstake + )), + 1024 * 1024, + OptionalInt.of(50), // 50 Txns per round + NUM_ROUNDS, + 1, + Amount.ofTokens(100), // Minimum stake + 150, // Two weeks worth of epochs + Amount.ofTokens(10), // Rewards per proposal + 9800, // 98.00% threshold for completed proposals to get any rewards + 100 // 100 max validators + )), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), + new MockedGenesisModule( + Set.of(TEST_KEY.getPublicKey()), Amount.ofTokens(100000), Amount.ofTokens(1000)), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } - @Test - public void large_epoch() throws Exception { - var rt = Runtime.getRuntime(); - logger.info("max mem: {}MB", rt.maxMemory() / 1024 / 1024); + @Test + public void large_epoch() throws Exception { + var rt = Runtime.getRuntime(); + logger.info("max mem: {}MB", rt.maxMemory() / 1024 / 1024); - int privKeyStart = 2; - int numTxnsPerRound = 10; + int privKeyStart = 2; + int numTxnsPerRound = 10; - createInjector().injectMembers(this); - // Arrange - var request = TxnConstructionRequest.create(); - IntStream.range(privKeyStart, NUM_ROUNDS * numTxnsPerRound + privKeyStart) - .forEach(i -> { - var k = PrivateKeys.ofNumeric(i); - var addr = REAddr.ofPubKeyAccount(k.getPublicKey()); - request.action(new MintToken(REAddr.ofNativeToken(), addr, Amount.ofTokens(NUM_ROUNDS * 1000).toSubunits())); - request.action(new RegisterValidator(k.getPublicKey())); - }); - var mint = sut.construct(request).buildWithoutSignature(); - logger.info("mint_txn_size={}", mint.getPayload().length); - var accumulator = new AccumulatorState(2, HashUtils.zero256()); - var proof = new LedgerProof(HashUtils.zero256(), LedgerHeader.create(1, View.of(1), accumulator, 0), new TimestampedECDSASignatures()); - sut.execute(List.of(mint), LedgerAndBFTProof.create(proof), PermissionLevel.SYSTEM); + createInjector().injectMembers(this); + // Arrange + var request = TxnConstructionRequest.create(); + IntStream.range(privKeyStart, NUM_ROUNDS * numTxnsPerRound + privKeyStart) + .forEach( + i -> { + var k = PrivateKeys.ofNumeric(i); + var addr = REAddr.ofPubKeyAccount(k.getPublicKey()); + request.action( + new MintToken( + REAddr.ofNativeToken(), + addr, + Amount.ofTokens(NUM_ROUNDS * 1000).toSubunits())); + request.action(new RegisterValidator(k.getPublicKey())); + }); + var mint = sut.construct(request).buildWithoutSignature(); + logger.info("mint_txn_size={}", mint.getPayload().length); + var accumulator = new AccumulatorState(2, HashUtils.zero256()); + var proof = + new LedgerProof( + HashUtils.zero256(), + LedgerHeader.create(1, View.of(1), accumulator, 0), + new TimestampedECDSASignatures()); + sut.execute(List.of(mint), LedgerAndBFTProof.create(proof), PermissionLevel.SYSTEM); - var systemConstruction = Stopwatch.createUnstarted(); - var construction = Stopwatch.createUnstarted(); - var signatures = Stopwatch.createUnstarted(); - var execution = Stopwatch.createUnstarted(); + var systemConstruction = Stopwatch.createUnstarted(); + var construction = Stopwatch.createUnstarted(); + var signatures = Stopwatch.createUnstarted(); + var execution = Stopwatch.createUnstarted(); - var feesPaid = UInt256.ZERO; + var feesPaid = UInt256.ZERO; - for (int round = 1; round <= NUM_ROUNDS; round++) { - if (round % NUM_ROUNDS == 0) { - logger.info( - "Staking txn {}/{} sys_construct_time: {}s user_construct_time: {}s sig_time: {}s execute_time: {}s", - round * (numTxnsPerRound + 1), - NUM_ROUNDS * (numTxnsPerRound + 1), - systemConstruction.elapsed(TimeUnit.SECONDS), - construction.elapsed(TimeUnit.SECONDS), - signatures.elapsed(TimeUnit.SECONDS), - execution.elapsed(TimeUnit.SECONDS) - ); - } - var txns = new ArrayList(); - systemConstruction.start(); - var sysTxn = sut.construct(new NextRound(round, false, 1, v -> TEST_KEY.getPublicKey())) - .buildWithoutSignature(); - systemConstruction.stop(); - txns.add(sysTxn); - for (int i = 0; i < numTxnsPerRound; i++) { - var privateKey = PrivateKeys.ofNumeric((round - 1) * numTxnsPerRound + i + privKeyStart); - var pubKey = privateKey.getPublicKey(); - var addr = REAddr.ofPubKeyAccount(privateKey.getPublicKey()); - construction.start(); - var builder = sut.construct( - TxnConstructionRequest.create() - .feePayer(addr) - .action(new StakeTokens(addr, pubKey, Amount.ofTokens(100 + i).toSubunits())) - ); - construction.stop(); - signatures.start(); - var txn = builder.signAndBuild(privateKey::sign); - signatures.stop(); - txns.add(txn); - } + for (int round = 1; round <= NUM_ROUNDS; round++) { + if (round % NUM_ROUNDS == 0) { + logger.info( + "Staking txn {}/{} sys_construct_time: {}s user_construct_time: {}s sig_time: {}s" + + " execute_time: {}s", + round * (numTxnsPerRound + 1), + NUM_ROUNDS * (numTxnsPerRound + 1), + systemConstruction.elapsed(TimeUnit.SECONDS), + construction.elapsed(TimeUnit.SECONDS), + signatures.elapsed(TimeUnit.SECONDS), + execution.elapsed(TimeUnit.SECONDS)); + } + var txns = new ArrayList(); + systemConstruction.start(); + var sysTxn = + sut.construct(new NextRound(round, false, 1, v -> TEST_KEY.getPublicKey())) + .buildWithoutSignature(); + systemConstruction.stop(); + txns.add(sysTxn); + for (int i = 0; i < numTxnsPerRound; i++) { + var privateKey = PrivateKeys.ofNumeric((round - 1) * numTxnsPerRound + i + privKeyStart); + var pubKey = privateKey.getPublicKey(); + var addr = REAddr.ofPubKeyAccount(privateKey.getPublicKey()); + construction.start(); + var builder = + sut.construct( + TxnConstructionRequest.create() + .feePayer(addr) + .action(new StakeTokens(addr, pubKey, Amount.ofTokens(100 + i).toSubunits()))); + construction.stop(); + signatures.start(); + var txn = builder.signAndBuild(privateKey::sign); + signatures.stop(); + txns.add(txn); + } - var acc = new AccumulatorState(2 + round * (numTxnsPerRound + 1), HashUtils.zero256()); - var proof2 = new LedgerProof(HashUtils.zero256(), LedgerHeader.create(1, View.of(1), acc, 0), new TimestampedECDSASignatures()); - execution.start(); - var result = sut.execute(txns, LedgerAndBFTProof.create(proof2), PermissionLevel.SUPER_USER); - execution.stop(); - for (var p : result.getProcessedTxns()) { - feesPaid = feesPaid.add(p.getFeePaid()); - } - } - logger.info("total_fees_paid: {}", Amount.ofSubunits(feesPaid)); + var acc = new AccumulatorState(2 + round * (numTxnsPerRound + 1), HashUtils.zero256()); + var proof2 = + new LedgerProof( + HashUtils.zero256(), + LedgerHeader.create(1, View.of(1), acc, 0), + new TimestampedECDSASignatures()); + execution.start(); + var result = sut.execute(txns, LedgerAndBFTProof.create(proof2), PermissionLevel.SUPER_USER); + execution.stop(); + for (var p : result.getProcessedTxns()) { + feesPaid = feesPaid.add(p.getFeePaid()); + } + } + logger.info("total_fees_paid: {}", Amount.ofSubunits(feesPaid)); - // Act - construction.reset(); - construction.start(); - logger.info("constructing epoch..."); - var txn = sut.construct(new NextEpoch(1)).buildWithoutSignature(); - construction.stop(); - logger.info("epoch_construction: size={}MB time={}s", txn.getPayload().length / 1024 / 1024, construction.elapsed(TimeUnit.SECONDS)); + // Act + construction.reset(); + construction.start(); + logger.info("constructing epoch..."); + var txn = sut.construct(new NextEpoch(1)).buildWithoutSignature(); + construction.stop(); + logger.info( + "epoch_construction: size={}MB time={}s", + txn.getPayload().length / 1024 / 1024, + construction.elapsed(TimeUnit.SECONDS)); - construction.reset(); - construction.start(); - logger.info("preparing epoch..."); - var result = sut.transientBranch().execute(List.of(txn), PermissionLevel.SUPER_USER); - sut.deleteBranches(); - var nextValidatorSet = result.getProcessedTxn().getEvents().stream() - .filter(NextValidatorSetEvent.class::isInstance) - .map(NextValidatorSetEvent.class::cast) - .findFirst() - .map(e -> BFTValidatorSet.from( - e.nextValidators().stream() - .map(v -> BFTValidator.from(BFTNode.create(v.getValidatorKey()), v.getAmount()))) - ); - var stateUpdates = result.getProcessedTxn().stateUpdates().count(); - construction.stop(); - logger.info( - "epoch_preparation: state_updates={} verification_time={}s store_time={}s total_time={}s", - stateUpdates, - result.getVerificationTime() / 1000, - result.getStoreTime() / 1000, - construction.elapsed(TimeUnit.SECONDS) - ); - construction.reset(); - construction.start(); - logger.info("executing epoch..."); - var acc = new AccumulatorState(2 + 1 + NUM_ROUNDS * (1 + numTxnsPerRound), HashUtils.zero256()); - var header = LedgerHeader.create(1, View.of(10), acc, 0, nextValidatorSet.orElseThrow()); - var proof2 = new LedgerProof(HashUtils.zero256(), header, new TimestampedECDSASignatures()); - var executionResult = this.sut.execute(List.of(txn), LedgerAndBFTProof.create(proof2), PermissionLevel.SUPER_USER); - construction.stop(); - logger.info( - "epoch_execution: verification_time={}s store_time={}s total_time={}s", - executionResult.getVerificationTime() / 1000, - executionResult.getStoreTime() / 1000, - construction.elapsed(TimeUnit.SECONDS) - ); - for (var v : nextValidatorSet.orElseThrow().getValidators()) { - logger.info("validator {} {}", v.getNode(), v.getPower()); - } - } + construction.reset(); + construction.start(); + logger.info("preparing epoch..."); + var result = sut.transientBranch().execute(List.of(txn), PermissionLevel.SUPER_USER); + sut.deleteBranches(); + var nextValidatorSet = + result.getProcessedTxn().getEvents().stream() + .filter(NextValidatorSetEvent.class::isInstance) + .map(NextValidatorSetEvent.class::cast) + .findFirst() + .map( + e -> + BFTValidatorSet.from( + e.nextValidators().stream() + .map( + v -> + BFTValidator.from( + BFTNode.create(v.getValidatorKey()), v.getAmount())))); + var stateUpdates = result.getProcessedTxn().stateUpdates().count(); + construction.stop(); + logger.info( + "epoch_preparation: state_updates={} verification_time={}s store_time={}s total_time={}s", + stateUpdates, + result.getVerificationTime() / 1000, + result.getStoreTime() / 1000, + construction.elapsed(TimeUnit.SECONDS)); + construction.reset(); + construction.start(); + logger.info("executing epoch..."); + var acc = new AccumulatorState(2 + 1 + NUM_ROUNDS * (1 + numTxnsPerRound), HashUtils.zero256()); + var header = LedgerHeader.create(1, View.of(10), acc, 0, nextValidatorSet.orElseThrow()); + var proof2 = new LedgerProof(HashUtils.zero256(), header, new TimestampedECDSASignatures()); + var executionResult = + this.sut.execute( + List.of(txn), LedgerAndBFTProof.create(proof2), PermissionLevel.SUPER_USER); + construction.stop(); + logger.info( + "epoch_execution: verification_time={}s store_time={}s total_time={}s", + executionResult.getVerificationTime() / 1000, + executionResult.getStoreTime() / 1000, + construction.elapsed(TimeUnit.SECONDS)); + for (var v : nextValidatorSet.orElseThrow().getValidators()) { + logger.info("validator {} {}", v.getNode(), v.getPower()); + } + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/RandomTxnTest.java b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/RandomTxnTest.java index 6446024fb0..95017db79e 100644 --- a/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/RandomTxnTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/com/radixdlt/integration/statemachine/RandomTxnTest.java @@ -64,97 +64,89 @@ package com.radixdlt.integration.statemachine; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.utils.PrivateKeys; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.RadixEngineException; import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.LastStoredProof; - +import com.radixdlt.utils.PrivateKeys; import java.util.List; import java.util.Random; import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class RandomTxnTest { - private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); - private static final Logger logger = LogManager.getLogger(); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); + private static final Logger logger = LogManager.getLogger(); + @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Inject - private RadixEngine engine; + @Inject private RadixEngine engine; - // FIXME: Hack, need this in order to cause provider for genesis to be stored - @Inject - @LastStoredProof - private LedgerProof ledgerProof; + // FIXME: Hack, need this in order to cause provider for genesis to be stored + @Inject @LastStoredProof private LedgerProof ledgerProof; - private Injector createInjector() { - return Guice.createInjector( - MempoolConfig.asModule(1000, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), - new MockedGenesisModule( - Set.of(TEST_KEY.getPublicKey()), - Amount.ofTokens(100000), - Amount.ofTokens(1000) - ), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } + private Injector createInjector() { + return Guice.createInjector( + MempoolConfig.asModule(1000, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule(), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), + new MockedGenesisModule( + Set.of(TEST_KEY.getPublicKey()), Amount.ofTokens(100000), Amount.ofTokens(1000)), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } - @Test - public void poor_mans_fuzz_test() { - var random = new Random(12345678); + @Test + public void poor_mans_fuzz_test() { + var random = new Random(12345678); - // Arrange - createInjector().injectMembers(this); - final var count = 1000000; + // Arrange + createInjector().injectMembers(this); + final var count = 1000000; - for (int i = 0; i < count; i++) { - int len = random.nextInt(512); - var payload = new byte[len]; - random.nextBytes(payload); - for (int j = 0; j < len; j++) { - payload[j] = random.nextBoolean() ? (byte) random.nextInt(10) : payload[j]; - } - var txns = List.of(Txn.create(payload)); - if (i % 1000 == 0) { - logger.info(i + "/" + count); - } - try { - engine.execute(txns, null, PermissionLevel.SYSTEM); - } catch (RadixEngineException e) { - // ignore - } - } - } + for (int i = 0; i < count; i++) { + int len = random.nextInt(512); + var payload = new byte[len]; + random.nextBytes(payload); + for (int j = 0; j < len; j++) { + payload[j] = random.nextBoolean() ? (byte) random.nextInt(10) : payload[j]; + } + var txns = List.of(Txn.create(payload)); + if (i % 1000 == 0) { + logger.info(i + "/" + count); + } + try { + engine.execute(txns, null, PermissionLevel.SYSTEM); + } catch (RadixEngineException e) { + // ignore + } + } + } } diff --git a/radixdlt-core/radixdlt/src/integration/java/org/radix/integration/RadixTest.java b/radixdlt-core/radixdlt/src/integration/java/org/radix/integration/RadixTest.java index 027e89bcdb..9025c7d20d 100644 --- a/radixdlt-core/radixdlt/src/integration/java/org/radix/integration/RadixTest.java +++ b/radixdlt-core/radixdlt/src/integration/java/org/radix/integration/RadixTest.java @@ -67,7 +67,7 @@ import com.radixdlt.DefaultSerialization; import com.radixdlt.properties.RuntimeProperties; import com.radixdlt.serialization.Serialization; - +import java.util.Objects; import org.json.JSONObject; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -77,49 +77,48 @@ import org.radix.serialization.TestSetupUtils; import org.radix.utils.IOUtils; -import java.util.Objects; - public class RadixTest { - private static Serialization serialization; - private static String dbLocation = null; - private static RuntimeProperties properties; - @ClassRule - public static TemporaryFolder folder = new TemporaryFolder(); + private static Serialization serialization; + private static String dbLocation = null; + private static RuntimeProperties properties; + @ClassRule public static TemporaryFolder folder = new TemporaryFolder(); - @BeforeClass - public static void startRadixTest() throws Exception { - TestSetupUtils.installBouncyCastleProvider(); + @BeforeClass + public static void startRadixTest() throws Exception { + TestSetupUtils.installBouncyCastleProvider(); - serialization = DefaultSerialization.getInstance(); + serialization = DefaultSerialization.getInstance(); - JSONObject runtimeConfigurationJSON = new JSONObject(); - if (Radix.class.getResourceAsStream("/runtime_options.json") != null) { - runtimeConfigurationJSON = new JSONObject(IOUtils.toString(Radix.class.getResourceAsStream("/runtime_options.json"))); - } + JSONObject runtimeConfigurationJSON = new JSONObject(); + if (Radix.class.getResourceAsStream("/runtime_options.json") != null) { + runtimeConfigurationJSON = + new JSONObject( + IOUtils.toString(Radix.class.getResourceAsStream("/runtime_options.json"))); + } - properties = new RuntimeProperties(runtimeConfigurationJSON, null); + properties = new RuntimeProperties(runtimeConfigurationJSON, null); - // Tests need this - properties.set("network.host_ip", "127.0.0.1"); + // Tests need this + properties.set("network.host_ip", "127.0.0.1"); - if (dbLocation == null) { - dbLocation = folder.getRoot().getAbsolutePath(); - } - properties.set("db.location", dbLocation); - } + if (dbLocation == null) { + dbLocation = folder.getRoot().getAbsolutePath(); + } + properties.set("db.location", dbLocation); + } - @AfterClass - public static void stopRadixTest() { - serialization = null; - dbLocation = null; - properties = null; - } + @AfterClass + public static void stopRadixTest() { + serialization = null; + dbLocation = null; + properties = null; + } - protected Serialization getSerialization() { - return Objects.requireNonNull(serialization, "serialization was not initialized"); - } + protected Serialization getSerialization() { + return Objects.requireNonNull(serialization, "serialization was not initialized"); + } - protected RuntimeProperties getProperties() { - return Objects.requireNonNull(properties, "properties was not initialized"); - } + protected RuntimeProperties getProperties() { + return Objects.requireNonNull(properties, "properties was not initialized"); + } } diff --git a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/CodecBenchmark.java b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/CodecBenchmark.java index 0868954da1..509ebf01d9 100644 --- a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/CodecBenchmark.java +++ b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/CodecBenchmark.java @@ -64,100 +64,98 @@ package org.radix.benchmark; - import com.radixdlt.DefaultSerialization; +import com.radixdlt.serialization.ClassScanningSerializerIds; +import com.radixdlt.serialization.DeserializeException; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.Serialization; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.config.Configurator; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.infra.Blackhole; - -import com.radixdlt.serialization.DeserializeException; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.ClassScanningSerializerIds; -import com.radixdlt.serialization.Serialization; - import org.radix.serialization.DummyTestObject; import org.radix.serialization.TestSetupUtils; /** - * Some JMH driven benchmarks for testing serialisation performance of - * Radix and some third party libraries. - *

- * Note that the build system has been set up to make it easier to - * run these performance tests under gradle. Using gradle, it should - * be possible to execute: + * Some JMH driven benchmarks for testing serialisation performance of Radix and some third party + * libraries. + * + *

Note that the build system has been set up to make it easier to run these performance tests + * under gradle. Using gradle, it should be possible to execute: + * *

  *    $ gradle clean jmh
  * 
- * from the RadixCode/radixdlt directory. Note that the JMH plugin - * does not appear to be super robust, and changes to benchmark tests - * and other code are not always re-instrumented correctly by gradle - * daemons. This can be worked around by stopping the daemons before - * starting the tests using: + * + * from the RadixCode/radixdlt directory. Note that the JMH plugin does not appear to be super + * robust, and changes to benchmark tests and other code are not always re-instrumented correctly by + * gradle daemons. This can be worked around by stopping the daemons before starting the tests + * using: + * *
  *    $ gradle --stop && gradle clean jmh
  * 
- * Alternatively, the configuration option {@code org.gradle.daemon=false} - * can be added to the {@code ~/.gradle/gradle.properties} to completely - * disable gradle daemons. + * + * Alternatively, the configuration option {@code org.gradle.daemon=false} can be added to the + * {@code ~/.gradle/gradle.properties} to completely disable gradle daemons. */ public class CodecBenchmark { - private static final DummyTestObject testObject; - - private static Serialization serialization; - - private static String jacksonJson; - private static byte[] jacksonBytes; - - static { - // Disable this output for now, as the serialiser is quite verbose when starting. - Configurator.setLevel(LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); - - TestSetupUtils.installBouncyCastleProvider(); - - serialization = DefaultSerialization.getInstance(); - - testObject = new DummyTestObject(true); - - jacksonJson = serialization.toJson(testObject, Output.ALL); - jacksonBytes = serialization.toDson(testObject, Output.ALL); - - System.out.format("DSON bytes length: %s%n", jacksonBytes.length); - System.out.format("JSON bytes length: %s%n", jacksonJson.length()); - } - - @Benchmark - public void jacksonToBytesTest(Blackhole bh) { - byte[] bytes = serialization.toDson(testObject, Output.WIRE); - bh.consume(bytes); - } - - @Benchmark - public void jacksonFromBytesTest(Blackhole bh) { - try { - DummyTestObject newObj = serialization.fromDson(jacksonBytes, DummyTestObject.class); - bh.consume(newObj); - } catch (DeserializeException ex) { - throw new IllegalStateException("While deserializing from DSON", ex); - } - } - - - @Benchmark - public void jacksonToJsonTest(Blackhole bh) { - String json = serialization.toJson(testObject, Output.WIRE); - bh.consume(json); - } - - @Benchmark - public void jacksonFromJsonTest(Blackhole bh) { - try { - DummyTestObject newObj = serialization.fromJson(jacksonJson, DummyTestObject.class); - bh.consume(newObj); - } catch (DeserializeException ex) { - throw new IllegalStateException("While deserializing from JSON", ex); - } - } + private static final DummyTestObject testObject; + + private static Serialization serialization; + + private static String jacksonJson; + private static byte[] jacksonBytes; + + static { + // Disable this output for now, as the serialiser is quite verbose when starting. + Configurator.setLevel( + LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); + + TestSetupUtils.installBouncyCastleProvider(); + + serialization = DefaultSerialization.getInstance(); + + testObject = new DummyTestObject(true); + + jacksonJson = serialization.toJson(testObject, Output.ALL); + jacksonBytes = serialization.toDson(testObject, Output.ALL); + + System.out.format("DSON bytes length: %s%n", jacksonBytes.length); + System.out.format("JSON bytes length: %s%n", jacksonJson.length()); + } + + @Benchmark + public void jacksonToBytesTest(Blackhole bh) { + byte[] bytes = serialization.toDson(testObject, Output.WIRE); + bh.consume(bytes); + } + + @Benchmark + public void jacksonFromBytesTest(Blackhole bh) { + try { + DummyTestObject newObj = serialization.fromDson(jacksonBytes, DummyTestObject.class); + bh.consume(newObj); + } catch (DeserializeException ex) { + throw new IllegalStateException("While deserializing from DSON", ex); + } + } + + @Benchmark + public void jacksonToJsonTest(Blackhole bh) { + String json = serialization.toJson(testObject, Output.WIRE); + bh.consume(json); + } + + @Benchmark + public void jacksonFromJsonTest(Blackhole bh) { + try { + DummyTestObject newObj = serialization.fromJson(jacksonJson, DummyTestObject.class); + bh.consume(newObj); + } catch (DeserializeException ex) { + throw new IllegalStateException("While deserializing from JSON", ex); + } + } } diff --git a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt128Benchmark.java b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt128Benchmark.java index 3649cd846b..4536ac477c 100644 --- a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt128Benchmark.java +++ b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt128Benchmark.java @@ -64,10 +64,11 @@ package org.radix.benchmark; +import com.google.common.math.BigIntegerMath; +import com.radixdlt.utils.UInt128; import java.math.BigInteger; import java.math.RoundingMode; import java.util.concurrent.TimeUnit; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Measurement; @@ -75,23 +76,21 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; -import com.radixdlt.utils.UInt128; - -import com.google.common.math.BigIntegerMath; /** * Some JMH driven benchmarks for testing UInt128 performance. - *

- * Note that the build system has been set up to make it easier to - * run these performance tests under gradle. Using gradle, it should - * be possible to execute: + * + *

Note that the build system has been set up to make it easier to run these performance tests + * under gradle. Using gradle, it should be possible to execute: + * *

  *    $ gradle clean jmh
  * 
- * from the RadixCode/radixdlt directory. Note that the JMH plugin - * does not appear to be super robust, and changes to benchmark tests - * and other code are not always re-instrumented correctly by gradle - * daemons. This can be worked around by avoiding the gradle daemon: + * + * from the RadixCode/radixdlt directory. Note that the JMH plugin does not appear to be super + * robust, and changes to benchmark tests and other code are not always re-instrumented correctly by + * gradle daemons. This can be worked around by avoiding the gradle daemon: + * *
  *    $ gradle --no-daemon clean jmh
  * 
@@ -102,71 +101,72 @@ @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) public class UInt128Benchmark { - private static final BigInteger BI_TWO = BigInteger.valueOf(2); - private static final BigInteger BI_SMALL_VALUE = BI_TWO.pow(27).subtract(BigInteger.ONE); - private static final BigInteger BI_LARGE_VALUE1 = BI_TWO.pow(UInt128.SIZE - 27).subtract(BigInteger.ONE); - private static final BigInteger BI_LARGE_VALUE2 = BI_TWO.pow(UInt128.SIZE - 28).subtract(BigInteger.ONE); - - private static final UInt128 UI_SMALL_VALUE = fromBigInt(BI_SMALL_VALUE); - private static final UInt128 UI_LARGE_VALUE1 = fromBigInt(BI_LARGE_VALUE1); - private static final UInt128 UI_LARGE_VALUE2 = fromBigInt(BI_LARGE_VALUE2); - - static { - assert BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE).bitLength() < UInt128.SIZE; - } - - static UInt128 fromBigInt(BigInteger bi) { - return UInt128.from(bi.toByteArray()); - } - - @Benchmark - public void addLargeLargeInt128(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.add(UI_LARGE_VALUE2)); - } - - @Benchmark - public void addLargeLargeBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.add(BI_LARGE_VALUE2)); - } - - @Benchmark - public void subLargeLargeInt128(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.subtract(UI_LARGE_VALUE2)); - } - - @Benchmark - public void subLargeLargeBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.subtract(BI_LARGE_VALUE2)); - } - - @Benchmark - public void mulLargeSmallInt128(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.multiply(UI_SMALL_VALUE)); - } - - @Benchmark - public void mulLargeSmallBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE)); - } - - @Benchmark - public void divLargeSmallInt128(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.divide(UI_SMALL_VALUE)); - } - - @Benchmark - public void divLargeSmallBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.divide(BI_SMALL_VALUE)); - } - - @Benchmark - public void sqrtLargeInt128(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.isqrt()); - } - - @Benchmark - public void sqrtLargeBigInt(Blackhole bh) { - bh.consume(BigIntegerMath.sqrt(BI_LARGE_VALUE1, RoundingMode.FLOOR)); - } - + private static final BigInteger BI_TWO = BigInteger.valueOf(2); + private static final BigInteger BI_SMALL_VALUE = BI_TWO.pow(27).subtract(BigInteger.ONE); + private static final BigInteger BI_LARGE_VALUE1 = + BI_TWO.pow(UInt128.SIZE - 27).subtract(BigInteger.ONE); + private static final BigInteger BI_LARGE_VALUE2 = + BI_TWO.pow(UInt128.SIZE - 28).subtract(BigInteger.ONE); + + private static final UInt128 UI_SMALL_VALUE = fromBigInt(BI_SMALL_VALUE); + private static final UInt128 UI_LARGE_VALUE1 = fromBigInt(BI_LARGE_VALUE1); + private static final UInt128 UI_LARGE_VALUE2 = fromBigInt(BI_LARGE_VALUE2); + + static { + assert BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE).bitLength() < UInt128.SIZE; + } + + static UInt128 fromBigInt(BigInteger bi) { + return UInt128.from(bi.toByteArray()); + } + + @Benchmark + public void addLargeLargeInt128(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.add(UI_LARGE_VALUE2)); + } + + @Benchmark + public void addLargeLargeBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.add(BI_LARGE_VALUE2)); + } + + @Benchmark + public void subLargeLargeInt128(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.subtract(UI_LARGE_VALUE2)); + } + + @Benchmark + public void subLargeLargeBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.subtract(BI_LARGE_VALUE2)); + } + + @Benchmark + public void mulLargeSmallInt128(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.multiply(UI_SMALL_VALUE)); + } + + @Benchmark + public void mulLargeSmallBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE)); + } + + @Benchmark + public void divLargeSmallInt128(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.divide(UI_SMALL_VALUE)); + } + + @Benchmark + public void divLargeSmallBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.divide(BI_SMALL_VALUE)); + } + + @Benchmark + public void sqrtLargeInt128(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.isqrt()); + } + + @Benchmark + public void sqrtLargeBigInt(Blackhole bh) { + bh.consume(BigIntegerMath.sqrt(BI_LARGE_VALUE1, RoundingMode.FLOOR)); + } } diff --git a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt256Benchmark.java b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt256Benchmark.java index 25f45cebe5..6f2937e35e 100644 --- a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt256Benchmark.java +++ b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt256Benchmark.java @@ -64,10 +64,11 @@ package org.radix.benchmark; +import com.google.common.math.BigIntegerMath; +import com.radixdlt.utils.UInt256; import java.math.BigInteger; import java.math.RoundingMode; import java.util.concurrent.TimeUnit; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Measurement; @@ -75,23 +76,21 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; -import com.radixdlt.utils.UInt256; - -import com.google.common.math.BigIntegerMath; /** * Some JMH driven benchmarks for testing UInt256 performance. - *

- * Note that the build system has been set up to make it easier to - * run these performance tests under gradle. Using gradle, it should - * be possible to execute: + * + *

Note that the build system has been set up to make it easier to run these performance tests + * under gradle. Using gradle, it should be possible to execute: + * *

  *    $ gradle clean jmh
  * 
- * from the RadixCode/radixdlt directory. Note that the JMH plugin - * does not appear to be super robust, and changes to benchmark tests - * and other code are not always re-instrumented correctly by gradle - * daemons. This can be worked around by avoiding the gradle daemon: + * + * from the RadixCode/radixdlt directory. Note that the JMH plugin does not appear to be super + * robust, and changes to benchmark tests and other code are not always re-instrumented correctly by + * gradle daemons. This can be worked around by avoiding the gradle daemon: + * *
  *    $ gradle --no-daemon clean jmh
  * 
@@ -102,67 +101,68 @@ @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) public class UInt256Benchmark { - private static final BigInteger BI_TWO = BigInteger.valueOf(2); - private static final BigInteger BI_SMALL_VALUE = BI_TWO.pow(27).subtract(BigInteger.ONE); - private static final BigInteger BI_LARGE_VALUE1 = BI_TWO.pow(UInt256.SIZE - 27).subtract(BigInteger.ONE); - private static final BigInteger BI_LARGE_VALUE2 = BI_TWO.pow(UInt256.SIZE - 28).subtract(BigInteger.ONE); - - private static final UInt256 UI_SMALL_VALUE = fromBigInt(BI_SMALL_VALUE); - private static final UInt256 UI_LARGE_VALUE1 = fromBigInt(BI_LARGE_VALUE1); - private static final UInt256 UI_LARGE_VALUE2 = fromBigInt(BI_LARGE_VALUE2); - - static UInt256 fromBigInt(BigInteger bi) { - return UInt256.from(bi.toByteArray()); - } - - @Benchmark - public void addLargeLargeInt256(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.add(UI_LARGE_VALUE2)); - } - - @Benchmark - public void addLargeLargeBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.add(BI_LARGE_VALUE2)); - } - - @Benchmark - public void subLargeLargeInt256(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.subtract(UI_LARGE_VALUE2)); - } - - @Benchmark - public void subLargeLargeBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.subtract(BI_LARGE_VALUE2)); - } - - @Benchmark - public void mulLargeSmallInt256(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.multiply(UI_SMALL_VALUE)); - } - - @Benchmark - public void mulLargeSmallBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE)); - } - - @Benchmark - public void divLargeSmallInt256(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.divide(UI_SMALL_VALUE)); - } - - @Benchmark - public void divLargeSmallBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.divide(BI_SMALL_VALUE)); - } - - @Benchmark - public void sqrtLargeInt256(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.isqrt()); - } - - @Benchmark - public void sqrtLargeBigInt(Blackhole bh) { - bh.consume(BigIntegerMath.sqrt(BI_LARGE_VALUE1, RoundingMode.FLOOR)); - } - + private static final BigInteger BI_TWO = BigInteger.valueOf(2); + private static final BigInteger BI_SMALL_VALUE = BI_TWO.pow(27).subtract(BigInteger.ONE); + private static final BigInteger BI_LARGE_VALUE1 = + BI_TWO.pow(UInt256.SIZE - 27).subtract(BigInteger.ONE); + private static final BigInteger BI_LARGE_VALUE2 = + BI_TWO.pow(UInt256.SIZE - 28).subtract(BigInteger.ONE); + + private static final UInt256 UI_SMALL_VALUE = fromBigInt(BI_SMALL_VALUE); + private static final UInt256 UI_LARGE_VALUE1 = fromBigInt(BI_LARGE_VALUE1); + private static final UInt256 UI_LARGE_VALUE2 = fromBigInt(BI_LARGE_VALUE2); + + static UInt256 fromBigInt(BigInteger bi) { + return UInt256.from(bi.toByteArray()); + } + + @Benchmark + public void addLargeLargeInt256(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.add(UI_LARGE_VALUE2)); + } + + @Benchmark + public void addLargeLargeBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.add(BI_LARGE_VALUE2)); + } + + @Benchmark + public void subLargeLargeInt256(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.subtract(UI_LARGE_VALUE2)); + } + + @Benchmark + public void subLargeLargeBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.subtract(BI_LARGE_VALUE2)); + } + + @Benchmark + public void mulLargeSmallInt256(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.multiply(UI_SMALL_VALUE)); + } + + @Benchmark + public void mulLargeSmallBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE)); + } + + @Benchmark + public void divLargeSmallInt256(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.divide(UI_SMALL_VALUE)); + } + + @Benchmark + public void divLargeSmallBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.divide(BI_SMALL_VALUE)); + } + + @Benchmark + public void sqrtLargeInt256(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.isqrt()); + } + + @Benchmark + public void sqrtLargeBigInt(Blackhole bh) { + bh.consume(BigIntegerMath.sqrt(BI_LARGE_VALUE1, RoundingMode.FLOOR)); + } } diff --git a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt384Benchmark.java b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt384Benchmark.java index 9ac5fdbea6..a1202fb544 100644 --- a/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt384Benchmark.java +++ b/radixdlt-core/radixdlt/src/jmh/java/org/radix/benchmark/UInt384Benchmark.java @@ -64,10 +64,11 @@ package org.radix.benchmark; +import com.google.common.math.BigIntegerMath; +import com.radixdlt.utils.UInt384; import java.math.BigInteger; import java.math.RoundingMode; import java.util.concurrent.TimeUnit; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Measurement; @@ -75,23 +76,21 @@ import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; -import com.radixdlt.utils.UInt384; - -import com.google.common.math.BigIntegerMath; /** * Some JMH driven benchmarks for testing UInt384 performance. - *

- * Note that the build system has been set up to make it easier to - * run these performance tests under gradle. Using gradle, it should - * be possible to execute: + * + *

Note that the build system has been set up to make it easier to run these performance tests + * under gradle. Using gradle, it should be possible to execute: + * *

  *    $ gradle clean jmh
  * 
- * from the RadixCode/radixdlt directory. Note that the JMH plugin - * does not appear to be super robust, and changes to benchmark tests - * and other code are not always re-instrumented correctly by gradle - * daemons. This can be worked around by avoiding the gradle daemon: + * + * from the RadixCode/radixdlt directory. Note that the JMH plugin does not appear to be super + * robust, and changes to benchmark tests and other code are not always re-instrumented correctly by + * gradle daemons. This can be worked around by avoiding the gradle daemon: + * *
  *    $ gradle --no-daemon clean jmh
  * 
@@ -102,67 +101,68 @@ @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) public class UInt384Benchmark { - private static final BigInteger BI_TWO = BigInteger.valueOf(2); - private static final BigInteger BI_SMALL_VALUE = BI_TWO.pow(27).subtract(BigInteger.ONE); - private static final BigInteger BI_LARGE_VALUE1 = BI_TWO.pow(UInt384.SIZE - 27).subtract(BigInteger.ONE); - private static final BigInteger BI_LARGE_VALUE2 = BI_TWO.pow(UInt384.SIZE - 28).subtract(BigInteger.ONE); - - private static final UInt384 UI_SMALL_VALUE = fromBigInt(BI_SMALL_VALUE); - private static final UInt384 UI_LARGE_VALUE1 = fromBigInt(BI_LARGE_VALUE1); - private static final UInt384 UI_LARGE_VALUE2 = fromBigInt(BI_LARGE_VALUE2); - - static UInt384 fromBigInt(BigInteger bi) { - return UInt384.from(bi.toByteArray()); - } - - @Benchmark - public void addLargeLargeInt384(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.add(UI_LARGE_VALUE2)); - } - - @Benchmark - public void addLargeLargeBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.add(BI_LARGE_VALUE2)); - } - - @Benchmark - public void subLargeLargeInt384(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.subtract(UI_LARGE_VALUE2)); - } - - @Benchmark - public void subLargeLargeBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.subtract(BI_LARGE_VALUE2)); - } - - @Benchmark - public void mulLargeSmallInt384(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.multiply(UI_SMALL_VALUE)); - } - - @Benchmark - public void mulLargeSmallBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE)); - } - - @Benchmark - public void divLargeSmallInt384(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.divide(UI_SMALL_VALUE)); - } - - @Benchmark - public void divLargeSmallBigInt(Blackhole bh) { - bh.consume(BI_LARGE_VALUE1.divide(BI_SMALL_VALUE)); - } - - @Benchmark - public void sqrtLargeInt384(Blackhole bh) { - bh.consume(UI_LARGE_VALUE1.isqrt()); - } - - @Benchmark - public void sqrtLargeBigInt(Blackhole bh) { - bh.consume(BigIntegerMath.sqrt(BI_LARGE_VALUE1, RoundingMode.FLOOR)); - } - + private static final BigInteger BI_TWO = BigInteger.valueOf(2); + private static final BigInteger BI_SMALL_VALUE = BI_TWO.pow(27).subtract(BigInteger.ONE); + private static final BigInteger BI_LARGE_VALUE1 = + BI_TWO.pow(UInt384.SIZE - 27).subtract(BigInteger.ONE); + private static final BigInteger BI_LARGE_VALUE2 = + BI_TWO.pow(UInt384.SIZE - 28).subtract(BigInteger.ONE); + + private static final UInt384 UI_SMALL_VALUE = fromBigInt(BI_SMALL_VALUE); + private static final UInt384 UI_LARGE_VALUE1 = fromBigInt(BI_LARGE_VALUE1); + private static final UInt384 UI_LARGE_VALUE2 = fromBigInt(BI_LARGE_VALUE2); + + static UInt384 fromBigInt(BigInteger bi) { + return UInt384.from(bi.toByteArray()); + } + + @Benchmark + public void addLargeLargeInt384(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.add(UI_LARGE_VALUE2)); + } + + @Benchmark + public void addLargeLargeBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.add(BI_LARGE_VALUE2)); + } + + @Benchmark + public void subLargeLargeInt384(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.subtract(UI_LARGE_VALUE2)); + } + + @Benchmark + public void subLargeLargeBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.subtract(BI_LARGE_VALUE2)); + } + + @Benchmark + public void mulLargeSmallInt384(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.multiply(UI_SMALL_VALUE)); + } + + @Benchmark + public void mulLargeSmallBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.multiply(BI_SMALL_VALUE)); + } + + @Benchmark + public void divLargeSmallInt384(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.divide(UI_SMALL_VALUE)); + } + + @Benchmark + public void divLargeSmallBigInt(Blackhole bh) { + bh.consume(BI_LARGE_VALUE1.divide(BI_SMALL_VALUE)); + } + + @Benchmark + public void sqrtLargeInt384(Blackhole bh) { + bh.consume(UI_LARGE_VALUE1.isqrt()); + } + + @Benchmark + public void sqrtLargeBigInt(Blackhole bh) { + bh.consume(BigIntegerMath.sqrt(BI_LARGE_VALUE1, RoundingMode.FLOOR)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusModule.java index 657f0418cd..817365c41d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusModule.java @@ -1,309 +1,296 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt; - -import com.google.common.util.concurrent.RateLimiter; -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.Scopes; -import com.google.inject.Singleton; -import com.google.inject.TypeLiteral; -import com.google.inject.multibindings.Multibinder; -import com.radixdlt.consensus.BFTConfiguration; -import com.radixdlt.consensus.BFTEventProcessor; -import com.radixdlt.consensus.BFTFactory; -import com.radixdlt.consensus.HashVerifier; -import com.radixdlt.consensus.LedgerProof; -import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.bft.BFTHighQCUpdate; -import com.radixdlt.consensus.bft.BFTRebuildUpdate; -import com.radixdlt.consensus.bft.BFTInsertUpdate; -import com.radixdlt.consensus.bft.NoVote; -import com.radixdlt.consensus.bft.Self; -import com.radixdlt.consensus.bft.ViewQuorumReached; -import com.radixdlt.consensus.bft.ViewUpdate; -import com.radixdlt.consensus.liveness.LocalTimeoutOccurrence; -import com.radixdlt.consensus.liveness.PacemakerReducer; -import com.radixdlt.consensus.liveness.ProposerElection; -import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -import com.radixdlt.consensus.liveness.ExponentialPacemakerTimeoutCalculator; -import com.radixdlt.consensus.liveness.PacemakerState; -import com.radixdlt.consensus.liveness.PacemakerTimeoutCalculator; -import com.radixdlt.consensus.sync.GetVerticesRequest; -import com.radixdlt.consensus.sync.VertexRequestTimeout; -import com.radixdlt.crypto.Hasher; -import com.radixdlt.consensus.Ledger; -import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.bft.BFTBuilder; -import com.radixdlt.consensus.bft.BFTCommittedUpdate; -import com.radixdlt.consensus.safety.SafetyRules; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.BFTValidatorSet; -import com.radixdlt.consensus.liveness.NextTxnsGenerator; -import com.radixdlt.consensus.liveness.Pacemaker; -import com.radixdlt.consensus.sync.BFTSync; -import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; -import com.radixdlt.consensus.sync.VertexStoreBFTSyncRequestProcessor; -import com.radixdlt.consensus.bft.VertexStore; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.environment.EventDispatcher; -import com.radixdlt.environment.LocalEvents; -import com.radixdlt.environment.RemoteEventDispatcher; -import com.radixdlt.environment.ScheduledEventDispatcher; -import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; -import com.radixdlt.utils.TimeSupplier; -import com.radixdlt.store.LastProof; -import com.radixdlt.sync.messages.local.LocalSyncRequest; -import java.util.Comparator; -import java.util.Random; - -/** - * Module responsible for running BFT validator logic - */ -public final class ConsensusModule extends AbstractModule { - - @Override - public void configure() { - bind(SafetyRules.class).in(Scopes.SINGLETON); - bind(PacemakerState.class).in(Scopes.SINGLETON); - bind(PacemakerReducer.class).to(PacemakerState.class); - bind(ExponentialPacemakerTimeoutCalculator.class).in(Scopes.SINGLETON); - bind(PacemakerTimeoutCalculator.class).to(ExponentialPacemakerTimeoutCalculator.class); - bind(VertexStoreBFTSyncRequestProcessor.class).in(Scopes.SINGLETON); - - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(ViewUpdate.class); - eventBinder.addBinding().toInstance(BFTRebuildUpdate.class); - eventBinder.addBinding().toInstance(BFTInsertUpdate.class); - eventBinder.addBinding().toInstance(Proposal.class); - eventBinder.addBinding().toInstance(Vote.class); - } - - @Provides - private BFTFactory bftFactory( - Hasher hasher, - HashVerifier verifier, - EventDispatcher viewQuorumReachedEventDispatcher, - EventDispatcher noVoteEventDispatcher, - RemoteEventDispatcher voteDispatcher - ) { - return ( - self, - pacemaker, - vertexStore, - bftSyncer, - viewQuorumReachedEventProcessor, - validatorSet, - viewUpdate, - safetyRules - ) -> - BFTBuilder.create() - .self(self) - .hasher(hasher) - .verifier(verifier) - .voteDispatcher(voteDispatcher) - .safetyRules(safetyRules) - .pacemaker(pacemaker) - .vertexStore(vertexStore) - .viewQuorumReachedEventDispatcher(viewQuorumReached -> { - // FIXME: a hack for now until replacement of epochmanager factories - viewQuorumReachedEventProcessor.process(viewQuorumReached); - viewQuorumReachedEventDispatcher.dispatch(viewQuorumReached); - }) - .noVoteEventDispatcher(noVoteEventDispatcher) - .viewUpdate(viewUpdate) - .bftSyncer(bftSyncer) - .validatorSet(validatorSet) - .build(); - } - - @Provides - @Singleton - public ProposerElection proposerElection(BFTConfiguration configuration) { - return configuration.getProposerElection(); - } - - @Provides - @Singleton - public BFTEventProcessor eventProcessor( - @Self BFTNode self, - BFTConfiguration config, - BFTFactory bftFactory, - Pacemaker pacemaker, - VertexStore vertexStore, - BFTSync bftSync, - SafetyRules safetyRules, - ViewUpdate viewUpdate - ) { - return bftFactory.create( - self, - pacemaker, - vertexStore, - bftSync, - bftSync.viewQuorumReachedEventProcessor(), - config.getValidatorSet(), - viewUpdate, - safetyRules - ); - } - - - @Provides - @Singleton - private Pacemaker pacemaker( - @Self BFTNode self, - SafetyRules safetyRules, - SystemCounters counters, - BFTConfiguration configuration, - VertexStore vertexStore, - EventDispatcher timeoutDispatcher, - ScheduledEventDispatcher timeoutSender, - PacemakerTimeoutCalculator timeoutCalculator, - NextTxnsGenerator nextTxnsGenerator, - Hasher hasher, - RemoteEventDispatcher proposalDispatcher, - RemoteEventDispatcher voteDispatcher, - TimeSupplier timeSupplier, - ViewUpdate initialViewUpdate, - SystemCounters systemCounters - ) { - BFTValidatorSet validatorSet = configuration.getValidatorSet(); - return new Pacemaker( - self, - counters, - validatorSet, - vertexStore, - safetyRules, - timeoutDispatcher, - timeoutSender, - timeoutCalculator, - nextTxnsGenerator, - proposalDispatcher, - voteDispatcher, - hasher, - timeSupplier, - initialViewUpdate, - systemCounters - ); - } - - - @Provides - @Singleton - private BFTSync bftSync( - @Self BFTNode self, - @GetVerticesRequestRateLimit RateLimiter syncRequestRateLimiter, - VertexStore vertexStore, - PacemakerReducer pacemakerReducer, - RemoteEventDispatcher requestSender, - EventDispatcher syncLedgerRequestSender, - ScheduledEventDispatcher timeoutDispatcher, - @LastProof LedgerProof ledgerLastProof, // Use this instead of configuration.getRoot() - Random random, - @BFTSyncPatienceMillis int bftSyncPatienceMillis, - Hasher hasher, - SystemCounters counters - ) { - return new BFTSync( - self, - syncRequestRateLimiter, - vertexStore, - hasher, - pacemakerReducer, - Comparator.comparingLong((LedgerHeader h) -> h.getAccumulatorState().getStateVersion()), - requestSender, - syncLedgerRequestSender, - timeoutDispatcher, - ledgerLastProof, - random, - bftSyncPatienceMillis, - counters - ); - } - - @Provides - @Singleton - private VertexStore vertexStore( - EventDispatcher updateSender, - EventDispatcher rebuildUpdateDispatcher, - EventDispatcher highQCUpdateEventDispatcher, - EventDispatcher committedSender, - BFTConfiguration bftConfiguration, - Ledger ledger, - Hasher hasher - ) { - return VertexStore.create( - bftConfiguration.getVertexStoreState(), - ledger, - hasher, - updateSender, - rebuildUpdateDispatcher, - highQCUpdateEventDispatcher, - committedSender - ); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt; + +import com.google.common.util.concurrent.RateLimiter; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.Multibinder; +import com.radixdlt.consensus.BFTConfiguration; +import com.radixdlt.consensus.BFTEventProcessor; +import com.radixdlt.consensus.BFTFactory; +import com.radixdlt.consensus.HashVerifier; +import com.radixdlt.consensus.Ledger; +import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.consensus.Proposal; +import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.bft.BFTBuilder; +import com.radixdlt.consensus.bft.BFTCommittedUpdate; +import com.radixdlt.consensus.bft.BFTHighQCUpdate; +import com.radixdlt.consensus.bft.BFTInsertUpdate; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.BFTRebuildUpdate; +import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.NoVote; +import com.radixdlt.consensus.bft.Self; +import com.radixdlt.consensus.bft.VertexStore; +import com.radixdlt.consensus.bft.ViewQuorumReached; +import com.radixdlt.consensus.bft.ViewUpdate; +import com.radixdlt.consensus.liveness.ExponentialPacemakerTimeoutCalculator; +import com.radixdlt.consensus.liveness.LocalTimeoutOccurrence; +import com.radixdlt.consensus.liveness.NextTxnsGenerator; +import com.radixdlt.consensus.liveness.Pacemaker; +import com.radixdlt.consensus.liveness.PacemakerReducer; +import com.radixdlt.consensus.liveness.PacemakerState; +import com.radixdlt.consensus.liveness.PacemakerTimeoutCalculator; +import com.radixdlt.consensus.liveness.ProposerElection; +import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; +import com.radixdlt.consensus.safety.SafetyRules; +import com.radixdlt.consensus.sync.BFTSync; +import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; +import com.radixdlt.consensus.sync.GetVerticesRequest; +import com.radixdlt.consensus.sync.VertexRequestTimeout; +import com.radixdlt.consensus.sync.VertexStoreBFTSyncRequestProcessor; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.crypto.Hasher; +import com.radixdlt.environment.EventDispatcher; +import com.radixdlt.environment.LocalEvents; +import com.radixdlt.environment.RemoteEventDispatcher; +import com.radixdlt.environment.ScheduledEventDispatcher; +import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; +import com.radixdlt.store.LastProof; +import com.radixdlt.sync.messages.local.LocalSyncRequest; +import com.radixdlt.utils.TimeSupplier; +import java.util.Comparator; +import java.util.Random; + +/** Module responsible for running BFT validator logic */ +public final class ConsensusModule extends AbstractModule { + + @Override + public void configure() { + bind(SafetyRules.class).in(Scopes.SINGLETON); + bind(PacemakerState.class).in(Scopes.SINGLETON); + bind(PacemakerReducer.class).to(PacemakerState.class); + bind(ExponentialPacemakerTimeoutCalculator.class).in(Scopes.SINGLETON); + bind(PacemakerTimeoutCalculator.class).to(ExponentialPacemakerTimeoutCalculator.class); + bind(VertexStoreBFTSyncRequestProcessor.class).in(Scopes.SINGLETON); + + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(ViewUpdate.class); + eventBinder.addBinding().toInstance(BFTRebuildUpdate.class); + eventBinder.addBinding().toInstance(BFTInsertUpdate.class); + eventBinder.addBinding().toInstance(Proposal.class); + eventBinder.addBinding().toInstance(Vote.class); + } + + @Provides + private BFTFactory bftFactory( + Hasher hasher, + HashVerifier verifier, + EventDispatcher viewQuorumReachedEventDispatcher, + EventDispatcher noVoteEventDispatcher, + RemoteEventDispatcher voteDispatcher) { + return (self, + pacemaker, + vertexStore, + bftSyncer, + viewQuorumReachedEventProcessor, + validatorSet, + viewUpdate, + safetyRules) -> + BFTBuilder.create() + .self(self) + .hasher(hasher) + .verifier(verifier) + .voteDispatcher(voteDispatcher) + .safetyRules(safetyRules) + .pacemaker(pacemaker) + .vertexStore(vertexStore) + .viewQuorumReachedEventDispatcher( + viewQuorumReached -> { + // FIXME: a hack for now until replacement of epochmanager factories + viewQuorumReachedEventProcessor.process(viewQuorumReached); + viewQuorumReachedEventDispatcher.dispatch(viewQuorumReached); + }) + .noVoteEventDispatcher(noVoteEventDispatcher) + .viewUpdate(viewUpdate) + .bftSyncer(bftSyncer) + .validatorSet(validatorSet) + .build(); + } + + @Provides + @Singleton + public ProposerElection proposerElection(BFTConfiguration configuration) { + return configuration.getProposerElection(); + } + + @Provides + @Singleton + public BFTEventProcessor eventProcessor( + @Self BFTNode self, + BFTConfiguration config, + BFTFactory bftFactory, + Pacemaker pacemaker, + VertexStore vertexStore, + BFTSync bftSync, + SafetyRules safetyRules, + ViewUpdate viewUpdate) { + return bftFactory.create( + self, + pacemaker, + vertexStore, + bftSync, + bftSync.viewQuorumReachedEventProcessor(), + config.getValidatorSet(), + viewUpdate, + safetyRules); + } + + @Provides + @Singleton + private Pacemaker pacemaker( + @Self BFTNode self, + SafetyRules safetyRules, + SystemCounters counters, + BFTConfiguration configuration, + VertexStore vertexStore, + EventDispatcher timeoutDispatcher, + ScheduledEventDispatcher timeoutSender, + PacemakerTimeoutCalculator timeoutCalculator, + NextTxnsGenerator nextTxnsGenerator, + Hasher hasher, + RemoteEventDispatcher proposalDispatcher, + RemoteEventDispatcher voteDispatcher, + TimeSupplier timeSupplier, + ViewUpdate initialViewUpdate, + SystemCounters systemCounters) { + BFTValidatorSet validatorSet = configuration.getValidatorSet(); + return new Pacemaker( + self, + counters, + validatorSet, + vertexStore, + safetyRules, + timeoutDispatcher, + timeoutSender, + timeoutCalculator, + nextTxnsGenerator, + proposalDispatcher, + voteDispatcher, + hasher, + timeSupplier, + initialViewUpdate, + systemCounters); + } + + @Provides + @Singleton + private BFTSync bftSync( + @Self BFTNode self, + @GetVerticesRequestRateLimit RateLimiter syncRequestRateLimiter, + VertexStore vertexStore, + PacemakerReducer pacemakerReducer, + RemoteEventDispatcher requestSender, + EventDispatcher syncLedgerRequestSender, + ScheduledEventDispatcher timeoutDispatcher, + @LastProof LedgerProof ledgerLastProof, // Use this instead of configuration.getRoot() + Random random, + @BFTSyncPatienceMillis int bftSyncPatienceMillis, + Hasher hasher, + SystemCounters counters) { + return new BFTSync( + self, + syncRequestRateLimiter, + vertexStore, + hasher, + pacemakerReducer, + Comparator.comparingLong((LedgerHeader h) -> h.getAccumulatorState().getStateVersion()), + requestSender, + syncLedgerRequestSender, + timeoutDispatcher, + ledgerLastProof, + random, + bftSyncPatienceMillis, + counters); + } + + @Provides + @Singleton + private VertexStore vertexStore( + EventDispatcher updateSender, + EventDispatcher rebuildUpdateDispatcher, + EventDispatcher highQCUpdateEventDispatcher, + EventDispatcher committedSender, + BFTConfiguration bftConfiguration, + Ledger ledger, + Hasher hasher) { + return VertexStore.create( + bftConfiguration.getVertexStoreState(), + ledger, + hasher, + updateSender, + rebuildUpdateDispatcher, + highQCUpdateEventDispatcher, + committedSender); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusRecoveryModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusRecoveryModule.java index d69ca1531c..e369af492e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusRecoveryModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusRecoveryModule.java @@ -78,64 +78,59 @@ import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.store.LastEpochProof; - import java.util.Optional; -/** - * Manages consensus recovery on startup - */ +/** Manages consensus recovery on startup */ public class ConsensusRecoveryModule extends AbstractModule { - @Provides - private ViewUpdate view( - VerifiedVertexStoreState vertexStoreState, - BFTConfiguration configuration - ) { - var highQC = vertexStoreState.getHighQC(); - var view = highQC.highestQC().getView().next(); - var proposerElection = configuration.getProposerElection(); - var leader = proposerElection.getProposer(view); - var nextLeader = proposerElection.getProposer(view.next()); + @Provides + private ViewUpdate view( + VerifiedVertexStoreState vertexStoreState, BFTConfiguration configuration) { + var highQC = vertexStoreState.getHighQC(); + var view = highQC.highestQC().getView().next(); + var proposerElection = configuration.getProposerElection(); + var leader = proposerElection.getProposer(view); + var nextLeader = proposerElection.getProposer(view.next()); - return ViewUpdate.create(view, highQC, leader, nextLeader); - } + return ViewUpdate.create(view, highQC, leader, nextLeader); + } - @Provides - @Singleton - private BFTConfiguration initialConfig( - BFTValidatorSet validatorSet, - VerifiedVertexStoreState vertexStoreState - ) { - var proposerElection = new WeightedRotatingLeaders(validatorSet); - return new BFTConfiguration(proposerElection, validatorSet, vertexStoreState); - } + @Provides + @Singleton + private BFTConfiguration initialConfig( + BFTValidatorSet validatorSet, VerifiedVertexStoreState vertexStoreState) { + var proposerElection = new WeightedRotatingLeaders(validatorSet); + return new BFTConfiguration(proposerElection, validatorSet, vertexStoreState); + } - @Provides - private BFTValidatorSet validatorSet( - @LastEpochProof LedgerProof lastEpochProof - ) { - return lastEpochProof.getNextValidatorSet().orElseThrow(() -> new IllegalStateException("Genesis has no validator set")); - } + @Provides + private BFTValidatorSet validatorSet(@LastEpochProof LedgerProof lastEpochProof) { + return lastEpochProof + .getNextValidatorSet() + .orElseThrow(() -> new IllegalStateException("Genesis has no validator set")); + } - @Provides - @Singleton - private SafetyState safetyState(EpochChange initialEpoch, PersistentSafetyStateStore safetyStore) { - return safetyStore.get().flatMap(safetyState -> { - final long safetyStateEpoch = - safetyState.getLastVote().map(Vote::getEpoch).orElse(0L); + @Provides + @Singleton + private SafetyState safetyState( + EpochChange initialEpoch, PersistentSafetyStateStore safetyStore) { + return safetyStore + .get() + .flatMap( + safetyState -> { + final long safetyStateEpoch = + safetyState.getLastVote().map(Vote::getEpoch).orElse(0L); - if (safetyStateEpoch > initialEpoch.getEpoch()) { - throw new IllegalStateException( - String.format( - "Last vote is in a future epoch. Vote epoch: %s, Epoch: %s", - safetyStateEpoch, - initialEpoch.getEpoch() - ) - ); - } else if (safetyStateEpoch == initialEpoch.getEpoch()) { - return Optional.of(safetyState); - } else { - return Optional.empty(); - } - }).orElse(new SafetyState()); - } + if (safetyStateEpoch > initialEpoch.getEpoch()) { + throw new IllegalStateException( + String.format( + "Last vote is in a future epoch. Vote epoch: %s, Epoch: %s", + safetyStateEpoch, initialEpoch.getEpoch())); + } else if (safetyStateEpoch == initialEpoch.getEpoch()) { + return Optional.of(safetyState); + } else { + return Optional.empty(); + } + }) + .orElse(new SafetyState()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/CryptoModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/CryptoModule.java index 0a1c94bd88..a20672a021 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/CryptoModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/CryptoModule.java @@ -69,55 +69,51 @@ import com.google.inject.Provides; import com.google.inject.Singleton; import com.radixdlt.consensus.HashVerifier; +import com.radixdlt.consensus.Sha256Hasher; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.Hasher; -import com.radixdlt.consensus.Sha256Hasher; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.Serialization; -/** - * Module which maintains crypto primitives for consensus - */ +/** Module which maintains crypto primitives for consensus */ public final class CryptoModule extends AbstractModule { - @Override - protected void configure() { - // Configuration - bind(Serialization.class).toProvider(DefaultSerialization::getInstance); - } - - - @Provides - Hasher hasher(Serialization serialization, SystemCounters counters) { - return new Hasher() { - private Sha256Hasher hasher = new Sha256Hasher(serialization); + @Override + protected void configure() { + // Configuration + bind(Serialization.class).toProvider(DefaultSerialization::getInstance); + } - @Override - public int bytes() { - return 32; - } + @Provides + Hasher hasher(Serialization serialization, SystemCounters counters) { + return new Hasher() { + private Sha256Hasher hasher = new Sha256Hasher(serialization); - @Override - public HashCode hash(Object o) { - // Call hashBytes to ensure counters incremented - return this.hashBytes(serialization.toDson(o, Output.HASH)); - } + @Override + public int bytes() { + return 32; + } - @Override - public HashCode hashBytes(byte[] bytes) { - counters.add(CounterType.HASHED_BYTES, bytes.length); - return hasher.hashBytes(bytes); - } - }; - } + @Override + public HashCode hash(Object o) { + // Call hashBytes to ensure counters incremented + return this.hashBytes(serialization.toDson(o, Output.HASH)); + } - @Provides - @Singleton - HashVerifier hashVerifier(SystemCounters counters) { - return (pubKey, hash, signature) -> { - counters.increment(CounterType.SIGNATURES_VERIFIED); - return pubKey.verify(hash, signature); - }; - } + @Override + public HashCode hashBytes(byte[] bytes) { + counters.add(CounterType.HASHED_BYTES, bytes.length); + return hasher.hashBytes(bytes); + } + }; + } + @Provides + @Singleton + HashVerifier hashVerifier(SystemCounters counters) { + return (pubKey, hash, signature) -> { + counters.increment(CounterType.SIGNATURES_VERIFIED); + return pubKey.verify(hash, signature); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/DispatcherModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/DispatcherModule.java index 27dd58d8b1..6c3a5ef87d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/DispatcherModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/DispatcherModule.java @@ -64,32 +64,14 @@ package com.radixdlt; -import com.radixdlt.api.system.health.ScheduledStatsCollecting; -import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.sync.GetVerticesErrorResponse; -import com.radixdlt.consensus.sync.GetVerticesResponse; -import com.radixdlt.mempool.MempoolRelayTrigger; -import com.radixdlt.network.p2p.PendingOutboundChannelsManager.PeerOutboundConnectionTimeout; -import com.radixdlt.network.p2p.discovery.DiscoverPeers; -import com.radixdlt.network.p2p.PeerEvent; -import com.radixdlt.network.p2p.discovery.GetPeers; -import com.radixdlt.network.p2p.discovery.PeersResponse; -import com.radixdlt.network.p2p.liveness.PeerPingTimeout; -import com.radixdlt.network.p2p.liveness.PeersLivenessCheckTrigger; -import com.radixdlt.network.p2p.liveness.Ping; -import com.radixdlt.network.p2p.liveness.Pong; -import com.radixdlt.statecomputer.REOutput; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Scopes; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.Multibinder; -import com.radixdlt.mempoolfiller.MempoolFillerUpdate; -import com.radixdlt.mempoolfiller.ScheduledMempoolFill; +import com.radixdlt.api.system.health.ScheduledStatsCollecting; +import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTCommittedUpdate; import com.radixdlt.consensus.bft.BFTHighQCUpdate; @@ -106,7 +88,9 @@ import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.consensus.liveness.LocalTimeoutOccurrence; import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; +import com.radixdlt.consensus.sync.GetVerticesErrorResponse; import com.radixdlt.consensus.sync.GetVerticesRequest; +import com.radixdlt.consensus.sync.GetVerticesResponse; import com.radixdlt.consensus.sync.VertexRequestTimeout; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; @@ -122,7 +106,20 @@ import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.mempool.MempoolAdd; import com.radixdlt.mempool.MempoolAddSuccess; +import com.radixdlt.mempool.MempoolRelayTrigger; +import com.radixdlt.mempoolfiller.MempoolFillerUpdate; +import com.radixdlt.mempoolfiller.ScheduledMempoolFill; +import com.radixdlt.network.p2p.PeerEvent; +import com.radixdlt.network.p2p.PendingOutboundChannelsManager.PeerOutboundConnectionTimeout; +import com.radixdlt.network.p2p.discovery.DiscoverPeers; +import com.radixdlt.network.p2p.discovery.GetPeers; +import com.radixdlt.network.p2p.discovery.PeersResponse; +import com.radixdlt.network.p2p.liveness.PeerPingTimeout; +import com.radixdlt.network.p2p.liveness.PeersLivenessCheckTrigger; +import com.radixdlt.network.p2p.liveness.Ping; +import com.radixdlt.network.p2p.liveness.Pong; import com.radixdlt.statecomputer.InvalidProposedTxn; +import com.radixdlt.statecomputer.REOutput; import com.radixdlt.statecomputer.TxnsRemovedFromMempool; import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.sync.messages.local.SyncCheckReceiveStatusTimeout; @@ -134,329 +131,355 @@ import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; - import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * Manages dispatching of internal events to a given environment - * TODO: Move all other events into this module + * Manages dispatching of internal events to a given environment TODO: Move all other events into + * this module */ public class DispatcherModule extends AbstractModule { - private static final Logger logger = LogManager.getLogger(); - - @Override - public void configure() { - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(MempoolAdd.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(MempoolAddSuccess.class)).in(Scopes.SINGLETON); - - // TODO: Remove, this hack required for initial genesis event emit - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(REOutput.class)).in(Scopes.SINGLETON); - - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(TxnsRemovedFromMempool.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(MempoolRelayTrigger.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(MempoolFillerUpdate.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(ScheduledMempoolFill.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(NoVote.class, v -> CounterType.BFT_NO_VOTES_SENT)) - .in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider( - InvalidProposedTxn.class, - v -> CounterType.RADIX_ENGINE_INVALID_PROPOSED_COMMANDS - )).in(Scopes.SINGLETON); - bind(new TypeLiteral>>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(new TypeLiteral>() { })) - .in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(VertexRequestTimeout.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(SyncRequestTimeout.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(SyncLedgerUpdateTimeout.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(SyncCheckReceiveStatusTimeout.class)) - .in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(SyncCheckTrigger.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(ScheduledMempoolFill.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(ScheduledStatsCollecting.class)).in(Scopes.SINGLETON); - - // BFT - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(Proposal.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(Vote.class)).in(Scopes.SINGLETON); - - // BFT Sync - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(GetVerticesResponse.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(GetVerticesErrorResponse.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(MempoolAdd.class)).in(Scopes.SINGLETON); - - final var scheduledTimeoutKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), scheduledTimeoutKey, ProcessOnDispatch.class); - Multibinder.newSetBinder(binder(), scheduledTimeoutKey); - - final var syncRequestKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), syncRequestKey, ProcessOnDispatch.class); - Multibinder.newSetBinder(binder(), syncRequestKey); - - final var timeoutOccurrenceKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), timeoutOccurrenceKey, ProcessOnDispatch.class); - Multibinder.newSetBinder(binder(), timeoutOccurrenceKey); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(EpochLocalTimeoutOccurrence.class)).in(Scopes.SINGLETON); - - final var viewUpdateKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), viewUpdateKey, ProcessOnDispatch.class); - Multibinder.newSetBinder(binder(), viewUpdateKey); - - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(EpochViewUpdate.class)).in(Scopes.SINGLETON); - - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(LedgerUpdate.class)).in(Scopes.SINGLETON); - - final var insertUpdateKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), insertUpdateKey, ProcessOnDispatch.class); - final var highQcUpdateKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), highQcUpdateKey, ProcessOnDispatch.class); - Multibinder.newSetBinder(binder(), highQcUpdateKey); - final var committedUpdateKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), committedUpdateKey); - Multibinder.newSetBinder(binder(), committedUpdateKey, ProcessOnDispatch.class); - final var syncUpdateKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), syncUpdateKey, ProcessOnDispatch.class); - - final var verticesRequestKey = new TypeLiteral>() { }; - Multibinder.newSetBinder(binder(), verticesRequestKey, ProcessOnDispatch.class); - - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider( - ViewQuorumReached.class, - v -> { - if (v.votingResult() instanceof ViewVotingResult.FormedTC) { - return CounterType.BFT_TIMEOUT_QUORUMS; - } - return CounterType.BFT_VOTE_QUORUMS; - } - )); - - - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }); - - configureP2p(); - configureSync(); - } - - private void configureP2p() { - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(PeerEvent.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(PeersLivenessCheckTrigger.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(PeerPingTimeout.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.scheduledDispatcherProvider(PeerOutboundConnectionTimeout.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(Ping.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(Pong.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(GetPeers.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(PeersResponse.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.dispatcherProvider(DiscoverPeers.class)).in(Scopes.SINGLETON); - } - - private void configureSync() { - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(StatusRequest.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(StatusResponse.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(SyncRequest.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(SyncResponse.class)).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .toProvider(Dispatchers.remoteDispatcherProvider(LedgerStatusUpdate.class)).in(Scopes.SINGLETON); - } - - @Provides - private EventDispatcher localSyncRequestEventDispatcher( - @Self BFTNode self, - @ProcessOnDispatch Set> syncProcessors, - Environment environment, - SystemCounters systemCounters - ) { - var envDispatcher = environment.getDispatcher(LocalSyncRequest.class); - return req -> { - if (logger.isTraceEnabled()) { - var callingClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass(); - logger.trace("LOCAL_SYNC_REQUEST dispatched by {}", callingClass); - } - - if (req.getTargetNodes().contains(self)) { - throw new IllegalStateException("Should not be targeting self."); - } - - long stateVersion = systemCounters.get(CounterType.SYNC_TARGET_STATE_VERSION); - if (req.getTarget().getStateVersion() > stateVersion) { - systemCounters.set(CounterType.SYNC_TARGET_STATE_VERSION, req.getTarget().getStateVersion()); - } - - syncProcessors.forEach(e -> e.process(req)); - envDispatcher.dispatch(req); - }; - } - - @Provides - private ScheduledEventDispatcher scheduledTimeoutDispatcher( - @ProcessOnDispatch Set> processors, - Environment environment - ) { - var dispatcher = environment.getScheduledDispatcher(ScheduledLocalTimeout.class); - return (timeout, ms) -> { - dispatcher.dispatch(timeout, ms); - processors.forEach(e -> e.process(timeout)); - }; - } - - @Provides - private EventDispatcher viewEventDispatcher( - @ProcessOnDispatch Set> processors, - Environment environment, - SystemCounters systemCounters - ) { - var dispatcher = environment.getDispatcher(BFTInsertUpdate.class); - return update -> { - if (update.getSiblingsCount() > 1) { - systemCounters.increment(CounterType.BFT_VERTEX_STORE_FORKS); - } - if (!update.getInserted().getVertex().hasDirectParent()) { - systemCounters.increment(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS); - } - systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, update.getVertexStoreSize()); - dispatcher.dispatch(update); - processors.forEach(p -> p.process(update)); - }; - } - - @Provides - private EventDispatcher bftRebuildUpdateEventDispatcher( - Environment environment, - SystemCounters systemCounters - ) { - var dispatcher = environment.getDispatcher(BFTRebuildUpdate.class); - return update -> { - systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, update.getVertexStoreState().getVertices().size()); - systemCounters.increment(CounterType.BFT_VERTEX_STORE_REBUILDS); - dispatcher.dispatch(update); - }; - } - - @Provides - private EventDispatcher bftHighQCUpdateEventDispatcher( - @ProcessOnDispatch Set> processors, - Environment environment - ) { - var dispatcher = environment.getDispatcher(BFTHighQCUpdate.class); - return update -> { - dispatcher.dispatch(update); - processors.forEach(p -> p.process(update)); - }; - } - - @Provides - private EventDispatcher syncUpdateEventDispatcher( - @ProcessOnDispatch Set> processors, - SystemCounters systemCounters - ) { - return commit -> { - systemCounters.add(CounterType.SYNC_VALID_RESPONSES_RECEIVED, commit.getTxns().size()); - processors.forEach(e -> e.process(commit)); - }; - } - - @Provides - private EventDispatcher committedUpdateEventDispatcher( - @ProcessOnDispatch Set> processors, - Set> asyncProcessors, - Environment environment, - SystemCounters systemCounters - ) { - if (asyncProcessors.isEmpty()) { - return commit -> { - systemCounters.add(CounterType.BFT_COMMITTED_VERTICES, commit.getCommitted().size()); - systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, commit.getVertexStoreSize()); - processors.forEach(e -> e.process(commit)); - }; - } else { - var dispatcher = environment.getDispatcher(BFTCommittedUpdate.class); - return commit -> { - systemCounters.add(CounterType.BFT_COMMITTED_VERTICES, commit.getCommitted().size()); - systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, commit.getVertexStoreSize()); - processors.forEach(e -> e.process(commit)); - dispatcher.dispatch(commit); - }; - } - } - - - @Provides - private EventDispatcher localConsensusTimeoutDispatcher( - @ProcessOnDispatch Set> syncProcessors, - Set> asyncProcessors, - Environment environment - ) { - if (asyncProcessors.isEmpty()) { - return viewTimeout -> syncProcessors.forEach(e -> e.process(viewTimeout)); - } else { - var dispatcher = environment.getDispatcher(LocalTimeoutOccurrence.class); - return timeout -> { - syncProcessors.forEach(e -> e.process(timeout)); - dispatcher.dispatch(timeout); - }; - } - } - - @Provides - private RemoteEventDispatcher verticesRequestDispatcher( - @ProcessOnDispatch Set> processors, - Environment environment, - SystemCounters counters - ) { - var dispatcher = environment.getRemoteDispatcher(GetVerticesRequest.class); - return (node, request) -> { - counters.increment(CounterType.BFT_SYNC_REQUESTS_SENT); - dispatcher.dispatch(node, request); - processors.forEach(e -> e.process(request)); - }; - } - - @Provides - @Singleton - private EventDispatcher viewUpdateEventDispatcher( - @ProcessOnDispatch Set> processors, - Environment environment - ) { - var dispatcher = environment.getDispatcher(ViewUpdate.class); - return viewUpdate -> { - processors.forEach(e -> e.process(viewUpdate)); - dispatcher.dispatch(viewUpdate); - }; - } - + private static final Logger logger = LogManager.getLogger(); + + @Override + public void configure() { + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(MempoolAdd.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(MempoolAddSuccess.class)) + .in(Scopes.SINGLETON); + + // TODO: Remove, this hack required for initial genesis event emit + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(REOutput.class)) + .in(Scopes.SINGLETON); + + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(TxnsRemovedFromMempool.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(MempoolRelayTrigger.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(MempoolFillerUpdate.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(ScheduledMempoolFill.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider( + Dispatchers.dispatcherProvider(NoVote.class, v -> CounterType.BFT_NO_VOTES_SENT)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider( + Dispatchers.dispatcherProvider( + InvalidProposedTxn.class, v -> CounterType.RADIX_ENGINE_INVALID_PROPOSED_COMMANDS)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>>() {}) + .toProvider( + Dispatchers.scheduledDispatcherProvider( + new TypeLiteral>() {})) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(VertexRequestTimeout.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(SyncRequestTimeout.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(SyncLedgerUpdateTimeout.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(SyncCheckReceiveStatusTimeout.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(SyncCheckTrigger.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(ScheduledMempoolFill.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(ScheduledStatsCollecting.class)) + .in(Scopes.SINGLETON); + + // BFT + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(Proposal.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(Vote.class)) + .in(Scopes.SINGLETON); + + // BFT Sync + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(GetVerticesResponse.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(GetVerticesErrorResponse.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(MempoolAdd.class)) + .in(Scopes.SINGLETON); + + final var scheduledTimeoutKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), scheduledTimeoutKey, ProcessOnDispatch.class); + Multibinder.newSetBinder(binder(), scheduledTimeoutKey); + + final var syncRequestKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), syncRequestKey, ProcessOnDispatch.class); + Multibinder.newSetBinder(binder(), syncRequestKey); + + final var timeoutOccurrenceKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), timeoutOccurrenceKey, ProcessOnDispatch.class); + Multibinder.newSetBinder(binder(), timeoutOccurrenceKey); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(EpochLocalTimeoutOccurrence.class)) + .in(Scopes.SINGLETON); + + final var viewUpdateKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), viewUpdateKey, ProcessOnDispatch.class); + Multibinder.newSetBinder(binder(), viewUpdateKey); + + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(EpochViewUpdate.class)) + .in(Scopes.SINGLETON); + + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(LedgerUpdate.class)) + .in(Scopes.SINGLETON); + + final var insertUpdateKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), insertUpdateKey, ProcessOnDispatch.class); + final var highQcUpdateKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), highQcUpdateKey, ProcessOnDispatch.class); + Multibinder.newSetBinder(binder(), highQcUpdateKey); + final var committedUpdateKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), committedUpdateKey); + Multibinder.newSetBinder(binder(), committedUpdateKey, ProcessOnDispatch.class); + final var syncUpdateKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), syncUpdateKey, ProcessOnDispatch.class); + + final var verticesRequestKey = new TypeLiteral>() {}; + Multibinder.newSetBinder(binder(), verticesRequestKey, ProcessOnDispatch.class); + + bind(new TypeLiteral>() {}) + .toProvider( + Dispatchers.dispatcherProvider( + ViewQuorumReached.class, + v -> { + if (v.votingResult() instanceof ViewVotingResult.FormedTC) { + return CounterType.BFT_TIMEOUT_QUORUMS; + } + return CounterType.BFT_VOTE_QUORUMS; + })); + + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}); + + configureP2p(); + configureSync(); + } + + private void configureP2p() { + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(PeerEvent.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(PeersLivenessCheckTrigger.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(PeerPingTimeout.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.scheduledDispatcherProvider(PeerOutboundConnectionTimeout.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(Ping.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(Pong.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(GetPeers.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(PeersResponse.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.dispatcherProvider(DiscoverPeers.class)) + .in(Scopes.SINGLETON); + } + + private void configureSync() { + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(StatusRequest.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(StatusResponse.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(SyncRequest.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(SyncResponse.class)) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .toProvider(Dispatchers.remoteDispatcherProvider(LedgerStatusUpdate.class)) + .in(Scopes.SINGLETON); + } + + @Provides + private EventDispatcher localSyncRequestEventDispatcher( + @Self BFTNode self, + @ProcessOnDispatch Set> syncProcessors, + Environment environment, + SystemCounters systemCounters) { + var envDispatcher = environment.getDispatcher(LocalSyncRequest.class); + return req -> { + if (logger.isTraceEnabled()) { + var callingClass = + StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass(); + logger.trace("LOCAL_SYNC_REQUEST dispatched by {}", callingClass); + } + + if (req.getTargetNodes().contains(self)) { + throw new IllegalStateException("Should not be targeting self."); + } + + long stateVersion = systemCounters.get(CounterType.SYNC_TARGET_STATE_VERSION); + if (req.getTarget().getStateVersion() > stateVersion) { + systemCounters.set( + CounterType.SYNC_TARGET_STATE_VERSION, req.getTarget().getStateVersion()); + } + + syncProcessors.forEach(e -> e.process(req)); + envDispatcher.dispatch(req); + }; + } + + @Provides + private ScheduledEventDispatcher scheduledTimeoutDispatcher( + @ProcessOnDispatch Set> processors, + Environment environment) { + var dispatcher = environment.getScheduledDispatcher(ScheduledLocalTimeout.class); + return (timeout, ms) -> { + dispatcher.dispatch(timeout, ms); + processors.forEach(e -> e.process(timeout)); + }; + } + + @Provides + private EventDispatcher viewEventDispatcher( + @ProcessOnDispatch Set> processors, + Environment environment, + SystemCounters systemCounters) { + var dispatcher = environment.getDispatcher(BFTInsertUpdate.class); + return update -> { + if (update.getSiblingsCount() > 1) { + systemCounters.increment(CounterType.BFT_VERTEX_STORE_FORKS); + } + if (!update.getInserted().getVertex().hasDirectParent()) { + systemCounters.increment(CounterType.BFT_VERTEX_STORE_INDIRECT_PARENTS); + } + systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, update.getVertexStoreSize()); + dispatcher.dispatch(update); + processors.forEach(p -> p.process(update)); + }; + } + + @Provides + private EventDispatcher bftRebuildUpdateEventDispatcher( + Environment environment, SystemCounters systemCounters) { + var dispatcher = environment.getDispatcher(BFTRebuildUpdate.class); + return update -> { + systemCounters.set( + CounterType.BFT_VERTEX_STORE_SIZE, update.getVertexStoreState().getVertices().size()); + systemCounters.increment(CounterType.BFT_VERTEX_STORE_REBUILDS); + dispatcher.dispatch(update); + }; + } + + @Provides + private EventDispatcher bftHighQCUpdateEventDispatcher( + @ProcessOnDispatch Set> processors, Environment environment) { + var dispatcher = environment.getDispatcher(BFTHighQCUpdate.class); + return update -> { + dispatcher.dispatch(update); + processors.forEach(p -> p.process(update)); + }; + } + + @Provides + private EventDispatcher syncUpdateEventDispatcher( + @ProcessOnDispatch Set> processors, + SystemCounters systemCounters) { + return commit -> { + systemCounters.add(CounterType.SYNC_VALID_RESPONSES_RECEIVED, commit.getTxns().size()); + processors.forEach(e -> e.process(commit)); + }; + } + + @Provides + private EventDispatcher committedUpdateEventDispatcher( + @ProcessOnDispatch Set> processors, + Set> asyncProcessors, + Environment environment, + SystemCounters systemCounters) { + if (asyncProcessors.isEmpty()) { + return commit -> { + systemCounters.add(CounterType.BFT_COMMITTED_VERTICES, commit.getCommitted().size()); + systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, commit.getVertexStoreSize()); + processors.forEach(e -> e.process(commit)); + }; + } else { + var dispatcher = environment.getDispatcher(BFTCommittedUpdate.class); + return commit -> { + systemCounters.add(CounterType.BFT_COMMITTED_VERTICES, commit.getCommitted().size()); + systemCounters.set(CounterType.BFT_VERTEX_STORE_SIZE, commit.getVertexStoreSize()); + processors.forEach(e -> e.process(commit)); + dispatcher.dispatch(commit); + }; + } + } + + @Provides + private EventDispatcher localConsensusTimeoutDispatcher( + @ProcessOnDispatch Set> syncProcessors, + Set> asyncProcessors, + Environment environment) { + if (asyncProcessors.isEmpty()) { + return viewTimeout -> syncProcessors.forEach(e -> e.process(viewTimeout)); + } else { + var dispatcher = environment.getDispatcher(LocalTimeoutOccurrence.class); + return timeout -> { + syncProcessors.forEach(e -> e.process(timeout)); + dispatcher.dispatch(timeout); + }; + } + } + + @Provides + private RemoteEventDispatcher verticesRequestDispatcher( + @ProcessOnDispatch Set> processors, + Environment environment, + SystemCounters counters) { + var dispatcher = environment.getRemoteDispatcher(GetVerticesRequest.class); + return (node, request) -> { + counters.increment(CounterType.BFT_SYNC_REQUESTS_SENT); + dispatcher.dispatch(node, request); + processors.forEach(e -> e.process(request)); + }; + } + + @Provides + @Singleton + private EventDispatcher viewUpdateEventDispatcher( + @ProcessOnDispatch Set> processors, Environment environment) { + var dispatcher = environment.getDispatcher(ViewUpdate.class); + return viewUpdate -> { + processors.forEach(e -> e.process(viewUpdate)); + dispatcher.dispatch(viewUpdate); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsConsensusModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsConsensusModule.java index bd8ade5ed4..03b6ad4657 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsConsensusModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsConsensusModule.java @@ -74,39 +74,39 @@ import com.radixdlt.consensus.BFTConfiguration; import com.radixdlt.consensus.Ledger; import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTCommittedUpdate; import com.radixdlt.consensus.bft.BFTHighQCUpdate; -import com.radixdlt.consensus.bft.BFTRebuildUpdate; import com.radixdlt.consensus.bft.BFTInsertUpdate; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.BFTRebuildUpdate; import com.radixdlt.consensus.bft.Self; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.bft.ViewUpdate; -import com.radixdlt.consensus.epoch.Epoched; -import com.radixdlt.consensus.epoch.EpochViewUpdate; -import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; -import com.radixdlt.consensus.LedgerProof; -import com.radixdlt.consensus.epoch.VertexStoreFactory; import com.radixdlt.consensus.epoch.BFTSyncFactory; import com.radixdlt.consensus.epoch.BFTSyncRequestProcessorFactory; -import com.radixdlt.consensus.liveness.LocalTimeoutOccurrence; -import com.radixdlt.consensus.liveness.Pacemaker; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.epoch.EpochChange; import com.radixdlt.consensus.epoch.EpochManager; +import com.radixdlt.consensus.epoch.EpochViewUpdate; +import com.radixdlt.consensus.epoch.Epoched; +import com.radixdlt.consensus.epoch.VertexStoreFactory; +import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; +import com.radixdlt.consensus.liveness.LocalTimeoutOccurrence; import com.radixdlt.consensus.liveness.NextTxnsGenerator; +import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.PacemakerFactory; import com.radixdlt.consensus.liveness.PacemakerState; import com.radixdlt.consensus.liveness.PacemakerStateFactory; import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; +import com.radixdlt.consensus.sync.BFTSync; import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; import com.radixdlt.consensus.sync.GetVerticesErrorResponse; import com.radixdlt.consensus.sync.GetVerticesRequest; import com.radixdlt.consensus.sync.GetVerticesResponse; import com.radixdlt.consensus.sync.VertexRequestTimeout; import com.radixdlt.consensus.sync.VertexStoreBFTSyncRequestProcessor; -import com.radixdlt.consensus.sync.BFTSync; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.EventDispatcher; @@ -121,304 +121,253 @@ import com.radixdlt.environment.StartProcessorOnRunner; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.store.LastEpochProof; import com.radixdlt.sync.messages.local.LocalSyncRequest; +import com.radixdlt.utils.TimeSupplier; import java.util.Comparator; import java.util.Random; -/** - * Module which allows for consensus to have multiple epochs - */ +/** Module which allows for consensus to have multiple epochs */ public class EpochsConsensusModule extends AbstractModule { - @Override - protected void configure() { - bind(EpochManager.class).in(Scopes.SINGLETON); - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(EpochViewUpdate.class); - eventBinder.addBinding().toInstance(VertexRequestTimeout.class); - eventBinder.addBinding().toInstance(LedgerUpdate.class); - eventBinder.addBinding().toInstance(Epoched.class); - } - - @ProvidesIntoSet - private StartProcessorOnRunner startProcessor(EpochManager epochManager) { - return new StartProcessorOnRunner( - Runners.CONSENSUS, - epochManager::start - ); - } + @Override + protected void configure() { + bind(EpochManager.class).in(Scopes.SINGLETON); + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(EpochViewUpdate.class); + eventBinder.addBinding().toInstance(VertexRequestTimeout.class); + eventBinder.addBinding().toInstance(LedgerUpdate.class); + eventBinder.addBinding().toInstance(Epoched.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner localVoteProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - Vote.class, - epochManager::processConsensusEvent - ); - } + @ProvidesIntoSet + private StartProcessorOnRunner startProcessor(EpochManager epochManager) { + return new StartProcessorOnRunner(Runners.CONSENSUS, epochManager::start); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner remoteVoteProcessor(EpochManager epochManager) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - Vote.class, - (node, vote) -> epochManager.processConsensusEvent(vote) - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner localVoteProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, Vote.class, epochManager::processConsensusEvent); + } - @ProvidesIntoSet - private EventProcessorOnRunner localProposalProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - Proposal.class, - epochManager::processConsensusEvent - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner remoteVoteProcessor(EpochManager epochManager) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, Vote.class, (node, vote) -> epochManager.processConsensusEvent(vote)); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner remoteProposalProcessor(EpochManager epochManager) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - Proposal.class, - (node, proposal) -> epochManager.processConsensusEvent(proposal) - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner localProposalProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, Proposal.class, epochManager::processConsensusEvent); + } - @ProvidesIntoSet - private EventProcessorOnRunner epochTimeoutProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - new TypeLiteral>() { }, - epochManager::processLocalTimeout - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner remoteProposalProcessor(EpochManager epochManager) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, + Proposal.class, + (node, proposal) -> epochManager.processConsensusEvent(proposal)); + } - @ProvidesIntoSet - private EventProcessorOnRunner epochsLedgerUpdateEventProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - LedgerUpdate.class, - epochManager.epochsLedgerUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner epochTimeoutProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, + new TypeLiteral>() {}, + epochManager::processLocalTimeout); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner localGetVerticesRequestRemoteEventProcessor(EpochManager epochManager) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - GetVerticesRequest.class, - epochManager.bftSyncRequestProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner epochsLedgerUpdateEventProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, LedgerUpdate.class, epochManager.epochsLedgerUpdateEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner responseRemoteEventProcessor(EpochManager epochManager) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - GetVerticesResponse.class, - epochManager.bftSyncResponseProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner localGetVerticesRequestRemoteEventProcessor( + EpochManager epochManager) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, GetVerticesRequest.class, epochManager.bftSyncRequestProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner errorResponseRemoteEventProcessor(EpochManager epochManager) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - GetVerticesErrorResponse.class, - epochManager.bftSyncErrorResponseProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner responseRemoteEventProcessor(EpochManager epochManager) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, GetVerticesResponse.class, epochManager.bftSyncResponseProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner bftUpdateProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - BFTInsertUpdate.class, - epochManager::processBFTUpdate - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner errorResponseRemoteEventProcessor( + EpochManager epochManager) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, + GetVerticesErrorResponse.class, + epochManager.bftSyncErrorResponseProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner bftRebuildUpdateEventProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - BFTRebuildUpdate.class, - epochManager.bftRebuildUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner bftUpdateProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, BFTInsertUpdate.class, epochManager::processBFTUpdate); + } - @ProvidesIntoSet - private EventProcessorOnRunner bftSyncTimeoutProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - VertexRequestTimeout.class, - epochManager.timeoutEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner bftRebuildUpdateEventProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, BFTRebuildUpdate.class, epochManager.bftRebuildUpdateEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner epochViewUpdateEventProcessor(EpochManager epochManager) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - EpochViewUpdate.class, - epochManager.epochViewUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner bftSyncTimeoutProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, VertexRequestTimeout.class, epochManager.timeoutEventProcessor()); + } - @Provides - private EpochChange initialEpoch( - @LastEpochProof LedgerProof proof, - BFTConfiguration initialBFTConfig - ) { - return new EpochChange(proof, initialBFTConfig); - } + @ProvidesIntoSet + private EventProcessorOnRunner epochViewUpdateEventProcessor(EpochManager epochManager) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, EpochViewUpdate.class, epochManager.epochViewUpdateEventProcessor()); + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor initialEpochsTimeoutSender( - ScheduledEventDispatcher> localTimeoutSender, - EpochChange initialEpoch - ) { - return localTimeout -> { - Epoched epochTimeout = Epoched.from(initialEpoch.getEpoch(), localTimeout); - localTimeoutSender.dispatch(epochTimeout, localTimeout.millisecondsWaitTime()); - }; - } + @Provides + private EpochChange initialEpoch( + @LastEpochProof LedgerProof proof, BFTConfiguration initialBFTConfig) { + return new EpochChange(proof, initialBFTConfig); + } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor initialEpochsTimeoutSender( + ScheduledEventDispatcher> localTimeoutSender, + EpochChange initialEpoch) { + return localTimeout -> { + Epoched epochTimeout = + Epoched.from(initialEpoch.getEpoch(), localTimeout); + localTimeoutSender.dispatch(epochTimeout, localTimeout.millisecondsWaitTime()); + }; + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor initialViewUpdateSender( - EventDispatcher epochViewUpdateEventDispatcher, - EpochChange initialEpoch - ) { - return viewUpdate -> { - EpochViewUpdate epochViewUpdate = new EpochViewUpdate(initialEpoch.getEpoch(), viewUpdate); - epochViewUpdateEventDispatcher.dispatch(epochViewUpdate); - }; - } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor initialViewUpdateSender( + EventDispatcher epochViewUpdateEventDispatcher, EpochChange initialEpoch) { + return viewUpdate -> { + EpochViewUpdate epochViewUpdate = new EpochViewUpdate(initialEpoch.getEpoch(), viewUpdate); + epochViewUpdateEventDispatcher.dispatch(epochViewUpdate); + }; + } - @Provides - private PacemakerStateFactory pacemakerStateFactory( - EventDispatcher epochViewUpdateEventDispatcher - ) { - return (initialView, epoch, proposerElection) -> - new PacemakerState(initialView, proposerElection, viewUpdate -> { - EpochViewUpdate epochViewUpdate = new EpochViewUpdate(epoch, viewUpdate); - epochViewUpdateEventDispatcher.dispatch(epochViewUpdate); - }); - } + @Provides + private PacemakerStateFactory pacemakerStateFactory( + EventDispatcher epochViewUpdateEventDispatcher) { + return (initialView, epoch, proposerElection) -> + new PacemakerState( + initialView, + proposerElection, + viewUpdate -> { + EpochViewUpdate epochViewUpdate = new EpochViewUpdate(epoch, viewUpdate); + epochViewUpdateEventDispatcher.dispatch(epochViewUpdate); + }); + } - @ProvidesIntoSet - @ProcessOnDispatch - EventProcessor initialEpochsTimeoutProcessor( - EpochChange initialEpoch, - EventDispatcher timeoutDispatcher - ) { - return timeoutOccurrence -> - timeoutDispatcher.dispatch(new EpochLocalTimeoutOccurrence(initialEpoch.getEpoch(), timeoutOccurrence)); - } + @ProvidesIntoSet + @ProcessOnDispatch + EventProcessor initialEpochsTimeoutProcessor( + EpochChange initialEpoch, EventDispatcher timeoutDispatcher) { + return timeoutOccurrence -> + timeoutDispatcher.dispatch( + new EpochLocalTimeoutOccurrence(initialEpoch.getEpoch(), timeoutOccurrence)); + } - @Provides - private PacemakerFactory pacemakerFactory( - @Self BFTNode self, - SystemCounters counters, - NextTxnsGenerator nextTxnsGenerator, - Hasher hasher, - EventDispatcher timeoutEventDispatcher, - ScheduledEventDispatcher> localTimeoutSender, - RemoteEventDispatcher proposalDispatcher, - RemoteEventDispatcher voteDispatcher, - TimeSupplier timeSupplier - ) { - return ( - validatorSet, - vertexStore, - timeoutCalculator, - safetyRules, - initialViewUpdate, - epoch - ) -> new Pacemaker( - self, - counters, - validatorSet, - vertexStore, - safetyRules, - timeout -> timeoutEventDispatcher.dispatch(new EpochLocalTimeoutOccurrence(epoch, timeout)), - (scheduledTimeout, ms) -> localTimeoutSender.dispatch(Epoched.from(epoch, scheduledTimeout), ms), - timeoutCalculator, - nextTxnsGenerator, - proposalDispatcher, - voteDispatcher, - hasher, - timeSupplier, - initialViewUpdate, - counters - ); - } + @Provides + private PacemakerFactory pacemakerFactory( + @Self BFTNode self, + SystemCounters counters, + NextTxnsGenerator nextTxnsGenerator, + Hasher hasher, + EventDispatcher timeoutEventDispatcher, + ScheduledEventDispatcher> localTimeoutSender, + RemoteEventDispatcher proposalDispatcher, + RemoteEventDispatcher voteDispatcher, + TimeSupplier timeSupplier) { + return (validatorSet, vertexStore, timeoutCalculator, safetyRules, initialViewUpdate, epoch) -> + new Pacemaker( + self, + counters, + validatorSet, + vertexStore, + safetyRules, + timeout -> + timeoutEventDispatcher.dispatch(new EpochLocalTimeoutOccurrence(epoch, timeout)), + (scheduledTimeout, ms) -> + localTimeoutSender.dispatch(Epoched.from(epoch, scheduledTimeout), ms), + timeoutCalculator, + nextTxnsGenerator, + proposalDispatcher, + voteDispatcher, + hasher, + timeSupplier, + initialViewUpdate, + counters); + } - @Provides - private BFTSyncRequestProcessorFactory vertexStoreSyncVerticesRequestProcessorFactory( - RemoteEventDispatcher errorResponseDispatcher, - RemoteEventDispatcher responseDispatcher, - SystemCounters systemCounters - ) { - return vertexStore -> new VertexStoreBFTSyncRequestProcessor( - vertexStore, - errorResponseDispatcher, - responseDispatcher, - systemCounters - ); - } + @Provides + private BFTSyncRequestProcessorFactory vertexStoreSyncVerticesRequestProcessorFactory( + RemoteEventDispatcher errorResponseDispatcher, + RemoteEventDispatcher responseDispatcher, + SystemCounters systemCounters) { + return vertexStore -> + new VertexStoreBFTSyncRequestProcessor( + vertexStore, errorResponseDispatcher, responseDispatcher, systemCounters); + } - @Provides - private BFTSyncFactory bftSyncFactory( - RemoteEventDispatcher requestSender, - @Self BFTNode self, - @GetVerticesRequestRateLimit RateLimiter syncRequestRateLimiter, - EventDispatcher syncLedgerRequestSender, - ScheduledEventDispatcher timeoutDispatcher, - Random random, - @BFTSyncPatienceMillis int bftSyncPatienceMillis, - SystemCounters counters, - Hasher hasher - ) { - return (vertexStore, pacemakerState, configuration) -> new BFTSync( - self, - syncRequestRateLimiter, - vertexStore, - hasher, - pacemakerState, - Comparator.comparingLong((LedgerHeader h) -> h.getAccumulatorState().getStateVersion()), - requestSender, - syncLedgerRequestSender, - timeoutDispatcher, - configuration.getVertexStoreState().getRootHeader(), - random, - bftSyncPatienceMillis, - counters - ); - } + @Provides + private BFTSyncFactory bftSyncFactory( + RemoteEventDispatcher requestSender, + @Self BFTNode self, + @GetVerticesRequestRateLimit RateLimiter syncRequestRateLimiter, + EventDispatcher syncLedgerRequestSender, + ScheduledEventDispatcher timeoutDispatcher, + Random random, + @BFTSyncPatienceMillis int bftSyncPatienceMillis, + SystemCounters counters, + Hasher hasher) { + return (vertexStore, pacemakerState, configuration) -> + new BFTSync( + self, + syncRequestRateLimiter, + vertexStore, + hasher, + pacemakerState, + Comparator.comparingLong((LedgerHeader h) -> h.getAccumulatorState().getStateVersion()), + requestSender, + syncLedgerRequestSender, + timeoutDispatcher, + configuration.getVertexStoreState().getRootHeader(), + random, + bftSyncPatienceMillis, + counters); + } - @Provides - private VertexStoreFactory vertexStoreFactory( - EventDispatcher updateSender, - EventDispatcher rebuildUpdateDispatcher, - EventDispatcher highQCUpdateEventDispatcher, - EventDispatcher committedDispatcher, - Ledger ledger, - Hasher hasher - ) { - return vertexStoreState -> VertexStore.create( - vertexStoreState, - ledger, - hasher, - updateSender, - rebuildUpdateDispatcher, - highQCUpdateEventDispatcher, - committedDispatcher - ); - } + @Provides + private VertexStoreFactory vertexStoreFactory( + EventDispatcher updateSender, + EventDispatcher rebuildUpdateDispatcher, + EventDispatcher highQCUpdateEventDispatcher, + EventDispatcher committedDispatcher, + Ledger ledger, + Hasher hasher) { + return vertexStoreState -> + VertexStore.create( + vertexStoreState, + ledger, + hasher, + updateSender, + rebuildUpdateDispatcher, + highQCUpdateEventDispatcher, + committedDispatcher); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsSyncModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsSyncModule.java index 9c898077ab..05a732a2a6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsSyncModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsSyncModule.java @@ -75,19 +75,19 @@ import com.radixdlt.environment.LocalEvents; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.RemoteEventProcessorOnRunner; -import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.environment.Runners; +import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.epochs.EpochsLocalSyncService; import com.radixdlt.epochs.LocalSyncServiceFactory; import com.radixdlt.ledger.AccumulatorState; import com.radixdlt.ledger.LedgerAccumulatorVerifier; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.network.p2p.PeersView; -import com.radixdlt.sync.RemoteSyncService; -import com.radixdlt.sync.SyncConfig; import com.radixdlt.sync.LocalSyncService; -import com.radixdlt.sync.LocalSyncService.VerifiedSyncResponseHandler; import com.radixdlt.sync.LocalSyncService.InvalidSyncResponseHandler; +import com.radixdlt.sync.LocalSyncService.VerifiedSyncResponseHandler; +import com.radixdlt.sync.RemoteSyncService; +import com.radixdlt.sync.SyncConfig; import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.sync.messages.local.SyncCheckReceiveStatusTimeout; import com.radixdlt.sync.messages.local.SyncCheckTrigger; @@ -99,170 +99,143 @@ import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; import com.radixdlt.sync.validation.RemoteSyncResponseSignaturesVerifier; - import java.util.Comparator; -/** - * Epoch+Sync extension - */ +/** Epoch+Sync extension */ public class EpochsSyncModule extends AbstractModule { - @Override - public void configure() { - bind(EpochsLocalSyncService.class).in(Scopes.SINGLETON); - - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(SyncCheckTrigger.class); - eventBinder.addBinding().toInstance(SyncCheckReceiveStatusTimeout.class); - eventBinder.addBinding().toInstance(SyncRequestTimeout.class); - eventBinder.addBinding().toInstance(LocalSyncRequest.class); - eventBinder.addBinding().toInstance(SyncLedgerUpdateTimeout.class); - } - - @ProvidesIntoSet - private EventProcessorOnRunner epochsLedgerUpdateEventProcessorLocalSync( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - LedgerUpdate.class, - epochsLocalSyncService.epochsLedgerUpdateEventProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner ledgerUpdateEventProcessorRemoteSync( - RemoteSyncService remoteSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - LedgerUpdate.class, - update -> remoteSyncService.ledgerUpdateEventProcessor().process(update) - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner syncCheckTriggerEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncCheckTrigger.class, - epochsLocalSyncService.syncCheckTriggerEventProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner syncCheckReceiveStatusTimeoutEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncCheckReceiveStatusTimeout.class, - epochsLocalSyncService.syncCheckReceiveStatusTimeoutEventProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner syncRequestTimeoutEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncRequestTimeout.class, - epochsLocalSyncService.syncRequestTimeoutEventProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner syncLedgerUpdateTimeoutProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncLedgerUpdateTimeout.class, - epochsLocalSyncService.syncLedgerUpdateTimeoutProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner localSyncRequestEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - LocalSyncRequest.class, - epochsLocalSyncService.localSyncRequestEventProcessor() - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner statusResponseEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - StatusResponse.class, - epochsLocalSyncService.statusResponseEventProcessor() - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner syncResponseEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - SyncResponse.class, - epochsLocalSyncService.syncResponseEventProcessor() - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner ledgerStatusUpdateEventProcessor( - EpochsLocalSyncService epochsLocalSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - LedgerStatusUpdate.class, - epochsLocalSyncService.ledgerStatusUpdateEventProcessor() - ); - } - - @Provides - private LocalSyncServiceFactory localSyncServiceFactory( - RemoteEventDispatcher statusRequestDispatcher, - ScheduledEventDispatcher syncCheckReceiveStatusTimeoutDispatcher, - RemoteEventDispatcher syncRequestDispatcher, - ScheduledEventDispatcher syncRequestTimeoutDispatcher, - ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher, - SyncConfig syncConfig, - SystemCounters systemCounters, - PeersView peersView, - Comparator accComparator, - RemoteSyncResponseSignaturesVerifier signaturesVerifier, - LedgerAccumulatorVerifier accumulatorVerifier, - VerifiedSyncResponseHandler verifiedSyncResponseHandler, - InvalidSyncResponseHandler invalidSyncResponseHandler - ) { - return (remoteSyncResponseValidatorSetVerifier, syncState) -> - new LocalSyncService( - statusRequestDispatcher, - syncCheckReceiveStatusTimeoutDispatcher, - syncRequestDispatcher, - syncRequestTimeoutDispatcher, - syncLedgerUpdateTimeoutDispatcher, - syncConfig, - systemCounters, - peersView, - accComparator, - remoteSyncResponseValidatorSetVerifier, - signaturesVerifier, - accumulatorVerifier, - verifiedSyncResponseHandler, - invalidSyncResponseHandler, - syncState - ); - } + @Override + public void configure() { + bind(EpochsLocalSyncService.class).in(Scopes.SINGLETON); + + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(SyncCheckTrigger.class); + eventBinder.addBinding().toInstance(SyncCheckReceiveStatusTimeout.class); + eventBinder.addBinding().toInstance(SyncRequestTimeout.class); + eventBinder.addBinding().toInstance(LocalSyncRequest.class); + eventBinder.addBinding().toInstance(SyncLedgerUpdateTimeout.class); + } + + @ProvidesIntoSet + private EventProcessorOnRunner epochsLedgerUpdateEventProcessorLocalSync( + EpochsLocalSyncService epochsLocalSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + LedgerUpdate.class, + epochsLocalSyncService.epochsLedgerUpdateEventProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner ledgerUpdateEventProcessorRemoteSync( + RemoteSyncService remoteSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + LedgerUpdate.class, + update -> remoteSyncService.ledgerUpdateEventProcessor().process(update)); + } + + @ProvidesIntoSet + private EventProcessorOnRunner syncCheckTriggerEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncCheckTrigger.class, + epochsLocalSyncService.syncCheckTriggerEventProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner syncCheckReceiveStatusTimeoutEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncCheckReceiveStatusTimeout.class, + epochsLocalSyncService.syncCheckReceiveStatusTimeoutEventProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner syncRequestTimeoutEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncRequestTimeout.class, + epochsLocalSyncService.syncRequestTimeoutEventProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner syncLedgerUpdateTimeoutProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncLedgerUpdateTimeout.class, + epochsLocalSyncService.syncLedgerUpdateTimeoutProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner localSyncRequestEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + LocalSyncRequest.class, + epochsLocalSyncService.localSyncRequestEventProcessor()); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner statusResponseEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, StatusResponse.class, epochsLocalSyncService.statusResponseEventProcessor()); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner syncResponseEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, SyncResponse.class, epochsLocalSyncService.syncResponseEventProcessor()); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner ledgerStatusUpdateEventProcessor( + EpochsLocalSyncService epochsLocalSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, + LedgerStatusUpdate.class, + epochsLocalSyncService.ledgerStatusUpdateEventProcessor()); + } + + @Provides + private LocalSyncServiceFactory localSyncServiceFactory( + RemoteEventDispatcher statusRequestDispatcher, + ScheduledEventDispatcher + syncCheckReceiveStatusTimeoutDispatcher, + RemoteEventDispatcher syncRequestDispatcher, + ScheduledEventDispatcher syncRequestTimeoutDispatcher, + ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher, + SyncConfig syncConfig, + SystemCounters systemCounters, + PeersView peersView, + Comparator accComparator, + RemoteSyncResponseSignaturesVerifier signaturesVerifier, + LedgerAccumulatorVerifier accumulatorVerifier, + VerifiedSyncResponseHandler verifiedSyncResponseHandler, + InvalidSyncResponseHandler invalidSyncResponseHandler) { + return (remoteSyncResponseValidatorSetVerifier, syncState) -> + new LocalSyncService( + statusRequestDispatcher, + syncCheckReceiveStatusTimeoutDispatcher, + syncRequestDispatcher, + syncRequestTimeoutDispatcher, + syncLedgerUpdateTimeoutDispatcher, + syncConfig, + systemCounters, + peersView, + accComparator, + remoteSyncResponseValidatorSetVerifier, + signaturesVerifier, + accumulatorVerifier, + verifiedSyncResponseHandler, + invalidSyncResponseHandler, + syncState); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EventLoggerModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EventLoggerModule.java index a55417d75d..2563e7b6cc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EventLoggerModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EventLoggerModule.java @@ -84,125 +84,134 @@ import com.radixdlt.statecomputer.InvalidProposedTxn; import com.radixdlt.statecomputer.REOutput; import com.radixdlt.utils.Bytes; +import java.util.function.Function; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.function.Function; - -//TODO: extract lambdas into dedicated methods -//TODO: suppress unstable API usage warnings +// TODO: extract lambdas into dedicated methods +// TODO: suppress unstable API usage warnings public final class EventLoggerModule extends AbstractModule { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - @Provides - Function stringForValidators1(Function stringForValidators) { - return n -> stringForValidators.apply(n.getKey()); - } + @Provides + Function stringForValidators1( + Function stringForValidators) { + return n -> stringForValidators.apply(n.getKey()); + } - @Provides - Function stringForValidators2(Addressing addressing) { - return k -> { - var addr = addressing.forValidators().of(k); - var len = addr.length(); - return addr.substring(0, 2) + "..." + addr.substring(len - 9); - }; - } + @Provides + Function stringForValidators2(Addressing addressing) { + return k -> { + var addr = addressing.forValidators().of(k); + var len = addr.length(); + return addr.substring(0, 2) + "..." + addr.substring(len - 9); + }; + } - @ProvidesIntoSet - EventProcessorOnDispatch invalidProposedTxn(Function nodeString) { - return new EventProcessorOnDispatch<>( - InvalidProposedTxn.class, - i -> logger.warn("eng_badprp{proposer={}}", nodeString.apply(i.getProposer())) - ); - } + @ProvidesIntoSet + EventProcessorOnDispatch invalidProposedTxn(Function nodeString) { + return new EventProcessorOnDispatch<>( + InvalidProposedTxn.class, + i -> logger.warn("eng_badprp{proposer={}}", nodeString.apply(i.getProposer()))); + } - @ProvidesIntoSet - EventProcessorOnDispatch logTimeouts(Function nodeString) { - return new EventProcessorOnDispatch<>( - EpochLocalTimeoutOccurrence.class, - t -> logger.warn("bft_timout{epoch={} round={} leader={} nextLeader={} count={}}", - t.getEpochView().getEpoch(), - t.getEpochView().getView().number(), - nodeString.apply(t.getLeader()), - nodeString.apply(t.getNextLeader()), - t.getBase().timeout().count() - ) - ); - } + @ProvidesIntoSet + EventProcessorOnDispatch logTimeouts(Function nodeString) { + return new EventProcessorOnDispatch<>( + EpochLocalTimeoutOccurrence.class, + t -> + logger.warn( + "bft_timout{epoch={} round={} leader={} nextLeader={} count={}}", + t.getEpochView().getEpoch(), + t.getEpochView().getView().number(), + nodeString.apply(t.getLeader()), + nodeString.apply(t.getNextLeader()), + t.getBase().timeout().count())); + } - @ProvidesIntoSet - EventProcessorOnDispatch logRounds(Function nodeString) { - final RateLimiter logLimiter = RateLimiter.create(1.0); - return new EventProcessorOnDispatch<>( - EpochViewUpdate.class, - u -> { - Level logLevel = logLimiter.tryAcquire() ? Level.INFO : Level.TRACE; - logger.log(logLevel, "bft_nxtrnd{epoch={} round={} leader={} nextLeader={}}", - u.getEpoch(), - u.getEpochView().getView().number(), - nodeString.apply(u.getViewUpdate().getLeader()), - nodeString.apply(u.getViewUpdate().getNextLeader()) - ); - } - ); - } + @ProvidesIntoSet + EventProcessorOnDispatch logRounds(Function nodeString) { + final RateLimiter logLimiter = RateLimiter.create(1.0); + return new EventProcessorOnDispatch<>( + EpochViewUpdate.class, + u -> { + Level logLevel = logLimiter.tryAcquire() ? Level.INFO : Level.TRACE; + logger.log( + logLevel, + "bft_nxtrnd{epoch={} round={} leader={} nextLeader={}}", + u.getEpoch(), + u.getEpochView().getView().number(), + nodeString.apply(u.getViewUpdate().getLeader()), + nodeString.apply(u.getViewUpdate().getNextLeader())); + }); + } - @ProvidesIntoSet - @Singleton - EventProcessorOnDispatch ledgerUpdate(@Self BFTNode self, Function nodeString) { - final RateLimiter logLimiter = RateLimiter.create(1.0); - return new EventProcessorOnDispatch<>( - LedgerUpdate.class, - u -> { - var output = u.getStateComputerOutput().getInstance(REOutput.class); - var epochChange = u.getStateComputerOutput().getInstance(EpochChange.class); - long userTxns = output != null ? output.getProcessedTxns().stream().filter(t -> !t.isSystemOnly()).count() : 0; - var logLevel = (epochChange != null || logLimiter.tryAcquire()) ? Level.INFO : Level.TRACE; - logger.log(logLevel, "lgr_commit{epoch={} round={} version={} hash={} user_txns={}}", - u.getTail().getEpoch(), - u.getTail().getView().number(), - u.getTail().getStateVersion(), - Bytes.toHexString(u.getTail().getAccumulatorState().getAccumulatorHash().asBytes()).substring(0, 16), - userTxns - ); + @ProvidesIntoSet + @Singleton + EventProcessorOnDispatch ledgerUpdate( + @Self BFTNode self, Function nodeString) { + final RateLimiter logLimiter = RateLimiter.create(1.0); + return new EventProcessorOnDispatch<>( + LedgerUpdate.class, + u -> { + var output = u.getStateComputerOutput().getInstance(REOutput.class); + var epochChange = u.getStateComputerOutput().getInstance(EpochChange.class); + long userTxns = + output != null + ? output.getProcessedTxns().stream().filter(t -> !t.isSystemOnly()).count() + : 0; + var logLevel = + (epochChange != null || logLimiter.tryAcquire()) ? Level.INFO : Level.TRACE; + logger.log( + logLevel, + "lgr_commit{epoch={} round={} version={} hash={} user_txns={}}", + u.getTail().getEpoch(), + u.getTail().getView().number(), + u.getTail().getStateVersion(), + Bytes.toHexString(u.getTail().getAccumulatorState().getAccumulatorHash().asBytes()) + .substring(0, 16), + userTxns); - if (epochChange != null) { - var validatorSet = epochChange.getBFTConfiguration().getValidatorSet(); - logger.info("lgr_nepoch{epoch={} included={} num_validators={} total_stake={}}", - epochChange.getEpoch(), - validatorSet.containsNode(self), - validatorSet.getValidators().size(), - Amount.ofSubunits(validatorSet.getTotalPower()) - ); - } + if (epochChange != null) { + var validatorSet = epochChange.getBFTConfiguration().getValidatorSet(); + logger.info( + "lgr_nepoch{epoch={} included={} num_validators={} total_stake={}}", + epochChange.getEpoch(), + validatorSet.containsNode(self), + validatorSet.getValidators().size(), + Amount.ofSubunits(validatorSet.getTotalPower())); + } - if (output == null) { - return; - } + if (output == null) { + return; + } - output.getProcessedTxns().stream().flatMap(t -> t.getEvents().stream()) - .forEach(e -> { - if (e instanceof ValidatorBFTDataEvent) { - var event = (ValidatorBFTDataEvent) e; - Level level = event.getMissedProposals() > 0 ? Level.WARN : Level.INFO; - logger.log(level, "vdr_epochr{validator={} completed_proposals={} missed_proposals={}}", - nodeString.apply(event.getValidatorKey()), - event.getCompletedProposals(), - event.getMissedProposals() - ); - } else if (e instanceof ValidatorMissedProposalsEvent) { - var event = (ValidatorMissedProposalsEvent) e; - var you = event.getValidatorKey().equals(self.getKey()); - Level level = you ? Level.ERROR : Level.WARN; - logger.log(level, "{}_failed{validator={} missed_proposals={}}", - you ? "you" : "vdr", - nodeString.apply(event.getValidatorKey()), - event.getMissedProposals() - ); - } - }); - } - ); - } + output.getProcessedTxns().stream() + .flatMap(t -> t.getEvents().stream()) + .forEach( + e -> { + if (e instanceof ValidatorBFTDataEvent) { + var event = (ValidatorBFTDataEvent) e; + Level level = event.getMissedProposals() > 0 ? Level.WARN : Level.INFO; + logger.log( + level, + "vdr_epochr{validator={} completed_proposals={} missed_proposals={}}", + nodeString.apply(event.getValidatorKey()), + event.getCompletedProposals(), + event.getMissedProposals()); + } else if (e instanceof ValidatorMissedProposalsEvent) { + var event = (ValidatorMissedProposalsEvent) e; + var you = event.getValidatorKey().equals(self.getKey()); + Level level = you ? Level.ERROR : Level.WARN; + logger.log( + level, + "{}_failed{validator={} missed_proposals={}}", + you ? "you" : "vdr", + nodeString.apply(event.getValidatorKey()), + event.getMissedProposals()); + } + }); + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerModule.java index 3fe6f56821..be0778155c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerModule.java @@ -83,33 +83,35 @@ import com.radixdlt.ledger.VerifiedTxnsAndProof; import java.util.Comparator; -/** - * Module which manages ledger state and synchronization of updates to ledger state - */ +/** Module which manages ledger state and synchronization of updates to ledger state */ public class LedgerModule extends AbstractModule { - @Override - protected void configure() { - bind(Ledger.class).to(StateComputerLedger.class); - bind(new TypeLiteral>() { }).to(OrderByEpochAndVersionComparator.class).in(Scopes.SINGLETON); - bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); - bind(LedgerAccumulatorVerifier.class).to(SimpleLedgerAccumulatorAndVerifier.class); - bind(StateComputerLedger.class).in(Scopes.SINGLETON); - } + @Override + protected void configure() { + bind(Ledger.class).to(StateComputerLedger.class); + bind(new TypeLiteral>() {}) + .to(OrderByEpochAndVersionComparator.class) + .in(Scopes.SINGLETON); + bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); + bind(LedgerAccumulatorVerifier.class).to(SimpleLedgerAccumulatorAndVerifier.class); + bind(StateComputerLedger.class).in(Scopes.SINGLETON); + } - @Provides - private Comparator accumulatorStateComparator() { - return Comparator.comparingLong(AccumulatorState::getStateVersion); - } + @Provides + private Comparator accumulatorStateComparator() { + return Comparator.comparingLong(AccumulatorState::getStateVersion); + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor syncToLedgerCommittor(StateComputerLedger stateComputerLedger) { - return stateComputerLedger.syncEventProcessor(); - } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor syncToLedgerCommittor( + StateComputerLedger stateComputerLedger) { + return stateComputerLedger.syncEventProcessor(); + } - @ProvidesIntoSet - @ProcessOnDispatch - private EventProcessor bftToLedgerCommittor(StateComputerLedger stateComputerLedger) { - return stateComputerLedger.bftCommittedUpdateEventProcessor(); - } + @ProvidesIntoSet + @ProcessOnDispatch + private EventProcessor bftToLedgerCommittor( + StateComputerLedger stateComputerLedger) { + return stateComputerLedger.bftCommittedUpdateEventProcessor(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerRecoveryModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerRecoveryModule.java index df34490996..dead11909c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerRecoveryModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerRecoveryModule.java @@ -70,9 +70,9 @@ import com.google.inject.Singleton; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.UnverifiedVertex; -import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.VerifiedVertex; import com.radixdlt.consensus.bft.VerifiedVertexStoreState; import com.radixdlt.consensus.bft.View; @@ -82,118 +82,113 @@ import com.radixdlt.engine.RadixEngineException; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.ledger.VerifiedTxnsAndProof; -import com.radixdlt.statecomputer.REOutput; import com.radixdlt.statecomputer.LedgerAndBFTProof; +import com.radixdlt.statecomputer.REOutput; import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.store.LastEpochProof; import com.radixdlt.store.LastProof; import com.radixdlt.store.LastStoredProof; import com.radixdlt.store.berkeley.SerializedVertexStoreState; import com.radixdlt.sync.CommittedReader; - import java.util.Optional; -/** - * Recovery for ledger - */ +/** Recovery for ledger */ public final class LedgerRecoveryModule extends AbstractModule { - @Provides - @Singleton - @LastStoredProof - LedgerProof lastStoredProof( - RadixEngine radixEngine, // TODO: Remove - CommittedReader committedReader, - @Genesis VerifiedTxnsAndProof genesis, - EventDispatcher committedDispatcher // FIXME: this is hack so client can get genesis - ) { - return committedReader.getLastProof().orElseGet(() -> { - var txns = genesis.getTxns(); - var proof = LedgerAndBFTProof.create(genesis.getProof()); - try { - var result = radixEngine.execute(txns, proof, PermissionLevel.SYSTEM); - committedDispatcher.dispatch(REOutput.create(result.getProcessedTxns())); - } catch (RadixEngineException e) { - throw new IllegalStateException("Error during node initialization", e); - } + @Provides + @Singleton + @LastStoredProof + LedgerProof lastStoredProof( + RadixEngine radixEngine, // TODO: Remove + CommittedReader committedReader, + @Genesis VerifiedTxnsAndProof genesis, + EventDispatcher committedDispatcher // FIXME: this is hack so client can get genesis + ) { + return committedReader + .getLastProof() + .orElseGet( + () -> { + var txns = genesis.getTxns(); + var proof = LedgerAndBFTProof.create(genesis.getProof()); + try { + var result = radixEngine.execute(txns, proof, PermissionLevel.SYSTEM); + committedDispatcher.dispatch(REOutput.create(result.getProcessedTxns())); + } catch (RadixEngineException e) { + throw new IllegalStateException("Error during node initialization", e); + } - return genesis.getProof(); - }); - } + return genesis.getProof(); + }); + } - @Provides - @Singleton - @LastProof - LedgerProof lastProof( - VerifiedVertexStoreState vertexStoreState, - @LastStoredProof LedgerProof lastStoredProof - ) { - if (lastStoredProof.isEndOfEpoch()) { - return vertexStoreState.getRootHeader(); - } else { - return lastStoredProof; - } - } + @Provides + @Singleton + @LastProof + LedgerProof lastProof( + VerifiedVertexStoreState vertexStoreState, @LastStoredProof LedgerProof lastStoredProof) { + if (lastStoredProof.isEndOfEpoch()) { + return vertexStoreState.getRootHeader(); + } else { + return lastStoredProof; + } + } - @Provides - @Singleton - @LastEpochProof - LedgerProof lastEpochProof( - CommittedReader committedReader, - @LastStoredProof LedgerProof lastStoredProof - ) { - if (lastStoredProof.isEndOfEpoch()) { - return lastStoredProof; - } - return committedReader.getEpochProof(lastStoredProof.getEpoch()).orElseThrow(); - } + @Provides + @Singleton + @LastEpochProof + LedgerProof lastEpochProof( + CommittedReader committedReader, @LastStoredProof LedgerProof lastStoredProof) { + if (lastStoredProof.isEndOfEpoch()) { + return lastStoredProof; + } + return committedReader.getEpochProof(lastStoredProof.getEpoch()).orElseThrow(); + } - private static VerifiedVertexStoreState serializedToVerifiedVertexStore( - SerializedVertexStoreState serializedVertexStoreState, - Hasher hasher - ) { - var root = serializedVertexStoreState.getRoot(); - var rootVertexId = hasher.hash(root); - var verifiedRoot = new VerifiedVertex(root, rootVertexId); + private static VerifiedVertexStoreState serializedToVerifiedVertexStore( + SerializedVertexStoreState serializedVertexStoreState, Hasher hasher) { + var root = serializedVertexStoreState.getRoot(); + var rootVertexId = hasher.hash(root); + var verifiedRoot = new VerifiedVertex(root, rootVertexId); - var vertices = serializedVertexStoreState.getVertices().stream() - .map(v -> new VerifiedVertex(v, hasher.hash(v))) - .collect(ImmutableList.toImmutableList()); + var vertices = + serializedVertexStoreState.getVertices().stream() + .map(v -> new VerifiedVertex(v, hasher.hash(v))) + .collect(ImmutableList.toImmutableList()); - return VerifiedVertexStoreState.create( - serializedVertexStoreState.getHighQC(), - verifiedRoot, - vertices, - serializedVertexStoreState.getHighestTC(), - hasher - ); - } + return VerifiedVertexStoreState.create( + serializedVertexStoreState.getHighQC(), + verifiedRoot, + vertices, + serializedVertexStoreState.getHighestTC(), + hasher); + } - private static VerifiedVertexStoreState epochProofToGenesisVertexStore( - LedgerProof lastEpochProof, - Hasher hasher - ) { - var genesisVertex = UnverifiedVertex.createGenesis(lastEpochProof.getRaw()); - var verifiedGenesisVertex = new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); - var nextLedgerHeader = LedgerHeader.create( - lastEpochProof.getEpoch() + 1, - View.genesis(), - lastEpochProof.getAccumulatorState(), - lastEpochProof.timestamp() - ); - var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); - return VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher); - } + private static VerifiedVertexStoreState epochProofToGenesisVertexStore( + LedgerProof lastEpochProof, Hasher hasher) { + var genesisVertex = UnverifiedVertex.createGenesis(lastEpochProof.getRaw()); + var verifiedGenesisVertex = new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); + var nextLedgerHeader = + LedgerHeader.create( + lastEpochProof.getEpoch() + 1, + View.genesis(), + lastEpochProof.getAccumulatorState(), + lastEpochProof.timestamp()); + var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); + return VerifiedVertexStoreState.create( + HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher); + } - @Provides - @Singleton - private VerifiedVertexStoreState vertexStoreState( - @LastEpochProof LedgerProof lastEpochProof, - Optional serializedVertexStoreState, - Hasher hasher - ) { - return serializedVertexStoreState - .filter(vertexStoreState -> vertexStoreState.getHighQC().highestQC().getEpoch() == lastEpochProof.getEpoch() + 1) - .map(state -> serializedToVerifiedVertexStore(state, hasher)) - .orElseGet(() -> epochProofToGenesisVertexStore(lastEpochProof, hasher)); - } + @Provides + @Singleton + private VerifiedVertexStoreState vertexStoreState( + @LastEpochProof LedgerProof lastEpochProof, + Optional serializedVertexStoreState, + Hasher hasher) { + return serializedVertexStoreState + .filter( + vertexStoreState -> + vertexStoreState.getHighQC().highestQC().getEpoch() + == lastEpochProof.getEpoch() + 1) + .map(state -> serializedToVerifiedVertexStore(state, hasher)) + .orElseGet(() -> epochProofToGenesisVertexStore(lastEpochProof, hasher)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ModuleRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ModuleRunner.java index 399099793b..265d61f980 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ModuleRunner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ModuleRunner.java @@ -64,17 +64,11 @@ package com.radixdlt; -/** - * Manages a module - */ +/** Manages a module */ public interface ModuleRunner { - /** - * Start running the module - */ - void start(); + /** Start running the module */ + void start(); - /** - * Stop running the module - */ - void stop(); + /** Stop running the module */ + void stop(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixEngineStoreModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixEngineStoreModule.java index f9c292b13a..f75f148f1e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixEngineStoreModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixEngineStoreModule.java @@ -75,12 +75,13 @@ import com.radixdlt.sync.CommittedReader; public class RadixEngineStoreModule extends AbstractModule { - @Override - protected void configure() { - bind(BerkeleyLedgerEntryStore.class).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }) - .to(BerkeleyLedgerEntryStore.class).in(Scopes.SINGLETON); - bind(CommittedReader.class).to(BerkeleyLedgerEntryStore.class); - Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class); - } + @Override + protected void configure() { + bind(BerkeleyLedgerEntryStore.class).in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .to(BerkeleyLedgerEntryStore.class) + .in(Scopes.SINGLETON); + bind(CommittedReader.class).to(BerkeleyLedgerEntryStore.class); + Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixNodeModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixNodeModule.java index b883e71f87..0b07bf3d63 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixNodeModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/RadixNodeModule.java @@ -64,18 +64,10 @@ package com.radixdlt; -import com.radixdlt.api.ApiModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.StokenetForkConfigsModule; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.util.Strings; -import org.json.JSONObject; -import org.radix.utils.IOUtils; - import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; +import com.radixdlt.api.ApiModule; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.bft.PacemakerMaxExponent; import com.radixdlt.consensus.bft.PacemakerRate; @@ -105,186 +97,195 @@ import com.radixdlt.statecomputer.checkpoint.RadixEngineCheckpointModule; import com.radixdlt.statecomputer.forks.ForkOverwritesFromPropertiesModule; import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.StokenetForkConfigsModule; import com.radixdlt.store.DatabasePropertiesModule; import com.radixdlt.store.PersistenceModule; import com.radixdlt.sync.SyncConfig; import com.radixdlt.utils.Bytes; - import java.io.FileInputStream; import java.io.IOException; import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.util.Strings; +import org.json.JSONObject; +import org.radix.utils.IOUtils; -/** - * Module which manages everything in a single node - */ +/** Module which manages everything in a single node */ public final class RadixNodeModule extends AbstractModule { - private static final int DEFAULT_CORE_PORT = 3333; - private static final String DEFAULT_BIND_ADDRESS = "0.0.0.0"; - private static final Logger log = LogManager.getLogger(); + private static final int DEFAULT_CORE_PORT = 3333; + private static final String DEFAULT_BIND_ADDRESS = "0.0.0.0"; + private static final Logger log = LogManager.getLogger(); - private final RuntimeProperties properties; - private final int networkId; + private final RuntimeProperties properties; + private final int networkId; - public RadixNodeModule(RuntimeProperties properties) { - this.properties = properties; - var networkId = properties.get("network.id"); - if (networkId == null) { - throw new IllegalStateException("Must specify network.id"); - } - this.networkId = Integer.parseInt(networkId); - } + public RadixNodeModule(RuntimeProperties properties) { + this.properties = properties; + var networkId = properties.get("network.id"); + if (networkId == null) { + throw new IllegalStateException("Must specify network.id"); + } + this.networkId = Integer.parseInt(networkId); + } - @Provides - @Genesis - @Singleton - VerifiedTxnsAndProof genesis(@Genesis Txn genesis, GenesisBuilder genesisBuilder) throws RadixEngineException { - var proof = genesisBuilder.generateGenesisProof(genesis); - return VerifiedTxnsAndProof.create(List.of(genesis), proof); - } + @Provides + @Genesis + @Singleton + VerifiedTxnsAndProof genesis(@Genesis Txn genesis, GenesisBuilder genesisBuilder) + throws RadixEngineException { + var proof = genesisBuilder.generateGenesisProof(genesis); + return VerifiedTxnsAndProof.create(List.of(genesis), proof); + } - private Txn loadGenesisFile(String genesisFile) { - try (var genesisJsonString = new FileInputStream(genesisFile)) { - var genesisJson = new JSONObject(IOUtils.toString(genesisJsonString)); - var genesisHex = genesisJson.getString("genesis"); - return Txn.create(Bytes.fromHexString(genesisHex)); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } + private Txn loadGenesisFile(String genesisFile) { + try (var genesisJsonString = new FileInputStream(genesisFile)) { + var genesisJson = new JSONObject(IOUtils.toString(genesisJsonString)); + var genesisHex = genesisJson.getString("genesis"); + return Txn.create(Bytes.fromHexString(genesisHex)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } - private Txn loadGenesis(int networkId) { - var genesisTxnHex = properties.get("network.genesis_txn"); - var genesisFile = properties.get("network.genesis_file"); - var network = Network.ofId(networkId); - var networkGenesis = network - .flatMap(Network::genesisTxn) - .map(Bytes::fromHexString) - .map(Txn::create); + private Txn loadGenesis(int networkId) { + var genesisTxnHex = properties.get("network.genesis_txn"); + var genesisFile = properties.get("network.genesis_file"); + var network = Network.ofId(networkId); + var networkGenesis = + network.flatMap(Network::genesisTxn).map(Bytes::fromHexString).map(Txn::create); - if (networkGenesis.isPresent()) { - if (Strings.isNotBlank(genesisTxnHex)) { - throw new IllegalStateException("Cannot provide genesis txn for well-known network " + network.orElseThrow()); - } + if (networkGenesis.isPresent()) { + if (Strings.isNotBlank(genesisTxnHex)) { + throw new IllegalStateException( + "Cannot provide genesis txn for well-known network " + network.orElseThrow()); + } - if (Strings.isNotBlank(genesisFile)) { - throw new IllegalStateException("Cannot provide genesis file for well-known network " + network.orElseThrow()); - } - return networkGenesis.get(); - } else { - var genesisCount = 0; - genesisCount += Strings.isNotBlank(genesisTxnHex) ? 1 : 0; - genesisCount += Strings.isNotBlank(genesisFile) ? 1 : 0; - if (genesisCount > 1) { - throw new IllegalStateException("Multiple genesis txn specified."); - } - if (genesisCount == 0) { - throw new IllegalStateException("No genesis txn specified."); - } - return Strings.isNotBlank(genesisTxnHex) ? Txn.create(Bytes.fromHexString(genesisTxnHex)) : loadGenesisFile(genesisFile); - } - } + if (Strings.isNotBlank(genesisFile)) { + throw new IllegalStateException( + "Cannot provide genesis file for well-known network " + network.orElseThrow()); + } + return networkGenesis.get(); + } else { + var genesisCount = 0; + genesisCount += Strings.isNotBlank(genesisTxnHex) ? 1 : 0; + genesisCount += Strings.isNotBlank(genesisFile) ? 1 : 0; + if (genesisCount > 1) { + throw new IllegalStateException("Multiple genesis txn specified."); + } + if (genesisCount == 0) { + throw new IllegalStateException("No genesis txn specified."); + } + return Strings.isNotBlank(genesisTxnHex) + ? Txn.create(Bytes.fromHexString(genesisTxnHex)) + : loadGenesisFile(genesisFile); + } + } - @Override - protected void configure() { - if (this.networkId <= 0) { - throw new IllegalStateException("Illegal networkId " + networkId); - } + @Override + protected void configure() { + if (this.networkId <= 0) { + throw new IllegalStateException("Illegal networkId " + networkId); + } - var addressing = Addressing.ofNetworkId(networkId); - bind(Addressing.class).toInstance(addressing); - bindConstant().annotatedWith(NetworkId.class).to(networkId); - var genesis = loadGenesis(networkId); - bind(Txn.class).annotatedWith(Genesis.class).toInstance(genesis); - // TODO: Refactor - if (networkId == Network.MAINNET.getId()) { - install(new MainnetForkConfigsModule()); - } else { - install(new StokenetForkConfigsModule()); - } - bind(Txn.class).annotatedWith(Genesis.class).toInstance(loadGenesis(networkId)); - bind(RuntimeProperties.class).toInstance(properties); + var addressing = Addressing.ofNetworkId(networkId); + bind(Addressing.class).toInstance(addressing); + bindConstant().annotatedWith(NetworkId.class).to(networkId); + var genesis = loadGenesis(networkId); + bind(Txn.class).annotatedWith(Genesis.class).toInstance(genesis); + // TODO: Refactor + if (networkId == Network.MAINNET.getId()) { + install(new MainnetForkConfigsModule()); + } else { + install(new StokenetForkConfigsModule()); + } + bind(Txn.class).annotatedWith(Genesis.class).toInstance(loadGenesis(networkId)); + bind(RuntimeProperties.class).toInstance(properties); - // Consensus configuration - // These cannot be changed without introducing possibilities of - // going out of sync with consensus. - bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(properties.get("bft.sync.patience", 200)); - // Default values mean that pacemakers will sync if they are within 5 views of each other. - // 5 consecutive failing views will take 1*(2^6)-1 seconds = 63 seconds. - bindConstant().annotatedWith(PacemakerTimeout.class).to(3000L); - bindConstant().annotatedWith(PacemakerRate.class).to(1.1); - bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); + // Consensus configuration + // These cannot be changed without introducing possibilities of + // going out of sync with consensus. + bindConstant() + .annotatedWith(BFTSyncPatienceMillis.class) + .to(properties.get("bft.sync.patience", 200)); + // Default values mean that pacemakers will sync if they are within 5 views of each other. + // 5 consecutive failing views will take 1*(2^6)-1 seconds = 63 seconds. + bindConstant().annotatedWith(PacemakerTimeout.class).to(3000L); + bindConstant().annotatedWith(PacemakerRate.class).to(1.1); + bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); - // Mempool configuration - var mempoolMaxSize = properties.get("mempool.maxSize", 10000); - install(MempoolConfig.asModule(mempoolMaxSize, 5, 60000, 60000, 100)); + // Mempool configuration + var mempoolMaxSize = properties.get("mempool.maxSize", 10000); + install(MempoolConfig.asModule(mempoolMaxSize, 5, 60000, 60000, 100)); - // Sync configuration - final long syncPatience = properties.get("sync.patience", 5000L); - bind(SyncConfig.class).toInstance(SyncConfig.of(syncPatience, 10, 3000L)); + // Sync configuration + final long syncPatience = properties.get("sync.patience", 5000L); + bind(SyncConfig.class).toInstance(SyncConfig.of(syncPatience, 10, 3000L)); - // System (e.g. time, random) - install(new SystemModule()); + // System (e.g. time, random) + install(new SystemModule()); - install(new RxEnvironmentModule()); + install(new RxEnvironmentModule()); - install(new EventLoggerModule()); - install(new DispatcherModule()); + install(new EventLoggerModule()); + install(new DispatcherModule()); - // Consensus - install(new PersistedBFTKeyModule()); - install(new CryptoModule()); - install(new ConsensusModule()); + // Consensus + install(new PersistedBFTKeyModule()); + install(new CryptoModule()); + install(new ConsensusModule()); - // Ledger - install(new LedgerModule()); - install(new MempoolReceiverModule()); + // Ledger + install(new LedgerModule()); + install(new MempoolReceiverModule()); - // Mempool Relay - install(new MempoolRelayerModule()); + // Mempool Relay + install(new MempoolRelayerModule()); - // Sync - install(new SyncServiceModule()); + // Sync + install(new SyncServiceModule()); - // Epochs - Consensus - install(new EpochsConsensusModule()); - // Epochs - Sync - install(new EpochsSyncModule()); + // Epochs - Consensus + install(new EpochsConsensusModule()); + // Epochs - Sync + install(new EpochsSyncModule()); - // State Computer - install(new ForksModule()); - if (properties.get("overwrite_forks.enable", false)) { - log.info("Enabling fork overwrites"); - install(new ForkOverwritesFromPropertiesModule()); - } - install(new RadixEngineStateComputerModule()); - install(new RadixEngineModule()); - install(new RadixEngineStoreModule()); + // State Computer + install(new ForksModule()); + if (properties.get("overwrite_forks.enable", false)) { + log.info("Enabling fork overwrites"); + install(new ForkOverwritesFromPropertiesModule()); + } + install(new RadixEngineStateComputerModule()); + install(new RadixEngineModule()); + install(new RadixEngineStoreModule()); - // Checkpoints - install(new RadixEngineCheckpointModule()); + // Checkpoints + install(new RadixEngineCheckpointModule()); - // Storage - install(new DatabasePropertiesModule()); - install(new PersistenceModule()); - install(new ConsensusRecoveryModule()); - install(new LedgerRecoveryModule()); + // Storage + install(new DatabasePropertiesModule()); + install(new PersistenceModule()); + install(new ConsensusRecoveryModule()); + install(new LedgerRecoveryModule()); - // System Info - install(new SystemInfoModule()); + // System Info + install(new SystemInfoModule()); - // Network - install(new MessagingModule()); - install(new MessageCentralModule(properties)); - install(new HostIpModule(properties)); - install(new P2PModule(properties)); - install(new PeerDiscoveryModule()); - install(new PeerLivenessMonitorModule()); + // Network + install(new MessagingModule()); + install(new MessageCentralModule(properties)); + install(new HostIpModule(properties)); + install(new P2PModule(properties)); + install(new PeerDiscoveryModule()); + install(new PeerLivenessMonitorModule()); - // API - String bindAddress = properties.get("api.bind.address", DEFAULT_BIND_ADDRESS); - int port = properties.get("api.port", DEFAULT_CORE_PORT); - boolean enableTransactions = properties.get("api.transactions.enable", false); - boolean enableSign = properties.get("api.sign.enable", false); - install(new ApiModule(bindAddress, port, enableTransactions, enableSign)); - } + // API + String bindAddress = properties.get("api.bind.address", DEFAULT_BIND_ADDRESS); + int port = properties.get("api.port", DEFAULT_CORE_PORT); + boolean enableTransactions = properties.get("api.transactions.enable", false); + boolean enableSign = properties.get("api.sign.enable", false); + install(new ApiModule(bindAddress, port, enableTransactions, enableSign)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SyncServiceModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SyncServiceModule.java index 2c97148eac..6098efbe84 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SyncServiceModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SyncServiceModule.java @@ -74,134 +74,115 @@ import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.Hasher; -import com.radixdlt.environment.EventProcessorOnRunner; -import com.radixdlt.environment.Runners; import com.radixdlt.environment.EventDispatcher; +import com.radixdlt.environment.EventProcessorOnRunner; import com.radixdlt.environment.RemoteEventProcessorOnRunner; +import com.radixdlt.environment.Runners; import com.radixdlt.environment.ScheduledEventProducerOnRunner; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.PeerControl; import com.radixdlt.store.LastProof; -import com.radixdlt.sync.LocalSyncService.VerifiedSyncResponseHandler; +import com.radixdlt.sync.LocalSyncService; import com.radixdlt.sync.LocalSyncService.InvalidSyncResponseHandler; +import com.radixdlt.sync.LocalSyncService.VerifiedSyncResponseHandler; +import com.radixdlt.sync.RemoteSyncService; import com.radixdlt.sync.SyncConfig; import com.radixdlt.sync.SyncState; -import com.radixdlt.sync.RemoteSyncService; -import com.radixdlt.sync.LocalSyncService; import com.radixdlt.sync.messages.local.SyncCheckTrigger; import com.radixdlt.sync.messages.remote.StatusRequest; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.validation.RemoteSyncResponseSignaturesVerifier; import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; - import java.time.Duration; -/** - * Module which manages synchronization of committed atoms across of nodes - */ +/** Module which manages synchronization of committed atoms across of nodes */ public class SyncServiceModule extends AbstractModule { - @Override - public void configure() { - bind(LocalSyncService.class).in(Scopes.SINGLETON); - bind(RemoteSyncService.class).in(Scopes.SINGLETON); - } - - @Provides - private SyncState initialSyncState(@LastProof LedgerProof currentHeader) { - return SyncState.IdleState.init(currentHeader); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner syncRequestEventProcessor( - RemoteSyncService remoteSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - SyncRequest.class, - remoteSyncService.syncRequestEventProcessor() - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner statusRequestEventProcessor( - RemoteSyncService remoteSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - StatusRequest.class, - remoteSyncService.statusRequestEventProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner ledgerUpdateEventProcessor( - RemoteSyncService remoteSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - LedgerUpdate.class, - remoteSyncService.ledgerUpdateEventProcessor() - ); - } - - @Provides - private InvalidSyncResponseHandler invalidSyncResponseHandler( - SystemCounters counters, - PeerControl peerControl - ) { - return (sender, resp) -> { - peerControl.banPeer(NodeId.fromPublicKey(sender.getKey()), Duration.ofMinutes(10), "Received invalid sync response"); - counters.increment(CounterType.SYNC_INVALID_RESPONSES_RECEIVED); - }; - } - - @Provides - private VerifiedSyncResponseHandler verifiedSyncResponseHandler( - EventDispatcher syncCommandsDispatcher - ) { - return resp -> { - var txnsAndProof = resp.getTxnsAndProof(); - // TODO: Stateful ledger header verification: - // TODO: -verify rootHash matches - var nextHeader = new LedgerProof( - txnsAndProof.getTail().getOpaque(), - txnsAndProof.getTail().getLedgerHeader(), - txnsAndProof.getTail().getSignatures() - ); - - var verified = VerifiedTxnsAndProof.create( - txnsAndProof.getTxns(), - nextHeader - ); - - syncCommandsDispatcher.dispatch(verified); - }; - } - - @Provides - private RemoteSyncResponseValidatorSetVerifier validatorSetVerifier(BFTConfiguration initialConfiguration) { - return new RemoteSyncResponseValidatorSetVerifier(initialConfiguration.getValidatorSet()); - } - - @Provides - private RemoteSyncResponseSignaturesVerifier signaturesVerifier(Hasher hasher, HashVerifier hashVerifier) { - return new RemoteSyncResponseSignaturesVerifier(hasher, hashVerifier); - } - - @ProvidesIntoSet - public ScheduledEventProducerOnRunner syncCheckTriggerEventProducer( - EventDispatcher syncCheckTriggerEventDispatcher, - SyncConfig syncConfig - ) { - return new ScheduledEventProducerOnRunner<>( - Runners.SYNC, - syncCheckTriggerEventDispatcher, - SyncCheckTrigger::create, - Duration.ofMillis(syncConfig.syncCheckInterval()), - Duration.ofMillis(syncConfig.syncCheckInterval()) - ); - } + @Override + public void configure() { + bind(LocalSyncService.class).in(Scopes.SINGLETON); + bind(RemoteSyncService.class).in(Scopes.SINGLETON); + } + + @Provides + private SyncState initialSyncState(@LastProof LedgerProof currentHeader) { + return SyncState.IdleState.init(currentHeader); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner syncRequestEventProcessor( + RemoteSyncService remoteSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, SyncRequest.class, remoteSyncService.syncRequestEventProcessor()); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner statusRequestEventProcessor( + RemoteSyncService remoteSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, StatusRequest.class, remoteSyncService.statusRequestEventProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner ledgerUpdateEventProcessor( + RemoteSyncService remoteSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, LedgerUpdate.class, remoteSyncService.ledgerUpdateEventProcessor()); + } + + @Provides + private InvalidSyncResponseHandler invalidSyncResponseHandler( + SystemCounters counters, PeerControl peerControl) { + return (sender, resp) -> { + peerControl.banPeer( + NodeId.fromPublicKey(sender.getKey()), + Duration.ofMinutes(10), + "Received invalid sync response"); + counters.increment(CounterType.SYNC_INVALID_RESPONSES_RECEIVED); + }; + } + + @Provides + private VerifiedSyncResponseHandler verifiedSyncResponseHandler( + EventDispatcher syncCommandsDispatcher) { + return resp -> { + var txnsAndProof = resp.getTxnsAndProof(); + // TODO: Stateful ledger header verification: + // TODO: -verify rootHash matches + var nextHeader = + new LedgerProof( + txnsAndProof.getTail().getOpaque(), + txnsAndProof.getTail().getLedgerHeader(), + txnsAndProof.getTail().getSignatures()); + + var verified = VerifiedTxnsAndProof.create(txnsAndProof.getTxns(), nextHeader); + + syncCommandsDispatcher.dispatch(verified); + }; + } + + @Provides + private RemoteSyncResponseValidatorSetVerifier validatorSetVerifier( + BFTConfiguration initialConfiguration) { + return new RemoteSyncResponseValidatorSetVerifier(initialConfiguration.getValidatorSet()); + } + + @Provides + private RemoteSyncResponseSignaturesVerifier signaturesVerifier( + Hasher hasher, HashVerifier hashVerifier) { + return new RemoteSyncResponseSignaturesVerifier(hasher, hashVerifier); + } + + @ProvidesIntoSet + public ScheduledEventProducerOnRunner syncCheckTriggerEventProducer( + EventDispatcher syncCheckTriggerEventDispatcher, SyncConfig syncConfig) { + return new ScheduledEventProducerOnRunner<>( + Runners.SYNC, + syncCheckTriggerEventDispatcher, + SyncCheckTrigger::create, + Duration.ofMillis(syncConfig.syncCheckInterval()), + Duration.ofMillis(syncConfig.syncCheckInterval())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemInfoModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemInfoModule.java index deb097d15f..5692c8e349 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemInfoModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemInfoModule.java @@ -81,64 +81,54 @@ import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.systeminfo.InMemorySystemInfo; -/** - * Module which manages system info - */ +/** Module which manages system info */ public class SystemInfoModule extends AbstractModule { - @Override - protected void configure() { - bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - bind(InMemorySystemInfo.class).in(Scopes.SINGLETON); - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(EpochViewUpdate.class); - eventBinder.addBinding().toInstance(EpochLocalTimeoutOccurrence.class); - eventBinder.addBinding().toInstance(BFTCommittedUpdate.class); - eventBinder.addBinding().toInstance(BFTHighQCUpdate.class); - } + @Override + protected void configure() { + bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); + bind(InMemorySystemInfo.class).in(Scopes.SINGLETON); + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(EpochViewUpdate.class); + eventBinder.addBinding().toInstance(EpochLocalTimeoutOccurrence.class); + eventBinder.addBinding().toInstance(BFTCommittedUpdate.class); + eventBinder.addBinding().toInstance(BFTHighQCUpdate.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner epochsLedgerUpdateProcessor(InMemorySystemInfo inMemorySystemInfo) { - return new EventProcessorOnRunner<>( - Runners.SYSTEM_INFO, - LedgerUpdate.class, - inMemorySystemInfo.ledgerUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner epochsLedgerUpdateProcessor( + InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, LedgerUpdate.class, inMemorySystemInfo.ledgerUpdateEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner epochViewEventProcessor(InMemorySystemInfo inMemorySystemInfo) { - return new EventProcessorOnRunner<>( - Runners.SYSTEM_INFO, - EpochViewUpdate.class, - v -> inMemorySystemInfo.processView(v.getEpochView()) - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner epochViewEventProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + EpochViewUpdate.class, + v -> inMemorySystemInfo.processView(v.getEpochView())); + } - @ProvidesIntoSet - private EventProcessorOnRunner timeoutEventProcessor(InMemorySystemInfo inMemorySystemInfo) { - return new EventProcessorOnRunner<>( - Runners.SYSTEM_INFO, - EpochLocalTimeoutOccurrence.class, - inMemorySystemInfo::processTimeout - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner timeoutEventProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, EpochLocalTimeoutOccurrence.class, inMemorySystemInfo::processTimeout); + } - @ProvidesIntoSet - private EventProcessorOnRunner committedUpdateEventProcessor(InMemorySystemInfo inMemorySystemInfo) { - return new EventProcessorOnRunner<>( - Runners.SYSTEM_INFO, - BFTCommittedUpdate.class, - inMemorySystemInfo.bftCommittedUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner committedUpdateEventProcessor( + InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + BFTCommittedUpdate.class, + inMemorySystemInfo.bftCommittedUpdateEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner highQCProcessor(InMemorySystemInfo inMemorySystemInfo) { - return new EventProcessorOnRunner<>( - Runners.SYSTEM_INFO, - BFTHighQCUpdate.class, - inMemorySystemInfo.bftHighQCEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner highQCProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, BFTHighQCUpdate.class, inMemorySystemInfo.bftHighQCEventProcessor()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemModule.java index 43e3b4daf4..811fa68145 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemModule.java @@ -68,26 +68,23 @@ import com.google.inject.Provides; import com.google.inject.Scopes; import com.google.inject.Singleton; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.properties.RuntimeProperties; +import com.radixdlt.utils.TimeSupplier; import java.security.SecureRandom; import java.util.Random; import org.radix.time.Time; -/** - * Module which specifies implementations of system objects such as - * random and time. - */ +/** Module which specifies implementations of system objects such as random and time. */ public class SystemModule extends AbstractModule { - @Override - public void configure() { - bind(Random.class).to(SecureRandom.class).in(Scopes.SINGLETON); - } + @Override + public void configure() { + bind(Random.class).to(SecureRandom.class).in(Scopes.SINGLETON); + } - @Provides - @Singleton - TimeSupplier time(RuntimeProperties properties) { - Time.start(properties); - return Time::currentTimestamp; - } + @Provides + @Singleton + TimeSupplier time(RuntimeProperties properties) { + Time.start(properties); + return Time::currentTimestamp; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/ApiModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/ApiModule.java index aad210ebec..6cef3a29a5 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/ApiModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/ApiModule.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -75,73 +76,71 @@ import io.undertow.server.HttpServerExchange; import io.undertow.server.handlers.RequestLimitingHandler; import io.undertow.util.StatusCodes; - import java.util.Map; public final class ApiModule extends AbstractModule { - private static final int MAXIMUM_CONCURRENT_REQUESTS = Runtime.getRuntime().availableProcessors() * 8; // same as workerThreads = ioThreads * 8 - private static final int QUEUE_SIZE = 2000; + private static final int MAXIMUM_CONCURRENT_REQUESTS = + Runtime.getRuntime().availableProcessors() * 8; // same as workerThreads = ioThreads * 8 + private static final int QUEUE_SIZE = 2000; - private final int port; - private final String bindAddress; - private final boolean enableTransactions; - private final boolean enableSign; + private final int port; + private final String bindAddress; + private final boolean enableTransactions; + private final boolean enableSign; - public ApiModule( - String bindAddress, - int port, - boolean enableTransactions, - boolean enableSign - ) { - this.bindAddress = bindAddress; - this.port = port; - this.enableTransactions = enableTransactions; - this.enableSign = enableSign; - } + public ApiModule(String bindAddress, int port, boolean enableTransactions, boolean enableSign) { + this.bindAddress = bindAddress; + this.port = port; + this.enableTransactions = enableTransactions; + this.enableSign = enableSign; + } - @Override - public void configure() { - MapBinder.newMapBinder(binder(), String.class, HttpHandler.class); - install(new SystemApiModule()); - install(new CoreApiModule(enableTransactions, enableSign)); - } + @Override + public void configure() { + MapBinder.newMapBinder(binder(), String.class, HttpHandler.class); + install(new SystemApiModule()); + install(new CoreApiModule(enableTransactions, enableSign)); + } - private static void fallbackHandler(HttpServerExchange exchange) { - exchange.setStatusCode(StatusCodes.NOT_FOUND); - exchange.getResponseSender().send( - "No matching path found for " + exchange.getRequestMethod() + " " + exchange.getRequestPath() - ); - } + private static void fallbackHandler(HttpServerExchange exchange) { + exchange.setStatusCode(StatusCodes.NOT_FOUND); + exchange + .getResponseSender() + .send( + "No matching path found for " + + exchange.getRequestMethod() + + " " + + exchange.getRequestPath()); + } - private static void invalidMethodHandler(HttpServerExchange exchange) { - exchange.setStatusCode(StatusCodes.NOT_ACCEPTABLE); - exchange.getResponseSender().send( - "Invalid method, path exists for " + exchange.getRequestMethod() + " " + exchange.getRequestPath() - ); - } + private static void invalidMethodHandler(HttpServerExchange exchange) { + exchange.setStatusCode(StatusCodes.NOT_ACCEPTABLE); + exchange + .getResponseSender() + .send( + "Invalid method, path exists for " + + exchange.getRequestMethod() + + " " + + exchange.getRequestPath()); + } - private HttpHandler configureRoutes(Map handlers) { - var handler = Handlers.routing(true); // add path params to query params with this flag - handlers.forEach((r, h) -> handler.add(r.method(), r.path(), h)); - handler.setFallbackHandler(ApiModule::fallbackHandler); - handler.setInvalidMethodHandler(ApiModule::invalidMethodHandler); - var exceptionHandler = Handlers.exceptionHandler(handler); - exceptionHandler.addExceptionHandler(Exception.class, new UnhandledExceptionHandler()); - return exceptionHandler; - } + private HttpHandler configureRoutes(Map handlers) { + var handler = Handlers.routing(true); // add path params to query params with this flag + handlers.forEach((r, h) -> handler.add(r.method(), r.path(), h)); + handler.setFallbackHandler(ApiModule::fallbackHandler); + handler.setInvalidMethodHandler(ApiModule::invalidMethodHandler); + var exceptionHandler = Handlers.exceptionHandler(handler); + exceptionHandler.addExceptionHandler(Exception.class, new UnhandledExceptionHandler()); + return exceptionHandler; + } - @Provides - @Singleton - public Undertow undertow(Map handlers) { - var handler = new RequestLimitingHandler( - MAXIMUM_CONCURRENT_REQUESTS, - QUEUE_SIZE, - configureRoutes(handlers) - ); + @Provides + @Singleton + public Undertow undertow(Map handlers) { + var handler = + new RequestLimitingHandler( + MAXIMUM_CONCURRENT_REQUESTS, QUEUE_SIZE, configureRoutes(handlers)); - return Undertow.builder() - .addHttpListener(port, bindAddress) - .setHandler(handler) - .build(); - } + return Undertow.builder().addHttpListener(port, bindAddress).setHandler(handler).build(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/HandlerRoute.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/HandlerRoute.java index ad6c483a14..193eb1364c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/HandlerRoute.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/HandlerRoute.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -65,17 +66,16 @@ import io.undertow.util.HttpString; import io.undertow.util.Methods; - import java.util.Objects; public record HandlerRoute(HttpString method, String path) { - public static HandlerRoute post(String path) { - Objects.requireNonNull(path); - return new HandlerRoute(Methods.POST, path); - } + public static HandlerRoute post(String path) { + Objects.requireNonNull(path); + return new HandlerRoute(Methods.POST, path); + } - public static HandlerRoute get(String path) { - Objects.requireNonNull(path); - return new HandlerRoute(Methods.GET, path); - } + public static HandlerRoute get(String path) { + Objects.requireNonNull(path); + return new HandlerRoute(Methods.GET, path); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/UnhandledExceptionHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/UnhandledExceptionHandler.java index 3297c0015e..438c75c7ff 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/UnhandledExceptionHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/UnhandledExceptionHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -74,21 +75,24 @@ import io.undertow.util.Headers; public class UnhandledExceptionHandler implements HttpHandler { - @Override - public void handleRequest(HttpServerExchange exchange) throws Exception { - // TODO: Find a place to log this - var ex = exchange.getAttachment(ExceptionHandler.THROWABLE); - var rootCause = Throwables.getRootCause(ex); - var unexpectedError = new UnexpectedError() - .code(CoreApiErrorCode.INTERNAL_SERVER_ERROR.getErrorCode()) - .message(CoreApiErrorCode.INTERNAL_SERVER_ERROR.getMessage()) - .details(new InternalServerError() - .cause(rootCause.getMessage()) - .exception(rootCause.getClass().getSimpleName()) - .type("InternalServerError") - ); - exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, "application/json"); - exchange.setStatusCode(500); - exchange.getResponseSender().send(JSON.getDefault().getMapper().writeValueAsString(unexpectedError)); - } + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + // TODO: Find a place to log this + var ex = exchange.getAttachment(ExceptionHandler.THROWABLE); + var rootCause = Throwables.getRootCause(ex); + var unexpectedError = + new UnexpectedError() + .code(CoreApiErrorCode.INTERNAL_SERVER_ERROR.getErrorCode()) + .message(CoreApiErrorCode.INTERNAL_SERVER_ERROR.getMessage()) + .details( + new InternalServerError() + .cause(rootCause.getMessage()) + .exception(rootCause.getClass().getSimpleName()) + .type("InternalServerError")); + exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, "application/json"); + exchange.setStatusCode(500); + exchange + .getResponseSender() + .send(JSON.getDefault().getMapper().writeValueAsString(unexpectedError)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreApiModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreApiModule.java index d4fa425998..71d6478cd5 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreApiModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreApiModule.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,64 +68,81 @@ import com.google.inject.Scopes; import com.google.inject.multibindings.MapBinder; import com.google.inject.multibindings.Multibinder; +import com.radixdlt.api.HandlerRoute; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; import com.radixdlt.api.core.handlers.ConstructionDeriveHandler; import com.radixdlt.api.core.handlers.ConstructionFinalizeHandler; import com.radixdlt.api.core.handlers.ConstructionHashHandler; +import com.radixdlt.api.core.handlers.ConstructionParseHandler; import com.radixdlt.api.core.handlers.ConstructionSubmitHandler; import com.radixdlt.api.core.handlers.EngineConfigurationHandler; import com.radixdlt.api.core.handlers.EngineStatusHandler; import com.radixdlt.api.core.handlers.EntityHandler; +import com.radixdlt.api.core.handlers.KeyListHandler; +import com.radixdlt.api.core.handlers.KeySignHandler; import com.radixdlt.api.core.handlers.MempoolHandler; import com.radixdlt.api.core.handlers.MempoolTransactionHandler; import com.radixdlt.api.core.handlers.NetworkConfigurationHandler; import com.radixdlt.api.core.handlers.NetworkStatusHandler; -import com.radixdlt.api.core.handlers.ConstructionParseHandler; -import com.radixdlt.api.core.handlers.KeyListHandler; -import com.radixdlt.api.core.handlers.KeySignHandler; import com.radixdlt.api.core.handlers.TransactionsHandler; import com.radixdlt.api.core.reconstruction.BerkeleyRecoverableProcessedTxnStore; -import com.radixdlt.api.HandlerRoute; import com.radixdlt.store.berkeley.BerkeleyAdditionalStore; import io.undertow.server.HttpHandler; public class CoreApiModule extends AbstractModule { - private final boolean transactionsEnable; - private final boolean signEnable; + private final boolean transactionsEnable; + private final boolean signEnable; - public CoreApiModule(boolean transactionsEnable, boolean signEnable) { - this.transactionsEnable = transactionsEnable; - this.signEnable = signEnable; - } + public CoreApiModule(boolean transactionsEnable, boolean signEnable) { + this.transactionsEnable = transactionsEnable; + this.signEnable = signEnable; + } - @Override - public void configure() { - var routeBinder = MapBinder.newMapBinder( - binder(), HandlerRoute.class, HttpHandler.class - ); + @Override + public void configure() { + var routeBinder = MapBinder.newMapBinder(binder(), HandlerRoute.class, HttpHandler.class); - routeBinder.addBinding(HandlerRoute.post("/entity")).to(EntityHandler.class); - routeBinder.addBinding(HandlerRoute.post("/mempool")).to(MempoolHandler.class); - routeBinder.addBinding(HandlerRoute.post("/mempool/transaction")).to(MempoolTransactionHandler.class); - routeBinder.addBinding(HandlerRoute.post("/network/configuration")).to(NetworkConfigurationHandler.class); - routeBinder.addBinding(HandlerRoute.post("/network/status")).to(NetworkStatusHandler.class); - routeBinder.addBinding(HandlerRoute.post("/engine/configuration")).to(EngineConfigurationHandler.class); - routeBinder.addBinding(HandlerRoute.post("/engine/status")).to(EngineStatusHandler.class); - if (transactionsEnable) { - bind(BerkeleyRecoverableProcessedTxnStore.class).in(Scopes.SINGLETON); - Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class) - .addBinding().to(BerkeleyRecoverableProcessedTxnStore.class); - routeBinder.addBinding(HandlerRoute.post("/transactions")).to(TransactionsHandler.class); - } - routeBinder.addBinding(HandlerRoute.post("/construction/derive")).to(ConstructionDeriveHandler.class); - routeBinder.addBinding(HandlerRoute.post("/construction/build")).to(ConstructionBuildHandler.class); - routeBinder.addBinding(HandlerRoute.post("/construction/parse")).to(ConstructionParseHandler.class); - routeBinder.addBinding(HandlerRoute.post("/construction/finalize")).to(ConstructionFinalizeHandler.class); - routeBinder.addBinding(HandlerRoute.post("/construction/hash")).to(ConstructionHashHandler.class); - routeBinder.addBinding(HandlerRoute.post("/construction/submit")).to(ConstructionSubmitHandler.class); - routeBinder.addBinding(HandlerRoute.post("/key/list")).to(KeyListHandler.class); - if (signEnable) { - routeBinder.addBinding(HandlerRoute.post("/key/sign")).to(KeySignHandler.class); - } - } + routeBinder.addBinding(HandlerRoute.post("/entity")).to(EntityHandler.class); + routeBinder.addBinding(HandlerRoute.post("/mempool")).to(MempoolHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/mempool/transaction")) + .to(MempoolTransactionHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/network/configuration")) + .to(NetworkConfigurationHandler.class); + routeBinder.addBinding(HandlerRoute.post("/network/status")).to(NetworkStatusHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/engine/configuration")) + .to(EngineConfigurationHandler.class); + routeBinder.addBinding(HandlerRoute.post("/engine/status")).to(EngineStatusHandler.class); + if (transactionsEnable) { + bind(BerkeleyRecoverableProcessedTxnStore.class).in(Scopes.SINGLETON); + Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class) + .addBinding() + .to(BerkeleyRecoverableProcessedTxnStore.class); + routeBinder.addBinding(HandlerRoute.post("/transactions")).to(TransactionsHandler.class); + } + routeBinder + .addBinding(HandlerRoute.post("/construction/derive")) + .to(ConstructionDeriveHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/construction/build")) + .to(ConstructionBuildHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/construction/parse")) + .to(ConstructionParseHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/construction/finalize")) + .to(ConstructionFinalizeHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/construction/hash")) + .to(ConstructionHashHandler.class); + routeBinder + .addBinding(HandlerRoute.post("/construction/submit")) + .to(ConstructionSubmitHandler.class); + routeBinder.addBinding(HandlerRoute.post("/key/list")).to(KeyListHandler.class); + if (signEnable) { + routeBinder.addBinding(HandlerRoute.post("/key/sign")).to(KeySignHandler.class); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreJsonRpcHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreJsonRpcHandler.java index 84899808ea..64c94def0c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreJsonRpcHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/CoreJsonRpcHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -77,62 +78,59 @@ import io.undertow.util.Headers; public abstract class CoreJsonRpcHandler implements HttpHandler { - private static final String CONTENT_TYPE_JSON = "application/json"; - private static final long DEFAULT_MAX_REQUEST_SIZE = 1024L * 1024L; + private static final String CONTENT_TYPE_JSON = "application/json"; + private static final long DEFAULT_MAX_REQUEST_SIZE = 1024L * 1024L; - private final Class requestClass; - private final ObjectMapper objectMapper; + private final Class requestClass; + private final ObjectMapper objectMapper; - protected CoreJsonRpcHandler(Class requestClass) { - this.requestClass = requestClass; - this.objectMapper = JSON.getDefault().getMapper(); - } + protected CoreJsonRpcHandler(Class requestClass) { + this.requestClass = requestClass; + this.objectMapper = JSON.getDefault().getMapper(); + } - public abstract U handleRequest(T request) throws CoreApiException; + public abstract U handleRequest(T request) throws CoreApiException; - @Override - public final void handleRequest(HttpServerExchange exchange) throws Exception { - if (exchange.isInIoThread()) { - exchange.dispatch(this); - return; - } + @Override + public final void handleRequest(HttpServerExchange exchange) throws Exception { + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } - exchange.setMaxEntitySize(DEFAULT_MAX_REQUEST_SIZE); - exchange.startBlocking(); - exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, CONTENT_TYPE_JSON); + exchange.setMaxEntitySize(DEFAULT_MAX_REQUEST_SIZE); + exchange.startBlocking(); + exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, CONTENT_TYPE_JSON); - T request; - try { - request = objectMapper.readValue(exchange.getInputStream(), requestClass); - } catch (JsonMappingException | JsonParseException e) { - var errorResponse = handleParseException(e); - exchange.setStatusCode(500); - exchange.getResponseSender().send(objectMapper.writeValueAsString(errorResponse)); - return; - } + T request; + try { + request = objectMapper.readValue(exchange.getInputStream(), requestClass); + } catch (JsonMappingException | JsonParseException e) { + var errorResponse = handleParseException(e); + exchange.setStatusCode(500); + exchange.getResponseSender().send(objectMapper.writeValueAsString(errorResponse)); + return; + } - U response; - try { - response = handleRequest(request); - } catch (CoreApiException e) { - var errorResponse = e.toError(); - exchange.setStatusCode(500); - exchange.getResponseSender().send(objectMapper.writeValueAsString(errorResponse)); - return; - } + U response; + try { + response = handleRequest(request); + } catch (CoreApiException e) { + var errorResponse = e.toError(); + exchange.setStatusCode(500); + exchange.getResponseSender().send(objectMapper.writeValueAsString(errorResponse)); + return; + } - exchange.setStatusCode(200); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - exchange.getResponseSender().send(objectMapper.writeValueAsString(response)); - } + exchange.setStatusCode(200); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + exchange.getResponseSender().send(objectMapper.writeValueAsString(response)); + } - public UnexpectedError handleParseException(Exception e) { - return new UnexpectedError() - .code(CoreApiErrorCode.BAD_REQUEST.getErrorCode()) - .message(CoreApiErrorCode.BAD_REQUEST.getMessage()) - .details(new InvalidJsonError() - .cause(e.getMessage()) - .type("InvalidJsonError") - ); - } + public UnexpectedError handleParseException(Exception e) { + return new UnexpectedError() + .code(CoreApiErrorCode.BAD_REQUEST.getErrorCode()) + .message(CoreApiErrorCode.BAD_REQUEST.getMessage()) + .details(new InvalidJsonError().cause(e.getMessage()).type("InvalidJsonError")); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionBuildHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionBuildHandler.java index e2d4469167..e5e7ec63f8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionBuildHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionBuildHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -76,46 +77,44 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.utils.Bytes; -public final class ConstructionBuildHandler extends CoreJsonRpcHandler { - private final RadixEngine radixEngine; - private final CoreModelMapper modelMapper; +public final class ConstructionBuildHandler + extends CoreJsonRpcHandler { + private final RadixEngine radixEngine; + private final CoreModelMapper modelMapper; - @Inject - ConstructionBuildHandler( - RadixEngine radixEngine, - CoreModelMapper modelMapper - ) { - super(ConstructionBuildRequest.class); - this.radixEngine = radixEngine; - this.modelMapper = modelMapper; - } + @Inject + ConstructionBuildHandler( + RadixEngine radixEngine, CoreModelMapper modelMapper) { + super(ConstructionBuildRequest.class); + this.radixEngine = radixEngine; + this.modelMapper = modelMapper; + } - @Override - public ConstructionBuildResponse handleRequest(ConstructionBuildRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public ConstructionBuildResponse handleRequest(ConstructionBuildRequest request) + throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var operationTxBuilder = modelMapper.operationTxBuilder( - request.getMessage(), - request.getOperationGroups() - ); - var feePayer = modelMapper.feePayerEntity(request.getFeePayer()); - var disableAllocAndDestroy = request.getDisableResourceAllocateAndDestroy(); - var disable = disableAllocAndDestroy != null && disableAllocAndDestroy; - TxBuilder builder; - try { - builder = radixEngine.constructWithFees( - operationTxBuilder, - disable, - feePayer.accountAddress(), - NotEnoughNativeTokensForFeesException::new - ); - } catch (TxBuilderException e) { - throw CoreApiException.badRequest(modelMapper.builderErrorDetails(e)); - } + var operationTxBuilder = + modelMapper.operationTxBuilder(request.getMessage(), request.getOperationGroups()); + var feePayer = modelMapper.feePayerEntity(request.getFeePayer()); + var disableAllocAndDestroy = request.getDisableResourceAllocateAndDestroy(); + var disable = disableAllocAndDestroy != null && disableAllocAndDestroy; + TxBuilder builder; + try { + builder = + radixEngine.constructWithFees( + operationTxBuilder, + disable, + feePayer.accountAddress(), + NotEnoughNativeTokensForFeesException::new); + } catch (TxBuilderException e) { + throw CoreApiException.badRequest(modelMapper.builderErrorDetails(e)); + } - var unsignedTransaction = builder.buildForExternalSign(); - return new ConstructionBuildResponse() - .unsignedTransaction(Bytes.toHexString(unsignedTransaction.blob())) - .payloadToSign(Bytes.toHexString(unsignedTransaction.hashToSign().asBytes())); - } + var unsignedTransaction = builder.buildForExternalSign(); + return new ConstructionBuildResponse() + .unsignedTransaction(Bytes.toHexString(unsignedTransaction.blob())) + .payloadToSign(Bytes.toHexString(unsignedTransaction.hashToSign().asBytes())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionDeriveHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionDeriveHandler.java index 2f26a75b39..fd511a1c13 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionDeriveHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionDeriveHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -79,57 +80,62 @@ import com.radixdlt.api.core.openapitools.model.ConstructionDeriveResponse; import com.radixdlt.identifiers.REAddr; -public final class ConstructionDeriveHandler extends CoreJsonRpcHandler { - private final CoreModelMapper coreModelMapper; +public final class ConstructionDeriveHandler + extends CoreJsonRpcHandler { + private final CoreModelMapper coreModelMapper; - @Inject - ConstructionDeriveHandler(CoreModelMapper coreModelMapper) { - super(ConstructionDeriveRequest.class); + @Inject + ConstructionDeriveHandler(CoreModelMapper coreModelMapper) { + super(ConstructionDeriveRequest.class); - this.coreModelMapper = coreModelMapper; - } + this.coreModelMapper = coreModelMapper; + } - @Override - public ConstructionDeriveResponse handleRequest(ConstructionDeriveRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public ConstructionDeriveResponse handleRequest(ConstructionDeriveRequest request) + throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - var publicKey = coreModelMapper.ecPublicKey(request.getPublicKey()); + var publicKey = coreModelMapper.ecPublicKey(request.getPublicKey()); - var response = new ConstructionDeriveResponse(); - var metadata = request.getMetadata(); - if (metadata instanceof ConstructionDeriveRequestMetadataAccount) { - var address = REAddr.ofPubKeyAccount(publicKey); - response.entityIdentifier(coreModelMapper.entityIdentifier(address)); - } else if (metadata instanceof ConstructionDeriveRequestMetadataValidator) { - response.entityIdentifier(coreModelMapper.entityIdentifier(publicKey)); - } else if (metadata instanceof ConstructionDeriveRequestMetadataToken token) { - var tokenAddress = REAddr.ofHashedKey(publicKey, token.getSymbol()); - response.entityIdentifier(coreModelMapper.entityIdentifier(tokenAddress, token.getSymbol())); - } else if (metadata instanceof ConstructionDeriveRequestMetadataPreparedStakes preparedStakesMetadata) { - var entity = coreModelMapper.entity(preparedStakesMetadata.getValidator()); - if (!(entity instanceof ValidatorEntity validatorEntity)) { - throw coreModelMapper.notValidatorEntityException(preparedStakesMetadata.getValidator()); - } - var address = REAddr.ofPubKeyAccount(publicKey); - response.entityIdentifier(coreModelMapper.entityIdentifierPreparedStake(address, validatorEntity.validatorKey())); - } else if (metadata instanceof ConstructionDeriveRequestMetadataPreparedUnstakes) { - var address = REAddr.ofPubKeyAccount(publicKey); - response.entityIdentifier(coreModelMapper.entityIdentifierPreparedUnstake(address)); - } else if (metadata instanceof ConstructionDeriveRequestMetadataExitingUnstakes exitingUnstakes) { - var entity = coreModelMapper.entity(exitingUnstakes.getValidator()); - if (!(entity instanceof ValidatorEntity validatorEntity)) { - throw coreModelMapper.notValidatorEntityException(exitingUnstakes.getValidator()); - } - var address = REAddr.ofPubKeyAccount(publicKey); - response.entityIdentifier(coreModelMapper.entityIdentifierExitingStake( - address, validatorEntity.validatorKey(), exitingUnstakes.getEpochUnlock() - )); - } else if (metadata instanceof ConstructionDeriveRequestMetadataValidatorSystem) { - response.entityIdentifier(coreModelMapper.entityIdentifierValidatorSystem(publicKey)); - } else { - throw new IllegalStateException("Unknown metadata type: " + metadata); - } + var response = new ConstructionDeriveResponse(); + var metadata = request.getMetadata(); + if (metadata instanceof ConstructionDeriveRequestMetadataAccount) { + var address = REAddr.ofPubKeyAccount(publicKey); + response.entityIdentifier(coreModelMapper.entityIdentifier(address)); + } else if (metadata instanceof ConstructionDeriveRequestMetadataValidator) { + response.entityIdentifier(coreModelMapper.entityIdentifier(publicKey)); + } else if (metadata instanceof ConstructionDeriveRequestMetadataToken token) { + var tokenAddress = REAddr.ofHashedKey(publicKey, token.getSymbol()); + response.entityIdentifier(coreModelMapper.entityIdentifier(tokenAddress, token.getSymbol())); + } else if (metadata + instanceof ConstructionDeriveRequestMetadataPreparedStakes preparedStakesMetadata) { + var entity = coreModelMapper.entity(preparedStakesMetadata.getValidator()); + if (!(entity instanceof ValidatorEntity validatorEntity)) { + throw coreModelMapper.notValidatorEntityException(preparedStakesMetadata.getValidator()); + } + var address = REAddr.ofPubKeyAccount(publicKey); + response.entityIdentifier( + coreModelMapper.entityIdentifierPreparedStake(address, validatorEntity.validatorKey())); + } else if (metadata instanceof ConstructionDeriveRequestMetadataPreparedUnstakes) { + var address = REAddr.ofPubKeyAccount(publicKey); + response.entityIdentifier(coreModelMapper.entityIdentifierPreparedUnstake(address)); + } else if (metadata + instanceof ConstructionDeriveRequestMetadataExitingUnstakes exitingUnstakes) { + var entity = coreModelMapper.entity(exitingUnstakes.getValidator()); + if (!(entity instanceof ValidatorEntity validatorEntity)) { + throw coreModelMapper.notValidatorEntityException(exitingUnstakes.getValidator()); + } + var address = REAddr.ofPubKeyAccount(publicKey); + response.entityIdentifier( + coreModelMapper.entityIdentifierExitingStake( + address, validatorEntity.validatorKey(), exitingUnstakes.getEpochUnlock())); + } else if (metadata instanceof ConstructionDeriveRequestMetadataValidatorSystem) { + response.entityIdentifier(coreModelMapper.entityIdentifierValidatorSystem(publicKey)); + } else { + throw new IllegalStateException("Unknown metadata type: " + metadata); + } - return response; - } + return response; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionFinalizeHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionFinalizeHandler.java index aa418d39b5..b14eff4d4e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionFinalizeHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionFinalizeHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -74,30 +75,29 @@ import com.radixdlt.crypto.HashUtils; import com.radixdlt.utils.Bytes; -public final class ConstructionFinalizeHandler extends CoreJsonRpcHandler { - private final CoreModelMapper coreModelMapper; +public final class ConstructionFinalizeHandler + extends CoreJsonRpcHandler { + private final CoreModelMapper coreModelMapper; - @Inject - public ConstructionFinalizeHandler( - CoreModelMapper coreModelMapper - ) { - super(ConstructionFinalizeRequest.class); - this.coreModelMapper = coreModelMapper; - } + @Inject + public ConstructionFinalizeHandler(CoreModelMapper coreModelMapper) { + super(ConstructionFinalizeRequest.class); + this.coreModelMapper = coreModelMapper; + } - @Override - public ConstructionFinalizeResponse handleRequest(ConstructionFinalizeRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public ConstructionFinalizeResponse handleRequest(ConstructionFinalizeRequest request) + throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - var keyAndSignature = coreModelMapper.keyAndSignature(request.getSignature()); - var unsignedTransaction = coreModelMapper.bytes(request.getUnsignedTransaction()); - var hash = HashUtils.sha256(unsignedTransaction).asBytes(); - var recoverable = ECKeyUtils.toRecoverableSig( - keyAndSignature.getSecond(), hash, keyAndSignature.getFirst() - ); + var keyAndSignature = coreModelMapper.keyAndSignature(request.getSignature()); + var unsignedTransaction = coreModelMapper.bytes(request.getUnsignedTransaction()); + var hash = HashUtils.sha256(unsignedTransaction).asBytes(); + var recoverable = + ECKeyUtils.toRecoverableSig(keyAndSignature.getSecond(), hash, keyAndSignature.getFirst()); - var txn = TxLowLevelBuilder.newBuilder(unsignedTransaction).sig(recoverable).build(); - return new ConstructionFinalizeResponse() - .signedTransaction(Bytes.toHexString(txn.getPayload())); - } + var txn = TxLowLevelBuilder.newBuilder(unsignedTransaction).sig(recoverable).build(); + return new ConstructionFinalizeResponse() + .signedTransaction(Bytes.toHexString(txn.getPayload())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionHashHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionHashHandler.java index 8d3fa3beaf..9a48d2fa2d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionHashHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionHashHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -70,19 +71,21 @@ import com.radixdlt.api.core.openapitools.model.ConstructionHashRequest; import com.radixdlt.api.core.openapitools.model.ConstructionHashResponse; -public final class ConstructionHashHandler extends CoreJsonRpcHandler { - private final CoreModelMapper modelMapper; +public final class ConstructionHashHandler + extends CoreJsonRpcHandler { + private final CoreModelMapper modelMapper; - @Inject - public ConstructionHashHandler(CoreModelMapper modelMapper) { - super(ConstructionHashRequest.class); - this.modelMapper = modelMapper; - } + @Inject + public ConstructionHashHandler(CoreModelMapper modelMapper) { + super(ConstructionHashRequest.class); + this.modelMapper = modelMapper; + } - @Override - public ConstructionHashResponse handleRequest(ConstructionHashRequest request) throws CoreApiException { - var txn = modelMapper.txn(request.getSignedTransaction()); - return new ConstructionHashResponse() - .transactionIdentifier(modelMapper.transactionIdentifier(txn.getId())); - } + @Override + public ConstructionHashResponse handleRequest(ConstructionHashRequest request) + throws CoreApiException { + var txn = modelMapper.txn(request.getSignedTransaction()); + return new ConstructionHashResponse() + .transactionIdentifier(modelMapper.transactionIdentifier(txn.getId())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionParseHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionParseHandler.java index a77b1f3106..b6919226e1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionParseHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionParseHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -81,55 +82,56 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.RadixEngineStateComputer; -public final class ConstructionParseHandler extends CoreJsonRpcHandler { - private final Provider> radixEngineProvider; - private final RadixEngineStateComputer radixEngineStateComputer; - private final CoreModelMapper modelMapper; +public final class ConstructionParseHandler + extends CoreJsonRpcHandler { + private final Provider> radixEngineProvider; + private final RadixEngineStateComputer radixEngineStateComputer; + private final CoreModelMapper modelMapper; - @Inject - ConstructionParseHandler( - Provider> radixEngineProvider, - RadixEngineStateComputer radixEngineStateComputer, - CoreModelMapper modelMapper - ) { - super(ConstructionParseRequest.class); + @Inject + ConstructionParseHandler( + Provider> radixEngineProvider, + RadixEngineStateComputer radixEngineStateComputer, + CoreModelMapper modelMapper) { + super(ConstructionParseRequest.class); - this.radixEngineProvider = radixEngineProvider; - this.radixEngineStateComputer = radixEngineStateComputer; - this.modelMapper = modelMapper; - } + this.radixEngineProvider = radixEngineProvider; + this.radixEngineStateComputer = radixEngineStateComputer; + this.modelMapper = modelMapper; + } - private String symbol(REAddr tokenAddress) { - var mapKey = SystemMapKey.ofResourceData(tokenAddress, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); - var substate = radixEngineProvider.get().read(reader -> reader.get(mapKey).orElseThrow()); - // TODO: This is a bit of a hack to require deserialization, figure out correct abstraction - var tokenResourceMetadata = (TokenResourceMetadata) substate; - return tokenResourceMetadata.getSymbol(); - } + private String symbol(REAddr tokenAddress) { + var mapKey = + SystemMapKey.ofResourceData(tokenAddress, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); + var substate = radixEngineProvider.get().read(reader -> reader.get(mapKey).orElseThrow()); + // TODO: This is a bit of a hack to require deserialization, figure out correct abstraction + var tokenResourceMetadata = (TokenResourceMetadata) substate; + return tokenResourceMetadata.getSymbol(); + } - @Override - public ConstructionParseResponse handleRequest(ConstructionParseRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public ConstructionParseResponse handleRequest(ConstructionParseRequest request) + throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var txn = modelMapper.bytes(request.getTransaction()); + var txn = modelMapper.bytes(request.getTransaction()); - REProcessedTxn processed; - try { - processed = radixEngineStateComputer.test(txn, request.getSigned()); - } catch (RadixEngineException e) { - throw modelMapper.radixEngineException(e); - } + REProcessedTxn processed; + try { + processed = radixEngineStateComputer.test(txn, request.getSigned()); + } catch (RadixEngineException e) { + throw modelMapper.radixEngineException(e); + } - var response = new ConstructionParseResponse(); - var transaction = modelMapper.transaction(processed, this::symbol); - transaction.getOperationGroups() - .forEach(response::addOperationGroupsItem); + var response = new ConstructionParseResponse(); + var transaction = modelMapper.transaction(processed, this::symbol); + transaction.getOperationGroups().forEach(response::addOperationGroupsItem); - response.metadata(new ParsedTransactionMetadata() - .fee(transaction.getMetadata().getFee()) - .message(transaction.getMetadata().getMessage()) - ); + response.metadata( + new ParsedTransactionMetadata() + .fee(transaction.getMetadata().getFee()) + .message(transaction.getMetadata().getMessage())); - return response; - } + return response; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionSubmitHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionSubmitHandler.java index 4e9fc468d7..3ad297f257 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionSubmitHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/ConstructionSubmitHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -75,40 +76,40 @@ import com.radixdlt.mempool.MempoolRejectedException; import com.radixdlt.statecomputer.RadixEngineStateComputer; -public final class ConstructionSubmitHandler extends CoreJsonRpcHandler { - private final RadixEngineStateComputer radixEngineStateComputer; - private final CoreModelMapper modelMapper; +public final class ConstructionSubmitHandler + extends CoreJsonRpcHandler { + private final RadixEngineStateComputer radixEngineStateComputer; + private final CoreModelMapper modelMapper; - @Inject - ConstructionSubmitHandler( - RadixEngineStateComputer radixEngineStateComputer, - CoreModelMapper modelMapper - ) { - super(ConstructionSubmitRequest.class); + @Inject + ConstructionSubmitHandler( + RadixEngineStateComputer radixEngineStateComputer, CoreModelMapper modelMapper) { + super(ConstructionSubmitRequest.class); - this.radixEngineStateComputer = radixEngineStateComputer; - this.modelMapper = modelMapper; - } + this.radixEngineStateComputer = radixEngineStateComputer; + this.modelMapper = modelMapper; + } - @Override - public ConstructionSubmitResponse handleRequest(ConstructionSubmitRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public ConstructionSubmitResponse handleRequest(ConstructionSubmitRequest request) + throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var txn = modelMapper.txn(request.getSignedTransaction()); - try { - radixEngineStateComputer.addToMempool(txn); - return new ConstructionSubmitResponse() - .transactionIdentifier(modelMapper.transactionIdentifier(txn.getId())) - .duplicate(false); - } catch (MempoolDuplicateException e) { - return new ConstructionSubmitResponse() - .transactionIdentifier(modelMapper.transactionIdentifier(txn.getId())) - .duplicate(true); - } catch (MempoolFullException e) { - throw modelMapper.mempoolFullException(e); - } catch (MempoolRejectedException e) { - var reException = (RadixEngineException) e.getCause(); - throw modelMapper.radixEngineException(reException); - } - } + var txn = modelMapper.txn(request.getSignedTransaction()); + try { + radixEngineStateComputer.addToMempool(txn); + return new ConstructionSubmitResponse() + .transactionIdentifier(modelMapper.transactionIdentifier(txn.getId())) + .duplicate(false); + } catch (MempoolDuplicateException e) { + return new ConstructionSubmitResponse() + .transactionIdentifier(modelMapper.transactionIdentifier(txn.getId())) + .duplicate(true); + } catch (MempoolFullException e) { + throw modelMapper.mempoolFullException(e); + } catch (MempoolRejectedException e) { + var reException = (RadixEngineException) e.getCause(); + throw modelMapper.radixEngineException(reException); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineConfigurationHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineConfigurationHandler.java index 6b936a1dee..8c2d42e4ea 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineConfigurationHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineConfigurationHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -77,47 +78,47 @@ import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.statecomputer.forks.ForkConfig; import com.radixdlt.utils.Bytes; - import java.util.TreeMap; -public final class EngineConfigurationHandler extends CoreJsonRpcHandler { - private final TreeMap forks; - private final CoreModelMapper modelMapper; - private final VerifiedTxnsAndProof genesis; +public final class EngineConfigurationHandler + extends CoreJsonRpcHandler { + private final TreeMap forks; + private final CoreModelMapper modelMapper; + private final VerifiedTxnsAndProof genesis; - @Inject - public EngineConfigurationHandler( - @Genesis VerifiedTxnsAndProof genesis, - TreeMap forks, - CoreModelMapper modelMapper - ) { - super(EngineConfigurationRequest.class); - this.genesis = genesis; - this.forks = forks; - this.modelMapper = modelMapper; - } + @Inject + public EngineConfigurationHandler( + @Genesis VerifiedTxnsAndProof genesis, + TreeMap forks, + CoreModelMapper modelMapper) { + super(EngineConfigurationRequest.class); + this.genesis = genesis; + this.forks = forks; + this.modelMapper = modelMapper; + } - @Override - public EngineConfigurationResponse handleRequest(EngineConfigurationRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public EngineConfigurationResponse handleRequest(EngineConfigurationRequest request) + throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var response = new EngineConfigurationResponse(); - forks.forEach((epoch, config) -> response.addForksItem(modelMapper.fork(config))); - response.addCheckpointsItem(genesisCheckpoint()); - return response; - } + var response = new EngineConfigurationResponse(); + forks.forEach((epoch, config) -> response.addForksItem(modelMapper.fork(config))); + response.addCheckpointsItem(genesisCheckpoint()); + return response; + } - private EngineCheckpoint genesisCheckpoint() { - return new EngineCheckpoint() - .checkpointTransaction(Bytes.toHexString(genesis.getTxns().get(0).getPayload())) - .engineStateIdentifier(new EngineStateIdentifier() - .stateIdentifier(new StateIdentifier() - .stateVersion(0L) - .transactionAccumulator(Bytes.toHexString(HashUtils.zero256().asBytes())) - ) - .epoch(0L) - .round(0L) - .timestamp(genesis.getProof().timestamp()) - ); - } + private EngineCheckpoint genesisCheckpoint() { + return new EngineCheckpoint() + .checkpointTransaction(Bytes.toHexString(genesis.getTxns().get(0).getPayload())) + .engineStateIdentifier( + new EngineStateIdentifier() + .stateIdentifier( + new StateIdentifier() + .stateVersion(0L) + .transactionAccumulator(Bytes.toHexString(HashUtils.zero256().asBytes()))) + .epoch(0L) + .round(0L) + .timestamp(genesis.getProof().timestamp())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineStatusHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineStatusHandler.java index 93ccb5ea69..db7b7f73f3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineStatusHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EngineStatusHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -79,59 +80,70 @@ import com.radixdlt.store.LastProof; import com.radixdlt.sync.CommittedReader; +public class EngineStatusHandler + extends CoreJsonRpcHandler { + private final Addressing addressing; + private final RadixEngine radixEngine; + private final CommittedReader committedReader; + private final CoreModelMapper modelMapper; + private final LedgerProof lastProof; + private final LedgerProof lastEpochProof; -public class EngineStatusHandler extends CoreJsonRpcHandler { - private final Addressing addressing; - private final RadixEngine radixEngine; - private final CommittedReader committedReader; - private final CoreModelMapper modelMapper; - private final LedgerProof lastProof; - private final LedgerProof lastEpochProof; - - @Inject - public EngineStatusHandler( - RadixEngine radixEngine, - CommittedReader committedReader, - @LastProof LedgerProof lastProof, - @LastEpochProof LedgerProof lastEpochProof, - CoreModelMapper modelMapper, - Addressing addressing - ) { - super(EngineStatusRequest.class); + @Inject + public EngineStatusHandler( + RadixEngine radixEngine, + CommittedReader committedReader, + @LastProof LedgerProof lastProof, + @LastEpochProof LedgerProof lastEpochProof, + CoreModelMapper modelMapper, + Addressing addressing) { + super(EngineStatusRequest.class); - this.radixEngine = radixEngine; - this.committedReader = committedReader; - this.lastProof = lastProof; - this.lastEpochProof = lastEpochProof; - this.modelMapper = modelMapper; - this.addressing = addressing; - } + this.radixEngine = radixEngine; + this.committedReader = committedReader; + this.lastProof = lastProof; + this.lastEpochProof = lastEpochProof; + this.modelMapper = modelMapper; + this.addressing = addressing; + } - private LedgerProof getEpochProof(long epoch) { - return committedReader.getEpochProof(epoch).orElse(lastEpochProof); - } + private LedgerProof getEpochProof(long epoch) { + return committedReader.getEpochProof(epoch).orElse(lastEpochProof); + } - private LedgerProof getCurrentProof() { - var ledgerAndBFTProof = radixEngine.read(RadixEngineReader::getMetadata); - return ledgerAndBFTProof == null ? lastProof : ledgerAndBFTProof.getProof(); - } + private LedgerProof getCurrentProof() { + var ledgerAndBFTProof = radixEngine.read(RadixEngineReader::getMetadata); + return ledgerAndBFTProof == null ? lastProof : ledgerAndBFTProof.getProof(); + } - @Override - public EngineStatusResponse handleRequest(EngineStatusRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public EngineStatusResponse handleRequest(EngineStatusRequest request) throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var response = new EngineStatusResponse(); - var currentProof = getCurrentProof(); - var epochProof = currentProof.getNextValidatorSet().map(v -> currentProof).orElse(getEpochProof(currentProof.getEpoch())); - var validatorSet = epochProof.getNextValidatorSet().orElseThrow(() -> new IllegalStateException("Epoch proof should have a validator set")); - response.engineStateIdentifier(modelMapper.engineStateIdentifier(currentProof)); - validatorSet.getValidators().forEach(v -> { - var validator = new Validator() - .validatorAddress(addressing.forValidators().of(v.getNode().getKey())) - .stake(v.getPower().toString()); - response.addValidatorSetItem(validator); - }); + var response = new EngineStatusResponse(); + var currentProof = getCurrentProof(); + var epochProof = + currentProof + .getNextValidatorSet() + .map(v -> currentProof) + .orElse(getEpochProof(currentProof.getEpoch())); + var validatorSet = + epochProof + .getNextValidatorSet() + .orElseThrow( + () -> new IllegalStateException("Epoch proof should have a validator set")); + response.engineStateIdentifier(modelMapper.engineStateIdentifier(currentProof)); + validatorSet + .getValidators() + .forEach( + v -> { + var validator = + new Validator() + .validatorAddress(addressing.forValidators().of(v.getNode().getKey())) + .stake(v.getPower().toString()); + response.addValidatorSetItem(validator); + }); - return response; - } + return response; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EntityHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EntityHandler.java index 42bad419bc..15866a8822 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EntityHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/EntityHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -76,62 +77,67 @@ import com.radixdlt.engine.RadixEngine; import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.LedgerAndBFTProof; - import java.util.function.Function; public class EntityHandler extends CoreJsonRpcHandler { - private final RadixEngine radixEngine; - private final CoreModelMapper modelMapper; + private final RadixEngine radixEngine; + private final CoreModelMapper modelMapper; - @Inject - EntityHandler( - RadixEngine radixEngine, - CoreModelMapper modelMapper - ) { - super(EntityRequest.class); - this.radixEngine = radixEngine; - this.modelMapper = modelMapper; - } + @Inject + EntityHandler(RadixEngine radixEngine, CoreModelMapper modelMapper) { + super(EntityRequest.class); + this.radixEngine = radixEngine; + this.modelMapper = modelMapper; + } - @Override - public EntityResponse handleRequest(EntityRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public EntityResponse handleRequest(EntityRequest request) throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var entity = modelMapper.entity(request.getEntityIdentifier()); - var keyQueries = entity.getKeyQueries(); - var resourceQueries = entity.getResourceQueries(); + var entity = modelMapper.entity(request.getEntityIdentifier()); + var keyQueries = entity.getKeyQueries(); + var resourceQueries = entity.getResourceQueries(); - // This must be read atomically - return radixEngine.read(reader -> { - Function addressToSymbol = addr -> { - var mapKey = SystemMapKey.ofResourceData(addr, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); - var substate = reader.get(mapKey).orElseThrow(); - var tokenResource = (TokenResourceMetadata) substate; - return tokenResource.getSymbol(); - }; - var proof = reader.getMetadata().getProof(); - var response = new EntityResponse() - .stateIdentifier(modelMapper.stateIdentifier(proof.getAccumulatorState())); + // This must be read atomically + return radixEngine.read( + reader -> { + Function addressToSymbol = + addr -> { + var mapKey = + SystemMapKey.ofResourceData(addr, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); + var substate = reader.get(mapKey).orElseThrow(); + var tokenResource = (TokenResourceMetadata) substate; + return tokenResource.getSymbol(); + }; + var proof = reader.getMetadata().getProof(); + var response = + new EntityResponse() + .stateIdentifier(modelMapper.stateIdentifier(proof.getAccumulatorState())); - for (var resourceQuery : resourceQueries) { - resourceQuery.fold( - (index, bucketPredicate) -> - reader.reduceResources(index, ResourceInBucket::bucket, bucketPredicate) - .entrySet() - .stream() - .map(e -> modelMapper.resourceOperation(e.getKey(), e.getValue(), addressToSymbol)), - systemMapKey -> - reader.get(systemMapKey).map(ResourceInBucket.class::cast).stream() - .map(b -> modelMapper.resourceOperation(b, true, addressToSymbol)) - ).forEach(response::addBalancesItem); - } + for (var resourceQuery : resourceQueries) { + resourceQuery + .fold( + (index, bucketPredicate) -> + reader + .reduceResources(index, ResourceInBucket::bucket, bucketPredicate) + .entrySet() + .stream() + .map( + e -> + modelMapper.resourceOperation( + e.getKey(), e.getValue(), addressToSymbol)), + systemMapKey -> + reader.get(systemMapKey).map(ResourceInBucket.class::cast).stream() + .map(b -> modelMapper.resourceOperation(b, true, addressToSymbol))) + .forEach(response::addBalancesItem); + } - for (var keyQuery : keyQueries) { - var substate = keyQuery.get(reader); - substate.flatMap(modelMapper::dataObject).ifPresent(response::addDataObjectsItem); - } + for (var keyQuery : keyQueries) { + var substate = keyQuery.get(reader); + substate.flatMap(modelMapper::dataObject).ifPresent(response::addDataObjectsItem); + } - return response; - }); - } + return response; + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeyListHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeyListHandler.java index 339eec6434..ce3b90a001 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeyListHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeyListHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,8 +65,8 @@ package com.radixdlt.api.core.handlers; import com.google.inject.Inject; -import com.radixdlt.api.core.model.CoreApiException; import com.radixdlt.api.core.CoreJsonRpcHandler; +import com.radixdlt.api.core.model.CoreApiException; import com.radixdlt.api.core.model.CoreModelMapper; import com.radixdlt.api.core.openapitools.model.KeyListRequest; import com.radixdlt.api.core.openapitools.model.KeyListResponse; @@ -76,33 +77,30 @@ import com.radixdlt.identifiers.REAddr; public final class KeyListHandler extends CoreJsonRpcHandler { - private final REAddr accountAddress; - private final ECPublicKey validatorKey; - private final CoreModelMapper coreModelMapper; + private final REAddr accountAddress; + private final ECPublicKey validatorKey; + private final CoreModelMapper coreModelMapper; - @Inject - KeyListHandler( - @Self ECPublicKey validatorKey, - CoreModelMapper coreModelMapper - ) { - super(KeyListRequest.class); + @Inject + KeyListHandler(@Self ECPublicKey validatorKey, CoreModelMapper coreModelMapper) { + super(KeyListRequest.class); - this.accountAddress = REAddr.ofPubKeyAccount(validatorKey); - this.validatorKey = validatorKey; - this.coreModelMapper = coreModelMapper; - } + this.accountAddress = REAddr.ofPubKeyAccount(validatorKey); + this.validatorKey = validatorKey; + this.coreModelMapper = coreModelMapper; + } - @Override - public KeyListResponse handleRequest(KeyListRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - return new KeyListResponse() - .addPublicKeysItem(new PublicKeyEntry() - .publicKey(coreModelMapper.publicKey(validatorKey)) - .identifiers(new PublicKeyIdentifiers() - .accountEntityIdentifier(coreModelMapper.entityIdentifier(accountAddress)) - .validatorEntityIdentifier(coreModelMapper.entityIdentifier(validatorKey)) - .p2pNode(coreModelMapper.peer(validatorKey)) - ) - ); - } + @Override + public KeyListResponse handleRequest(KeyListRequest request) throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + return new KeyListResponse() + .addPublicKeysItem( + new PublicKeyEntry() + .publicKey(coreModelMapper.publicKey(validatorKey)) + .identifiers( + new PublicKeyIdentifiers() + .accountEntityIdentifier(coreModelMapper.entityIdentifier(accountAddress)) + .validatorEntityIdentifier(coreModelMapper.entityIdentifier(validatorKey)) + .p2pNode(coreModelMapper.peer(validatorKey)))); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeySignHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeySignHandler.java index faa2590241..719244a852 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeySignHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/KeySignHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,9 +69,9 @@ import com.radixdlt.api.core.CoreJsonRpcHandler; import com.radixdlt.api.core.model.CoreApiException; import com.radixdlt.api.core.model.CoreModelMapper; -import com.radixdlt.api.core.openapitools.model.PublicKeyNotSupportedError; import com.radixdlt.api.core.openapitools.model.KeySignRequest; import com.radixdlt.api.core.openapitools.model.KeySignResponse; +import com.radixdlt.api.core.openapitools.model.PublicKeyNotSupportedError; import com.radixdlt.atom.TxLowLevelBuilder; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.HashSigner; @@ -83,54 +84,51 @@ import com.radixdlt.utils.Bytes; public final class KeySignHandler extends CoreJsonRpcHandler { - private final ECPublicKey self; - private final HashSigner hashSigner; - private final Provider> radixEngineProvider; - private final CoreModelMapper coreModelMapper; + private final ECPublicKey self; + private final HashSigner hashSigner; + private final Provider> radixEngineProvider; + private final CoreModelMapper coreModelMapper; - @Inject - KeySignHandler( - @Self ECPublicKey self, - @LocalSigner HashSigner hashSigner, - Provider> radixEngineProvider, - CoreModelMapper coreModelMapper - ) { - super(KeySignRequest.class); + @Inject + KeySignHandler( + @Self ECPublicKey self, + @LocalSigner HashSigner hashSigner, + Provider> radixEngineProvider, + CoreModelMapper coreModelMapper) { + super(KeySignRequest.class); - this.self = self; - this.hashSigner = hashSigner; - this.radixEngineProvider = radixEngineProvider; - this.coreModelMapper = coreModelMapper; - } + this.self = self; + this.hashSigner = hashSigner; + this.radixEngineProvider = radixEngineProvider; + this.coreModelMapper = coreModelMapper; + } - @Override - public KeySignResponse handleRequest(KeySignRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public KeySignResponse handleRequest(KeySignRequest request) throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - var pubKey = coreModelMapper.ecPublicKey(request.getPublicKey()); - if (!self.equals(pubKey)) { - throw CoreApiException.notSupported( - new PublicKeyNotSupportedError() - .unsupportedPublicKey(request.getPublicKey()) - .type(PublicKeyNotSupportedError.class.getSimpleName()) - ); - } + var pubKey = coreModelMapper.ecPublicKey(request.getPublicKey()); + if (!self.equals(pubKey)) { + throw CoreApiException.notSupported( + new PublicKeyNotSupportedError() + .unsupportedPublicKey(request.getPublicKey()) + .type(PublicKeyNotSupportedError.class.getSimpleName())); + } - // Verify this is a valid transaction and not anything more malicious - var bytes = coreModelMapper.bytes(request.getUnsignedTransaction()); - var txn = Txn.create(bytes); - try { - radixEngineProvider.get().getParser().parse(txn); - } catch (TxnParseException e) { - throw coreModelMapper.parseException(e); - } + // Verify this is a valid transaction and not anything more malicious + var bytes = coreModelMapper.bytes(request.getUnsignedTransaction()); + var txn = Txn.create(bytes); + try { + radixEngineProvider.get().getParser().parse(txn); + } catch (TxnParseException e) { + throw coreModelMapper.parseException(e); + } - var builder = TxLowLevelBuilder.newBuilder(bytes); - var hash = builder.hashToSign(); - var signature = this.hashSigner.sign(hash); - var signedTransaction = builder.sig(signature).blob(); + var builder = TxLowLevelBuilder.newBuilder(bytes); + var hash = builder.hashToSign(); + var signature = this.hashSigner.sign(hash); + var signedTransaction = builder.sig(signature).blob(); - return new KeySignResponse() - .signedTransaction(Bytes.toHexString(signedTransaction)); - } + return new KeySignResponse().signedTransaction(Bytes.toHexString(signedTransaction)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolHandler.java index 0f8f4eab5c..97d39b386a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -73,30 +74,30 @@ import com.radixdlt.statecomputer.RadixEngineMempool; public final class MempoolHandler extends CoreJsonRpcHandler { - private final CoreModelMapper coreModelMapper; - private final RadixEngineMempool mempool; + private final CoreModelMapper coreModelMapper; + private final RadixEngineMempool mempool; - @Inject - private MempoolHandler( - CoreModelMapper coreModelMapper, - RadixEngineMempool mempool - ) { - super(MempoolRequest.class); + @Inject + private MempoolHandler(CoreModelMapper coreModelMapper, RadixEngineMempool mempool) { + super(MempoolRequest.class); - this.coreModelMapper = coreModelMapper; - this.mempool = mempool; - } + this.coreModelMapper = coreModelMapper; + this.mempool = mempool; + } - @Override - public MempoolResponse handleRequest(MempoolRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public MempoolResponse handleRequest(MempoolRequest request) throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - return mempool.getData(map -> { - var response = new MempoolResponse(); - map.keySet().forEach(txnId -> response.addTransactionIdentifiersItem( - new TransactionIdentifier().hash(txnId.toString()) - )); - return response; - }); - } + return mempool.getData( + map -> { + var response = new MempoolResponse(); + map.keySet() + .forEach( + txnId -> + response.addTransactionIdentifiersItem( + new TransactionIdentifier().hash(txnId.toString()))); + return response; + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolTransactionHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolTransactionHandler.java index dca6150227..04fec4dcd0 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolTransactionHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/MempoolTransactionHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -79,50 +80,50 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.RadixEngineMempool; -public class MempoolTransactionHandler extends CoreJsonRpcHandler { - private final RadixEngineMempool mempool; - private final CoreModelMapper modelMapper; - private final Provider> radixEngineProvider; +public class MempoolTransactionHandler + extends CoreJsonRpcHandler { + private final RadixEngineMempool mempool; + private final CoreModelMapper modelMapper; + private final Provider> radixEngineProvider; - @Inject - private MempoolTransactionHandler( - RadixEngineMempool mempool, - Provider> radixEngineProvider, - CoreModelMapper modelMapper - ) { - super(MempoolTransactionRequest.class); + @Inject + private MempoolTransactionHandler( + RadixEngineMempool mempool, + Provider> radixEngineProvider, + CoreModelMapper modelMapper) { + super(MempoolTransactionRequest.class); - this.mempool = mempool; - this.modelMapper = modelMapper; - this.radixEngineProvider = radixEngineProvider; - } + this.mempool = mempool; + this.modelMapper = modelMapper; + this.radixEngineProvider = radixEngineProvider; + } - private String symbol(REAddr tokenAddress) { - var mapKey = SystemMapKey.ofResourceData(tokenAddress, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); - var substate = radixEngineProvider.get().read(reader -> reader.get(mapKey).orElseThrow()); - // TODO: This is a bit of a hack to require deserialization, figure out correct abstraction - var tokenResourceMetadata = (TokenResourceMetadata) substate; - return tokenResourceMetadata.getSymbol(); - } + private String symbol(REAddr tokenAddress) { + var mapKey = + SystemMapKey.ofResourceData(tokenAddress, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); + var substate = radixEngineProvider.get().read(reader -> reader.get(mapKey).orElseThrow()); + // TODO: This is a bit of a hack to require deserialization, figure out correct abstraction + var tokenResourceMetadata = (TokenResourceMetadata) substate; + return tokenResourceMetadata.getSymbol(); + } - @Override - public MempoolTransactionResponse handleRequest(MempoolTransactionRequest request) throws CoreApiException { - modelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public MempoolTransactionResponse handleRequest(MempoolTransactionRequest request) + throws CoreApiException { + modelMapper.verifyNetwork(request.getNetworkIdentifier()); - var transactionIdentifier = request.getTransactionIdentifier(); - var txnId = modelMapper.txnId(transactionIdentifier); - var transaction = mempool.getData(map -> map.get(txnId)); - if (transaction == null) { - throw CoreApiException.notFound( - new TransactionNotFoundError() - .transactionIdentifier(transactionIdentifier) - .type(TransactionNotFoundError.class.getSimpleName()) - ); - } + var transactionIdentifier = request.getTransactionIdentifier(); + var txnId = modelMapper.txnId(transactionIdentifier); + var transaction = mempool.getData(map -> map.get(txnId)); + if (transaction == null) { + throw CoreApiException.notFound( + new TransactionNotFoundError() + .transactionIdentifier(transactionIdentifier) + .type(TransactionNotFoundError.class.getSimpleName())); + } - var processed = transaction.getFirst(); - var transactionModel = modelMapper.transaction(processed, this::symbol); - return new MempoolTransactionResponse() - .transaction(transactionModel); - } + var processed = transaction.getFirst(); + var transactionModel = modelMapper.transaction(processed, this::symbol); + return new MempoolTransactionResponse().transaction(transactionModel); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkConfigurationHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkConfigurationHandler.java index ef9dd8901e..da18844a80 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkConfigurationHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkConfigurationHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,9 @@ package com.radixdlt.api.core.handlers; +import static org.radix.Radix.SYSTEM_VERSION_KEY; +import static org.radix.Radix.VERSION_STRING_KEY; + import com.google.inject.Inject; import com.radixdlt.api.core.CoreJsonRpcHandler; import com.radixdlt.api.core.openapitools.model.Bech32HRPs; @@ -73,39 +77,33 @@ import com.radixdlt.networks.NetworkId; import org.radix.Radix; -import static org.radix.Radix.SYSTEM_VERSION_KEY; -import static org.radix.Radix.VERSION_STRING_KEY; - -public class NetworkConfigurationHandler extends CoreJsonRpcHandler { - private final Network network; +public class NetworkConfigurationHandler + extends CoreJsonRpcHandler { + private final Network network; - @Inject - NetworkConfigurationHandler( - @NetworkId int networkId - ) { - super(Void.class); - this.network = Network.ofId(networkId).orElseThrow(); - } + @Inject + NetworkConfigurationHandler(@NetworkId int networkId) { + super(Void.class); + this.network = Network.ofId(networkId).orElseThrow(); + } - @Override - public NetworkConfigurationResponse handleRequest(Void request) { - return new NetworkConfigurationResponse() - .networkIdentifier(new NetworkIdentifier().network(network.name().toLowerCase())) - .bech32HumanReadableParts( - new Bech32HRPs() - .accountHrp(network.getAccountHrp()) - .validatorHrp(network.getValidatorHrp()) - .nodeHrp(network.getNodeHrp()) - .resourceHrpSuffix(network.getResourceHrpSuffix()) - ) - .version( - new NetworkConfigurationResponseVersion() - .apiVersion("1.0.0") - .coreVersion(Radix.systemVersionInfo() - .get(SYSTEM_VERSION_KEY) - .get(VERSION_STRING_KEY) - .toString() - ) - ); - } + @Override + public NetworkConfigurationResponse handleRequest(Void request) { + return new NetworkConfigurationResponse() + .networkIdentifier(new NetworkIdentifier().network(network.name().toLowerCase())) + .bech32HumanReadableParts( + new Bech32HRPs() + .accountHrp(network.getAccountHrp()) + .validatorHrp(network.getValidatorHrp()) + .nodeHrp(network.getNodeHrp()) + .resourceHrpSuffix(network.getResourceHrpSuffix())) + .version( + new NetworkConfigurationResponseVersion() + .apiVersion("1.0.0") + .coreVersion( + Radix.systemVersionInfo() + .get(SYSTEM_VERSION_KEY) + .get(VERSION_STRING_KEY) + .toString())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkStatusHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkStatusHandler.java index 8a3d52b22d..b73589a1e8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkStatusHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/NetworkStatusHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -83,56 +84,60 @@ import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.store.LastProof; -public final class NetworkStatusHandler extends CoreJsonRpcHandler { - private final RadixEngine radixEngine; - private final LedgerProof lastProof; - private final AccumulatorState preGenesisAccumulatorState; - private final AccumulatorState genesisAccumulatorState; - private final CoreModelMapper coreModelMapper; - private final PeersView peersView; - private final SystemCounters systemCounters; +public final class NetworkStatusHandler + extends CoreJsonRpcHandler { + private final RadixEngine radixEngine; + private final LedgerProof lastProof; + private final AccumulatorState preGenesisAccumulatorState; + private final AccumulatorState genesisAccumulatorState; + private final CoreModelMapper coreModelMapper; + private final PeersView peersView; + private final SystemCounters systemCounters; - @Inject - NetworkStatusHandler( - RadixEngine radixEngine, - @Genesis VerifiedTxnsAndProof txnsAndProof, - @LastProof LedgerProof lastProof, - LedgerAccumulator ledgerAccumulator, - PeersView peersView, - CoreModelMapper coreModelMapper, - SystemCounters systemCounters - ) { - super(NetworkStatusRequest.class); + @Inject + NetworkStatusHandler( + RadixEngine radixEngine, + @Genesis VerifiedTxnsAndProof txnsAndProof, + @LastProof LedgerProof lastProof, + LedgerAccumulator ledgerAccumulator, + PeersView peersView, + CoreModelMapper coreModelMapper, + SystemCounters systemCounters) { + super(NetworkStatusRequest.class); - this.radixEngine = radixEngine; - this.lastProof = lastProof; - this.preGenesisAccumulatorState = new AccumulatorState(0, HashUtils.zero256()); - this.genesisAccumulatorState = ledgerAccumulator.accumulate( - preGenesisAccumulatorState, txnsAndProof.getTxns().get(0).getId().asHashCode() - ); - this.peersView = peersView; - this.coreModelMapper = coreModelMapper; - this.systemCounters = systemCounters; - } + this.radixEngine = radixEngine; + this.lastProof = lastProof; + this.preGenesisAccumulatorState = new AccumulatorState(0, HashUtils.zero256()); + this.genesisAccumulatorState = + ledgerAccumulator.accumulate( + preGenesisAccumulatorState, txnsAndProof.getTxns().get(0).getId().asHashCode()); + this.peersView = peersView; + this.coreModelMapper = coreModelMapper; + this.systemCounters = systemCounters; + } - private LedgerProof getCurrentProof() { - var ledgerAndBFTProof = radixEngine.read(RadixEngineReader::getMetadata); - return ledgerAndBFTProof == null ? lastProof : ledgerAndBFTProof.getProof(); - } + private LedgerProof getCurrentProof() { + var ledgerAndBFTProof = radixEngine.read(RadixEngineReader::getMetadata); + return ledgerAndBFTProof == null ? lastProof : ledgerAndBFTProof.getProof(); + } - @Override - public NetworkStatusResponse handleRequest(NetworkStatusRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - var currentProof = getCurrentProof(); - var response = new NetworkStatusResponse() - .preGenesisStateIdentifier(coreModelMapper.stateIdentifier(preGenesisAccumulatorState)) - .genesisStateIdentifier(coreModelMapper.stateIdentifier(genesisAccumulatorState)) - .currentStateIdentifier(coreModelMapper.stateIdentifier(currentProof.getAccumulatorState())) - .syncStatus(new SyncStatus() - .currentStateVersion(systemCounters.get(SystemCounters.CounterType.LEDGER_STATE_VERSION)) - .targetStateVersion(systemCounters.get(SystemCounters.CounterType.SYNC_TARGET_STATE_VERSION)) - ); - peersView.peers().map(coreModelMapper::peer).forEach(response::addPeersItem); - return response; - } + @Override + public NetworkStatusResponse handleRequest(NetworkStatusRequest request) throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + var currentProof = getCurrentProof(); + var response = + new NetworkStatusResponse() + .preGenesisStateIdentifier(coreModelMapper.stateIdentifier(preGenesisAccumulatorState)) + .genesisStateIdentifier(coreModelMapper.stateIdentifier(genesisAccumulatorState)) + .currentStateIdentifier( + coreModelMapper.stateIdentifier(currentProof.getAccumulatorState())) + .syncStatus( + new SyncStatus() + .currentStateVersion( + systemCounters.get(SystemCounters.CounterType.LEDGER_STATE_VERSION)) + .targetStateVersion( + systemCounters.get(SystemCounters.CounterType.SYNC_TARGET_STATE_VERSION))); + peersView.peers().map(coreModelMapper::peer).forEach(response::addPeersItem); + return response; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/TransactionsHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/TransactionsHandler.java index c3c1e1e21d..cdb8ba2200 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/TransactionsHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/handlers/TransactionsHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,7 +69,6 @@ import com.radixdlt.api.core.CoreJsonRpcHandler; import com.radixdlt.api.core.model.CoreApiException; import com.radixdlt.api.core.model.CoreModelMapper; -import com.radixdlt.api.core.reconstruction.RecoverableProcessedTxn; import com.radixdlt.api.core.openapitools.model.CommittedTransaction; import com.radixdlt.api.core.openapitools.model.CommittedTransactionMetadata; import com.radixdlt.api.core.openapitools.model.CommittedTransactionsRequest; @@ -76,6 +76,7 @@ import com.radixdlt.api.core.openapitools.model.OperationGroup; import com.radixdlt.api.core.openapitools.model.StateIdentifier; import com.radixdlt.api.core.reconstruction.BerkeleyRecoverableProcessedTxnStore; +import com.radixdlt.api.core.reconstruction.RecoverableProcessedTxn; import com.radixdlt.application.tokens.state.TokenResourceMetadata; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.Txn; @@ -91,119 +92,135 @@ import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; import com.radixdlt.utils.Bytes; -public final class TransactionsHandler extends CoreJsonRpcHandler { - private final Provider> radixEngineProvider; - private final BerkeleyRecoverableProcessedTxnStore txnStore; - private final BerkeleyLedgerEntryStore ledgerEntryStore; - private final LedgerAccumulator ledgerAccumulator; - private final CoreModelMapper coreModelMapper; +public final class TransactionsHandler + extends CoreJsonRpcHandler { + private final Provider> radixEngineProvider; + private final BerkeleyRecoverableProcessedTxnStore txnStore; + private final BerkeleyLedgerEntryStore ledgerEntryStore; + private final LedgerAccumulator ledgerAccumulator; + private final CoreModelMapper coreModelMapper; - @Inject - TransactionsHandler( - CoreModelMapper coreModelMapper, - BerkeleyRecoverableProcessedTxnStore txnStore, - BerkeleyLedgerEntryStore ledgerEntryStore, - LedgerAccumulator ledgerAccumulator, - Provider> radixEngineProvider - ) { - super(CommittedTransactionsRequest.class); - this.coreModelMapper = coreModelMapper; - this.txnStore = txnStore; - this.ledgerEntryStore = ledgerEntryStore; - this.ledgerAccumulator = ledgerAccumulator; - this.radixEngineProvider = radixEngineProvider; - } + @Inject + TransactionsHandler( + CoreModelMapper coreModelMapper, + BerkeleyRecoverableProcessedTxnStore txnStore, + BerkeleyLedgerEntryStore ledgerEntryStore, + LedgerAccumulator ledgerAccumulator, + Provider> radixEngineProvider) { + super(CommittedTransactionsRequest.class); + this.coreModelMapper = coreModelMapper; + this.txnStore = txnStore; + this.ledgerEntryStore = ledgerEntryStore; + this.ledgerAccumulator = ledgerAccumulator; + this.radixEngineProvider = radixEngineProvider; + } - private String symbol(REAddr tokenAddress) { - var mapKey = SystemMapKey.ofResourceData(tokenAddress, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); - var substate = radixEngineProvider.get().read(reader -> reader.get(mapKey).orElseThrow()); - // TODO: This is a bit of a hack to require deserialization, figure out correct abstraction - var tokenResourceMetadata = (TokenResourceMetadata) substate; - return tokenResourceMetadata.getSymbol(); - } + private String symbol(REAddr tokenAddress) { + var mapKey = + SystemMapKey.ofResourceData(tokenAddress, SubstateTypeId.TOKEN_RESOURCE_METADATA.id()); + var substate = radixEngineProvider.get().read(reader -> reader.get(mapKey).orElseThrow()); + // TODO: This is a bit of a hack to require deserialization, figure out correct abstraction + var tokenResourceMetadata = (TokenResourceMetadata) substate; + return tokenResourceMetadata.getSymbol(); + } - private CommittedTransaction construct(Txn txn, RecoverableProcessedTxn recoveryInfo, AccumulatorState accumulatorState) { - var parser = radixEngineProvider.get().getParser(); - ParsedTxn parsedTxn; - try { - parsedTxn = parser.parse(txn); - } catch (TxnParseException e) { - throw new IllegalStateException("Could not parse already committed transaction", e); - } + private CommittedTransaction construct( + Txn txn, RecoverableProcessedTxn recoveryInfo, AccumulatorState accumulatorState) { + var parser = radixEngineProvider.get().getParser(); + ParsedTxn parsedTxn; + try { + parsedTxn = parser.parse(txn); + } catch (TxnParseException e) { + throw new IllegalStateException("Could not parse already committed transaction", e); + } - var committedTransaction = new CommittedTransaction(); - recoveryInfo.recoverStateUpdates(parsedTxn).stream() - .map(stateUpdateGroup -> { - var operationGroup = new OperationGroup(); - stateUpdateGroup.stream() - .map(stateUpdate -> { - var substateOperation = stateUpdate.recover(radixEngineProvider); - return coreModelMapper.operation( - substateOperation.getSubstate(), - substateOperation.getSubstateId(), - substateOperation.isBootUp(), - this::symbol - ); - }) - .forEach(operationGroup::addOperationsItem); - return operationGroup; - }) - .forEach(committedTransaction::addOperationGroupsItem); + var committedTransaction = new CommittedTransaction(); + recoveryInfo.recoverStateUpdates(parsedTxn).stream() + .map( + stateUpdateGroup -> { + var operationGroup = new OperationGroup(); + stateUpdateGroup.stream() + .map( + stateUpdate -> { + var substateOperation = stateUpdate.recover(radixEngineProvider); + return coreModelMapper.operation( + substateOperation.getSubstate(), + substateOperation.getSubstateId(), + substateOperation.isBootUp(), + this::symbol); + }) + .forEach(operationGroup::addOperationsItem); + return operationGroup; + }) + .forEach(committedTransaction::addOperationGroupsItem); - var signedBy = parsedTxn.getPayloadHashAndSig() - .map(hashAndSig -> { - var hash = hashAndSig.getFirst(); - var sig = hashAndSig.getSecond(); - return ECPublicKey.recoverFrom(hash, sig) - .orElseThrow(() -> new IllegalStateException("Invalid signature on already committed transaction")); - }); - var transactionIdentifier = coreModelMapper.transactionIdentifier(txn.getId()); + var signedBy = + parsedTxn + .getPayloadHashAndSig() + .map( + hashAndSig -> { + var hash = hashAndSig.getFirst(); + var sig = hashAndSig.getSecond(); + return ECPublicKey.recoverFrom(hash, sig) + .orElseThrow( + () -> + new IllegalStateException( + "Invalid signature on already committed transaction")); + }); + var transactionIdentifier = coreModelMapper.transactionIdentifier(txn.getId()); - return committedTransaction - .committedStateIdentifier(coreModelMapper.stateIdentifier(accumulatorState)) - .metadata(new CommittedTransactionMetadata() - .fee(coreModelMapper.nativeTokenAmount(parsedTxn.getFeePaid())) - .message(parsedTxn.getMsg().map(Bytes::toHexString).orElse(null)) - .size(txn.getPayload().length) - .hex(Bytes.toHexString(txn.getPayload())) - .signedBy(signedBy.map(coreModelMapper::publicKey).orElse(null)) - ) - .transactionIdentifier(transactionIdentifier); - } + return committedTransaction + .committedStateIdentifier(coreModelMapper.stateIdentifier(accumulatorState)) + .metadata( + new CommittedTransactionMetadata() + .fee(coreModelMapper.nativeTokenAmount(parsedTxn.getFeePaid())) + .message(parsedTxn.getMsg().map(Bytes::toHexString).orElse(null)) + .size(txn.getPayload().length) + .hex(Bytes.toHexString(txn.getPayload())) + .signedBy(signedBy.map(coreModelMapper::publicKey).orElse(null))) + .transactionIdentifier(transactionIdentifier); + } - @Override - public CommittedTransactionsResponse handleRequest(CommittedTransactionsRequest request) throws CoreApiException { - coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); + @Override + public CommittedTransactionsResponse handleRequest(CommittedTransactionsRequest request) + throws CoreApiException { + coreModelMapper.verifyNetwork(request.getNetworkIdentifier()); - var stateIdentifier = coreModelMapper.partialStateIdentifier(request.getStateIdentifier()); - long stateVersion = stateIdentifier.getFirst(); - var accumulator = stateIdentifier.getSecond(); - var currentAccumulator = txnStore.getAccumulator(stateVersion) - .orElseThrow(() -> CoreApiException.notFound(coreModelMapper.notFoundErrorDetails(request.getStateIdentifier()))); - if (accumulator != null) { - var matchesInput = accumulator.equals(currentAccumulator); - if (!matchesInput) { - throw CoreApiException.notFound(coreModelMapper.notFoundErrorDetails(request.getStateIdentifier())); - } - } + var stateIdentifier = coreModelMapper.partialStateIdentifier(request.getStateIdentifier()); + long stateVersion = stateIdentifier.getFirst(); + var accumulator = stateIdentifier.getSecond(); + var currentAccumulator = + txnStore + .getAccumulator(stateVersion) + .orElseThrow( + () -> + CoreApiException.notFound( + coreModelMapper.notFoundErrorDetails(request.getStateIdentifier()))); + if (accumulator != null) { + var matchesInput = accumulator.equals(currentAccumulator); + if (!matchesInput) { + throw CoreApiException.notFound( + coreModelMapper.notFoundErrorDetails(request.getStateIdentifier())); + } + } - var limit = coreModelMapper.limit(request.getLimit()); - var recoverable = txnStore.get(stateVersion, limit); - var accumulatorState = new AccumulatorState(stateVersion, currentAccumulator); - var response = new CommittedTransactionsResponse(); - var txns = ledgerEntryStore.getCommittedTxns(stateVersion, recoverable.size()); - for (int i = 0; i < txns.size(); i++) { - var txn = txns.get(i); - var recoveryInfo = recoverable.get(i); - var nextAccumulatorState = ledgerAccumulator.accumulate(accumulatorState, txn.getId().asHashCode()); - accumulatorState = nextAccumulatorState; - response.addTransactionsItem(construct(txn, recoveryInfo, nextAccumulatorState)); - } + var limit = coreModelMapper.limit(request.getLimit()); + var recoverable = txnStore.get(stateVersion, limit); + var accumulatorState = new AccumulatorState(stateVersion, currentAccumulator); + var response = new CommittedTransactionsResponse(); + var txns = ledgerEntryStore.getCommittedTxns(stateVersion, recoverable.size()); + for (int i = 0; i < txns.size(); i++) { + var txn = txns.get(i); + var recoveryInfo = recoverable.get(i); + var nextAccumulatorState = + ledgerAccumulator.accumulate(accumulatorState, txn.getId().asHashCode()); + accumulatorState = nextAccumulatorState; + response.addTransactionsItem(construct(txn, recoveryInfo, nextAccumulatorState)); + } - return response - .stateIdentifier(new StateIdentifier() - .stateVersion(stateVersion) - .transactionAccumulator(Bytes.toHexString(currentAccumulator.asBytes())) - ); - } + return response.stateIdentifier( + new StateIdentifier() + .stateVersion(stateVersion) + .transactionAccumulator(Bytes.toHexString(currentAccumulator.asBytes()))); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiErrorCode.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiErrorCode.java index 9290ca358c..8b50e2e21b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiErrorCode.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiErrorCode.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,31 +65,31 @@ package com.radixdlt.api.core.model; public enum CoreApiErrorCode { - BAD_REQUEST(400, "Bad request"), - NOT_FOUND(404, "Not found"), - CONFLICT(409, "State Conflict"), - INTERNAL_SERVER_ERROR(500, "Internal Server Error"), - NOT_SUPPORTED(501, "Not supported"), - UNAVAILABLE(503, "Unavailable"); + BAD_REQUEST(400, "Bad request"), + NOT_FOUND(404, "Not found"), + CONFLICT(409, "State Conflict"), + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + NOT_SUPPORTED(501, "Not supported"), + UNAVAILABLE(503, "Unavailable"); - private final int errorCode; - private final String message; + private final int errorCode; + private final String message; - CoreApiErrorCode(int errorCode, String message) { - this.errorCode = errorCode; - this.message = message; - } + CoreApiErrorCode(int errorCode, String message) { + this.errorCode = errorCode; + this.message = message; + } - public int getErrorCode() { - return errorCode; - } + public int getErrorCode() { + return errorCode; + } - public String getMessage() { - return message; - } + public String getMessage() { + return message; + } - @Override - public String toString() { - return errorCode + " " + message; - } + @Override + public String toString() { + return errorCode + " " + message; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiException.java index 60775e41fe..82320c9e22 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreApiException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,40 +68,39 @@ import com.radixdlt.api.core.openapitools.model.UnexpectedError; public final class CoreApiException extends Exception { - private final CoreApiErrorCode errorCode; - private final CoreError errorDetails; - - private CoreApiException(CoreApiErrorCode errorCode, CoreError errorDetails) { - super(errorCode + ": " + errorDetails); - this.errorCode = errorCode; - this.errorDetails = errorDetails; - } + private final CoreApiErrorCode errorCode; + private final CoreError errorDetails; - public UnexpectedError toError() { - return new UnexpectedError() - .code(errorCode.getErrorCode()) - .message(errorCode.getMessage()) - .details(errorDetails); - } + private CoreApiException(CoreApiErrorCode errorCode, CoreError errorDetails) { + super(errorCode + ": " + errorDetails); + this.errorCode = errorCode; + this.errorDetails = errorDetails; + } - public static CoreApiException badRequest(CoreError errorDetails) { - return new CoreApiException(CoreApiErrorCode.BAD_REQUEST, errorDetails); - } + public UnexpectedError toError() { + return new UnexpectedError() + .code(errorCode.getErrorCode()) + .message(errorCode.getMessage()) + .details(errorDetails); + } - public static CoreApiException conflict(CoreError errorDetails) { - return new CoreApiException(CoreApiErrorCode.CONFLICT, errorDetails); - } + public static CoreApiException badRequest(CoreError errorDetails) { + return new CoreApiException(CoreApiErrorCode.BAD_REQUEST, errorDetails); + } - public static CoreApiException notFound(CoreError errorDetails) { - return new CoreApiException(CoreApiErrorCode.NOT_FOUND, errorDetails); - } + public static CoreApiException conflict(CoreError errorDetails) { + return new CoreApiException(CoreApiErrorCode.CONFLICT, errorDetails); + } - public static CoreApiException notSupported(CoreError errorDetails) { - return new CoreApiException(CoreApiErrorCode.NOT_SUPPORTED, errorDetails); - } + public static CoreApiException notFound(CoreError errorDetails) { + return new CoreApiException(CoreApiErrorCode.NOT_FOUND, errorDetails); + } - public static CoreApiException unavailable(CoreError errorDetails) { - return new CoreApiException(CoreApiErrorCode.UNAVAILABLE, errorDetails); - } + public static CoreApiException notSupported(CoreError errorDetails) { + return new CoreApiException(CoreApiErrorCode.NOT_SUPPORTED, errorDetails); + } + public static CoreApiException unavailable(CoreError errorDetails) { + return new CoreApiException(CoreApiErrorCode.UNAVAILABLE, errorDetails); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreModelMapper.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreModelMapper.java index b82c429f2e..5d74a9718a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreModelMapper.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/CoreModelMapper.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,17 +70,17 @@ import com.google.common.hash.HashCode; import com.google.inject.Inject; import com.radixdlt.api.core.model.entities.AccountVaultEntity; +import com.radixdlt.api.core.model.entities.EntityDoesNotSupportDataObjectException; import com.radixdlt.api.core.model.entities.EntityDoesNotSupportResourceDepositException; import com.radixdlt.api.core.model.entities.EntityDoesNotSupportResourceWithdrawException; import com.radixdlt.api.core.model.entities.ExitingStakeVaultEntity; import com.radixdlt.api.core.model.entities.InvalidDataObjectException; -import com.radixdlt.api.core.model.entities.SystemEntity; -import com.radixdlt.api.core.model.entities.ValidatorSystemEntity; -import com.radixdlt.api.core.model.entities.EntityDoesNotSupportDataObjectException; import com.radixdlt.api.core.model.entities.PreparedStakeVaultEntity; import com.radixdlt.api.core.model.entities.PreparedUnstakeVaultEntity; +import com.radixdlt.api.core.model.entities.SystemEntity; import com.radixdlt.api.core.model.entities.TokenEntity; import com.radixdlt.api.core.model.entities.ValidatorEntity; +import com.radixdlt.api.core.model.entities.ValidatorSystemEntity; import com.radixdlt.api.core.openapitools.model.*; import com.radixdlt.application.system.state.EpochData; import com.radixdlt.application.system.state.RoundData; @@ -113,7 +114,6 @@ import com.radixdlt.constraintmachine.Particle; import com.radixdlt.constraintmachine.REProcessedTxn; import com.radixdlt.constraintmachine.REStateUpdate; - import com.radixdlt.constraintmachine.exceptions.SubstateNotFoundException; import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECPublicKey; @@ -137,7 +137,6 @@ import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt384; - import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -146,1063 +145,1127 @@ import java.util.function.Function; public final class CoreModelMapper { - private static final String TOKEN_TYPE = "Token"; - private static final String STAKE_UNIT_TYPE = "StakeUnit"; - private static final String SYSTEM_ADDRESS = "system"; - private static final String PREPARED_STAKES_ADDRESS = "prepared_stakes"; - private static final String PREPARED_UNSTAKES_ADDRESS = "prepared_unstakes"; - private static final String EXITING_UNSTAKES_ADDRESS = "exiting_unstakes"; - private static final ECPublicKey MOCK_PUBLIC_KEY = PrivateKeys.ofNumeric(1).getPublicKey(); - private final Addressing addressing; - private final Network network; - private final Forks forks; - - @Inject - CoreModelMapper( - @NetworkId int networkId, - Addressing addressing, - Forks forks - ) { - this.network = Network.ofId(networkId).orElseThrow(); - this.addressing = addressing; - this.forks = forks; - } - - public void verifyNetwork(NetworkIdentifier networkIdentifier) throws CoreApiException { - if (!networkIdentifier.getNetwork().equals(this.network.name().toLowerCase())) { - throw CoreApiException.notSupported( - new NetworkNotSupportedError() - .addSupportedNetworksItem(new NetworkIdentifier().network(this.network.name().toLowerCase())) - .type(NetworkNotSupportedError.class.getSimpleName()) - ); - } - } - - public Pair keyAndSignature(Signature signature) throws CoreApiException { - var bytes = bytes(signature.getBytes()); - ECDSASignature sig; - try { - sig = ECDSASignature.decodeFromDER(bytes); - } catch (IllegalArgumentException e) { - throw CoreApiException.badRequest( - new InvalidSignatureError() - .invalidSignature(signature.getBytes()) - .type(InvalidSignatureError.class.getSimpleName()) - ); - } - var publicKey = ecPublicKey(signature.getPublicKey()); - return Pair.of(publicKey, sig); - } - - public ECPublicKey ecPublicKey(PublicKey publicKey) throws CoreApiException { - var bytes = bytes(publicKey.getHex()); - try { - return ECPublicKey.fromBytes(bytes); - } catch (PublicKeyException e) { - throw CoreApiException.badRequest(new InvalidPublicKeyError() - .invalidPublicKey(publicKey) - .type(InvalidPublicKeyError.class.getSimpleName()) - ); - } - } - - public byte[] bytes(String hex) throws CoreApiException { - try { - return Bytes.fromHexString(hex); - } catch (IllegalArgumentException e) { - throw CoreApiException.badRequest(new InvalidHexError() - .invalidHex(hex) - .type(InvalidHexError.class.getSimpleName()) - ); - } - } - - public CoreError builderErrorDetails(TxBuilderException e) { - if (e instanceof MinimumStakeException minimumStakeException) { - return new BelowMinimumStakeError() - .minimumStake(nativeTokenAmount(minimumStakeException.getMinimumStake())) - .attemptedToStake(nativeTokenAmount(minimumStakeException.getAttempt())) - .type(BelowMinimumStakeError.class.getSimpleName()); - } else if (e instanceof DelegateStakePermissionException delegateException) { - return new NotValidatorOwnerError() - .owner(entityIdentifier(delegateException.getOwner())) - .user(entityIdentifier(delegateException.getUser())) - .type(NotValidatorOwnerError.class.getSimpleName()); - } else if (e instanceof InvalidDataObjectException invalidDataObjectException) { - return new InvalidDataObjectError() - .invalidDataObject(invalidDataObjectException.getDataObject().getDataObject()) - .message(invalidDataObjectException.getMessage()) - .type(InvalidDataObjectError.class.getSimpleName()); - } else if (e instanceof InvalidRakeIncreaseException rakeIncreaseException) { - return new AboveMaximumValidatorFeeIncreaseError() - .maximumValidatorFeeIncrease(rakeIncreaseException.getMaxRakeIncrease()) - .attemptedValidatorFeeIncrease(rakeIncreaseException.getIncreaseAttempt()) - .type(AboveMaximumValidatorFeeIncreaseError.class.getSimpleName()); - } else if (e instanceof EntityDoesNotSupportDataObjectException dataObjectException) { - return new DataObjectNotSupportedByEntityError() - .dataObjectNotSupported(dataObjectException.getDataObject().getDataObject()) - .entityIdentifier(entityIdentifier(dataObjectException.getEntity())) - .type(DataObjectNotSupportedByEntityError.class.getSimpleName()); - } else if (e instanceof EntityDoesNotSupportResourceDepositException depositException) { - return new ResourceDepositOperationNotSupportedByEntityError() - .resourceDepositNotSupported(resourceIdentifier(depositException.getResource())) - .entityIdentifier(entityIdentifier(depositException.getEntity())) - .type(ResourceDepositOperationNotSupportedByEntityError.class.getSimpleName()); - } else if (e instanceof EntityDoesNotSupportResourceWithdrawException withdrawException) { - return new ResourceWithdrawOperationNotSupportedByEntityError() - .resourceWithdrawNotSupported(resourceIdentifier(withdrawException.getResource())) - .entityIdentifier(entityIdentifier(withdrawException.getEntity())) - .type(ResourceWithdrawOperationNotSupportedByEntityError.class.getSimpleName()); - } else if (e instanceof MessageTooLongException messageTooLongException) { - return new MessageTooLongError() - .maximumMessageLength(255) - .attemptedMessageLength(messageTooLongException.getAttemptedLength()) - .type(MessageTooLongError.class.getSimpleName()); - } else if (e instanceof FeeConstructionException feeConstructionException) { - return new FeeConstructionError() - .attempts(feeConstructionException.getAttempts()) - .type(FeeConstructionError.class.getSimpleName()); - } else if (e instanceof NotEnoughResourcesException notEnoughResourcesException) { - var resourceIdentifier = resourceIdentifier(notEnoughResourcesException.getResource()); - return new NotEnoughResourcesError() - .attemptedToTake(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(notEnoughResourcesException.getRequested().toString())) - .available(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(notEnoughResourcesException.getAvailable().toString())) - .fee(nativeTokenAmount(notEnoughResourcesException.getFee())) - .type(NotEnoughResourcesError.class.getSimpleName()); - } else if (e instanceof NotEnoughNativeTokensForFeesException notEnoughNativeTokensForFeesException) { - return new NotEnoughNativeTokensForFeesError() - .available(nativeTokenAmount(notEnoughNativeTokensForFeesException.getAvailable())) - .feeEstimate(nativeTokenAmount(notEnoughNativeTokensForFeesException.getFee())) - .type(NotEnoughNativeTokensForFeesError.class.getSimpleName()); - } - - throw new IllegalStateException(e); - } - - public AccountVaultEntity feePayerEntity(EntityIdentifier entityIdentifier) throws CoreApiException { - var feePayer = entity(entityIdentifier); - if (!(feePayer instanceof AccountVaultEntity accountVaultEntity)) { - throw CoreApiException.badRequest(new InvalidFeePayerEntityError() - .invalidFeePayerEntity(entityIdentifier) - .type(InvalidFeePayerEntityError.class.getSimpleName()) - ); - } - return accountVaultEntity; - } - - private static CoreApiException invalidAddress(String address) { - return CoreApiException.badRequest(new InvalidAddressError() - .invalidAddress(address) - .type(InvalidAddressError.class.getSimpleName()) - ); - } - - private static CoreApiException invalidSubEntity(SubEntity subEntity) { - return CoreApiException.badRequest( - new InvalidSubEntityError() - .invalidSubEntity(subEntity) - .type(InvalidSubEntityError.class.getSimpleName()) - ); - } - - private Entity validatorAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { - var address = entityIdentifier.getAddress(); - var key = addressing.forValidators().parseOrThrow(address, s -> invalidAddress(address)); - var subEntity = entityIdentifier.getSubEntity(); - if (subEntity == null) { - return new ValidatorEntity(key); - } - - var metadata = subEntity.getMetadata(); - if (metadata != null) { - throw invalidSubEntity(subEntity); - } - - var subEntityAddress = subEntity.getAddress(); - if (!subEntityAddress.equals(SYSTEM_ADDRESS)) { - throw invalidSubEntity(subEntity); - } - - return new ValidatorSystemEntity(key); - } - - private Entity accountAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { - var address = entityIdentifier.getAddress(); - var accountAddress = addressing.forAccounts().parseOrThrow(address, s -> invalidAddress(address)); - var subEntity = entityIdentifier.getSubEntity(); - if (subEntity == null) { - return new AccountVaultEntity(accountAddress); - } - - switch (subEntity.getAddress()) { - case PREPARED_STAKES_ADDRESS -> { - var metadata = subEntity.getMetadata(); - if (metadata == null || metadata.getEpochUnlock() != null || metadata.getValidatorAddress() == null) { - throw invalidSubEntity(subEntity); - } - var validator = addressing.forValidators().parseOrThrow(metadata.getValidatorAddress(), s -> invalidAddress(address)); - return new PreparedStakeVaultEntity( - accountAddress, - validator - ); - } - case PREPARED_UNSTAKES_ADDRESS -> { - var metadata = subEntity.getMetadata(); - if (metadata != null) { - throw invalidSubEntity(subEntity); - } - return new PreparedUnstakeVaultEntity(accountAddress); - } - case EXITING_UNSTAKES_ADDRESS -> { - var metadata = subEntity.getMetadata(); - if (metadata == null || metadata.getEpochUnlock() == null || metadata.getValidatorAddress() == null) { - throw invalidSubEntity(subEntity); - } - - var validator = addressing.forValidators().parseOrThrow(metadata.getValidatorAddress(), s -> invalidAddress(address)); - return new ExitingStakeVaultEntity( - accountAddress, - validator, - metadata.getEpochUnlock() - ); - } - default -> throw invalidSubEntity(subEntity); - } - } - - private Entity resourceAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { - var address = entityIdentifier.getAddress(); - var pair = addressing.forResources().parseOrThrow(address, s -> invalidAddress(address)); - return new TokenEntity(pair.getFirst(), pair.getSecond()); - } - - private Entity systemAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { - var subEntity = entityIdentifier.getSubEntity(); - if (subEntity != null) { - throw invalidSubEntity(subEntity); - } - - return SystemEntity.instance(); - } - - public Entity entity(EntityIdentifier entityIdentifier) throws CoreApiException { - var address = entityIdentifier.getAddress(); - if (address.equals(SYSTEM_ADDRESS)) { - return systemAddressEntity(entityIdentifier); - } - - // TODO: Combine addressing schemes to remove switch/case statement - var addressType = addressing.getAddressType(address).orElseThrow(() -> invalidAddress(address)); - return switch (addressType) { - case VALIDATOR -> validatorAddressEntity(entityIdentifier); - case ACCOUNT -> accountAddressEntity(entityIdentifier); - case RESOURCE -> resourceAddressEntity(entityIdentifier); - default -> throw new IllegalStateException("Unknown addressType: " + addressType); - }; - } - - public Optional accountVaultEntity(EntityIdentifier entityIdentifier) throws CoreApiException { - var entity = entity(entityIdentifier); - if (!(entity instanceof AccountVaultEntity accountVaultEntity)) { - return Optional.empty(); - } - return Optional.of(accountVaultEntity); - } - - public DataOperation dataOperation(Data data) throws CoreApiException { - if (data == null) { - return null; - } - - final ClassToInstanceMap parsed; - var dataObject = data.getDataObject(); - if (dataObject instanceof PreparedValidatorOwner preparedValidatorOwner) { - var owner = preparedValidatorOwner.getOwner(); - var accountVaultEntity = accountVaultEntity(owner) - .orElseThrow(() -> CoreApiException.badRequest(new InvalidDataObjectError().invalidDataObject(preparedValidatorOwner))); - var ownerAddress = accountVaultEntity.accountAddress(); - parsed = ImmutableClassToInstanceMap.of(REAddr.class, ownerAddress); - } else if (dataObject instanceof TokenData tokenData && tokenData.getOwner() != null) { - var owner = tokenData.getOwner(); - var accountVaultEntity = accountVaultEntity(owner) - .orElseThrow(() -> CoreApiException.badRequest(new InvalidDataObjectError().invalidDataObject(tokenData))); - var key = accountVaultEntity.accountAddress().publicKey() - .orElseThrow(() -> new IllegalStateException("Account vault should only have account addresses")); - parsed = ImmutableClassToInstanceMap.of(ECPublicKey.class, key); - } else { - parsed = ImmutableClassToInstanceMap.of(); - } - - return new DataOperation(data, parsed); - } - - public OperationTxBuilder operationTxBuilder(String message, List operationGroups) throws CoreApiException { - var entityOperationGroups = new ArrayList>(); - for (var group : operationGroups) { - var entityOperationGroup = new ArrayList(); - for (var op : group.getOperations()) { - var entityOperation = EntityOperation.from( - entity(op.getEntityIdentifier()), - resourceOperation(op.getAmount()), - dataOperation(op.getData()) - ); - entityOperationGroup.add(entityOperation); - } - entityOperationGroups.add(entityOperationGroup); - } - - return new OperationTxBuilder(message, entityOperationGroups, forks); - } - - - public Txn txn(String hex) throws CoreApiException { - var bytes = bytes(hex); - return Txn.create(bytes); - } - - public AID txnId(TransactionIdentifier transactionIdentifier) throws CoreApiException { - var hash = transactionIdentifier.getHash(); - try { - return AID.from(hash); - } catch (IllegalArgumentException e) { - throw CoreApiException.badRequest(new InvalidTransactionHashError() - .invalidTransactionHash(hash) - .type(InvalidTransactionHashError.class.getSimpleName())); - } - } - - public long limit(Long limit) { - if (limit == null) { - return 0L; - } - - if (limit < 0) { - throw new IllegalStateException("Limit must be >= 0"); - } - - return limit; - } - - public CoreError notFoundErrorDetails(PartialStateIdentifier partialStateIdentifier) { - return new StateIdentifierNotFoundError() - .stateIdentifier(partialStateIdentifier) - .type(StateIdentifierNotFoundError.class.getSimpleName()); - } - - public Pair partialStateIdentifier(PartialStateIdentifier partialStateIdentifier) - throws CoreApiException { - if (partialStateIdentifier == null) { - return Pair.of(0L, null); - } - - if (partialStateIdentifier.getStateVersion() < 0L) { - throw CoreApiException.badRequest( - new InvalidPartialStateIdentifierError() - .invalidPartialStateIdentifier(partialStateIdentifier) - .type(InvalidPartialStateIdentifierError.class.getSimpleName()) - ); - } - - final HashCode accumulator; - if (partialStateIdentifier.getTransactionAccumulator() != null) { - var bytes = bytes(partialStateIdentifier.getTransactionAccumulator()); - accumulator = HashCode.fromBytes(bytes); - } else { - accumulator = null; - } - - return Pair.of(partialStateIdentifier.getStateVersion(), accumulator); - } - - public ResourceOperation resourceOperation(ResourceAmount resourceAmount) throws CoreApiException { - if (resourceAmount == null) { - return null; - } - - var bigInteger = new BigInteger(resourceAmount.getValue()); - var isPositive = bigInteger.compareTo(BigInteger.ZERO) > 0; - - return ResourceOperation.from( - resource(resourceAmount.getResourceIdentifier()), - UInt256.from((isPositive ? bigInteger : bigInteger.negate()).toByteArray()), - isPositive - ); - } - - public Resource resource(ResourceIdentifier resourceIdentifier) throws CoreApiException { - if (resourceIdentifier instanceof TokenResourceIdentifier tokenResourceIdentifier) { - var rri = tokenResourceIdentifier.getRri(); - var symbolAndAddr = addressing.forResources().parseOrThrow(rri, s -> invalidAddress(rri)); - return new com.radixdlt.api.core.model.TokenResource(symbolAndAddr.getFirst(), symbolAndAddr.getSecond()); - } else if (resourceIdentifier instanceof StakeUnitResourceIdentifier stakeUnitResourceIdentifier) { - var validatorAddress = stakeUnitResourceIdentifier.getValidatorAddress(); - var key = addressing.forValidators().parseOrThrow(validatorAddress, s -> invalidAddress(validatorAddress)); - return new StakeUnitResource(key); - } else { - throw new IllegalStateException("Unknown resourceIdentifier: " + resourceIdentifier); - } - } - - public Peer peer(ECPublicKey key) { - return new Peer().peerId(addressing.forNodes().of(key)); - } - - public Peer peer(PeersView.PeerInfo peerInfo) { - return new Peer().peerId(addressing.forNodes().of(peerInfo.getNodeId().getPublicKey())); - } - - public ResourceIdentifier resourceIdentifier(Resource resource) { - if (resource instanceof com.radixdlt.api.core.model.TokenResource tokenResource) { - return new TokenResourceIdentifier() - .rri(addressing.forResources().of(tokenResource.symbol(), tokenResource.tokenAddress())) - .type(TOKEN_TYPE); - } else if (resource instanceof StakeUnitResource stakeUnitResource) { - return new StakeUnitResourceIdentifier() - .validatorAddress(addressing.forValidators().of(stakeUnitResource.validatorKey())) - .type(STAKE_UNIT_TYPE); - } else { - throw new IllegalStateException("Unknown resource " + resource); - } - } - - public EntityIdentifier entityIdentifier(Entity entity) { - if (entity instanceof AccountVaultEntity account) { - return entityIdentifier(account.accountAddress()); - } else if (entity instanceof PreparedStakeVaultEntity stake) { - return entityIdentifier(stake.accountAddress()) - .subEntity(new SubEntity() - .address(PREPARED_STAKES_ADDRESS) - .metadata(new SubEntityMetadata() - .validatorAddress(addressing.forValidators().of(stake.validatorKey())) - ) - ); - } else if (entity instanceof PreparedUnstakeVaultEntity unstake) { - return entityIdentifier(unstake.accountAddress()) - .subEntity(new SubEntity().address(PREPARED_UNSTAKES_ADDRESS)); - } else if (entity instanceof ValidatorEntity validator) { - return entityIdentifier(validator.validatorKey()); - } else if (entity instanceof ValidatorSystemEntity validatorSystem) { - return entityIdentifier(validatorSystem.validatorKey()).subEntity(new SubEntity().address(SYSTEM_ADDRESS)); - } else if (entity instanceof TokenEntity tokenEntity) { - return entityIdentifier(tokenEntity.tokenAddr(), tokenEntity.symbol()); - } else if (entity instanceof ExitingStakeVaultEntity exiting) { - return entityIdentifier(exiting.accountAddress(), exiting.validatorKey(), exiting.epochUnlock()); - } else if (entity instanceof SystemEntity) { - return new EntityIdentifier().address(SYSTEM_ADDRESS); - } else { - throw new IllegalStateException("Unknown entity: " + entity); - } - } - - public EntityIdentifier entityIdentifier(REAddr accountAddress, ECPublicKey validatorKey, long epochUnlock) { - return new EntityIdentifier().address(addressing.forAccounts().of(accountAddress)) - .subEntity(new SubEntity() - .address(EXITING_UNSTAKES_ADDRESS) - .metadata(new SubEntityMetadata() - .validatorAddress(addressing.forValidators().of(validatorKey)) - .epochUnlock(epochUnlock) - ) - ); - } - - public EntityIdentifier entityIdentifier(REAddr tokenAddress, String symbol) { - return new EntityIdentifier().address(addressing.forResources().of(symbol, tokenAddress)); - } - - public EntityIdentifier entityIdentifierExitingStake(REAddr accountAddress, ECPublicKey validatorKey, long epochUnlock) { - return new EntityIdentifier() - .address(addressing.forAccounts().of(accountAddress)) - .subEntity(new SubEntity() - .address(EXITING_UNSTAKES_ADDRESS) - .metadata(new SubEntityMetadata() - .validatorAddress(addressing.forValidators().of(validatorKey)) - .epochUnlock(epochUnlock) - ) - ); - } - - - public EntityIdentifier entityIdentifierPreparedUnstake(REAddr accountAddress) { - return new EntityIdentifier() - .address(addressing.forAccounts().of(accountAddress)) - .subEntity(new SubEntity().address(PREPARED_UNSTAKES_ADDRESS)); - } - - public EntityIdentifier entityIdentifierPreparedStake(REAddr accountAddress, ECPublicKey validatorKey) { - return new EntityIdentifier() - .address(addressing.forAccounts().of(accountAddress)) - .subEntity(new SubEntity() - .address(PREPARED_STAKES_ADDRESS) - .metadata(new SubEntityMetadata() - .validatorAddress(addressing.forValidators().of(validatorKey)) - ) - ); - } - - public EntityIdentifier entityIdentifier(REAddr accountAddress) { - return new EntityIdentifier().address(addressing.forAccounts().of(accountAddress)); - } - - public EntityIdentifier entityIdentifier(ECPublicKey validatorKey) { - return new EntityIdentifier().address(addressing.forValidators().of(validatorKey)); - } - - public EntityIdentifier entityIdentifierValidatorSystem(ECPublicKey validatorKey) { - return new EntityIdentifier() - .address(addressing.forValidators().of(validatorKey)) - .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); - } - - public PublicKey publicKey(ECPublicKey publicKey) { - return new PublicKey().hex(publicKey.toHex()); - } - - public StateIdentifier stateIdentifier(AccumulatorState accumulatorState) { - return new StateIdentifier() - .stateVersion(accumulatorState.getStateVersion()) - .transactionAccumulator(Bytes.toHexString(accumulatorState.getAccumulatorHash().asBytes())); - } - - public EngineStateIdentifier engineStateIdentifier(LedgerProof ledgerProof) { - return ledgerProof.getNextValidatorSet().map(vset -> new EngineStateIdentifier() - .stateIdentifier(stateIdentifier(ledgerProof.getAccumulatorState())) - .epoch(ledgerProof.getEpoch() + 1) - .round(0L) - .timestamp(ledgerProof.timestamp()) - ).orElseGet(() -> new EngineStateIdentifier() - .stateIdentifier(stateIdentifier(ledgerProof.getAccumulatorState())) - .epoch(ledgerProof.getEpoch()) - .round(ledgerProof.getView().number()) - .timestamp(ledgerProof.timestamp())); - } - - public SubstateTypeIdentifier substateTypeIdentifier(Class substateClass) { - var name = SubstateTypeMapping.getName(SubstateTypeId.valueOf(substateClass)); - return new SubstateTypeIdentifier() - .type(name); - } - - public TokenResourceIdentifier create(REAddr tokenAddress, String symbol) { - return (TokenResourceIdentifier) new TokenResourceIdentifier() - .rri(addressing.forResources().of(symbol, tokenAddress)) - .type(TOKEN_TYPE); - } - - public ResourceAmount nativeTokenAmount(boolean positive, UInt256 value) { - return new ResourceAmount() - .resourceIdentifier(nativeToken()) - .value(positive ? value.toString() : "-" + value); - } - - public ResourceAmount stakeUnitAmount(ECPublicKey validatorKey, UInt256 value) { - return new ResourceAmount() - .resourceIdentifier(stakeUnit(validatorKey)) - .value(value.toString()); - } - - public ResourceAmount nativeTokenAmount(UInt256 value) { - return nativeTokenAmount(true, value); - } - - public TokenResourceIdentifier nativeToken() { - return create(REAddr.ofNativeToken(), "xrd"); - } - - public ResourceIdentifier stakeUnit(ECPublicKey validatorKey) { - return new StakeUnitResourceIdentifier() - .validatorAddress(addressing.forValidators().of(validatorKey)) - .type(STAKE_UNIT_TYPE); - } - - public ResourceIdentifier resourceIdentifier(Bucket bucket, Function tokenAddressToSymbol) { - if (bucket.resourceAddr() != null) { - var addr = bucket.resourceAddr(); - var symbol = tokenAddressToSymbol.apply(addr); - return new TokenResourceIdentifier() - .rri(addressing.forResources().of(symbol, addr)) - .type(TOKEN_TYPE); - } - - return new StakeUnitResourceIdentifier() - .validatorAddress(addressing.forValidators().of(bucket.getValidatorKey())) - .type(STAKE_UNIT_TYPE); - } - - public ResourceAmount resourceOperation(Bucket bucket, UInt384 amount, Function tokenAddressToSymbol) { - return new ResourceAmount() - .resourceIdentifier(resourceIdentifier(bucket, tokenAddressToSymbol)) - .value(amount.toString()); - } - - public FeeTable feeTable(com.radixdlt.application.system.FeeTable feeTable) { - var dto = new com.radixdlt.api.core.openapitools.model.FeeTable(); - feeTable.getPerUpSubstateFee().forEach((p, fee) -> - dto.addPerUpSubstateFeeItem(new UpSubstateFeeEntry() - .substateTypeIdentifier(substateTypeIdentifier(p)) - .fee(nativeTokenAmount(fee)) - ) - ); - dto.perByteFee(nativeTokenAmount(feeTable.getPerByteFee())); - return dto; - } - - public EngineConfiguration engineConfiguration(RERulesConfig config) { - return new EngineConfiguration() - .nativeToken(nativeToken()) - .maximumMessageLength(255) // TODO: Remove hardcode - .maximumValidatorFeeIncrease(ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE) - .feeTable(feeTable(config.getFeeTable())) - .reservedSymbols(config.getReservedSymbols().stream().toList()) - .tokenSymbolPattern(config.getTokenSymbolPattern().pattern()) - .maximumTransactionSize(config.getMaxTxnSize()) - .maximumTransactionsPerRound(config.getMaxSigsPerRound().orElse(0)) - .maximumRoundsPerEpoch(config.getMaxRounds()) - .validatorFeeIncreaseDebouncerEpochLength(config.getRakeIncreaseDebouncerEpochLength()) - .minimumStake(nativeTokenAmount(config.getMinimumStake().toSubunits())) - .unstakingDelayEpochLength(config.getUnstakingEpochDelay()) - .rewardsPerProposal(nativeTokenAmount(config.getRewardsPerProposal().toSubunits())) - .minimumCompletedProposalsPercentage(config.getMinimumCompletedProposalsPercentage()) - .maximumValidators(config.getMaxValidators()); - } - - public Fork fork(ForkConfig forkConfig) { - return new Fork() - .forkIdentifier(new ForkIdentifier() - .epoch(forkConfig.getEpoch()) - .fork(forkConfig.getName()) - ) - .engineIdentifier(new EngineIdentifier().engine(forkConfig.getVersion().name().toLowerCase())) - .engineConfiguration(engineConfiguration(forkConfig.getConfig())); - } - - public DataObject tokenData(TokenResource tokenResource) { - var tokenData = new TokenData() - .granularity(tokenResource.getGranularity().toString()) - .isMutable(tokenResource.isMutable()); - tokenResource.getOwner() - .map(REAddr::ofPubKeyAccount) - .ifPresent(addr -> tokenData.setOwner(entityIdentifier(addr))); - return tokenData - .type(SubstateTypeMapping.getName(SubstateTypeId.TOKEN_RESOURCE)); - } - - public DataObject tokenMetadata(TokenResourceMetadata tokenResourceMetadata) { - return new TokenMetadata() - .symbol(tokenResourceMetadata.getSymbol()) - .name(tokenResourceMetadata.getName()) - .description(tokenResourceMetadata.getDescription()) - .url(tokenResourceMetadata.getUrl()) - .iconUrl(tokenResourceMetadata.getIconUrl()) - .type(SubstateTypeMapping.getName(SubstateTypeId.TOKEN_RESOURCE_METADATA)); - } - - public DataObject epochData(EpochData epochData) { - return new com.radixdlt.api.core.openapitools.model.EpochData() - .epoch(epochData.getEpoch()) - .type(SubstateTypeMapping.getName(SubstateTypeId.EPOCH_DATA)); - } - - public DataObject roundData(RoundData roundData) { - return new com.radixdlt.api.core.openapitools.model.RoundData() - .round(roundData.getView()) - .timestamp(roundData.getTimestamp()) - .type(SubstateTypeMapping.getName(SubstateTypeId.ROUND_DATA)); - } - - public DataObject preparedValidatorRegistered(ValidatorRegisteredCopy copy) { - var preparedValidatorRegistered = new PreparedValidatorRegistered(); - copy.getEpochUpdate().ifPresent(preparedValidatorRegistered::epoch); - return preparedValidatorRegistered - .registered(copy.isRegistered()) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY)); - } - - public DataObject preparedValidatorOwner(ValidatorOwnerCopy copy, boolean virtual) { - var preparedValidatorOwner = new PreparedValidatorOwner(); - copy.getEpochUpdate().ifPresent(preparedValidatorOwner::epoch); - return preparedValidatorOwner - .owner(virtual ? selfAccountEntityIdentifier() : entityIdentifier(copy.getOwner())) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_OWNER_COPY)); - } - - public DataObject preparedValidatorFee(ValidatorFeeCopy copy) { - var preparedValidatorFee = new PreparedValidatorFee(); - copy.getEpochUpdate().ifPresent(preparedValidatorFee::epoch); - return preparedValidatorFee - .fee(copy.getRakePercentage()) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_RAKE_COPY)); - } - - public DataObject validatorMetadata(ValidatorMetaData metaData) { - var validatorMetadata = new ValidatorMetadata(); - return validatorMetadata - .name(metaData.getName()) - .url(metaData.getUrl()) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_META_DATA)); - } - - public DataObject validatorBFTData(ValidatorBFTData validatorBFTData) { - var bftData = new com.radixdlt.api.core.openapitools.model.ValidatorBFTData(); - return bftData - .proposalsCompleted(validatorBFTData.proposalsCompleted()) - .proposalsMissed(validatorBFTData.proposalsMissed()) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_BFT_DATA)); - } - - public DataObject allowDelegationFlag(AllowDelegationFlag allowDelegationFlag) { - var allowDelegation = new ValidatorAllowDelegation(); - return allowDelegation - .allowDelegation(allowDelegationFlag.allowsDelegation()) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG)); - } - - public DataObject validatorSystemMetadata(ValidatorSystemMetadata validatorSystemMetadata) { - var systemMetadata = new com.radixdlt.api.core.openapitools.model.ValidatorSystemMetadata(); - return systemMetadata - .data(Bytes.toHexString(validatorSystemMetadata.getData())) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_SYSTEM_META_DATA)); - } - - private EntityIdentifier selfAccountEntityIdentifier() { - return new EntityIdentifier() - .address(addressing.forAccounts().getHrp() + "1"); - } - - public DataObject validatorStakeData(ValidatorStakeData validatorStakeData, boolean virtual) { - var validatorData = new com.radixdlt.api.core.openapitools.model.ValidatorData(); - return validatorData - .owner(virtual ? selfAccountEntityIdentifier() : entityIdentifier(validatorStakeData.getOwnerAddr())) - .registered(validatorStakeData.isRegistered()) - .fee(validatorStakeData.getRakePercentage()) - .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_STAKE_DATA)); - } - - public DataObject unclaimedREAddrData() { - var data = new UnclaimedRadixEngineAddress(); - return data.type(SubstateTypeMapping.getName(SubstateTypeId.UNCLAIMED_READDR)); - } - - private EntitySetIdentifier validatorsEntitySetIdentifier() { - var regex = addressing.forValidators().getHrp() + "1[023456789ACDEFGHJKLMNPQRSTUVWXYZacdefghjklmnpqrstuvwxyz]{6,90}"; - return new EntitySetIdentifier().addressRegex(regex); - } - - private EntitySetIdentifier tokensEntitySetIdentifier() { - var regex = "[a-z0-9]+" + addressing.forResources().getHrpSuffix() + "1[023456789ACDEFGHJKLMNPQRSTUVWXYZacdefghjklmnpqrstuvwxyz]{6,90}"; - return new EntitySetIdentifier().addressRegex(regex); - } - - public DataObject virtualParent(VirtualParent virtualParent) { - var childType = SubstateTypeId.valueOf(virtualParent.getData()[0]); - var virtualDataObject = switch (childType) { - case UNCLAIMED_READDR -> unclaimedREAddrData(); - case VALIDATOR_META_DATA -> validatorMetadata(ValidatorMetaData.createVirtual(MOCK_PUBLIC_KEY)); - case VALIDATOR_STAKE_DATA -> validatorStakeData(ValidatorStakeData.createVirtual(MOCK_PUBLIC_KEY), true); - case VALIDATOR_ALLOW_DELEGATION_FLAG -> allowDelegationFlag(AllowDelegationFlag.createVirtual(MOCK_PUBLIC_KEY)); - case VALIDATOR_REGISTERED_FLAG_COPY -> preparedValidatorRegistered(ValidatorRegisteredCopy.createVirtual(MOCK_PUBLIC_KEY)); - case VALIDATOR_RAKE_COPY -> preparedValidatorFee(ValidatorFeeCopy.createVirtual(MOCK_PUBLIC_KEY)); - case VALIDATOR_OWNER_COPY -> preparedValidatorOwner(ValidatorOwnerCopy.createVirtual(MOCK_PUBLIC_KEY), true); - case VALIDATOR_SYSTEM_META_DATA -> validatorSystemMetadata(ValidatorSystemMetadata.createVirtual(MOCK_PUBLIC_KEY)); - default -> throw new IllegalStateException("Virtualization of " + childType + " unsupported"); - }; - - var entitySetIdentifier = childType == SubstateTypeId.UNCLAIMED_READDR - ? tokensEntitySetIdentifier() : validatorsEntitySetIdentifier(); - - return new VirtualParentData() - .entitySetIdentifier(entitySetIdentifier) - .virtualDataObject(virtualDataObject) - .type(SubstateTypeMapping.getName(SubstateTypeId.VIRTUAL_PARENT)); - } - - public Optional dataObject(Particle substate) { - final DataObject dataObject; - if (substate instanceof TokenResource tokenResource) { - dataObject = tokenData(tokenResource); - } else if (substate instanceof TokenResourceMetadata metadata) { - dataObject = tokenMetadata(metadata); - } else if (substate instanceof EpochData epochData) { - dataObject = epochData(epochData); - } else if (substate instanceof RoundData roundData) { - dataObject = roundData(roundData); - } else if (substate instanceof ValidatorRegisteredCopy validatorRegisteredCopy) { - dataObject = preparedValidatorRegistered(validatorRegisteredCopy); - } else if (substate instanceof ValidatorOwnerCopy validatorOwnerCopy) { - dataObject = preparedValidatorOwner(validatorOwnerCopy, false); - } else if (substate instanceof ValidatorFeeCopy validatorFeeCopy) { - dataObject = preparedValidatorFee(validatorFeeCopy); - } else if (substate instanceof ValidatorMetaData validatorMetaData) { - dataObject = validatorMetadata(validatorMetaData); - } else if (substate instanceof ValidatorBFTData validatorBFTData) { - dataObject = validatorBFTData(validatorBFTData); - } else if (substate instanceof AllowDelegationFlag allowDelegationFlag) { - dataObject = allowDelegationFlag(allowDelegationFlag); - } else if (substate instanceof ValidatorSystemMetadata validatorSystemMetadata) { - dataObject = validatorSystemMetadata(validatorSystemMetadata); - } else if (substate instanceof ValidatorStakeData validatorStakeData) { - dataObject = validatorStakeData(validatorStakeData, false); - } else if (substate instanceof UnclaimedREAddr) { - dataObject = unclaimedREAddrData(); - } else if (substate instanceof VirtualParent virtualParent) { - dataObject = virtualParent(virtualParent); - } else { - return Optional.empty(); - } - - return Optional.of(dataObject); - } - - public SubstateIdentifier substateIdentifier(SubstateId substateId) { - return new SubstateIdentifier() - .identifier(Bytes.toHexString(substateId.asBytes())); - } - - public Substate substate(SubstateId substateId, boolean bootUp) { - return new Substate() - .substateIdentifier(substateIdentifier(substateId)) - .substateOperation(bootUp ? Substate.SubstateOperationEnum.BOOTUP : Substate.SubstateOperationEnum.SHUTDOWN); - } - - public ResourceAmount resourceOperation(ResourceInBucket resourceInBucket, boolean bootUp, Function addressToSymbol) { - var amount = new BigInteger(bootUp ? 1 : -1, resourceInBucket.getAmount().toByteArray()); - var bucket = resourceInBucket.bucket(); - var resourceIdentifier = resourceIdentifier(bucket, addressToSymbol); - return new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(amount.toString()); - } - - private EntityIdentifier entityIdentifierOwnedBucket(ResourceInBucket resourceInBucket) { - var entityIdentifier = new EntityIdentifier(); - var bucket = resourceInBucket.bucket(); - entityIdentifier.address(addressing.forAccounts().of(bucket.getOwner())); - if (bucket.getValidatorKey() != null) { - if (bucket.resourceAddr() != null && bucket.getEpochUnlock() == null) { - entityIdentifier.subEntity(new SubEntity() - .address(PREPARED_STAKES_ADDRESS) - .metadata(new SubEntityMetadata() - .validatorAddress(addressing.forValidators().of(bucket.getValidatorKey())) - ) - ); - } else if (bucket.resourceAddr() == null && Objects.equals(bucket.getEpochUnlock(), 0L)) { - // Don't add validator as validator is already part of resource - entityIdentifier.subEntity(new SubEntity() - .address(PREPARED_UNSTAKES_ADDRESS) - ); - } else if (bucket.resourceAddr() != null && bucket.getEpochUnlock() != null) { - entityIdentifier.subEntity(new SubEntity() - .address(EXITING_UNSTAKES_ADDRESS) - .metadata(new SubEntityMetadata() - .validatorAddress(addressing.forValidators().of(bucket.getValidatorKey())) - .epochUnlock(bucket.getEpochUnlock()) - ) - ); - } - } - return entityIdentifier; - } - - private EntityIdentifier entityIdentifier(Particle substate, Function addressToSymbol) { - if (substate instanceof ResourceInBucket resourceInBucket && resourceInBucket.bucket().getOwner() != null) { - return entityIdentifierOwnedBucket(resourceInBucket); - } else if (substate instanceof ValidatorStakeData validatorStakeData) { - return new EntityIdentifier() - .address(addressing.forValidators().of(validatorStakeData.getValidatorKey())) - .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); - } else if (substate instanceof ResourceData resourceData) { - var symbol = addressToSymbol.apply(resourceData.getAddr()); - return new EntityIdentifier().address(addressing.forResources().of(symbol, resourceData.getAddr())); - } else if (substate instanceof SystemData) { - return new EntityIdentifier().address(SYSTEM_ADDRESS); - } else if (substate instanceof ValidatorUpdatingData validatorUpdatingData) { - return new EntityIdentifier().address(addressing.forValidators().of(validatorUpdatingData.getValidatorKey())); - } else if (substate instanceof com.radixdlt.application.validators.state.ValidatorData validatorData) { - return new EntityIdentifier() - .address(addressing.forValidators().of(validatorData.getValidatorKey())) - .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); - } else if (substate instanceof UnclaimedREAddr unclaimedREAddr) { - var addr = unclaimedREAddr.getAddr(); - final String address; - if (addr.isSystem()) { - address = SYSTEM_ADDRESS; - } else { - var symbol = addressToSymbol.apply(addr); - address = addressing.forResources().of(symbol, addr); - } - return new EntityIdentifier().address(address); - } else if (substate instanceof VirtualParent) { - return new EntityIdentifier().address(SYSTEM_ADDRESS); - } else { - throw new IllegalStateException("Unknown substate " + substate); - } - } - - public Optional data(Particle substate, boolean bootUp) { - return dataObject(substate).map(dataObject -> new Data() - .dataObject(dataObject) - .action(bootUp ? Data.ActionEnum.CREATE : Data.ActionEnum.DELETE)); - } - - public Operation operation( - Particle substate, - SubstateId substateId, - boolean isBootUp, - Function addressToSymbol - ) { - var operation = new Operation(); - var typeId = SubstateTypeId.valueOf(substate.getClass()); - operation.type(SubstateTypeMapping.getType(typeId)); - operation.substate(substate(substateId, isBootUp)); - operation.entityIdentifier(entityIdentifier(substate, addressToSymbol)); - if (substate instanceof ResourceInBucket resourceInBucket && !resourceInBucket.getAmount().isZero()) { - operation.amount(resourceOperation(resourceInBucket, isBootUp, addressToSymbol)); - } - data(substate, isBootUp).ifPresent(operation::data); - return operation; - } - - public Operation operation(REStateUpdate update, Function addressToSymbol) { - var operation = new Operation(); - var substate = (Particle) update.getParsed(); - operation.type(SubstateTypeMapping.getType(SubstateTypeId.valueOf(update.typeByte()))); - operation.substate(substate(update.getId(), update.isBootUp())); - operation.entityIdentifier(entityIdentifier(substate, addressToSymbol)); - if (substate instanceof ResourceInBucket resourceInBucket && !resourceInBucket.getAmount().isZero()) { - operation.amount(resourceOperation(resourceInBucket, update.isBootUp(), addressToSymbol)); - } - data(substate, update.isBootUp()).ifPresent(operation::data); - return operation; - } - - public OperationGroup operationGroup(List stateUpdates, Function addressToSymbol) { - var operationGroup = new OperationGroup(); - stateUpdates.forEach(u -> operationGroup.addOperationsItem(operation(u, addressToSymbol))); - return operationGroup; - } - - public CommittedTransaction committedTransaction( - REProcessedTxn txn, - AccumulatorState accumulatorState, - Function addressToSymbol - ) { - Function localizedAddressToSymbol = addr -> { - var localSymbol = txn.getGroupedStateUpdates().stream() - .flatMap(List::stream) - .map(REStateUpdate::getParsed) - .filter(TokenResourceMetadata.class::isInstance) - .map(TokenResourceMetadata.class::cast) - .filter(r -> r.getAddr().equals(addr)) - .map(TokenResourceMetadata::getSymbol) - .findFirst(); - - return localSymbol.orElseGet(() -> addressToSymbol.apply(addr)); - }; - - var transaction = new CommittedTransaction(); - - for (var stateUpdates : txn.getGroupedStateUpdates()) { - var operationGroup = operationGroup(stateUpdates, localizedAddressToSymbol); - transaction.addOperationGroupsItem(operationGroup); - } - - var metadata = new CommittedTransactionMetadata() - .fee(nativeTokenAmount(txn.getFeePaid())) - .hex(Bytes.toHexString(txn.getTxn().getPayload())) - .size(txn.getTxn().getPayload().length); - txn.getSignedBy().ifPresent(s -> metadata.setSignedBy(publicKey(s))); - txn.getMsg().ifPresent(msg -> metadata.setMessage(Bytes.toHexString(msg))); - - transaction.metadata(metadata); - transaction.transactionIdentifier(transactionIdentifier(txn.getTxnId())); - transaction.committedStateIdentifier(stateIdentifier(accumulatorState)); - - return transaction; - } - - public Transaction transaction(REProcessedTxn txn, Function addressToSymbol) { - Function localizedAddressToSymbol = addr -> { - var localSymbol = txn.getGroupedStateUpdates().stream() - .flatMap(List::stream) - .map(REStateUpdate::getParsed) - .filter(TokenResourceMetadata.class::isInstance) - .map(TokenResourceMetadata.class::cast) - .filter(r -> r.getAddr().equals(addr)) - .map(TokenResourceMetadata::getSymbol) - .findFirst(); - - return localSymbol.orElseGet(() -> addressToSymbol.apply(addr)); - }; - - var transaction = new Transaction(); - - for (var stateUpdates : txn.getGroupedStateUpdates()) { - var operationGroup = operationGroup(stateUpdates, localizedAddressToSymbol); - transaction.addOperationGroupsItem(operationGroup); - } - - var metadata = new CommittedTransactionMetadata() - .fee(nativeTokenAmount(txn.getFeePaid())) - .message(txn.getMsg().map(Bytes::toHexString).orElse(null)); - transaction.metadata(metadata); - - // If user transaction is signed then we can return back complete information - txn.getSignedBy().ifPresent(publicKey -> { - metadata.signedBy(publicKey(publicKey)); - metadata.size(txn.getTxn().getPayload().length); - metadata.hex(Bytes.toHexString(txn.getTxn().getPayload())); - transaction.transactionIdentifier(transactionIdentifier(txn.getTxnId())); - }); - - return transaction; - } - - public TransactionIdentifier transactionIdentifier(AID txnId) { - return new TransactionIdentifier() - .hash(txnId.toString()); - } - - public CoreApiException notValidatorEntityException(EntityIdentifier entityIdentifier) { - return CoreApiException.badRequest(new NotValidatorEntityError() - .entity(entityIdentifier) - .type(NotValidatorEntityError.class.getSimpleName()) - ); - } - - public CoreApiException parseException(TxnParseException exception) { - var cause = Throwables.getRootCause(exception); - return CoreApiException.badRequest( - new InvalidTransactionError() - .message(cause.getMessage()) - .type(InvalidTransactionError.class.getSimpleName()) - ); - } - - public CoreApiException mempoolFullException(MempoolFullException e) { - return CoreApiException.unavailable( - new MempoolFullError() - .mempoolTransactionCount(e.getMaxSize()) - .type(MempoolFullError.class.getSimpleName()) - ); - } - - public CoreApiException radixEngineException(RadixEngineException exception) { - var cause = Throwables.getRootCause(exception); - if (cause instanceof SubstateNotFoundException notFoundException) { - return CoreApiException.conflict( - new SubstateDependencyNotFoundError() - .substateIdentifierNotFound(substateIdentifier(notFoundException.getSubstateId())) - .type(SubstateDependencyNotFoundError.class.getSimpleName()) - ); - } - - return CoreApiException.badRequest( - new InvalidTransactionError() - .message(cause.getMessage()) - .type(InvalidTransactionError.class.getSimpleName()) - ); - } + private static final String TOKEN_TYPE = "Token"; + private static final String STAKE_UNIT_TYPE = "StakeUnit"; + private static final String SYSTEM_ADDRESS = "system"; + private static final String PREPARED_STAKES_ADDRESS = "prepared_stakes"; + private static final String PREPARED_UNSTAKES_ADDRESS = "prepared_unstakes"; + private static final String EXITING_UNSTAKES_ADDRESS = "exiting_unstakes"; + private static final ECPublicKey MOCK_PUBLIC_KEY = PrivateKeys.ofNumeric(1).getPublicKey(); + private final Addressing addressing; + private final Network network; + private final Forks forks; + + @Inject + CoreModelMapper(@NetworkId int networkId, Addressing addressing, Forks forks) { + this.network = Network.ofId(networkId).orElseThrow(); + this.addressing = addressing; + this.forks = forks; + } + + public void verifyNetwork(NetworkIdentifier networkIdentifier) throws CoreApiException { + if (!networkIdentifier.getNetwork().equals(this.network.name().toLowerCase())) { + throw CoreApiException.notSupported( + new NetworkNotSupportedError() + .addSupportedNetworksItem( + new NetworkIdentifier().network(this.network.name().toLowerCase())) + .type(NetworkNotSupportedError.class.getSimpleName())); + } + } + + public Pair keyAndSignature(Signature signature) + throws CoreApiException { + var bytes = bytes(signature.getBytes()); + ECDSASignature sig; + try { + sig = ECDSASignature.decodeFromDER(bytes); + } catch (IllegalArgumentException e) { + throw CoreApiException.badRequest( + new InvalidSignatureError() + .invalidSignature(signature.getBytes()) + .type(InvalidSignatureError.class.getSimpleName())); + } + var publicKey = ecPublicKey(signature.getPublicKey()); + return Pair.of(publicKey, sig); + } + + public ECPublicKey ecPublicKey(PublicKey publicKey) throws CoreApiException { + var bytes = bytes(publicKey.getHex()); + try { + return ECPublicKey.fromBytes(bytes); + } catch (PublicKeyException e) { + throw CoreApiException.badRequest( + new InvalidPublicKeyError() + .invalidPublicKey(publicKey) + .type(InvalidPublicKeyError.class.getSimpleName())); + } + } + + public byte[] bytes(String hex) throws CoreApiException { + try { + return Bytes.fromHexString(hex); + } catch (IllegalArgumentException e) { + throw CoreApiException.badRequest( + new InvalidHexError().invalidHex(hex).type(InvalidHexError.class.getSimpleName())); + } + } + + public CoreError builderErrorDetails(TxBuilderException e) { + if (e instanceof MinimumStakeException minimumStakeException) { + return new BelowMinimumStakeError() + .minimumStake(nativeTokenAmount(minimumStakeException.getMinimumStake())) + .attemptedToStake(nativeTokenAmount(minimumStakeException.getAttempt())) + .type(BelowMinimumStakeError.class.getSimpleName()); + } else if (e instanceof DelegateStakePermissionException delegateException) { + return new NotValidatorOwnerError() + .owner(entityIdentifier(delegateException.getOwner())) + .user(entityIdentifier(delegateException.getUser())) + .type(NotValidatorOwnerError.class.getSimpleName()); + } else if (e instanceof InvalidDataObjectException invalidDataObjectException) { + return new InvalidDataObjectError() + .invalidDataObject(invalidDataObjectException.getDataObject().getDataObject()) + .message(invalidDataObjectException.getMessage()) + .type(InvalidDataObjectError.class.getSimpleName()); + } else if (e instanceof InvalidRakeIncreaseException rakeIncreaseException) { + return new AboveMaximumValidatorFeeIncreaseError() + .maximumValidatorFeeIncrease(rakeIncreaseException.getMaxRakeIncrease()) + .attemptedValidatorFeeIncrease(rakeIncreaseException.getIncreaseAttempt()) + .type(AboveMaximumValidatorFeeIncreaseError.class.getSimpleName()); + } else if (e instanceof EntityDoesNotSupportDataObjectException dataObjectException) { + return new DataObjectNotSupportedByEntityError() + .dataObjectNotSupported(dataObjectException.getDataObject().getDataObject()) + .entityIdentifier(entityIdentifier(dataObjectException.getEntity())) + .type(DataObjectNotSupportedByEntityError.class.getSimpleName()); + } else if (e instanceof EntityDoesNotSupportResourceDepositException depositException) { + return new ResourceDepositOperationNotSupportedByEntityError() + .resourceDepositNotSupported(resourceIdentifier(depositException.getResource())) + .entityIdentifier(entityIdentifier(depositException.getEntity())) + .type(ResourceDepositOperationNotSupportedByEntityError.class.getSimpleName()); + } else if (e instanceof EntityDoesNotSupportResourceWithdrawException withdrawException) { + return new ResourceWithdrawOperationNotSupportedByEntityError() + .resourceWithdrawNotSupported(resourceIdentifier(withdrawException.getResource())) + .entityIdentifier(entityIdentifier(withdrawException.getEntity())) + .type(ResourceWithdrawOperationNotSupportedByEntityError.class.getSimpleName()); + } else if (e instanceof MessageTooLongException messageTooLongException) { + return new MessageTooLongError() + .maximumMessageLength(255) + .attemptedMessageLength(messageTooLongException.getAttemptedLength()) + .type(MessageTooLongError.class.getSimpleName()); + } else if (e instanceof FeeConstructionException feeConstructionException) { + return new FeeConstructionError() + .attempts(feeConstructionException.getAttempts()) + .type(FeeConstructionError.class.getSimpleName()); + } else if (e instanceof NotEnoughResourcesException notEnoughResourcesException) { + var resourceIdentifier = resourceIdentifier(notEnoughResourcesException.getResource()); + return new NotEnoughResourcesError() + .attemptedToTake( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value(notEnoughResourcesException.getRequested().toString())) + .available( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value(notEnoughResourcesException.getAvailable().toString())) + .fee(nativeTokenAmount(notEnoughResourcesException.getFee())) + .type(NotEnoughResourcesError.class.getSimpleName()); + } else if (e + instanceof NotEnoughNativeTokensForFeesException notEnoughNativeTokensForFeesException) { + return new NotEnoughNativeTokensForFeesError() + .available(nativeTokenAmount(notEnoughNativeTokensForFeesException.getAvailable())) + .feeEstimate(nativeTokenAmount(notEnoughNativeTokensForFeesException.getFee())) + .type(NotEnoughNativeTokensForFeesError.class.getSimpleName()); + } + + throw new IllegalStateException(e); + } + + public AccountVaultEntity feePayerEntity(EntityIdentifier entityIdentifier) + throws CoreApiException { + var feePayer = entity(entityIdentifier); + if (!(feePayer instanceof AccountVaultEntity accountVaultEntity)) { + throw CoreApiException.badRequest( + new InvalidFeePayerEntityError() + .invalidFeePayerEntity(entityIdentifier) + .type(InvalidFeePayerEntityError.class.getSimpleName())); + } + return accountVaultEntity; + } + + private static CoreApiException invalidAddress(String address) { + return CoreApiException.badRequest( + new InvalidAddressError() + .invalidAddress(address) + .type(InvalidAddressError.class.getSimpleName())); + } + + private static CoreApiException invalidSubEntity(SubEntity subEntity) { + return CoreApiException.badRequest( + new InvalidSubEntityError() + .invalidSubEntity(subEntity) + .type(InvalidSubEntityError.class.getSimpleName())); + } + + private Entity validatorAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { + var address = entityIdentifier.getAddress(); + var key = addressing.forValidators().parseOrThrow(address, s -> invalidAddress(address)); + var subEntity = entityIdentifier.getSubEntity(); + if (subEntity == null) { + return new ValidatorEntity(key); + } + + var metadata = subEntity.getMetadata(); + if (metadata != null) { + throw invalidSubEntity(subEntity); + } + + var subEntityAddress = subEntity.getAddress(); + if (!subEntityAddress.equals(SYSTEM_ADDRESS)) { + throw invalidSubEntity(subEntity); + } + + return new ValidatorSystemEntity(key); + } + + private Entity accountAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { + var address = entityIdentifier.getAddress(); + var accountAddress = + addressing.forAccounts().parseOrThrow(address, s -> invalidAddress(address)); + var subEntity = entityIdentifier.getSubEntity(); + if (subEntity == null) { + return new AccountVaultEntity(accountAddress); + } + + switch (subEntity.getAddress()) { + case PREPARED_STAKES_ADDRESS -> { + var metadata = subEntity.getMetadata(); + if (metadata == null + || metadata.getEpochUnlock() != null + || metadata.getValidatorAddress() == null) { + throw invalidSubEntity(subEntity); + } + var validator = + addressing + .forValidators() + .parseOrThrow(metadata.getValidatorAddress(), s -> invalidAddress(address)); + return new PreparedStakeVaultEntity(accountAddress, validator); + } + case PREPARED_UNSTAKES_ADDRESS -> { + var metadata = subEntity.getMetadata(); + if (metadata != null) { + throw invalidSubEntity(subEntity); + } + return new PreparedUnstakeVaultEntity(accountAddress); + } + case EXITING_UNSTAKES_ADDRESS -> { + var metadata = subEntity.getMetadata(); + if (metadata == null + || metadata.getEpochUnlock() == null + || metadata.getValidatorAddress() == null) { + throw invalidSubEntity(subEntity); + } + + var validator = + addressing + .forValidators() + .parseOrThrow(metadata.getValidatorAddress(), s -> invalidAddress(address)); + return new ExitingStakeVaultEntity(accountAddress, validator, metadata.getEpochUnlock()); + } + default -> throw invalidSubEntity(subEntity); + } + } + + private Entity resourceAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { + var address = entityIdentifier.getAddress(); + var pair = addressing.forResources().parseOrThrow(address, s -> invalidAddress(address)); + return new TokenEntity(pair.getFirst(), pair.getSecond()); + } + + private Entity systemAddressEntity(EntityIdentifier entityIdentifier) throws CoreApiException { + var subEntity = entityIdentifier.getSubEntity(); + if (subEntity != null) { + throw invalidSubEntity(subEntity); + } + + return SystemEntity.instance(); + } + + public Entity entity(EntityIdentifier entityIdentifier) throws CoreApiException { + var address = entityIdentifier.getAddress(); + if (address.equals(SYSTEM_ADDRESS)) { + return systemAddressEntity(entityIdentifier); + } + + // TODO: Combine addressing schemes to remove switch/case statement + var addressType = addressing.getAddressType(address).orElseThrow(() -> invalidAddress(address)); + return switch (addressType) { + case VALIDATOR -> validatorAddressEntity(entityIdentifier); + case ACCOUNT -> accountAddressEntity(entityIdentifier); + case RESOURCE -> resourceAddressEntity(entityIdentifier); + default -> throw new IllegalStateException("Unknown addressType: " + addressType); + }; + } + + public Optional accountVaultEntity(EntityIdentifier entityIdentifier) + throws CoreApiException { + var entity = entity(entityIdentifier); + if (!(entity instanceof AccountVaultEntity accountVaultEntity)) { + return Optional.empty(); + } + return Optional.of(accountVaultEntity); + } + + public DataOperation dataOperation(Data data) throws CoreApiException { + if (data == null) { + return null; + } + + final ClassToInstanceMap parsed; + var dataObject = data.getDataObject(); + if (dataObject instanceof PreparedValidatorOwner preparedValidatorOwner) { + var owner = preparedValidatorOwner.getOwner(); + var accountVaultEntity = + accountVaultEntity(owner) + .orElseThrow( + () -> + CoreApiException.badRequest( + new InvalidDataObjectError().invalidDataObject(preparedValidatorOwner))); + var ownerAddress = accountVaultEntity.accountAddress(); + parsed = ImmutableClassToInstanceMap.of(REAddr.class, ownerAddress); + } else if (dataObject instanceof TokenData tokenData && tokenData.getOwner() != null) { + var owner = tokenData.getOwner(); + var accountVaultEntity = + accountVaultEntity(owner) + .orElseThrow( + () -> + CoreApiException.badRequest( + new InvalidDataObjectError().invalidDataObject(tokenData))); + var key = + accountVaultEntity + .accountAddress() + .publicKey() + .orElseThrow( + () -> + new IllegalStateException( + "Account vault should only have account addresses")); + parsed = ImmutableClassToInstanceMap.of(ECPublicKey.class, key); + } else { + parsed = ImmutableClassToInstanceMap.of(); + } + + return new DataOperation(data, parsed); + } + + public OperationTxBuilder operationTxBuilder(String message, List operationGroups) + throws CoreApiException { + var entityOperationGroups = new ArrayList>(); + for (var group : operationGroups) { + var entityOperationGroup = new ArrayList(); + for (var op : group.getOperations()) { + var entityOperation = + EntityOperation.from( + entity(op.getEntityIdentifier()), + resourceOperation(op.getAmount()), + dataOperation(op.getData())); + entityOperationGroup.add(entityOperation); + } + entityOperationGroups.add(entityOperationGroup); + } + + return new OperationTxBuilder(message, entityOperationGroups, forks); + } + + public Txn txn(String hex) throws CoreApiException { + var bytes = bytes(hex); + return Txn.create(bytes); + } + + public AID txnId(TransactionIdentifier transactionIdentifier) throws CoreApiException { + var hash = transactionIdentifier.getHash(); + try { + return AID.from(hash); + } catch (IllegalArgumentException e) { + throw CoreApiException.badRequest( + new InvalidTransactionHashError() + .invalidTransactionHash(hash) + .type(InvalidTransactionHashError.class.getSimpleName())); + } + } + + public long limit(Long limit) { + if (limit == null) { + return 0L; + } + + if (limit < 0) { + throw new IllegalStateException("Limit must be >= 0"); + } + + return limit; + } + + public CoreError notFoundErrorDetails(PartialStateIdentifier partialStateIdentifier) { + return new StateIdentifierNotFoundError() + .stateIdentifier(partialStateIdentifier) + .type(StateIdentifierNotFoundError.class.getSimpleName()); + } + + public Pair partialStateIdentifier(PartialStateIdentifier partialStateIdentifier) + throws CoreApiException { + if (partialStateIdentifier == null) { + return Pair.of(0L, null); + } + + if (partialStateIdentifier.getStateVersion() < 0L) { + throw CoreApiException.badRequest( + new InvalidPartialStateIdentifierError() + .invalidPartialStateIdentifier(partialStateIdentifier) + .type(InvalidPartialStateIdentifierError.class.getSimpleName())); + } + + final HashCode accumulator; + if (partialStateIdentifier.getTransactionAccumulator() != null) { + var bytes = bytes(partialStateIdentifier.getTransactionAccumulator()); + accumulator = HashCode.fromBytes(bytes); + } else { + accumulator = null; + } + + return Pair.of(partialStateIdentifier.getStateVersion(), accumulator); + } + + public ResourceOperation resourceOperation(ResourceAmount resourceAmount) + throws CoreApiException { + if (resourceAmount == null) { + return null; + } + + var bigInteger = new BigInteger(resourceAmount.getValue()); + var isPositive = bigInteger.compareTo(BigInteger.ZERO) > 0; + + return ResourceOperation.from( + resource(resourceAmount.getResourceIdentifier()), + UInt256.from((isPositive ? bigInteger : bigInteger.negate()).toByteArray()), + isPositive); + } + + public Resource resource(ResourceIdentifier resourceIdentifier) throws CoreApiException { + if (resourceIdentifier instanceof TokenResourceIdentifier tokenResourceIdentifier) { + var rri = tokenResourceIdentifier.getRri(); + var symbolAndAddr = addressing.forResources().parseOrThrow(rri, s -> invalidAddress(rri)); + return new com.radixdlt.api.core.model.TokenResource( + symbolAndAddr.getFirst(), symbolAndAddr.getSecond()); + } else if (resourceIdentifier + instanceof StakeUnitResourceIdentifier stakeUnitResourceIdentifier) { + var validatorAddress = stakeUnitResourceIdentifier.getValidatorAddress(); + var key = + addressing + .forValidators() + .parseOrThrow(validatorAddress, s -> invalidAddress(validatorAddress)); + return new StakeUnitResource(key); + } else { + throw new IllegalStateException("Unknown resourceIdentifier: " + resourceIdentifier); + } + } + + public Peer peer(ECPublicKey key) { + return new Peer().peerId(addressing.forNodes().of(key)); + } + + public Peer peer(PeersView.PeerInfo peerInfo) { + return new Peer().peerId(addressing.forNodes().of(peerInfo.getNodeId().getPublicKey())); + } + + public ResourceIdentifier resourceIdentifier(Resource resource) { + if (resource instanceof com.radixdlt.api.core.model.TokenResource tokenResource) { + return new TokenResourceIdentifier() + .rri(addressing.forResources().of(tokenResource.symbol(), tokenResource.tokenAddress())) + .type(TOKEN_TYPE); + } else if (resource instanceof StakeUnitResource stakeUnitResource) { + return new StakeUnitResourceIdentifier() + .validatorAddress(addressing.forValidators().of(stakeUnitResource.validatorKey())) + .type(STAKE_UNIT_TYPE); + } else { + throw new IllegalStateException("Unknown resource " + resource); + } + } + + public EntityIdentifier entityIdentifier(Entity entity) { + if (entity instanceof AccountVaultEntity account) { + return entityIdentifier(account.accountAddress()); + } else if (entity instanceof PreparedStakeVaultEntity stake) { + return entityIdentifier(stake.accountAddress()) + .subEntity( + new SubEntity() + .address(PREPARED_STAKES_ADDRESS) + .metadata( + new SubEntityMetadata() + .validatorAddress(addressing.forValidators().of(stake.validatorKey())))); + } else if (entity instanceof PreparedUnstakeVaultEntity unstake) { + return entityIdentifier(unstake.accountAddress()) + .subEntity(new SubEntity().address(PREPARED_UNSTAKES_ADDRESS)); + } else if (entity instanceof ValidatorEntity validator) { + return entityIdentifier(validator.validatorKey()); + } else if (entity instanceof ValidatorSystemEntity validatorSystem) { + return entityIdentifier(validatorSystem.validatorKey()) + .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); + } else if (entity instanceof TokenEntity tokenEntity) { + return entityIdentifier(tokenEntity.tokenAddr(), tokenEntity.symbol()); + } else if (entity instanceof ExitingStakeVaultEntity exiting) { + return entityIdentifier( + exiting.accountAddress(), exiting.validatorKey(), exiting.epochUnlock()); + } else if (entity instanceof SystemEntity) { + return new EntityIdentifier().address(SYSTEM_ADDRESS); + } else { + throw new IllegalStateException("Unknown entity: " + entity); + } + } + + public EntityIdentifier entityIdentifier( + REAddr accountAddress, ECPublicKey validatorKey, long epochUnlock) { + return new EntityIdentifier() + .address(addressing.forAccounts().of(accountAddress)) + .subEntity( + new SubEntity() + .address(EXITING_UNSTAKES_ADDRESS) + .metadata( + new SubEntityMetadata() + .validatorAddress(addressing.forValidators().of(validatorKey)) + .epochUnlock(epochUnlock))); + } + + public EntityIdentifier entityIdentifier(REAddr tokenAddress, String symbol) { + return new EntityIdentifier().address(addressing.forResources().of(symbol, tokenAddress)); + } + + public EntityIdentifier entityIdentifierExitingStake( + REAddr accountAddress, ECPublicKey validatorKey, long epochUnlock) { + return new EntityIdentifier() + .address(addressing.forAccounts().of(accountAddress)) + .subEntity( + new SubEntity() + .address(EXITING_UNSTAKES_ADDRESS) + .metadata( + new SubEntityMetadata() + .validatorAddress(addressing.forValidators().of(validatorKey)) + .epochUnlock(epochUnlock))); + } + + public EntityIdentifier entityIdentifierPreparedUnstake(REAddr accountAddress) { + return new EntityIdentifier() + .address(addressing.forAccounts().of(accountAddress)) + .subEntity(new SubEntity().address(PREPARED_UNSTAKES_ADDRESS)); + } + + public EntityIdentifier entityIdentifierPreparedStake( + REAddr accountAddress, ECPublicKey validatorKey) { + return new EntityIdentifier() + .address(addressing.forAccounts().of(accountAddress)) + .subEntity( + new SubEntity() + .address(PREPARED_STAKES_ADDRESS) + .metadata( + new SubEntityMetadata() + .validatorAddress(addressing.forValidators().of(validatorKey)))); + } + + public EntityIdentifier entityIdentifier(REAddr accountAddress) { + return new EntityIdentifier().address(addressing.forAccounts().of(accountAddress)); + } + + public EntityIdentifier entityIdentifier(ECPublicKey validatorKey) { + return new EntityIdentifier().address(addressing.forValidators().of(validatorKey)); + } + + public EntityIdentifier entityIdentifierValidatorSystem(ECPublicKey validatorKey) { + return new EntityIdentifier() + .address(addressing.forValidators().of(validatorKey)) + .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); + } + + public PublicKey publicKey(ECPublicKey publicKey) { + return new PublicKey().hex(publicKey.toHex()); + } + + public StateIdentifier stateIdentifier(AccumulatorState accumulatorState) { + return new StateIdentifier() + .stateVersion(accumulatorState.getStateVersion()) + .transactionAccumulator(Bytes.toHexString(accumulatorState.getAccumulatorHash().asBytes())); + } + + public EngineStateIdentifier engineStateIdentifier(LedgerProof ledgerProof) { + return ledgerProof + .getNextValidatorSet() + .map( + vset -> + new EngineStateIdentifier() + .stateIdentifier(stateIdentifier(ledgerProof.getAccumulatorState())) + .epoch(ledgerProof.getEpoch() + 1) + .round(0L) + .timestamp(ledgerProof.timestamp())) + .orElseGet( + () -> + new EngineStateIdentifier() + .stateIdentifier(stateIdentifier(ledgerProof.getAccumulatorState())) + .epoch(ledgerProof.getEpoch()) + .round(ledgerProof.getView().number()) + .timestamp(ledgerProof.timestamp())); + } + + public SubstateTypeIdentifier substateTypeIdentifier(Class substateClass) { + var name = SubstateTypeMapping.getName(SubstateTypeId.valueOf(substateClass)); + return new SubstateTypeIdentifier().type(name); + } + + public TokenResourceIdentifier create(REAddr tokenAddress, String symbol) { + return (TokenResourceIdentifier) + new TokenResourceIdentifier() + .rri(addressing.forResources().of(symbol, tokenAddress)) + .type(TOKEN_TYPE); + } + + public ResourceAmount nativeTokenAmount(boolean positive, UInt256 value) { + return new ResourceAmount() + .resourceIdentifier(nativeToken()) + .value(positive ? value.toString() : "-" + value); + } + + public ResourceAmount stakeUnitAmount(ECPublicKey validatorKey, UInt256 value) { + return new ResourceAmount().resourceIdentifier(stakeUnit(validatorKey)).value(value.toString()); + } + + public ResourceAmount nativeTokenAmount(UInt256 value) { + return nativeTokenAmount(true, value); + } + + public TokenResourceIdentifier nativeToken() { + return create(REAddr.ofNativeToken(), "xrd"); + } + + public ResourceIdentifier stakeUnit(ECPublicKey validatorKey) { + return new StakeUnitResourceIdentifier() + .validatorAddress(addressing.forValidators().of(validatorKey)) + .type(STAKE_UNIT_TYPE); + } + + public ResourceIdentifier resourceIdentifier( + Bucket bucket, Function tokenAddressToSymbol) { + if (bucket.resourceAddr() != null) { + var addr = bucket.resourceAddr(); + var symbol = tokenAddressToSymbol.apply(addr); + return new TokenResourceIdentifier() + .rri(addressing.forResources().of(symbol, addr)) + .type(TOKEN_TYPE); + } + + return new StakeUnitResourceIdentifier() + .validatorAddress(addressing.forValidators().of(bucket.getValidatorKey())) + .type(STAKE_UNIT_TYPE); + } + + public ResourceAmount resourceOperation( + Bucket bucket, UInt384 amount, Function tokenAddressToSymbol) { + return new ResourceAmount() + .resourceIdentifier(resourceIdentifier(bucket, tokenAddressToSymbol)) + .value(amount.toString()); + } + + public FeeTable feeTable(com.radixdlt.application.system.FeeTable feeTable) { + var dto = new com.radixdlt.api.core.openapitools.model.FeeTable(); + feeTable + .getPerUpSubstateFee() + .forEach( + (p, fee) -> + dto.addPerUpSubstateFeeItem( + new UpSubstateFeeEntry() + .substateTypeIdentifier(substateTypeIdentifier(p)) + .fee(nativeTokenAmount(fee)))); + dto.perByteFee(nativeTokenAmount(feeTable.getPerByteFee())); + return dto; + } + + public EngineConfiguration engineConfiguration(RERulesConfig config) { + return new EngineConfiguration() + .nativeToken(nativeToken()) + .maximumMessageLength(255) // TODO: Remove hardcode + .maximumValidatorFeeIncrease(ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE) + .feeTable(feeTable(config.getFeeTable())) + .reservedSymbols(config.getReservedSymbols().stream().toList()) + .tokenSymbolPattern(config.getTokenSymbolPattern().pattern()) + .maximumTransactionSize(config.getMaxTxnSize()) + .maximumTransactionsPerRound(config.getMaxSigsPerRound().orElse(0)) + .maximumRoundsPerEpoch(config.getMaxRounds()) + .validatorFeeIncreaseDebouncerEpochLength(config.getRakeIncreaseDebouncerEpochLength()) + .minimumStake(nativeTokenAmount(config.getMinimumStake().toSubunits())) + .unstakingDelayEpochLength(config.getUnstakingEpochDelay()) + .rewardsPerProposal(nativeTokenAmount(config.getRewardsPerProposal().toSubunits())) + .minimumCompletedProposalsPercentage(config.getMinimumCompletedProposalsPercentage()) + .maximumValidators(config.getMaxValidators()); + } + + public Fork fork(ForkConfig forkConfig) { + return new Fork() + .forkIdentifier( + new ForkIdentifier().epoch(forkConfig.getEpoch()).fork(forkConfig.getName())) + .engineIdentifier( + new EngineIdentifier().engine(forkConfig.getVersion().name().toLowerCase())) + .engineConfiguration(engineConfiguration(forkConfig.getConfig())); + } + + public DataObject tokenData(TokenResource tokenResource) { + var tokenData = + new TokenData() + .granularity(tokenResource.getGranularity().toString()) + .isMutable(tokenResource.isMutable()); + tokenResource + .getOwner() + .map(REAddr::ofPubKeyAccount) + .ifPresent(addr -> tokenData.setOwner(entityIdentifier(addr))); + return tokenData.type(SubstateTypeMapping.getName(SubstateTypeId.TOKEN_RESOURCE)); + } + + public DataObject tokenMetadata(TokenResourceMetadata tokenResourceMetadata) { + return new TokenMetadata() + .symbol(tokenResourceMetadata.getSymbol()) + .name(tokenResourceMetadata.getName()) + .description(tokenResourceMetadata.getDescription()) + .url(tokenResourceMetadata.getUrl()) + .iconUrl(tokenResourceMetadata.getIconUrl()) + .type(SubstateTypeMapping.getName(SubstateTypeId.TOKEN_RESOURCE_METADATA)); + } + + public DataObject epochData(EpochData epochData) { + return new com.radixdlt.api.core.openapitools.model.EpochData() + .epoch(epochData.getEpoch()) + .type(SubstateTypeMapping.getName(SubstateTypeId.EPOCH_DATA)); + } + + public DataObject roundData(RoundData roundData) { + return new com.radixdlt.api.core.openapitools.model.RoundData() + .round(roundData.getView()) + .timestamp(roundData.getTimestamp()) + .type(SubstateTypeMapping.getName(SubstateTypeId.ROUND_DATA)); + } + + public DataObject preparedValidatorRegistered(ValidatorRegisteredCopy copy) { + var preparedValidatorRegistered = new PreparedValidatorRegistered(); + copy.getEpochUpdate().ifPresent(preparedValidatorRegistered::epoch); + return preparedValidatorRegistered + .registered(copy.isRegistered()) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY)); + } + + public DataObject preparedValidatorOwner(ValidatorOwnerCopy copy, boolean virtual) { + var preparedValidatorOwner = new PreparedValidatorOwner(); + copy.getEpochUpdate().ifPresent(preparedValidatorOwner::epoch); + return preparedValidatorOwner + .owner(virtual ? selfAccountEntityIdentifier() : entityIdentifier(copy.getOwner())) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_OWNER_COPY)); + } + + public DataObject preparedValidatorFee(ValidatorFeeCopy copy) { + var preparedValidatorFee = new PreparedValidatorFee(); + copy.getEpochUpdate().ifPresent(preparedValidatorFee::epoch); + return preparedValidatorFee + .fee(copy.getRakePercentage()) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_RAKE_COPY)); + } + + public DataObject validatorMetadata(ValidatorMetaData metaData) { + var validatorMetadata = new ValidatorMetadata(); + return validatorMetadata + .name(metaData.getName()) + .url(metaData.getUrl()) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_META_DATA)); + } + + public DataObject validatorBFTData(ValidatorBFTData validatorBFTData) { + var bftData = new com.radixdlt.api.core.openapitools.model.ValidatorBFTData(); + return bftData + .proposalsCompleted(validatorBFTData.proposalsCompleted()) + .proposalsMissed(validatorBFTData.proposalsMissed()) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_BFT_DATA)); + } + + public DataObject allowDelegationFlag(AllowDelegationFlag allowDelegationFlag) { + var allowDelegation = new ValidatorAllowDelegation(); + return allowDelegation + .allowDelegation(allowDelegationFlag.allowsDelegation()) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG)); + } + + public DataObject validatorSystemMetadata(ValidatorSystemMetadata validatorSystemMetadata) { + var systemMetadata = new com.radixdlt.api.core.openapitools.model.ValidatorSystemMetadata(); + return systemMetadata + .data(Bytes.toHexString(validatorSystemMetadata.getData())) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_SYSTEM_META_DATA)); + } + + private EntityIdentifier selfAccountEntityIdentifier() { + return new EntityIdentifier().address(addressing.forAccounts().getHrp() + "1"); + } + + public DataObject validatorStakeData(ValidatorStakeData validatorStakeData, boolean virtual) { + var validatorData = new com.radixdlt.api.core.openapitools.model.ValidatorData(); + return validatorData + .owner( + virtual + ? selfAccountEntityIdentifier() + : entityIdentifier(validatorStakeData.getOwnerAddr())) + .registered(validatorStakeData.isRegistered()) + .fee(validatorStakeData.getRakePercentage()) + .type(SubstateTypeMapping.getName(SubstateTypeId.VALIDATOR_STAKE_DATA)); + } + + public DataObject unclaimedREAddrData() { + var data = new UnclaimedRadixEngineAddress(); + return data.type(SubstateTypeMapping.getName(SubstateTypeId.UNCLAIMED_READDR)); + } + + private EntitySetIdentifier validatorsEntitySetIdentifier() { + var regex = + addressing.forValidators().getHrp() + + "1[023456789ACDEFGHJKLMNPQRSTUVWXYZacdefghjklmnpqrstuvwxyz]{6,90}"; + return new EntitySetIdentifier().addressRegex(regex); + } + + private EntitySetIdentifier tokensEntitySetIdentifier() { + var regex = + "[a-z0-9]+" + + addressing.forResources().getHrpSuffix() + + "1[023456789ACDEFGHJKLMNPQRSTUVWXYZacdefghjklmnpqrstuvwxyz]{6,90}"; + return new EntitySetIdentifier().addressRegex(regex); + } + + public DataObject virtualParent(VirtualParent virtualParent) { + var childType = SubstateTypeId.valueOf(virtualParent.getData()[0]); + var virtualDataObject = + switch (childType) { + case UNCLAIMED_READDR -> unclaimedREAddrData(); + case VALIDATOR_META_DATA -> validatorMetadata( + ValidatorMetaData.createVirtual(MOCK_PUBLIC_KEY)); + case VALIDATOR_STAKE_DATA -> validatorStakeData( + ValidatorStakeData.createVirtual(MOCK_PUBLIC_KEY), true); + case VALIDATOR_ALLOW_DELEGATION_FLAG -> allowDelegationFlag( + AllowDelegationFlag.createVirtual(MOCK_PUBLIC_KEY)); + case VALIDATOR_REGISTERED_FLAG_COPY -> preparedValidatorRegistered( + ValidatorRegisteredCopy.createVirtual(MOCK_PUBLIC_KEY)); + case VALIDATOR_RAKE_COPY -> preparedValidatorFee( + ValidatorFeeCopy.createVirtual(MOCK_PUBLIC_KEY)); + case VALIDATOR_OWNER_COPY -> preparedValidatorOwner( + ValidatorOwnerCopy.createVirtual(MOCK_PUBLIC_KEY), true); + case VALIDATOR_SYSTEM_META_DATA -> validatorSystemMetadata( + ValidatorSystemMetadata.createVirtual(MOCK_PUBLIC_KEY)); + default -> throw new IllegalStateException( + "Virtualization of " + childType + " unsupported"); + }; + + var entitySetIdentifier = + childType == SubstateTypeId.UNCLAIMED_READDR + ? tokensEntitySetIdentifier() + : validatorsEntitySetIdentifier(); + + return new VirtualParentData() + .entitySetIdentifier(entitySetIdentifier) + .virtualDataObject(virtualDataObject) + .type(SubstateTypeMapping.getName(SubstateTypeId.VIRTUAL_PARENT)); + } + + public Optional dataObject(Particle substate) { + final DataObject dataObject; + if (substate instanceof TokenResource tokenResource) { + dataObject = tokenData(tokenResource); + } else if (substate instanceof TokenResourceMetadata metadata) { + dataObject = tokenMetadata(metadata); + } else if (substate instanceof EpochData epochData) { + dataObject = epochData(epochData); + } else if (substate instanceof RoundData roundData) { + dataObject = roundData(roundData); + } else if (substate instanceof ValidatorRegisteredCopy validatorRegisteredCopy) { + dataObject = preparedValidatorRegistered(validatorRegisteredCopy); + } else if (substate instanceof ValidatorOwnerCopy validatorOwnerCopy) { + dataObject = preparedValidatorOwner(validatorOwnerCopy, false); + } else if (substate instanceof ValidatorFeeCopy validatorFeeCopy) { + dataObject = preparedValidatorFee(validatorFeeCopy); + } else if (substate instanceof ValidatorMetaData validatorMetaData) { + dataObject = validatorMetadata(validatorMetaData); + } else if (substate instanceof ValidatorBFTData validatorBFTData) { + dataObject = validatorBFTData(validatorBFTData); + } else if (substate instanceof AllowDelegationFlag allowDelegationFlag) { + dataObject = allowDelegationFlag(allowDelegationFlag); + } else if (substate instanceof ValidatorSystemMetadata validatorSystemMetadata) { + dataObject = validatorSystemMetadata(validatorSystemMetadata); + } else if (substate instanceof ValidatorStakeData validatorStakeData) { + dataObject = validatorStakeData(validatorStakeData, false); + } else if (substate instanceof UnclaimedREAddr) { + dataObject = unclaimedREAddrData(); + } else if (substate instanceof VirtualParent virtualParent) { + dataObject = virtualParent(virtualParent); + } else { + return Optional.empty(); + } + + return Optional.of(dataObject); + } + + public SubstateIdentifier substateIdentifier(SubstateId substateId) { + return new SubstateIdentifier().identifier(Bytes.toHexString(substateId.asBytes())); + } + + public Substate substate(SubstateId substateId, boolean bootUp) { + return new Substate() + .substateIdentifier(substateIdentifier(substateId)) + .substateOperation( + bootUp + ? Substate.SubstateOperationEnum.BOOTUP + : Substate.SubstateOperationEnum.SHUTDOWN); + } + + public ResourceAmount resourceOperation( + ResourceInBucket resourceInBucket, boolean bootUp, Function addressToSymbol) { + var amount = new BigInteger(bootUp ? 1 : -1, resourceInBucket.getAmount().toByteArray()); + var bucket = resourceInBucket.bucket(); + var resourceIdentifier = resourceIdentifier(bucket, addressToSymbol); + return new ResourceAmount().resourceIdentifier(resourceIdentifier).value(amount.toString()); + } + + private EntityIdentifier entityIdentifierOwnedBucket(ResourceInBucket resourceInBucket) { + var entityIdentifier = new EntityIdentifier(); + var bucket = resourceInBucket.bucket(); + entityIdentifier.address(addressing.forAccounts().of(bucket.getOwner())); + if (bucket.getValidatorKey() != null) { + if (bucket.resourceAddr() != null && bucket.getEpochUnlock() == null) { + entityIdentifier.subEntity( + new SubEntity() + .address(PREPARED_STAKES_ADDRESS) + .metadata( + new SubEntityMetadata() + .validatorAddress( + addressing.forValidators().of(bucket.getValidatorKey())))); + } else if (bucket.resourceAddr() == null && Objects.equals(bucket.getEpochUnlock(), 0L)) { + // Don't add validator as validator is already part of resource + entityIdentifier.subEntity(new SubEntity().address(PREPARED_UNSTAKES_ADDRESS)); + } else if (bucket.resourceAddr() != null && bucket.getEpochUnlock() != null) { + entityIdentifier.subEntity( + new SubEntity() + .address(EXITING_UNSTAKES_ADDRESS) + .metadata( + new SubEntityMetadata() + .validatorAddress(addressing.forValidators().of(bucket.getValidatorKey())) + .epochUnlock(bucket.getEpochUnlock()))); + } + } + return entityIdentifier; + } + + private EntityIdentifier entityIdentifier( + Particle substate, Function addressToSymbol) { + if (substate instanceof ResourceInBucket resourceInBucket + && resourceInBucket.bucket().getOwner() != null) { + return entityIdentifierOwnedBucket(resourceInBucket); + } else if (substate instanceof ValidatorStakeData validatorStakeData) { + return new EntityIdentifier() + .address(addressing.forValidators().of(validatorStakeData.getValidatorKey())) + .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); + } else if (substate instanceof ResourceData resourceData) { + var symbol = addressToSymbol.apply(resourceData.getAddr()); + return new EntityIdentifier() + .address(addressing.forResources().of(symbol, resourceData.getAddr())); + } else if (substate instanceof SystemData) { + return new EntityIdentifier().address(SYSTEM_ADDRESS); + } else if (substate instanceof ValidatorUpdatingData validatorUpdatingData) { + return new EntityIdentifier() + .address(addressing.forValidators().of(validatorUpdatingData.getValidatorKey())); + } else if (substate + instanceof com.radixdlt.application.validators.state.ValidatorData validatorData) { + return new EntityIdentifier() + .address(addressing.forValidators().of(validatorData.getValidatorKey())) + .subEntity(new SubEntity().address(SYSTEM_ADDRESS)); + } else if (substate instanceof UnclaimedREAddr unclaimedREAddr) { + var addr = unclaimedREAddr.getAddr(); + final String address; + if (addr.isSystem()) { + address = SYSTEM_ADDRESS; + } else { + var symbol = addressToSymbol.apply(addr); + address = addressing.forResources().of(symbol, addr); + } + return new EntityIdentifier().address(address); + } else if (substate instanceof VirtualParent) { + return new EntityIdentifier().address(SYSTEM_ADDRESS); + } else { + throw new IllegalStateException("Unknown substate " + substate); + } + } + + public Optional data(Particle substate, boolean bootUp) { + return dataObject(substate) + .map( + dataObject -> + new Data() + .dataObject(dataObject) + .action(bootUp ? Data.ActionEnum.CREATE : Data.ActionEnum.DELETE)); + } + + public Operation operation( + Particle substate, + SubstateId substateId, + boolean isBootUp, + Function addressToSymbol) { + var operation = new Operation(); + var typeId = SubstateTypeId.valueOf(substate.getClass()); + operation.type(SubstateTypeMapping.getType(typeId)); + operation.substate(substate(substateId, isBootUp)); + operation.entityIdentifier(entityIdentifier(substate, addressToSymbol)); + if (substate instanceof ResourceInBucket resourceInBucket + && !resourceInBucket.getAmount().isZero()) { + operation.amount(resourceOperation(resourceInBucket, isBootUp, addressToSymbol)); + } + data(substate, isBootUp).ifPresent(operation::data); + return operation; + } + + public Operation operation(REStateUpdate update, Function addressToSymbol) { + var operation = new Operation(); + var substate = (Particle) update.getParsed(); + operation.type(SubstateTypeMapping.getType(SubstateTypeId.valueOf(update.typeByte()))); + operation.substate(substate(update.getId(), update.isBootUp())); + operation.entityIdentifier(entityIdentifier(substate, addressToSymbol)); + if (substate instanceof ResourceInBucket resourceInBucket + && !resourceInBucket.getAmount().isZero()) { + operation.amount(resourceOperation(resourceInBucket, update.isBootUp(), addressToSymbol)); + } + data(substate, update.isBootUp()).ifPresent(operation::data); + return operation; + } + + public OperationGroup operationGroup( + List stateUpdates, Function addressToSymbol) { + var operationGroup = new OperationGroup(); + stateUpdates.forEach(u -> operationGroup.addOperationsItem(operation(u, addressToSymbol))); + return operationGroup; + } + + public CommittedTransaction committedTransaction( + REProcessedTxn txn, + AccumulatorState accumulatorState, + Function addressToSymbol) { + Function localizedAddressToSymbol = + addr -> { + var localSymbol = + txn.getGroupedStateUpdates().stream() + .flatMap(List::stream) + .map(REStateUpdate::getParsed) + .filter(TokenResourceMetadata.class::isInstance) + .map(TokenResourceMetadata.class::cast) + .filter(r -> r.getAddr().equals(addr)) + .map(TokenResourceMetadata::getSymbol) + .findFirst(); + + return localSymbol.orElseGet(() -> addressToSymbol.apply(addr)); + }; + + var transaction = new CommittedTransaction(); + + for (var stateUpdates : txn.getGroupedStateUpdates()) { + var operationGroup = operationGroup(stateUpdates, localizedAddressToSymbol); + transaction.addOperationGroupsItem(operationGroup); + } + + var metadata = + new CommittedTransactionMetadata() + .fee(nativeTokenAmount(txn.getFeePaid())) + .hex(Bytes.toHexString(txn.getTxn().getPayload())) + .size(txn.getTxn().getPayload().length); + txn.getSignedBy().ifPresent(s -> metadata.setSignedBy(publicKey(s))); + txn.getMsg().ifPresent(msg -> metadata.setMessage(Bytes.toHexString(msg))); + + transaction.metadata(metadata); + transaction.transactionIdentifier(transactionIdentifier(txn.getTxnId())); + transaction.committedStateIdentifier(stateIdentifier(accumulatorState)); + + return transaction; + } + + public Transaction transaction(REProcessedTxn txn, Function addressToSymbol) { + Function localizedAddressToSymbol = + addr -> { + var localSymbol = + txn.getGroupedStateUpdates().stream() + .flatMap(List::stream) + .map(REStateUpdate::getParsed) + .filter(TokenResourceMetadata.class::isInstance) + .map(TokenResourceMetadata.class::cast) + .filter(r -> r.getAddr().equals(addr)) + .map(TokenResourceMetadata::getSymbol) + .findFirst(); + + return localSymbol.orElseGet(() -> addressToSymbol.apply(addr)); + }; + + var transaction = new Transaction(); + + for (var stateUpdates : txn.getGroupedStateUpdates()) { + var operationGroup = operationGroup(stateUpdates, localizedAddressToSymbol); + transaction.addOperationGroupsItem(operationGroup); + } + + var metadata = + new CommittedTransactionMetadata() + .fee(nativeTokenAmount(txn.getFeePaid())) + .message(txn.getMsg().map(Bytes::toHexString).orElse(null)); + transaction.metadata(metadata); + + // If user transaction is signed then we can return back complete information + txn.getSignedBy() + .ifPresent( + publicKey -> { + metadata.signedBy(publicKey(publicKey)); + metadata.size(txn.getTxn().getPayload().length); + metadata.hex(Bytes.toHexString(txn.getTxn().getPayload())); + transaction.transactionIdentifier(transactionIdentifier(txn.getTxnId())); + }); + + return transaction; + } + + public TransactionIdentifier transactionIdentifier(AID txnId) { + return new TransactionIdentifier().hash(txnId.toString()); + } + + public CoreApiException notValidatorEntityException(EntityIdentifier entityIdentifier) { + return CoreApiException.badRequest( + new NotValidatorEntityError() + .entity(entityIdentifier) + .type(NotValidatorEntityError.class.getSimpleName())); + } + + public CoreApiException parseException(TxnParseException exception) { + var cause = Throwables.getRootCause(exception); + return CoreApiException.badRequest( + new InvalidTransactionError() + .message(cause.getMessage()) + .type(InvalidTransactionError.class.getSimpleName())); + } + + public CoreApiException mempoolFullException(MempoolFullException e) { + return CoreApiException.unavailable( + new MempoolFullError() + .mempoolTransactionCount(e.getMaxSize()) + .type(MempoolFullError.class.getSimpleName())); + } + + public CoreApiException radixEngineException(RadixEngineException exception) { + var cause = Throwables.getRootCause(exception); + if (cause instanceof SubstateNotFoundException notFoundException) { + return CoreApiException.conflict( + new SubstateDependencyNotFoundError() + .substateIdentifierNotFound(substateIdentifier(notFoundException.getSubstateId())) + .type(SubstateDependencyNotFoundError.class.getSimpleName())); + } + + return CoreApiException.badRequest( + new InvalidTransactionError() + .message(cause.getMessage()) + .type(InvalidTransactionError.class.getSimpleName())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/DataOperation.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/DataOperation.java index 1aa3ea8115..f2565a644c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/DataOperation.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/DataOperation.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,19 +68,19 @@ import com.radixdlt.api.core.openapitools.model.Data; public final class DataOperation { - private final Data data; - private final ClassToInstanceMap parsedData; + private final Data data; + private final ClassToInstanceMap parsedData; - public DataOperation(Data data, ClassToInstanceMap parsedData) { - this.data = data; - this.parsedData = parsedData; - } + public DataOperation(Data data, ClassToInstanceMap parsedData) { + this.data = data; + this.parsedData = parsedData; + } - public Data.ActionEnum getDataAction() { - return data.getAction(); - } + public Data.ActionEnum getDataAction() { + return data.getAction(); + } - public ParsedDataObject getParsedDataObject() { - return new ParsedDataObject(data.getDataObject(), parsedData); - } + public ParsedDataObject getParsedDataObject() { + return new ParsedDataObject(data.getDataObject(), parsedData); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Entity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Entity.java index ddcd8e02fa..0f9419cfe4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Entity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Entity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,30 +70,29 @@ import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.statecomputer.forks.RERulesConfig; - import java.util.List; import java.util.function.Supplier; public interface Entity { - // Read - List getResourceQueries(); - List getKeyQueries(); + // Read + List getResourceQueries(); + + List getKeyQueries(); - // Write - default void deposit(ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) - throws TxBuilderException { - throw new EntityDoesNotSupportResourceDepositException(this, amount.resource()); - } + // Write + default void deposit( + ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) + throws TxBuilderException { + throw new EntityDoesNotSupportResourceDepositException(this, amount.resource()); + } - default SubstateWithdrawal withdraw(Resource resource) throws TxBuilderException { - throw new EntityDoesNotSupportResourceWithdrawException(this, resource); - } + default SubstateWithdrawal withdraw(Resource resource) throws TxBuilderException { + throw new EntityDoesNotSupportResourceWithdrawException(this, resource); + } - default void overwriteDataObject( - ParsedDataObject parsedDataObject, - TxBuilder txBuilder, - Supplier config - ) throws TxBuilderException { - throw new EntityDoesNotSupportDataObjectException(this, parsedDataObject); - } + default void overwriteDataObject( + ParsedDataObject parsedDataObject, TxBuilder txBuilder, Supplier config) + throws TxBuilderException { + throw new EntityDoesNotSupportDataObjectException(this, parsedDataObject); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/EntityOperation.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/EntityOperation.java index 1a58764934..45d78dcbaf 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/EntityOperation.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/EntityOperation.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,12 +64,14 @@ package com.radixdlt.api.core.model; -public record EntityOperation(Entity entity, ResourceOperation resourceOperation, DataOperation dataOperation) { - public static EntityOperation from(Entity entity, ResourceOperation resourceOperation, DataOperation dataOperation) { - return new EntityOperation(entity, resourceOperation, dataOperation); - } +public record EntityOperation( + Entity entity, ResourceOperation resourceOperation, DataOperation dataOperation) { + public static EntityOperation from( + Entity entity, ResourceOperation resourceOperation, DataOperation dataOperation) { + return new EntityOperation(entity, resourceOperation, dataOperation); + } - public static EntityOperation from(Entity entity, ResourceOperation resourceOperation) { - return new EntityOperation(entity, resourceOperation, null); - } -} \ No newline at end of file + public static EntityOperation from(Entity entity, ResourceOperation resourceOperation) { + return new EntityOperation(entity, resourceOperation, null); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/KeyQuery.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/KeyQuery.java index 97bd44ad1d..850ef08308 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/KeyQuery.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/KeyQuery.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,46 +70,49 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.engine.RadixEngineReader; import com.radixdlt.identifiers.REAddr; - import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; public final class KeyQuery { - private final SystemMapKey key; - private final Supplier> virtualSubstate; + private final SystemMapKey key; + private final Supplier> virtualSubstate; - private KeyQuery(SystemMapKey key, Supplier> virtualSubstate) { - this.key = key; - this.virtualSubstate = virtualSubstate; - } + private KeyQuery(SystemMapKey key, Supplier> virtualSubstate) { + this.key = key; + this.virtualSubstate = virtualSubstate; + } - public Optional get(RadixEngineReader reader) { - return reader.get(key).or(virtualSubstate); - } + public Optional get(RadixEngineReader reader) { + return reader.get(key).or(virtualSubstate); + } - public static KeyQuery fromToken(REAddr tokenAddress, SubstateTypeId typeId, Function virtualSubstate) { - var key = SystemMapKey.ofResourceData(tokenAddress, typeId.id()); - return new KeyQuery(key, () -> Optional.of(virtualSubstate.apply(tokenAddress))); - } + public static KeyQuery fromToken( + REAddr tokenAddress, SubstateTypeId typeId, Function virtualSubstate) { + var key = SystemMapKey.ofResourceData(tokenAddress, typeId.id()); + return new KeyQuery(key, () -> Optional.of(virtualSubstate.apply(tokenAddress))); + } - public static KeyQuery fromToken(REAddr tokenAddress, SubstateTypeId typeId) { - var key = SystemMapKey.ofResourceData(tokenAddress, typeId.id()); - return new KeyQuery(key, Optional::empty); - } + public static KeyQuery fromToken(REAddr tokenAddress, SubstateTypeId typeId) { + var key = SystemMapKey.ofResourceData(tokenAddress, typeId.id()); + return new KeyQuery(key, Optional::empty); + } - public static KeyQuery fromSystem(SubstateTypeId typeId) { - var key = SystemMapKey.ofSystem(typeId.id()); - return new KeyQuery(key, Optional::empty); - } + public static KeyQuery fromSystem(SubstateTypeId typeId) { + var key = SystemMapKey.ofSystem(typeId.id()); + return new KeyQuery(key, Optional::empty); + } - public static KeyQuery fromValidator(ECPublicKey validatorKey, SubstateTypeId typeId, Function virtualSubstate) { - var key = SystemMapKey.ofSystem(typeId.id(), validatorKey.getCompressedBytes()); - return new KeyQuery(key, () -> Optional.of(virtualSubstate.apply(validatorKey))); - } + public static KeyQuery fromValidator( + ECPublicKey validatorKey, + SubstateTypeId typeId, + Function virtualSubstate) { + var key = SystemMapKey.ofSystem(typeId.id(), validatorKey.getCompressedBytes()); + return new KeyQuery(key, () -> Optional.of(virtualSubstate.apply(validatorKey))); + } - public static KeyQuery fromValidator(ECPublicKey validatorKey, SubstateTypeId typeId) { - var key = SystemMapKey.ofSystem(typeId.id(), validatorKey.getCompressedBytes()); - return new KeyQuery(key, Optional::empty); - } + public static KeyQuery fromValidator(ECPublicKey validatorKey, SubstateTypeId typeId) { + var key = SystemMapKey.ofSystem(typeId.id(), validatorKey.getCompressedBytes()); + return new KeyQuery(key, Optional::empty); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughNativeTokensForFeesException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughNativeTokensForFeesException.java index a1410fcaab..a043afe618 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughNativeTokensForFeesException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughNativeTokensForFeesException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,20 +68,20 @@ import com.radixdlt.utils.UInt256; public final class NotEnoughNativeTokensForFeesException extends TxBuilderException { - private final UInt256 fee; - private final UInt256 available; + private final UInt256 fee; + private final UInt256 available; - public NotEnoughNativeTokensForFeesException(UInt256 fee, UInt256 available) { - super("Fee is " + fee + " but only " + available + " available"); - this.fee = fee; - this.available = available; - } + public NotEnoughNativeTokensForFeesException(UInt256 fee, UInt256 available) { + super("Fee is " + fee + " but only " + available + " available"); + this.fee = fee; + this.available = available; + } - public UInt256 getFee() { - return fee; - } + public UInt256 getFee() { + return fee; + } - public UInt256 getAvailable() { - return available; - } + public UInt256 getAvailable() { + return available; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughResourcesException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughResourcesException.java index c960242022..5af4d8f36e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughResourcesException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/NotEnoughResourcesException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,32 +68,33 @@ import com.radixdlt.utils.UInt256; public final class NotEnoughResourcesException extends TxBuilderException { - private final UInt256 fee; - private final UInt256 requested; - private final UInt256 available; - private final Resource resource; + private final UInt256 fee; + private final UInt256 requested; + private final UInt256 available; + private final Resource resource; - public NotEnoughResourcesException(Resource resource, UInt256 requested, UInt256 available, UInt256 fee) { - super("Requested " + requested + " but only " + available + " available"); - this.resource = resource; - this.requested = requested; - this.available = available; - this.fee = fee; - } + public NotEnoughResourcesException( + Resource resource, UInt256 requested, UInt256 available, UInt256 fee) { + super("Requested " + requested + " but only " + available + " available"); + this.resource = resource; + this.requested = requested; + this.available = available; + this.fee = fee; + } - public UInt256 getFee() { - return fee; - } + public UInt256 getFee() { + return fee; + } - public Resource getResource() { - return resource; - } + public Resource getResource() { + return resource; + } - public UInt256 getRequested() { - return requested; - } + public UInt256 getRequested() { + return requested; + } - public UInt256 getAvailable() { - return available; - } + public UInt256 getAvailable() { + return available; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/OperationTxBuilder.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/OperationTxBuilder.java index ce9edd1279..fa534c57a8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/OperationTxBuilder.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/OperationTxBuilder.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -73,104 +74,99 @@ import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Optional; import java.util.function.Supplier; public final class OperationTxBuilder implements RadixEngine.TxBuilderExecutable { - private final Forks forks; - private final List> operationGroups; - private final String message; - - public OperationTxBuilder( - String message, - List> operationGroups, - Forks forks - ) { - this.message = message; - this.operationGroups = operationGroups; - this.forks = forks; - } - - private void executeResourceOperation( - Entity entity, - ResourceOperation operation, - TxBuilder txBuilder, - Supplier config - ) throws TxBuilderException { - if (operation == null) { - return; - } - - var amount = operation.getAmount(); - if (operation.isDeposit()) { - entity.deposit(amount, txBuilder, config); - } else { - var withdrawal = entity.withdraw(amount.resource()); - var feeInReserve = Optional.ofNullable(txBuilder.getFeeReserve()).orElse(UInt256.ZERO); - var change = withdrawal.execute( - txBuilder, - amount.amount(), - available -> new NotEnoughResourcesException(amount.resource(), amount.amount(), available, feeInReserve) - ); - - if (!change.isZero()) { - var changeAmount = new ResourceUnsignedAmount(amount.resource(), change); - entity.deposit(changeAmount, txBuilder, config); - } - } - } - - private void executeDataOperation( - Entity entity, - DataOperation operation, - TxBuilder txBuilder, - Supplier config - ) throws TxBuilderException { - if (operation == null) { - return; - } - - var dataAction = operation.getDataAction(); - if (dataAction == Data.ActionEnum.CREATE) { - var parsedDataObject = operation.getParsedDataObject(); - entity.overwriteDataObject(parsedDataObject, txBuilder, config); - } else { - throw new IllegalStateException("DataAction: " + dataAction + " not supported yet."); - } - } - - private void execute( - EntityOperation operation, - TxBuilder txBuilder, - Supplier config - ) throws TxBuilderException { - var entity = operation.entity(); - var resourceOperation = operation.resourceOperation(); - executeResourceOperation(entity, resourceOperation, txBuilder, config); - - var dataOperation = operation.dataOperation(); - executeDataOperation(entity, dataOperation, txBuilder, config); - } - - @Override - public void execute(TxBuilder txBuilder) throws TxBuilderException { - - var configSupplier = Suppliers.memoize(() -> { - var epochData = txBuilder.findSystem(EpochData.class); - return forks.get(epochData.getEpoch()).getConfig(); - }); - - for (var operationGroup : this.operationGroups) { - for (var operation : operationGroup) { - execute(operation, txBuilder, configSupplier); - } - txBuilder.end(); - } - - if (this.message != null) { - txBuilder.message(Bytes.fromHexString(message)); - } - } + private final Forks forks; + private final List> operationGroups; + private final String message; + + public OperationTxBuilder( + String message, List> operationGroups, Forks forks) { + this.message = message; + this.operationGroups = operationGroups; + this.forks = forks; + } + + private void executeResourceOperation( + Entity entity, + ResourceOperation operation, + TxBuilder txBuilder, + Supplier config) + throws TxBuilderException { + if (operation == null) { + return; + } + + var amount = operation.getAmount(); + if (operation.isDeposit()) { + entity.deposit(amount, txBuilder, config); + } else { + var withdrawal = entity.withdraw(amount.resource()); + var feeInReserve = Optional.ofNullable(txBuilder.getFeeReserve()).orElse(UInt256.ZERO); + var change = + withdrawal.execute( + txBuilder, + amount.amount(), + available -> + new NotEnoughResourcesException( + amount.resource(), amount.amount(), available, feeInReserve)); + + if (!change.isZero()) { + var changeAmount = new ResourceUnsignedAmount(amount.resource(), change); + entity.deposit(changeAmount, txBuilder, config); + } + } + } + + private void executeDataOperation( + Entity entity, DataOperation operation, TxBuilder txBuilder, Supplier config) + throws TxBuilderException { + if (operation == null) { + return; + } + + var dataAction = operation.getDataAction(); + if (dataAction == Data.ActionEnum.CREATE) { + var parsedDataObject = operation.getParsedDataObject(); + entity.overwriteDataObject(parsedDataObject, txBuilder, config); + } else { + throw new IllegalStateException("DataAction: " + dataAction + " not supported yet."); + } + } + + private void execute( + EntityOperation operation, TxBuilder txBuilder, Supplier config) + throws TxBuilderException { + var entity = operation.entity(); + var resourceOperation = operation.resourceOperation(); + executeResourceOperation(entity, resourceOperation, txBuilder, config); + + var dataOperation = operation.dataOperation(); + executeDataOperation(entity, dataOperation, txBuilder, config); + } + + @Override + public void execute(TxBuilder txBuilder) throws TxBuilderException { + + var configSupplier = + Suppliers.memoize( + () -> { + var epochData = txBuilder.findSystem(EpochData.class); + return forks.get(epochData.getEpoch()).getConfig(); + }); + + for (var operationGroup : this.operationGroups) { + for (var operation : operationGroup) { + execute(operation, txBuilder, configSupplier); + } + txBuilder.end(); + } + + if (this.message != null) { + txBuilder.message(Bytes.fromHexString(message)); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ParsedDataObject.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ParsedDataObject.java index b773738e11..2212870ed3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ParsedDataObject.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ParsedDataObject.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,19 +68,19 @@ import com.radixdlt.api.core.openapitools.model.DataObject; public final class ParsedDataObject { - private final DataObject dataObject; - private final ClassToInstanceMap parsedData; + private final DataObject dataObject; + private final ClassToInstanceMap parsedData; - public ParsedDataObject(DataObject dataObject, ClassToInstanceMap parsedData) { - this.dataObject = dataObject; - this.parsedData = parsedData; - } + public ParsedDataObject(DataObject dataObject, ClassToInstanceMap parsedData) { + this.dataObject = dataObject; + this.parsedData = parsedData; + } - public DataObject getDataObject() { - return dataObject; - } + public DataObject getDataObject() { + return dataObject; + } - public T getParsed(Class parsedClass) { - return parsedData.getInstance(parsedClass); - } + public T getParsed(Class parsedClass) { + return parsedData.getInstance(parsedClass); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Resource.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Resource.java index e900b61815..c6c629b522 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Resource.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/Resource.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,5 +64,4 @@ package com.radixdlt.api.core.model; -public sealed interface Resource permits TokenResource, StakeUnitResource { -} +public sealed interface Resource permits TokenResource, StakeUnitResource {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceOperation.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceOperation.java index 246b0ffde2..6b5f1ce580 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceOperation.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceOperation.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,33 +67,33 @@ import com.radixdlt.utils.UInt256; public final class ResourceOperation { - private final Resource resource; - private final UInt256 amount; - private final boolean deposit; + private final Resource resource; + private final UInt256 amount; + private final boolean deposit; - private ResourceOperation(Resource resource, UInt256 amount, boolean deposit) { - this.resource = resource; - this.amount = amount; - this.deposit = deposit; - } + private ResourceOperation(Resource resource, UInt256 amount, boolean deposit) { + this.resource = resource; + this.amount = amount; + this.deposit = deposit; + } - public ResourceUnsignedAmount getAmount() { - return new ResourceUnsignedAmount(resource, amount); - } + public ResourceUnsignedAmount getAmount() { + return new ResourceUnsignedAmount(resource, amount); + } - public boolean isDeposit() { - return deposit; - } + public boolean isDeposit() { + return deposit; + } - public static ResourceOperation from(Resource resource, UInt256 amount, boolean deposit) { - return new ResourceOperation(resource, amount, deposit); - } + public static ResourceOperation from(Resource resource, UInt256 amount, boolean deposit) { + return new ResourceOperation(resource, amount, deposit); + } - public static ResourceOperation deposit(Resource resource, UInt256 amount) { - return new ResourceOperation(resource, amount, true); - } + public static ResourceOperation deposit(Resource resource, UInt256 amount) { + return new ResourceOperation(resource, amount, true); + } - public static ResourceOperation withdraw(Resource resource, UInt256 amount) { - return new ResourceOperation(resource, amount, false); - } + public static ResourceOperation withdraw(Resource resource, UInt256 amount) { + return new ResourceOperation(resource, amount, false); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceQuery.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceQuery.java index 59cfb12718..81c5bf361e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceQuery.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceQuery.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,42 +67,44 @@ import com.radixdlt.application.tokens.ResourceInBucket; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SystemMapKey; - import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; public final class ResourceQuery { - private final SubstateIndex index; - private final Predicate predicate; - private final SystemMapKey systemMapKey; + private final SubstateIndex index; + private final Predicate predicate; + private final SystemMapKey systemMapKey; - private ResourceQuery(SubstateIndex index, Predicate predicate, SystemMapKey systemMapKey) { - this.index = index; - this.predicate = predicate; - this.systemMapKey = systemMapKey; - } + private ResourceQuery( + SubstateIndex index, + Predicate predicate, + SystemMapKey systemMapKey) { + this.index = index; + this.predicate = predicate; + this.systemMapKey = systemMapKey; + } - public T fold( - BiFunction, Predicate, T> indexFunction, - Function keyFunction - ) { - if (index != null) { - return indexFunction.apply(index, predicate); - } else { - return keyFunction.apply(systemMapKey); - } - } + public T fold( + BiFunction, Predicate, T> indexFunction, + Function keyFunction) { + if (index != null) { + return indexFunction.apply(index, predicate); + } else { + return keyFunction.apply(systemMapKey); + } + } - public static ResourceQuery from(SystemMapKey systemMapKey) { - return new ResourceQuery(null, null, systemMapKey); - } + public static ResourceQuery from(SystemMapKey systemMapKey) { + return new ResourceQuery(null, null, systemMapKey); + } - public static ResourceQuery from(SubstateIndex index) { - return new ResourceQuery(index, b -> true, null); - } + public static ResourceQuery from(SubstateIndex index) { + return new ResourceQuery(index, b -> true, null); + } - public static ResourceQuery from(SubstateIndex index, Predicate predicate) { - return new ResourceQuery(index, predicate, null); - } + public static ResourceQuery from( + SubstateIndex index, Predicate predicate) { + return new ResourceQuery(index, predicate, null); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceUnsignedAmount.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceUnsignedAmount.java index 747e579fbf..7f3a903339 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceUnsignedAmount.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/ResourceUnsignedAmount.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -65,5 +66,4 @@ import com.radixdlt.utils.UInt256; -public record ResourceUnsignedAmount(Resource resource, UInt256 amount) { -} +public record ResourceUnsignedAmount(Resource resource, UInt256 amount) {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/StakeUnitResource.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/StakeUnitResource.java index 9b6fc11eb0..c58a9c24a2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/StakeUnitResource.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/StakeUnitResource.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -65,5 +66,4 @@ import com.radixdlt.crypto.ECPublicKey; -public record StakeUnitResource(ECPublicKey validatorKey) implements Resource { -} +public record StakeUnitResource(ECPublicKey validatorKey) implements Resource {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateOperation.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateOperation.java index b0facc11f5..0e00cd93f3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateOperation.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateOperation.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,25 +68,25 @@ import com.radixdlt.constraintmachine.Particle; public final class SubstateOperation { - private final Particle substate; - private final SubstateId substateId; - private final boolean isBootUp; + private final Particle substate; + private final SubstateId substateId; + private final boolean isBootUp; - public SubstateOperation(Particle substate, SubstateId substateId, boolean isBootUp) { - this.substate = substate; - this.substateId = substateId; - this.isBootUp = isBootUp; - } + public SubstateOperation(Particle substate, SubstateId substateId, boolean isBootUp) { + this.substate = substate; + this.substateId = substateId; + this.isBootUp = isBootUp; + } - public Particle getSubstate() { - return substate; - } + public Particle getSubstate() { + return substate; + } - public SubstateId getSubstateId() { - return substateId; - } + public SubstateId getSubstateId() { + return substateId; + } - public boolean isBootUp() { - return isBootUp; - } + public boolean isBootUp() { + return isBootUp; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateTypeMapping.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateTypeMapping.java index 15e09a104e..49fed97af2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateTypeMapping.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateTypeMapping.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,67 +64,71 @@ package com.radixdlt.api.core.model; +import static com.radixdlt.api.core.model.SubstateTypeMapping.OperationType.*; +import static com.radixdlt.atom.SubstateTypeId.*; + import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.utils.Pair; - import java.util.EnumMap; import java.util.Map; -import static com.radixdlt.atom.SubstateTypeId.*; -import static com.radixdlt.api.core.model.SubstateTypeMapping.OperationType.*; - public final class SubstateTypeMapping { - public enum OperationType { - RESOURCE("Resource"), - DATA("Data"), - RESOURCE_AND_DATA("ResourceAndData"); + public enum OperationType { + RESOURCE("Resource"), + DATA("Data"), + RESOURCE_AND_DATA("ResourceAndData"); + + private final String name; - private final String name; + OperationType(String name) { + this.name = name; + } + } - OperationType(String name) { - this.name = name; - } - } + private static final Map> SUBSTATE_TYPE_ID_STRING_MAP; - private static final Map> SUBSTATE_TYPE_ID_STRING_MAP; - static { - SUBSTATE_TYPE_ID_STRING_MAP = new EnumMap<>(SubstateTypeId.class); - SUBSTATE_TYPE_ID_STRING_MAP.put(VIRTUAL_PARENT, Pair.of(DATA, "VirtualParentData")); - SUBSTATE_TYPE_ID_STRING_MAP.put(UNCLAIMED_READDR, Pair.of(DATA, "UnclaimedRadixEngineAddress")); - SUBSTATE_TYPE_ID_STRING_MAP.put(ROUND_DATA, Pair.of(DATA, "RoundData")); - SUBSTATE_TYPE_ID_STRING_MAP.put(EPOCH_DATA, Pair.of(DATA, "EpochData")); - SUBSTATE_TYPE_ID_STRING_MAP.put(TOKEN_RESOURCE, Pair.of(DATA, "TokenData")); - SUBSTATE_TYPE_ID_STRING_MAP.put(TOKEN_RESOURCE_METADATA, Pair.of(DATA, "TokenMetadata")); - SUBSTATE_TYPE_ID_STRING_MAP.put(TOKENS, Pair.of(RESOURCE, "Tokens")); - SUBSTATE_TYPE_ID_STRING_MAP.put(PREPARED_STAKE, Pair.of(RESOURCE, "PreparedStake")); - SUBSTATE_TYPE_ID_STRING_MAP.put(STAKE_OWNERSHIP, Pair.of(RESOURCE, "StakeOwnership")); - SUBSTATE_TYPE_ID_STRING_MAP.put(PREPARED_UNSTAKE, Pair.of(RESOURCE, "PreparedUnstake")); - SUBSTATE_TYPE_ID_STRING_MAP.put(EXITING_STAKE, Pair.of(RESOURCE, "ExitingStake")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_META_DATA, Pair.of(DATA, "ValidatorMetadata")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_STAKE_DATA, Pair.of(RESOURCE_AND_DATA, "ValidatorData")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_BFT_DATA, Pair.of(DATA, "ValidatorBFTData")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_ALLOW_DELEGATION_FLAG, Pair.of(DATA, "ValidatorAllowDelegation")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_REGISTERED_FLAG_COPY, Pair.of(DATA, "PreparedValidatorRegistered")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_RAKE_COPY, Pair.of(DATA, "PreparedValidatorFee")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_OWNER_COPY, Pair.of(DATA, "PreparedValidatorOwner")); - SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_SYSTEM_META_DATA, Pair.of(DATA, "ValidatorSystemMetadata")); + static { + SUBSTATE_TYPE_ID_STRING_MAP = new EnumMap<>(SubstateTypeId.class); + SUBSTATE_TYPE_ID_STRING_MAP.put(VIRTUAL_PARENT, Pair.of(DATA, "VirtualParentData")); + SUBSTATE_TYPE_ID_STRING_MAP.put(UNCLAIMED_READDR, Pair.of(DATA, "UnclaimedRadixEngineAddress")); + SUBSTATE_TYPE_ID_STRING_MAP.put(ROUND_DATA, Pair.of(DATA, "RoundData")); + SUBSTATE_TYPE_ID_STRING_MAP.put(EPOCH_DATA, Pair.of(DATA, "EpochData")); + SUBSTATE_TYPE_ID_STRING_MAP.put(TOKEN_RESOURCE, Pair.of(DATA, "TokenData")); + SUBSTATE_TYPE_ID_STRING_MAP.put(TOKEN_RESOURCE_METADATA, Pair.of(DATA, "TokenMetadata")); + SUBSTATE_TYPE_ID_STRING_MAP.put(TOKENS, Pair.of(RESOURCE, "Tokens")); + SUBSTATE_TYPE_ID_STRING_MAP.put(PREPARED_STAKE, Pair.of(RESOURCE, "PreparedStake")); + SUBSTATE_TYPE_ID_STRING_MAP.put(STAKE_OWNERSHIP, Pair.of(RESOURCE, "StakeOwnership")); + SUBSTATE_TYPE_ID_STRING_MAP.put(PREPARED_UNSTAKE, Pair.of(RESOURCE, "PreparedUnstake")); + SUBSTATE_TYPE_ID_STRING_MAP.put(EXITING_STAKE, Pair.of(RESOURCE, "ExitingStake")); + SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_META_DATA, Pair.of(DATA, "ValidatorMetadata")); + SUBSTATE_TYPE_ID_STRING_MAP.put( + VALIDATOR_STAKE_DATA, Pair.of(RESOURCE_AND_DATA, "ValidatorData")); + SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_BFT_DATA, Pair.of(DATA, "ValidatorBFTData")); + SUBSTATE_TYPE_ID_STRING_MAP.put( + VALIDATOR_ALLOW_DELEGATION_FLAG, Pair.of(DATA, "ValidatorAllowDelegation")); + SUBSTATE_TYPE_ID_STRING_MAP.put( + VALIDATOR_REGISTERED_FLAG_COPY, Pair.of(DATA, "PreparedValidatorRegistered")); + SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_RAKE_COPY, Pair.of(DATA, "PreparedValidatorFee")); + SUBSTATE_TYPE_ID_STRING_MAP.put(VALIDATOR_OWNER_COPY, Pair.of(DATA, "PreparedValidatorOwner")); + SUBSTATE_TYPE_ID_STRING_MAP.put( + VALIDATOR_SYSTEM_META_DATA, Pair.of(DATA, "ValidatorSystemMetadata")); - for (var id : SubstateTypeId.values()) { - if (!SUBSTATE_TYPE_ID_STRING_MAP.containsKey(id)) { - throw new IllegalStateException("No string associated with substate type id " + id); - } - } - } + for (var id : SubstateTypeId.values()) { + if (!SUBSTATE_TYPE_ID_STRING_MAP.containsKey(id)) { + throw new IllegalStateException("No string associated with substate type id " + id); + } + } + } - private SubstateTypeMapping() { - throw new IllegalStateException("Cannot instantiate."); - } + private SubstateTypeMapping() { + throw new IllegalStateException("Cannot instantiate."); + } - public static String getType(SubstateTypeId typeId) { - return SUBSTATE_TYPE_ID_STRING_MAP.get(typeId).getFirst().name; - } + public static String getType(SubstateTypeId typeId) { + return SUBSTATE_TYPE_ID_STRING_MAP.get(typeId).getFirst().name; + } - public static String getName(SubstateTypeId typeId) { - return SUBSTATE_TYPE_ID_STRING_MAP.get(typeId).getSecond(); - } + public static String getName(SubstateTypeId typeId) { + return SUBSTATE_TYPE_ID_STRING_MAP.get(typeId).getSecond(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateWithdrawal.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateWithdrawal.java index 4e32a1da5b..fe8051efd8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateWithdrawal.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/SubstateWithdrawal.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,28 +68,21 @@ import com.radixdlt.atom.TxBuilder; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.utils.UInt256; - import java.util.function.Function; import java.util.function.Predicate; public final class SubstateWithdrawal { - private final SubstateIndex index; - private final Predicate predicate; + private final SubstateIndex index; + private final Predicate predicate; - public SubstateWithdrawal( - SubstateIndex index, - Predicate predicate - ) { - this.index = index; - this.predicate = predicate; - } + public SubstateWithdrawal( + SubstateIndex index, Predicate predicate) { + this.index = index; + this.predicate = predicate; + } - public UInt256 execute(TxBuilder builder, UInt256 amount, Function exceptionSupplier) throws X { - return builder.downFungible( - index, - predicate, - amount, - exceptionSupplier - ); - } + public UInt256 execute( + TxBuilder builder, UInt256 amount, Function exceptionSupplier) throws X { + return builder.downFungible(index, predicate, amount, exceptionSupplier); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/TokenResource.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/TokenResource.java index 630ad708ad..ecf02d4a85 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/TokenResource.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/TokenResource.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -65,5 +66,4 @@ import com.radixdlt.identifiers.REAddr; -public record TokenResource(String symbol, REAddr tokenAddress) implements Resource { -} +public record TokenResource(String symbol, REAddr tokenAddress) implements Resource {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/AccountVaultEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/AccountVaultEntity.java index 3aa65f22e4..ab04312a50 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/AccountVaultEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/AccountVaultEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,9 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.STAKE_OWNERSHIP; +import static com.radixdlt.atom.SubstateTypeId.TOKENS; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.Resource; @@ -81,80 +85,83 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.forks.RERulesConfig; -import org.bouncycastle.util.Arrays; - import java.nio.ByteBuffer; import java.util.List; import java.util.function.Supplier; - -import static com.radixdlt.atom.SubstateTypeId.STAKE_OWNERSHIP; -import static com.radixdlt.atom.SubstateTypeId.TOKENS; +import org.bouncycastle.util.Arrays; public record AccountVaultEntity(REAddr accountAddress) implements Entity { - @Override - public void deposit(ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) { - final Particle substate; - if (amount.resource() instanceof TokenResource tokenResource) { - var tokenAddress = tokenResource.tokenAddress(); - substate = new TokensInAccount(accountAddress, tokenAddress, amount.amount()); - } else if (amount.resource() instanceof StakeUnitResource stakeUnitResource) { - substate = new StakeOwnership(stakeUnitResource.validatorKey(), accountAddress, amount.amount()); - } else { - throw new IllegalStateException("Unknown resource type " + amount.resource()); - } - txBuilder.up(substate); - } + @Override + public void deposit( + ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) { + final Particle substate; + if (amount.resource() instanceof TokenResource tokenResource) { + var tokenAddress = tokenResource.tokenAddress(); + substate = new TokensInAccount(accountAddress, tokenAddress, amount.amount()); + } else if (amount.resource() instanceof StakeUnitResource stakeUnitResource) { + substate = + new StakeOwnership(stakeUnitResource.validatorKey(), accountAddress, amount.amount()); + } else { + throw new IllegalStateException("Unknown resource type " + amount.resource()); + } + txBuilder.up(substate); + } - @Override - public SubstateWithdrawal withdraw(Resource resource) { - if (resource instanceof TokenResource tokenResource) { - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(accountAddress.getBytes()); - SubstateIndex index = SubstateIndex.create(buf.array(), TokensInAccount.class); - return new SubstateWithdrawal( - index, - p -> p.bucket().resourceAddr().equals(tokenResource.tokenAddress()) - && p.bucket().getOwner().equals(accountAddress) - ); - } else if (resource instanceof StakeUnitResource stakeUnitResource) { - var validatorKey = stakeUnitResource.validatorKey(); - var buf = ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES)); - buf.put(SubstateTypeId.STAKE_OWNERSHIP.id()); - buf.put((byte) 0); - buf.put(validatorKey.getCompressedBytes()); - buf.put(accountAddress.getBytes()); - if (buf.hasRemaining()) { - // Sanity - throw new IllegalStateException("Buffer Size sanity check failed."); - } - SubstateIndex index = SubstateIndex.create(buf.array(), StakeOwnership.class); - return new SubstateWithdrawal( - index, - p -> p.bucket().getOwner().equals(accountAddress) && p.bucket().getValidatorKey().equals(validatorKey) - ); - } else { - throw new IllegalStateException("Unknown resource: " + resource); - } - } + @Override + public SubstateWithdrawal withdraw(Resource resource) { + if (resource instanceof TokenResource tokenResource) { + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(accountAddress.getBytes()); + SubstateIndex index = + SubstateIndex.create(buf.array(), TokensInAccount.class); + return new SubstateWithdrawal( + index, + p -> + p.bucket().resourceAddr().equals(tokenResource.tokenAddress()) + && p.bucket().getOwner().equals(accountAddress)); + } else if (resource instanceof StakeUnitResource stakeUnitResource) { + var validatorKey = stakeUnitResource.validatorKey(); + var buf = + ByteBuffer.allocate( + 2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES)); + buf.put(SubstateTypeId.STAKE_OWNERSHIP.id()); + buf.put((byte) 0); + buf.put(validatorKey.getCompressedBytes()); + buf.put(accountAddress.getBytes()); + if (buf.hasRemaining()) { + // Sanity + throw new IllegalStateException("Buffer Size sanity check failed."); + } + SubstateIndex index = + SubstateIndex.create(buf.array(), StakeOwnership.class); + return new SubstateWithdrawal( + index, + p -> + p.bucket().getOwner().equals(accountAddress) + && p.bucket().getValidatorKey().equals(validatorKey)); + } else { + throw new IllegalStateException("Unknown resource: " + resource); + } + } - @Override - public List getResourceQueries() { - var tokenIndex = SubstateIndex.create( - Arrays.concatenate(new byte[]{TOKENS.id(), 0}, accountAddress.getBytes()), - TokensInAccount.class - ); - // Unfortunately we prefixed Stakeownership in the wrong order so we'll need to do a scan - var ownershipIndex = SubstateIndex.create(STAKE_OWNERSHIP.id(), StakeOwnership.class); - return List.of( - ResourceQuery.from(tokenIndex), - ResourceQuery.from(ownershipIndex, b -> b.bucket().getOwner().equals(accountAddress)) - ); - } + @Override + public List getResourceQueries() { + var tokenIndex = + SubstateIndex.create( + Arrays.concatenate(new byte[] {TOKENS.id(), 0}, accountAddress.getBytes()), + TokensInAccount.class); + // Unfortunately we prefixed Stakeownership in the wrong order so we'll need to do a scan + var ownershipIndex = + SubstateIndex.create(STAKE_OWNERSHIP.id(), StakeOwnership.class); + return List.of( + ResourceQuery.from(tokenIndex), + ResourceQuery.from(ownershipIndex, b -> b.bucket().getOwner().equals(accountAddress))); + } - @Override - public List getKeyQueries() { - return List.of(); - } + @Override + public List getKeyQueries() { + return List.of(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportDataObjectException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportDataObjectException.java index e2d8e65d47..83a6a95166 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportDataObjectException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportDataObjectException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,21 +69,21 @@ import com.radixdlt.atom.TxBuilderException; public class EntityDoesNotSupportDataObjectException extends TxBuilderException { - private final Entity entity; - private final ParsedDataObject dataObject; + private final Entity entity; + private final ParsedDataObject dataObject; - public EntityDoesNotSupportDataObjectException(Entity entity, ParsedDataObject dataObject) { - super(entity + " does not support data operation: " + dataObject); + public EntityDoesNotSupportDataObjectException(Entity entity, ParsedDataObject dataObject) { + super(entity + " does not support data operation: " + dataObject); - this.entity = entity; - this.dataObject = dataObject; - } + this.entity = entity; + this.dataObject = dataObject; + } - public Entity getEntity() { - return entity; - } + public Entity getEntity() { + return entity; + } - public ParsedDataObject getDataObject() { - return dataObject; - } + public ParsedDataObject getDataObject() { + return dataObject; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceDepositException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceDepositException.java index 5eec3b597f..1232ad8b25 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceDepositException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceDepositException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,21 +69,21 @@ import com.radixdlt.atom.TxBuilderException; public final class EntityDoesNotSupportResourceDepositException extends TxBuilderException { - private final Entity entity; - private final Resource resource; + private final Entity entity; + private final Resource resource; - public EntityDoesNotSupportResourceDepositException(Entity entity, Resource resource) { - super(entity + " does not support resource deposit: " + resource); + public EntityDoesNotSupportResourceDepositException(Entity entity, Resource resource) { + super(entity + " does not support resource deposit: " + resource); - this.entity = entity; - this.resource = resource; - } + this.entity = entity; + this.resource = resource; + } - public Entity getEntity() { - return entity; - } + public Entity getEntity() { + return entity; + } - public Resource getResource() { - return resource; - } + public Resource getResource() { + return resource; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceWithdrawException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceWithdrawException.java index 1863a407d1..dad1b34706 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceWithdrawException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/EntityDoesNotSupportResourceWithdrawException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,21 +69,21 @@ import com.radixdlt.atom.TxBuilderException; public final class EntityDoesNotSupportResourceWithdrawException extends TxBuilderException { - private final Entity entity; - private final Resource resource; + private final Entity entity; + private final Resource resource; - public EntityDoesNotSupportResourceWithdrawException(Entity entity, Resource resource) { - super(entity + " does not support resource withdraw: " + resource); + public EntityDoesNotSupportResourceWithdrawException(Entity entity, Resource resource) { + super(entity + " does not support resource withdraw: " + resource); - this.entity = entity; - this.resource = resource; - } + this.entity = entity; + this.resource = resource; + } - public Entity getEntity() { - return entity; - } + public Entity getEntity() { + return entity; + } - public Resource getResource() { - return resource; - } + public Resource getResource() { + return resource; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ExitingStakeVaultEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ExitingStakeVaultEntity.java index 3173010149..aa129ac559 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ExitingStakeVaultEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ExitingStakeVaultEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.EXITING_STAKE; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.ResourceQuery; @@ -71,27 +74,26 @@ import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.nio.ByteBuffer; import java.util.List; -import static com.radixdlt.atom.SubstateTypeId.EXITING_STAKE; - -public record ExitingStakeVaultEntity(REAddr accountAddress, ECPublicKey validatorKey, long epochUnlock) implements Entity { - @Override - public List getResourceQueries() { - var buf = ByteBuffer.allocate(2 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES + REAddr.PUB_KEY_BYTES); - buf.put(EXITING_STAKE.id()); - buf.put((byte) 0); // Reserved byte - buf.putLong(epochUnlock); - buf.put(validatorKey.getCompressedBytes()); - buf.put(accountAddress.getBytes()); - var index = SubstateIndex.create(buf.array(), ExitingStake.class); - return List.of(ResourceQuery.from(index)); - } +public record ExitingStakeVaultEntity( + REAddr accountAddress, ECPublicKey validatorKey, long epochUnlock) implements Entity { + @Override + public List getResourceQueries() { + var buf = + ByteBuffer.allocate(2 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES + REAddr.PUB_KEY_BYTES); + buf.put(EXITING_STAKE.id()); + buf.put((byte) 0); // Reserved byte + buf.putLong(epochUnlock); + buf.put(validatorKey.getCompressedBytes()); + buf.put(accountAddress.getBytes()); + var index = SubstateIndex.create(buf.array(), ExitingStake.class); + return List.of(ResourceQuery.from(index)); + } - @Override - public List getKeyQueries() { - return List.of(); - } + @Override + public List getKeyQueries() { + return List.of(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/InvalidDataObjectException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/InvalidDataObjectException.java index dad9173ab9..cb9778c370 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/InvalidDataObjectException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/InvalidDataObjectException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,15 +68,15 @@ import com.radixdlt.atom.TxBuilderException; public final class InvalidDataObjectException extends TxBuilderException { - private final ParsedDataObject dataObject; + private final ParsedDataObject dataObject; - public InvalidDataObjectException(ParsedDataObject dataObject, String message) { - super(message); + public InvalidDataObjectException(ParsedDataObject dataObject, String message) { + super(message); - this.dataObject = dataObject; - } + this.dataObject = dataObject; + } - public ParsedDataObject getDataObject() { - return dataObject; - } + public ParsedDataObject getDataObject() { + return dataObject; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedStakeVaultEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedStakeVaultEntity.java index bd2f94c28f..ee40f26858 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedStakeVaultEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedStakeVaultEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.PREPARED_STAKE; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.ResourceQuery; @@ -81,59 +84,58 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.utils.UInt256; - import java.nio.ByteBuffer; import java.util.List; import java.util.function.Supplier; -import static com.radixdlt.atom.SubstateTypeId.PREPARED_STAKE; - -public record PreparedStakeVaultEntity(REAddr accountAddress, ECPublicKey validatorKey) implements Entity { - private boolean amountIsNative(ResourceUnsignedAmount amount) { - if (!(amount.resource() instanceof TokenResource tokenResource)) { - return false; - } - return tokenResource.tokenAddress().isNativeToken(); - } +public record PreparedStakeVaultEntity(REAddr accountAddress, ECPublicKey validatorKey) + implements Entity { + private boolean amountIsNative(ResourceUnsignedAmount amount) { + if (!(amount.resource() instanceof TokenResource tokenResource)) { + return false; + } + return tokenResource.tokenAddress().isNativeToken(); + } - @Override - public void deposit(ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) - throws TxBuilderException { - if (!amountIsNative(amount)) { - throw new EntityDoesNotSupportResourceDepositException(this, amount.resource()); - } + @Override + public void deposit( + ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) + throws TxBuilderException { + if (!amountIsNative(amount)) { + throw new EntityDoesNotSupportResourceDepositException(this, amount.resource()); + } - var minStake = config.get().getMinimumStake().toSubunits(); - var attempt = UInt256.from(amount.amount().toByteArray()); - if (attempt.compareTo(minStake) < 0) { - throw new MinimumStakeException(minStake, attempt); - } + var minStake = config.get().getMinimumStake().toSubunits(); + var attempt = UInt256.from(amount.amount().toByteArray()); + if (attempt.compareTo(minStake) < 0) { + throw new MinimumStakeException(minStake, attempt); + } - var flag = txBuilder.read(AllowDelegationFlag.class, validatorKey); - if (!flag.allowsDelegation()) { - var validator = txBuilder.read(ValidatorOwnerCopy.class, validatorKey); - var owner = validator.getOwner(); - if (!accountAddress.equals(owner)) { - throw new DelegateStakePermissionException(owner, accountAddress); - } - } - var substate = new PreparedStake(attempt, accountAddress, validatorKey); - txBuilder.up(substate); - } + var flag = txBuilder.read(AllowDelegationFlag.class, validatorKey); + if (!flag.allowsDelegation()) { + var validator = txBuilder.read(ValidatorOwnerCopy.class, validatorKey); + var owner = validator.getOwner(); + if (!accountAddress.equals(owner)) { + throw new DelegateStakePermissionException(owner, accountAddress); + } + } + var substate = new PreparedStake(attempt, accountAddress, validatorKey); + txBuilder.up(substate); + } - @Override - public List getResourceQueries() { - var buf = ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + REAddr.PUB_KEY_BYTES); - buf.put(PREPARED_STAKE.id()); - buf.put((byte) 0); // Reserved byte - buf.put(validatorKey.getCompressedBytes()); - buf.put(accountAddress.getBytes()); - var index = SubstateIndex.create(buf.array(), PreparedStake.class); - return List.of(ResourceQuery.from(index)); - } + @Override + public List getResourceQueries() { + var buf = ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + REAddr.PUB_KEY_BYTES); + buf.put(PREPARED_STAKE.id()); + buf.put((byte) 0); // Reserved byte + buf.put(validatorKey.getCompressedBytes()); + buf.put(accountAddress.getBytes()); + var index = SubstateIndex.create(buf.array(), PreparedStake.class); + return List.of(ResourceQuery.from(index)); + } - @Override - public List getKeyQueries() { - return List.of(); - } + @Override + public List getKeyQueries() { + return List.of(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedUnstakeVaultEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedUnstakeVaultEntity.java index f3b9edaee0..45da292acd 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedUnstakeVaultEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/PreparedUnstakeVaultEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.PREPARED_UNSTAKE; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.ResourceQuery; @@ -75,40 +78,39 @@ import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.forks.RERulesConfig; - import java.util.List; import java.util.function.Supplier; -import static com.radixdlt.atom.SubstateTypeId.PREPARED_UNSTAKE; - public record PreparedUnstakeVaultEntity(REAddr accountAddress) implements Entity { - @Override - public void deposit(ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) - throws TxBuilderException { - if (!(amount.resource() instanceof StakeUnitResource stakeUnitResource)) { - throw new EntityDoesNotSupportResourceDepositException(this, amount.resource()); - } - var stakeOwnershipKey = stakeUnitResource.validatorKey(); - var substate = new PreparedUnstakeOwnership(stakeOwnershipKey, accountAddress, amount.amount()); - txBuilder.up(substate); - } + @Override + public void deposit( + ResourceUnsignedAmount amount, TxBuilder txBuilder, Supplier config) + throws TxBuilderException { + if (!(amount.resource() instanceof StakeUnitResource stakeUnitResource)) { + throw new EntityDoesNotSupportResourceDepositException(this, amount.resource()); + } + var stakeOwnershipKey = stakeUnitResource.validatorKey(); + var substate = new PreparedUnstakeOwnership(stakeOwnershipKey, accountAddress, amount.amount()); + txBuilder.up(substate); + } - @Override - public List getResourceQueries() { - var index = SubstateIndex.create( - PREPARED_UNSTAKE.id(), - PreparedUnstakeOwnership.class - ); - var query = ResourceQuery.from( - index, - r -> r.bucket().resourceAddr() == null && r.bucket().getEpochUnlock().equals(0L) - && r.bucket().getOwner().equals(accountAddress) - ); - return List.of(query); - } + @Override + public List getResourceQueries() { + var index = + SubstateIndex.create( + PREPARED_UNSTAKE.id(), PreparedUnstakeOwnership.class); + var query = + ResourceQuery.from( + index, + r -> + r.bucket().resourceAddr() == null + && r.bucket().getEpochUnlock().equals(0L) + && r.bucket().getOwner().equals(accountAddress)); + return List.of(query); + } - @Override - public List getKeyQueries() { - return List.of(); - } + @Override + public List getKeyQueries() { + return List.of(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/SystemEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/SystemEntity.java index 951aef8e21..145c581341 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/SystemEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/SystemEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,37 +64,35 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.*; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.ResourceQuery; - import java.util.List; -import static com.radixdlt.atom.SubstateTypeId.*; - public record SystemEntity() implements Entity { - @Override - public List getResourceQueries() { - return List.of(); - } + @Override + public List getResourceQueries() { + return List.of(); + } - @Override - public List getKeyQueries() { - return List.of( - KeyQuery.fromSystem(UNCLAIMED_READDR), - KeyQuery.fromSystem(EPOCH_DATA), - KeyQuery.fromSystem(ROUND_DATA), - KeyQuery.fromSystem(VALIDATOR_META_DATA), - KeyQuery.fromSystem(VALIDATOR_STAKE_DATA), - KeyQuery.fromSystem(VALIDATOR_ALLOW_DELEGATION_FLAG), - KeyQuery.fromSystem(VALIDATOR_REGISTERED_FLAG_COPY), - KeyQuery.fromSystem(VALIDATOR_RAKE_COPY), - KeyQuery.fromSystem(VALIDATOR_OWNER_COPY), - KeyQuery.fromSystem(VALIDATOR_SYSTEM_META_DATA) - ); - } + @Override + public List getKeyQueries() { + return List.of( + KeyQuery.fromSystem(UNCLAIMED_READDR), + KeyQuery.fromSystem(EPOCH_DATA), + KeyQuery.fromSystem(ROUND_DATA), + KeyQuery.fromSystem(VALIDATOR_META_DATA), + KeyQuery.fromSystem(VALIDATOR_STAKE_DATA), + KeyQuery.fromSystem(VALIDATOR_ALLOW_DELEGATION_FLAG), + KeyQuery.fromSystem(VALIDATOR_REGISTERED_FLAG_COPY), + KeyQuery.fromSystem(VALIDATOR_RAKE_COPY), + KeyQuery.fromSystem(VALIDATOR_OWNER_COPY), + KeyQuery.fromSystem(VALIDATOR_SYSTEM_META_DATA)); + } - public static SystemEntity instance() { - return new SystemEntity(); - } + public static SystemEntity instance() { + return new SystemEntity(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/TokenEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/TokenEntity.java index eb4148e94a..655f86dbb6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/TokenEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/TokenEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -80,69 +81,72 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.utils.UInt256; - import java.nio.charset.StandardCharsets; import java.util.List; import java.util.function.Supplier; public record TokenEntity(String symbol, REAddr tokenAddr) implements Entity { - @Override - public void overwriteDataObject( - ParsedDataObject parsedDataObject, - TxBuilder builder, - Supplier config - ) throws TxBuilderException { - var dataObject = parsedDataObject.getDataObject(); - if (dataObject instanceof TokenData tokenData) { - boolean isMutable = tokenData.getIsMutable(); - var ownerKey = parsedDataObject.getParsed(ECPublicKey.class); - if (!isMutable && ownerKey != null) { - throw new InvalidDataObjectException(parsedDataObject, "Cannot have owner on fixed supply token."); - } + @Override + public void overwriteDataObject( + ParsedDataObject parsedDataObject, TxBuilder builder, Supplier config) + throws TxBuilderException { + var dataObject = parsedDataObject.getDataObject(); + if (dataObject instanceof TokenData tokenData) { + boolean isMutable = tokenData.getIsMutable(); + var ownerKey = parsedDataObject.getParsed(ECPublicKey.class); + if (!isMutable && ownerKey != null) { + throw new InvalidDataObjectException( + parsedDataObject, "Cannot have owner on fixed supply token."); + } - if (isMutable && ownerKey == null) { - throw new InvalidDataObjectException(parsedDataObject, "Must have an owner on mutable supply token."); - } + if (isMutable && ownerKey == null) { + throw new InvalidDataObjectException( + parsedDataObject, "Must have an owner on mutable supply token."); + } - if (!tokenData.getGranularity().equals("1")) { - throw new InvalidDataObjectException(parsedDataObject, "granularity must be 1"); - } + if (!tokenData.getGranularity().equals("1")) { + throw new InvalidDataObjectException(parsedDataObject, "granularity must be 1"); + } - builder.toLowLevelBuilder().syscall(Syscall.READDR_CLAIM, symbol.getBytes(StandardCharsets.UTF_8)); - builder.downREAddr(tokenAddr); - var tokenResource = new TokenResource(tokenAddr, UInt256.ONE, isMutable, ownerKey); - builder.up(tokenResource); - } else if (dataObject instanceof TokenMetadata tokenMetadata) { - if (!tokenMetadata.getSymbol().equals(this.symbol)) { - throw new InvalidDataObjectException( - parsedDataObject, "TokenMetadata symbol (" + tokenMetadata.getSymbol() - + " does not match Entity symbol (" + this.symbol + ")" - ); - } + builder + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, symbol.getBytes(StandardCharsets.UTF_8)); + builder.downREAddr(tokenAddr); + var tokenResource = new TokenResource(tokenAddr, UInt256.ONE, isMutable, ownerKey); + builder.up(tokenResource); + } else if (dataObject instanceof TokenMetadata tokenMetadata) { + if (!tokenMetadata.getSymbol().equals(this.symbol)) { + throw new InvalidDataObjectException( + parsedDataObject, + "TokenMetadata symbol (" + + tokenMetadata.getSymbol() + + " does not match Entity symbol (" + + this.symbol + + ")"); + } - builder.up(new TokenResourceMetadata( - tokenAddr, - symbol, - tokenMetadata.getName(), - tokenMetadata.getDescription(), - tokenMetadata.getIconUrl(), - tokenMetadata.getUrl() - )); - } else { - throw new EntityDoesNotSupportDataObjectException(this, parsedDataObject); - } - } + builder.up( + new TokenResourceMetadata( + tokenAddr, + symbol, + tokenMetadata.getName(), + tokenMetadata.getDescription(), + tokenMetadata.getIconUrl(), + tokenMetadata.getUrl())); + } else { + throw new EntityDoesNotSupportDataObjectException(this, parsedDataObject); + } + } - @Override - public List getResourceQueries() { - return List.of(); - } + @Override + public List getResourceQueries() { + return List.of(); + } - @Override - public List getKeyQueries() { - return List.of( - KeyQuery.fromToken(tokenAddr, SubstateTypeId.TOKEN_RESOURCE, UnclaimedREAddr::new), - KeyQuery.fromToken(tokenAddr, SubstateTypeId.TOKEN_RESOURCE_METADATA) - ); - } + @Override + public List getKeyQueries() { + return List.of( + KeyQuery.fromToken(tokenAddr, SubstateTypeId.TOKEN_RESOURCE, UnclaimedREAddr::new), + KeyQuery.fromToken(tokenAddr, SubstateTypeId.TOKEN_RESOURCE_METADATA)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorEntity.java index 199cbd086a..c2b993f9dd 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.*; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.ParsedDataObject; @@ -88,115 +91,116 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.utils.Bytes; - import java.util.List; import java.util.OptionalLong; import java.util.function.Supplier; -import static com.radixdlt.atom.SubstateTypeId.*; - public record ValidatorEntity(ECPublicKey validatorKey) implements Entity { - @Override - public void overwriteDataObject( - ParsedDataObject parsedDataObject, - TxBuilder builder, - Supplier config - ) throws TxBuilderException { - var dataObject = parsedDataObject.getDataObject(); - if (dataObject instanceof PreparedValidatorRegistered preparedValidatorRegistered) { - updateRegistered(builder, preparedValidatorRegistered); - } else if (dataObject instanceof PreparedValidatorOwner) { - var owner = parsedDataObject.getParsed(REAddr.class); - updateOwner(builder, owner); - } else if (dataObject instanceof PreparedValidatorFee preparedValidatorFee) { - updateValidatorFee(builder, preparedValidatorFee, config); - } else if (dataObject instanceof ValidatorMetadata metadata) { - updateMetadata(builder, metadata); - } else if (dataObject instanceof ValidatorAllowDelegation allowDelegation) { - updateAllowDelegation(builder, allowDelegation); - } else if (dataObject instanceof com.radixdlt.api.core.openapitools.model.ValidatorSystemMetadata metadata) { - updateValidatorSystemMetadata(builder, metadata); - } else { - throw new EntityDoesNotSupportDataObjectException(this, parsedDataObject); - } - } + @Override + public void overwriteDataObject( + ParsedDataObject parsedDataObject, TxBuilder builder, Supplier config) + throws TxBuilderException { + var dataObject = parsedDataObject.getDataObject(); + if (dataObject instanceof PreparedValidatorRegistered preparedValidatorRegistered) { + updateRegistered(builder, preparedValidatorRegistered); + } else if (dataObject instanceof PreparedValidatorOwner) { + var owner = parsedDataObject.getParsed(REAddr.class); + updateOwner(builder, owner); + } else if (dataObject instanceof PreparedValidatorFee preparedValidatorFee) { + updateValidatorFee(builder, preparedValidatorFee, config); + } else if (dataObject instanceof ValidatorMetadata metadata) { + updateMetadata(builder, metadata); + } else if (dataObject instanceof ValidatorAllowDelegation allowDelegation) { + updateAllowDelegation(builder, allowDelegation); + } else if (dataObject + instanceof com.radixdlt.api.core.openapitools.model.ValidatorSystemMetadata metadata) { + updateValidatorSystemMetadata(builder, metadata); + } else { + throw new EntityDoesNotSupportDataObjectException(this, parsedDataObject); + } + } - private void updateValidatorSystemMetadata(TxBuilder builder, com.radixdlt.api.core.openapitools.model.ValidatorSystemMetadata metadata) { - builder.down(com.radixdlt.application.validators.state.ValidatorSystemMetadata.class, validatorKey); - builder.up(new com.radixdlt.application.validators.state.ValidatorSystemMetadata( - validatorKey, - Bytes.fromHexString(metadata.getData()) - )); - } + private void updateValidatorSystemMetadata( + TxBuilder builder, + com.radixdlt.api.core.openapitools.model.ValidatorSystemMetadata metadata) { + builder.down( + com.radixdlt.application.validators.state.ValidatorSystemMetadata.class, validatorKey); + builder.up( + new com.radixdlt.application.validators.state.ValidatorSystemMetadata( + validatorKey, Bytes.fromHexString(metadata.getData()))); + } - private void updateAllowDelegation(TxBuilder builder, ValidatorAllowDelegation allowDelegation) { - builder.down(AllowDelegationFlag.class, validatorKey); - builder.up(new AllowDelegationFlag(validatorKey, allowDelegation.getAllowDelegation())); - } + private void updateAllowDelegation(TxBuilder builder, ValidatorAllowDelegation allowDelegation) { + builder.down(AllowDelegationFlag.class, validatorKey); + builder.up(new AllowDelegationFlag(validatorKey, allowDelegation.getAllowDelegation())); + } - private void updateMetadata(TxBuilder builder, ValidatorMetadata metadata) { - var substateDown = builder.down(ValidatorMetaData.class, validatorKey); - builder.up(new ValidatorMetaData( - validatorKey, - metadata.getName() == null ? substateDown.getName() : metadata.getName(), - metadata.getUrl() == null ? substateDown.getUrl() : metadata.getUrl() - )); - } + private void updateMetadata(TxBuilder builder, ValidatorMetadata metadata) { + var substateDown = builder.down(ValidatorMetaData.class, validatorKey); + builder.up( + new ValidatorMetaData( + validatorKey, + metadata.getName() == null ? substateDown.getName() : metadata.getName(), + metadata.getUrl() == null ? substateDown.getUrl() : metadata.getUrl())); + } - private void updateRegistered(TxBuilder builder, PreparedValidatorRegistered preparedValidatorRegistered) { - builder.down(ValidatorRegisteredCopy.class, validatorKey); - var curEpoch = builder.readSystem(EpochData.class); - builder.up(new ValidatorRegisteredCopy( - OptionalLong.of(curEpoch.getEpoch() + 1), - validatorKey, - preparedValidatorRegistered.getRegistered() - )); - } + private void updateRegistered( + TxBuilder builder, PreparedValidatorRegistered preparedValidatorRegistered) { + builder.down(ValidatorRegisteredCopy.class, validatorKey); + var curEpoch = builder.readSystem(EpochData.class); + builder.up( + new ValidatorRegisteredCopy( + OptionalLong.of(curEpoch.getEpoch() + 1), + validatorKey, + preparedValidatorRegistered.getRegistered())); + } - private void updateOwner(TxBuilder builder, REAddr owner) { - builder.down(ValidatorOwnerCopy.class, validatorKey); - var curEpoch = builder.readSystem(EpochData.class); - builder.up(new ValidatorOwnerCopy(OptionalLong.of(curEpoch.getEpoch() + 1), validatorKey, owner)); - } + private void updateOwner(TxBuilder builder, REAddr owner) { + builder.down(ValidatorOwnerCopy.class, validatorKey); + var curEpoch = builder.readSystem(EpochData.class); + builder.up( + new ValidatorOwnerCopy(OptionalLong.of(curEpoch.getEpoch() + 1), validatorKey, owner)); + } - private void updateValidatorFee( - TxBuilder builder, - PreparedValidatorFee preparedValidatorFee, - Supplier config - ) throws InvalidRakeIncreaseException { - builder.down(ValidatorFeeCopy.class, validatorKey); - var curRakePercentage = builder.read(ValidatorStakeData.class, validatorKey) - .getRakePercentage(); - int validatorFee = preparedValidatorFee.getFee(); - var isIncrease = validatorFee > curRakePercentage; - var rakeIncrease = validatorFee - curRakePercentage; - var maxRakeIncrease = ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE; - if (isIncrease && rakeIncrease >= maxRakeIncrease) { - throw new InvalidRakeIncreaseException(maxRakeIncrease, rakeIncrease); - } + private void updateValidatorFee( + TxBuilder builder, PreparedValidatorFee preparedValidatorFee, Supplier config) + throws InvalidRakeIncreaseException { + builder.down(ValidatorFeeCopy.class, validatorKey); + var curRakePercentage = + builder.read(ValidatorStakeData.class, validatorKey).getRakePercentage(); + int validatorFee = preparedValidatorFee.getFee(); + var isIncrease = validatorFee > curRakePercentage; + var rakeIncrease = validatorFee - curRakePercentage; + var maxRakeIncrease = ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE; + if (isIncrease && rakeIncrease >= maxRakeIncrease) { + throw new InvalidRakeIncreaseException(maxRakeIncrease, rakeIncrease); + } - var rakeIncreaseDebounceEpochLength = config.get().getRakeIncreaseDebouncerEpochLength(); - var epochDiff = isIncrease ? (1 + rakeIncreaseDebounceEpochLength) : 1; - var curEpoch = builder.readSystem(EpochData.class); - var epoch = curEpoch.getEpoch() + epochDiff; - builder.up(new ValidatorFeeCopy(OptionalLong.of(epoch), validatorKey, validatorFee)); - } + var rakeIncreaseDebounceEpochLength = config.get().getRakeIncreaseDebouncerEpochLength(); + var epochDiff = isIncrease ? (1 + rakeIncreaseDebounceEpochLength) : 1; + var curEpoch = builder.readSystem(EpochData.class); + var epoch = curEpoch.getEpoch() + epochDiff; + builder.up(new ValidatorFeeCopy(OptionalLong.of(epoch), validatorKey, validatorFee)); + } - @Override - public List getResourceQueries() { - return List.of(); - } + @Override + public List getResourceQueries() { + return List.of(); + } - @Override - public List getKeyQueries() { - return List.of( - KeyQuery.fromValidator(validatorKey, VALIDATOR_META_DATA, ValidatorMetaData::createVirtual), - KeyQuery.fromValidator(validatorKey, VALIDATOR_ALLOW_DELEGATION_FLAG, AllowDelegationFlag::createVirtual), - KeyQuery.fromValidator(validatorKey, VALIDATOR_REGISTERED_FLAG_COPY, ValidatorRegisteredCopy::createVirtual), - KeyQuery.fromValidator(validatorKey, VALIDATOR_RAKE_COPY, ValidatorFeeCopy::createVirtual), - KeyQuery.fromValidator(validatorKey, VALIDATOR_OWNER_COPY, ValidatorOwnerCopy::createVirtual), - KeyQuery.fromValidator(validatorKey, VALIDATOR_SYSTEM_META_DATA, ValidatorSystemMetadata::createVirtual) - ); - } + @Override + public List getKeyQueries() { + return List.of( + KeyQuery.fromValidator(validatorKey, VALIDATOR_META_DATA, ValidatorMetaData::createVirtual), + KeyQuery.fromValidator( + validatorKey, VALIDATOR_ALLOW_DELEGATION_FLAG, AllowDelegationFlag::createVirtual), + KeyQuery.fromValidator( + validatorKey, VALIDATOR_REGISTERED_FLAG_COPY, ValidatorRegisteredCopy::createVirtual), + KeyQuery.fromValidator(validatorKey, VALIDATOR_RAKE_COPY, ValidatorFeeCopy::createVirtual), + KeyQuery.fromValidator( + validatorKey, VALIDATOR_OWNER_COPY, ValidatorOwnerCopy::createVirtual), + KeyQuery.fromValidator( + validatorKey, VALIDATOR_SYSTEM_META_DATA, ValidatorSystemMetadata::createVirtual)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorSystemEntity.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorSystemEntity.java index 6fff8b8e2e..e318aa0c8f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorSystemEntity.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/model/entities/ValidatorSystemEntity.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,29 +64,29 @@ package com.radixdlt.api.core.model.entities; +import static com.radixdlt.atom.SubstateTypeId.*; + import com.radixdlt.api.core.model.Entity; import com.radixdlt.api.core.model.KeyQuery; import com.radixdlt.api.core.model.ResourceQuery; import com.radixdlt.application.system.state.ValidatorStakeData; import com.radixdlt.constraintmachine.SystemMapKey; import com.radixdlt.crypto.ECPublicKey; - import java.util.List; -import static com.radixdlt.atom.SubstateTypeId.*; - public record ValidatorSystemEntity(ECPublicKey validatorKey) implements Entity { - @Override - public List getResourceQueries() { - var systemMapKey = SystemMapKey.ofSystem(VALIDATOR_STAKE_DATA.id(), validatorKey.getCompressedBytes()); - return List.of(ResourceQuery.from(systemMapKey)); - } + @Override + public List getResourceQueries() { + var systemMapKey = + SystemMapKey.ofSystem(VALIDATOR_STAKE_DATA.id(), validatorKey.getCompressedBytes()); + return List.of(ResourceQuery.from(systemMapKey)); + } - @Override - public List getKeyQueries() { - return List.of( - KeyQuery.fromValidator(validatorKey, VALIDATOR_STAKE_DATA, ValidatorStakeData::createVirtual), - KeyQuery.fromValidator(validatorKey, VALIDATOR_BFT_DATA) - ); - } + @Override + public List getKeyQueries() { + return List.of( + KeyQuery.fromValidator( + validatorKey, VALIDATOR_STAKE_DATA, ValidatorStakeData::createVirtual), + KeyQuery.fromValidator(validatorKey, VALIDATOR_BFT_DATA)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/BerkeleyRecoverableProcessedTxnStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/BerkeleyRecoverableProcessedTxnStore.java index b75c22c5ad..79505057ce 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/BerkeleyRecoverableProcessedTxnStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/BerkeleyRecoverableProcessedTxnStore.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,9 @@ package com.radixdlt.api.core.reconstruction; +import static com.google.common.primitives.UnsignedBytes.lexicographicalComparator; +import static com.sleepycat.je.OperationStatus.SUCCESS; + import com.google.common.collect.Streams; import com.google.common.hash.HashCode; import com.google.inject.Inject; @@ -88,7 +92,6 @@ import com.sleepycat.je.Get; import com.sleepycat.je.OperationStatus; import com.sleepycat.je.Transaction; - import java.io.IOException; import java.time.Instant; import java.util.Iterator; @@ -98,161 +101,180 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; -import static com.google.common.primitives.UnsignedBytes.lexicographicalComparator; -import static com.sleepycat.je.OperationStatus.SUCCESS; - /** * Stores recovery information per transaction. This allows the Transaction API to return * transaction info with full state updates. */ public final class BerkeleyRecoverableProcessedTxnStore implements BerkeleyAdditionalStore { - private static final String RECOVERABLE_TRANSACTIONS_DB_NAME = "radix.recoverable_txns"; - private static final String ACCUMULATOR_HASH_DB_NAME = "radix.accumulator_hash"; - private Database recoverableTransactionsDatabase; // Txns by index; Append-only - private Database accumulatorDatabase; // Txns by index; Append-only + private static final String RECOVERABLE_TRANSACTIONS_DB_NAME = "radix.recoverable_txns"; + private static final String ACCUMULATOR_HASH_DB_NAME = "radix.accumulator_hash"; + private Database recoverableTransactionsDatabase; // Txns by index; Append-only + private Database accumulatorDatabase; // Txns by index; Append-only - private final AtomicReference timestamp = new AtomicReference<>(); - private final Provider> radixEngineProvider; - private final LedgerAccumulator ledgerAccumulator; - private final Serialization serialization; - private AccumulatorState accumulatorState; + private final AtomicReference timestamp = new AtomicReference<>(); + private final Provider> radixEngineProvider; + private final LedgerAccumulator ledgerAccumulator; + private final Serialization serialization; + private AccumulatorState accumulatorState; - @Inject - public BerkeleyRecoverableProcessedTxnStore( - Serialization serialization, - Provider> radixEngineProvider, - LedgerAccumulator ledgerAccumulator - ) { - this.serialization = serialization; - // TODO: Fix this when we move AdditionalStore to be a RadixEngine construct rather than Berkeley construct - this.radixEngineProvider = radixEngineProvider; - this.ledgerAccumulator = ledgerAccumulator; - } + @Inject + public BerkeleyRecoverableProcessedTxnStore( + Serialization serialization, + Provider> radixEngineProvider, + LedgerAccumulator ledgerAccumulator) { + this.serialization = serialization; + // TODO: Fix this when we move AdditionalStore to be a RadixEngine construct rather than + // Berkeley construct + this.radixEngineProvider = radixEngineProvider; + this.ledgerAccumulator = ledgerAccumulator; + } - @Override - public void open(DatabaseEnvironment dbEnv) { - recoverableTransactionsDatabase = dbEnv.getEnvironment().openDatabase(null, RECOVERABLE_TRANSACTIONS_DB_NAME, new DatabaseConfig() - .setAllowCreate(true) - .setTransactional(true) - .setKeyPrefixing(true) - .setBtreeComparator(lexicographicalComparator()) - ); + @Override + public void open(DatabaseEnvironment dbEnv) { + recoverableTransactionsDatabase = + dbEnv + .getEnvironment() + .openDatabase( + null, + RECOVERABLE_TRANSACTIONS_DB_NAME, + new DatabaseConfig() + .setAllowCreate(true) + .setTransactional(true) + .setKeyPrefixing(true) + .setBtreeComparator(lexicographicalComparator())); - accumulatorDatabase = dbEnv.getEnvironment().openDatabase(null, ACCUMULATOR_HASH_DB_NAME, new DatabaseConfig() - .setAllowCreate(true) - .setTransactional(true) - .setKeyPrefixing(true) - .setBtreeComparator(lexicographicalComparator()) - ); + accumulatorDatabase = + dbEnv + .getEnvironment() + .openDatabase( + null, + ACCUMULATOR_HASH_DB_NAME, + new DatabaseConfig() + .setAllowCreate(true) + .setTransactional(true) + .setKeyPrefixing(true) + .setBtreeComparator(lexicographicalComparator())); - try (var cursor = accumulatorDatabase.openCursor(null, null)) { - var key = new DatabaseEntry(Longs.toByteArray(Long.MAX_VALUE)); - var value = new DatabaseEntry(); - cursor.getSearchKeyRange(key, value, null); - var status = cursor.getPrev(key, value, null); - if (status == SUCCESS) { - var accumulatorHash = HashCode.fromBytes(value.getData()); - var stateVersion = Longs.fromByteArray(key.getData()); - this.accumulatorState = new AccumulatorState(stateVersion, accumulatorHash); - } else { - this.accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - } - } - } + try (var cursor = accumulatorDatabase.openCursor(null, null)) { + var key = new DatabaseEntry(Longs.toByteArray(Long.MAX_VALUE)); + var value = new DatabaseEntry(); + cursor.getSearchKeyRange(key, value, null); + var status = cursor.getPrev(key, value, null); + if (status == SUCCESS) { + var accumulatorHash = HashCode.fromBytes(value.getData()); + var stateVersion = Longs.fromByteArray(key.getData()); + this.accumulatorState = new AccumulatorState(stateVersion, accumulatorHash); + } else { + this.accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + } + } + } - @Override - public void close() { - if (recoverableTransactionsDatabase != null) { - recoverableTransactionsDatabase.close(); - } + @Override + public void close() { + if (recoverableTransactionsDatabase != null) { + recoverableTransactionsDatabase.close(); + } - if (accumulatorDatabase != null) { - accumulatorDatabase.close(); - } - } + if (accumulatorDatabase != null) { + accumulatorDatabase.close(); + } + } - public Optional getAccumulator(long stateVersion) { - if (stateVersion == 0) { - return Optional.of(HashUtils.zero256()); - } - var key = new DatabaseEntry(Longs.toByteArray(stateVersion)); - var value = new DatabaseEntry(); - var result = accumulatorDatabase.get(null, key, value, null); - if (result != SUCCESS) { - return Optional.empty(); - } - return Optional.of(HashCode.fromBytes(value.getData())); - } + public Optional getAccumulator(long stateVersion) { + if (stateVersion == 0) { + return Optional.of(HashUtils.zero256()); + } + var key = new DatabaseEntry(Longs.toByteArray(stateVersion)); + var value = new DatabaseEntry(); + var result = accumulatorDatabase.get(null, key, value, null); + if (result != SUCCESS) { + return Optional.empty(); + } + return Optional.of(HashCode.fromBytes(value.getData())); + } - public List get(long index, long limit) { - try (var cursor = recoverableTransactionsDatabase.openCursor(null, null)) { - var iterator = new Iterator() { - final DatabaseEntry key = new DatabaseEntry(Longs.toByteArray(index)); - final DatabaseEntry value = new DatabaseEntry(); - OperationStatus status = cursor.get(key, value, Get.SEARCH, null) != null ? SUCCESS : OperationStatus.NOTFOUND; + public List get(long index, long limit) { + try (var cursor = recoverableTransactionsDatabase.openCursor(null, null)) { + var iterator = + new Iterator() { + final DatabaseEntry key = new DatabaseEntry(Longs.toByteArray(index)); + final DatabaseEntry value = new DatabaseEntry(); + OperationStatus status = + cursor.get(key, value, Get.SEARCH, null) != null + ? SUCCESS + : OperationStatus.NOTFOUND; - @Override - public boolean hasNext() { - return status == SUCCESS; - } + @Override + public boolean hasNext() { + return status == SUCCESS; + } - @Override - public RecoverableProcessedTxn next() { - if (status != SUCCESS) { - throw new NoSuchElementException(); - } - RecoverableProcessedTxn next; - try { - next = serialization.fromDson(Compress.uncompress(value.getData()), RecoverableProcessedTxn.class); - } catch (IOException e) { - throw new IllegalStateException("Failed to deserialize committed transaction.", e); - } + @Override + public RecoverableProcessedTxn next() { + if (status != SUCCESS) { + throw new NoSuchElementException(); + } + RecoverableProcessedTxn next; + try { + next = + serialization.fromDson( + Compress.uncompress(value.getData()), RecoverableProcessedTxn.class); + } catch (IOException e) { + throw new IllegalStateException("Failed to deserialize committed transaction.", e); + } - status = cursor.getNext(key, value, null); - return next; - } - }; - return Streams.stream(iterator).limit(limit).toList(); - } - } + status = cursor.getNext(key, value, null); + return next; + } + }; + return Streams.stream(iterator).limit(limit).toList(); + } + } - @Override - public void process(Transaction dbTxn, REProcessedTxn txn, long stateVersion, Function> mapper) { - if (accumulatorState.getStateVersion() != stateVersion - 1) { - throw new IllegalStateException("Accumulator out of sync."); - } + @Override + public void process( + Transaction dbTxn, + REProcessedTxn txn, + long stateVersion, + Function> mapper) { + if (accumulatorState.getStateVersion() != stateVersion - 1) { + throw new IllegalStateException("Accumulator out of sync."); + } - txn.stateUpdates() - .filter(u -> u.getParsed() instanceof RoundData) - .map(u -> (RoundData) u.getParsed()) - .filter(r -> r.getTimestamp() > 0) - .map(RoundData::asInstant) - .forEach(timestamp::set); + txn.stateUpdates() + .filter(u -> u.getParsed() instanceof RoundData) + .map(u -> (RoundData) u.getParsed()) + .filter(r -> r.getTimestamp() > 0) + .map(RoundData::asInstant) + .forEach(timestamp::set); - var substateSerialization = radixEngineProvider.get().getSubstateSerialization(); - var stored = RecoverableProcessedTxn.from(txn, substateSerialization); - byte[] data; - try { - data = Compress.compress(serialization.toDson(stored, DsonOutput.Output.ALL)); - } catch (IOException e) { - throw new IllegalStateException(e); - } + var substateSerialization = radixEngineProvider.get().getSubstateSerialization(); + var stored = RecoverableProcessedTxn.from(txn, substateSerialization); + byte[] data; + try { + data = Compress.compress(serialization.toDson(stored, DsonOutput.Output.ALL)); + } catch (IOException e) { + throw new IllegalStateException(e); + } - var nextAccumulatorState = ledgerAccumulator.accumulate(accumulatorState, txn.getTxnId().asHashCode()); - this.accumulatorState = nextAccumulatorState; + var nextAccumulatorState = + ledgerAccumulator.accumulate(accumulatorState, txn.getTxnId().asHashCode()); + this.accumulatorState = nextAccumulatorState; - var key = new DatabaseEntry(Longs.toByteArray(stateVersion - 1)); - var value = new DatabaseEntry(data); - var result = recoverableTransactionsDatabase.putNoOverwrite(dbTxn, key, value); - if (result != SUCCESS) { - throw new IllegalStateException("Unexpected operation status " + result); - } + var key = new DatabaseEntry(Longs.toByteArray(stateVersion - 1)); + var value = new DatabaseEntry(data); + var result = recoverableTransactionsDatabase.putNoOverwrite(dbTxn, key, value); + if (result != SUCCESS) { + throw new IllegalStateException("Unexpected operation status " + result); + } - var versionEntry = new DatabaseEntry(Longs.toByteArray(nextAccumulatorState.getStateVersion())); - var accumulatorHashEntry = new DatabaseEntry(nextAccumulatorState.getAccumulatorHash().asBytes()); - result = accumulatorDatabase.put(dbTxn, versionEntry, accumulatorHashEntry); - if (result != SUCCESS) { - throw new IllegalStateException("Unexpected operation status " + result); - } - } + var versionEntry = new DatabaseEntry(Longs.toByteArray(nextAccumulatorState.getStateVersion())); + var accumulatorHashEntry = + new DatabaseEntry(nextAccumulatorState.getAccumulatorHash().asBytes()); + result = accumulatorDatabase.put(dbTxn, versionEntry, accumulatorHashEntry); + if (result != SUCCESS) { + throw new IllegalStateException("Unexpected operation status " + result); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableProcessedTxn.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableProcessedTxn.java index 66a9a25984..a679308003 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableProcessedTxn.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableProcessedTxn.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -77,8 +78,6 @@ import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; import com.radixdlt.utils.Pair; -import org.bouncycastle.util.Arrays; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -86,131 +85,150 @@ import java.util.function.IntFunction; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.bouncycastle.util.Arrays; @SerializerId2("xtx") public class RecoverableProcessedTxn { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {DsonOutput.Output.API, DsonOutput.Output.WIRE, DsonOutput.Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - // TODO: Change this to be a map - @JsonProperty("s") - @DsonOutput(DsonOutput.Output.ALL) - private final Map> shutdownSubstates; - - @JsonCreator - public RecoverableProcessedTxn( - @JsonProperty("s") Map> shutdownSubstates - ) { - this.shutdownSubstates = shutdownSubstates == null ? Map.of() : shutdownSubstates; - } - - public static RecoverableProcessedTxn from(REProcessedTxn txn, SubstateSerialization serialization) { - var parsedTxn = txn.getParsedTxn(); - - var stateUpdateGroups = txn.getGroupedStateUpdates() - .stream() - .flatMap(stateUpdates -> stateUpdates.stream() - .filter(u -> { - var microOp = parsedTxn.instructions().get(u.getInstructionIndex()).getMicroOp(); - return switch (microOp) { - case DOWN, DOWNINDEX, VDOWN, LVDOWN -> true; - default -> false; - }; - }) - .map(u -> { - var microOp = parsedTxn.instructions().get(u.getInstructionIndex()).getMicroOp(); - var data = switch (microOp) { - case DOWN -> serialization.serialize((Particle) u.getParsed()); - case DOWNINDEX -> Arrays.concatenate(u.getId().asBytes(), serialization.serialize((Particle) u.getParsed())); - case VDOWN, LVDOWN -> new byte[] {u.typeByte()}; - default -> throw new IllegalStateException(); - }; - return Pair.of(u.getInstructionIndex(), data); - }) - ) - .collect(Collectors.groupingBy(Pair::getFirst, Collectors.mapping(Pair::getSecond, Collectors.toList()))); - return new RecoverableProcessedTxn(stateUpdateGroups); - } - - private RecoverableSubstate recoverUp(UpSubstate upSubstate) { - ByteBuffer substate = upSubstate.getSubstateBuffer(); - SubstateId substateId = upSubstate.getSubstateId(); - return new RecoverableSubstateShutdown(substate, substateId, true); - } - - private RecoverableSubstate recoverDown(REInstruction instruction, int index) { - var dataList = shutdownSubstates.get(index); - if (dataList.size() != 1) { - throw new IllegalStateException("Multiple substates found for down instruction"); - } - var substate = ByteBuffer.wrap(dataList.get(0)); - SubstateId substateId = instruction.getData(); - return new RecoverableSubstateShutdown(substate, substateId, false); - } - - private RecoverableSubstate recoverLocalDown(REInstruction instruction, IntFunction localUpSubstates) { - SubstateId substateId = instruction.getData(); - var index = substateId.getIndex().orElseThrow(() -> new IllegalStateException("Could not find index")); - var substate = localUpSubstates.apply(index).getSubstateBuffer(); - return new RecoverableSubstateShutdown(substate, substateId, false); - } - - private RecoverableSubstate recoverVirtualDown(REInstruction instruction, int index) { - var dataList = shutdownSubstates.get(index); - if (dataList.size() != 1) { - throw new IllegalStateException("Multiple substates found for virtual down instruction"); - } - SubstateId substateId = instruction.getData(); - return new RecoverableSubstateVirtualShutdown(dataList.get(0)[0], substateId); - } - - private Stream recoverDownIndex(int index) { - var substates = shutdownSubstates.get(index); - if (substates == null) { - return Stream.of(); - } - return substates.stream() - .map(data -> { - var buf = ByteBuffer.wrap(data); - var substateId = SubstateId.fromBuffer(buf); - var substate = ByteBuffer.wrap(data, SubstateId.BYTES, data.length - SubstateId.BYTES); - return new RecoverableSubstateShutdown(substate, substateId, false); - }); - } - - - public List> recoverStateUpdates(ParsedTxn parsedTxn) { - var substateGroups = new ArrayList>(); - var substateUpdates = new ArrayList(); - var upSubstates = new ArrayList(); - - for (int i = 0; i < parsedTxn.instructions().size(); i++) { - var instruction = parsedTxn.instructions().get(i); - if (!instruction.isStateUpdate()) { - if (instruction.getMicroOp() == REInstruction.REMicroOp.END) { - substateGroups.add(substateUpdates); - substateUpdates = new ArrayList<>(); - } - continue; - } - - switch (instruction.getMicroOp()) { - case UP -> { - UpSubstate upSubstate = instruction.getData(); - substateUpdates.add(recoverUp(upSubstate)); - upSubstates.add(upSubstate); - } - case DOWN -> substateUpdates.add(recoverDown(instruction, i)); - case LDOWN -> substateUpdates.add(recoverLocalDown(instruction, upSubstates::get)); - case VDOWN, LVDOWN -> substateUpdates.add(recoverVirtualDown(instruction, i)); - case DOWNINDEX -> recoverDownIndex(i).forEach(substateUpdates::add); - default -> { - // ignored - } - } - } - - return substateGroups; - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {DsonOutput.Output.API, DsonOutput.Output.WIRE, DsonOutput.Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + // TODO: Change this to be a map + @JsonProperty("s") + @DsonOutput(DsonOutput.Output.ALL) + private final Map> shutdownSubstates; + + @JsonCreator + public RecoverableProcessedTxn(@JsonProperty("s") Map> shutdownSubstates) { + this.shutdownSubstates = shutdownSubstates == null ? Map.of() : shutdownSubstates; + } + + public static RecoverableProcessedTxn from( + REProcessedTxn txn, SubstateSerialization serialization) { + var parsedTxn = txn.getParsedTxn(); + + var stateUpdateGroups = + txn.getGroupedStateUpdates().stream() + .flatMap( + stateUpdates -> + stateUpdates.stream() + .filter( + u -> { + var microOp = + parsedTxn + .instructions() + .get(u.getInstructionIndex()) + .getMicroOp(); + return switch (microOp) { + case DOWN, DOWNINDEX, VDOWN, LVDOWN -> true; + default -> false; + }; + }) + .map( + u -> { + var microOp = + parsedTxn + .instructions() + .get(u.getInstructionIndex()) + .getMicroOp(); + var data = + switch (microOp) { + case DOWN -> serialization.serialize((Particle) u.getParsed()); + case DOWNINDEX -> Arrays.concatenate( + u.getId().asBytes(), + serialization.serialize((Particle) u.getParsed())); + case VDOWN, LVDOWN -> new byte[] {u.typeByte()}; + default -> throw new IllegalStateException(); + }; + return Pair.of(u.getInstructionIndex(), data); + })) + .collect( + Collectors.groupingBy( + Pair::getFirst, Collectors.mapping(Pair::getSecond, Collectors.toList()))); + return new RecoverableProcessedTxn(stateUpdateGroups); + } + + private RecoverableSubstate recoverUp(UpSubstate upSubstate) { + ByteBuffer substate = upSubstate.getSubstateBuffer(); + SubstateId substateId = upSubstate.getSubstateId(); + return new RecoverableSubstateShutdown(substate, substateId, true); + } + + private RecoverableSubstate recoverDown(REInstruction instruction, int index) { + var dataList = shutdownSubstates.get(index); + if (dataList.size() != 1) { + throw new IllegalStateException("Multiple substates found for down instruction"); + } + var substate = ByteBuffer.wrap(dataList.get(0)); + SubstateId substateId = instruction.getData(); + return new RecoverableSubstateShutdown(substate, substateId, false); + } + + private RecoverableSubstate recoverLocalDown( + REInstruction instruction, IntFunction localUpSubstates) { + SubstateId substateId = instruction.getData(); + var index = + substateId.getIndex().orElseThrow(() -> new IllegalStateException("Could not find index")); + var substate = localUpSubstates.apply(index).getSubstateBuffer(); + return new RecoverableSubstateShutdown(substate, substateId, false); + } + + private RecoverableSubstate recoverVirtualDown(REInstruction instruction, int index) { + var dataList = shutdownSubstates.get(index); + if (dataList.size() != 1) { + throw new IllegalStateException("Multiple substates found for virtual down instruction"); + } + SubstateId substateId = instruction.getData(); + return new RecoverableSubstateVirtualShutdown(dataList.get(0)[0], substateId); + } + + private Stream recoverDownIndex(int index) { + var substates = shutdownSubstates.get(index); + if (substates == null) { + return Stream.of(); + } + return substates.stream() + .map( + data -> { + var buf = ByteBuffer.wrap(data); + var substateId = SubstateId.fromBuffer(buf); + var substate = + ByteBuffer.wrap(data, SubstateId.BYTES, data.length - SubstateId.BYTES); + return new RecoverableSubstateShutdown(substate, substateId, false); + }); + } + + public List> recoverStateUpdates(ParsedTxn parsedTxn) { + var substateGroups = new ArrayList>(); + var substateUpdates = new ArrayList(); + var upSubstates = new ArrayList(); + + for (int i = 0; i < parsedTxn.instructions().size(); i++) { + var instruction = parsedTxn.instructions().get(i); + if (!instruction.isStateUpdate()) { + if (instruction.getMicroOp() == REInstruction.REMicroOp.END) { + substateGroups.add(substateUpdates); + substateUpdates = new ArrayList<>(); + } + continue; + } + + switch (instruction.getMicroOp()) { + case UP -> { + UpSubstate upSubstate = instruction.getData(); + substateUpdates.add(recoverUp(upSubstate)); + upSubstates.add(upSubstate); + } + case DOWN -> substateUpdates.add(recoverDown(instruction, i)); + case LDOWN -> substateUpdates.add(recoverLocalDown(instruction, upSubstates::get)); + case VDOWN, LVDOWN -> substateUpdates.add(recoverVirtualDown(instruction, i)); + case DOWNINDEX -> recoverDownIndex(i).forEach(substateUpdates::add); + default -> { + // ignored + } + } + } + + return substateGroups; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstate.java index 0048213a26..5eaa5fdf93 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstate.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,5 +70,5 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; public interface RecoverableSubstate { - SubstateOperation recover(Provider> radixEngineProvider); + SubstateOperation recover(Provider> radixEngineProvider); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateShutdown.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateShutdown.java index 29d6b55e6c..7d59935af9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateShutdown.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateShutdown.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -70,35 +71,31 @@ import com.radixdlt.engine.RadixEngine; import com.radixdlt.serialization.DeserializeException; import com.radixdlt.statecomputer.LedgerAndBFTProof; - import java.nio.ByteBuffer; public final class RecoverableSubstateShutdown implements RecoverableSubstate { - private final ByteBuffer substateBuffer; - private final SubstateId substateId; - private final boolean isBootUp; + private final ByteBuffer substateBuffer; + private final SubstateId substateId; + private final boolean isBootUp; - public RecoverableSubstateShutdown(ByteBuffer substateBuffer, SubstateId substateId, boolean isBootUp) { - this.substateBuffer = substateBuffer; - this.substateId = substateId; - this.isBootUp = isBootUp; - } + public RecoverableSubstateShutdown( + ByteBuffer substateBuffer, SubstateId substateId, boolean isBootUp) { + this.substateBuffer = substateBuffer; + this.substateId = substateId; + this.isBootUp = isBootUp; + } - private Particle deserialize(RadixEngine radixEngine) { - var deserialization = radixEngine.getSubstateDeserialization(); - try { - return deserialization.deserialize(substateBuffer); - } catch (DeserializeException e) { - throw new IllegalStateException("Failed to deserialize substate.", e); - } - } + private Particle deserialize(RadixEngine radixEngine) { + var deserialization = radixEngine.getSubstateDeserialization(); + try { + return deserialization.deserialize(substateBuffer); + } catch (DeserializeException e) { + throw new IllegalStateException("Failed to deserialize substate.", e); + } + } - @Override - public SubstateOperation recover(Provider> radixEngineProvider) { - return new SubstateOperation( - deserialize(radixEngineProvider.get()), - substateId, - isBootUp - ); - } + @Override + public SubstateOperation recover(Provider> radixEngineProvider) { + return new SubstateOperation(deserialize(radixEngineProvider.get()), substateId, isBootUp); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateVirtualShutdown.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateVirtualShutdown.java index 0d74a4a36e..1abfd1fb75 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateVirtualShutdown.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/core/reconstruction/RecoverableSubstateVirtualShutdown.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -72,24 +73,24 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; public final class RecoverableSubstateVirtualShutdown implements RecoverableSubstate { - private final byte typeByte; - private final SubstateId substateId; + private final byte typeByte; + private final SubstateId substateId; - public RecoverableSubstateVirtualShutdown(byte typeByte, SubstateId substateId) { - this.typeByte = typeByte; - this.substateId = substateId; - } + public RecoverableSubstateVirtualShutdown(byte typeByte, SubstateId substateId) { + this.typeByte = typeByte; + this.substateId = substateId; + } - @Override - public SubstateOperation recover(Provider> radixEngineProvider) { - var radixEngine = radixEngineProvider.get(); - var keyBuf = substateId.getVirtualKey().orElseThrow(); - Particle substate; - try { - substate = radixEngine.getVirtualSubstateDeserialization().keyToSubstate(typeByte, keyBuf); - } catch (DeserializeException e) { - throw new IllegalStateException("Could not deserialize virtual substate."); - } - return new SubstateOperation(substate, substateId, false); - } + @Override + public SubstateOperation recover(Provider> radixEngineProvider) { + var radixEngine = radixEngineProvider.get(); + var keyBuf = substateId.getVirtualKey().orElseThrow(); + Particle substate; + try { + substate = radixEngine.getVirtualSubstateDeserialization().keyToSubstate(typeByte, keyBuf); + } catch (DeserializeException e) { + throw new IllegalStateException("Could not deserialize virtual substate."); + } + return new SubstateOperation(substate, substateId, false); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/AddressBookHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/AddressBookHandler.java index d5e6844dc3..8929502a60 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/AddressBookHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/AddressBookHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,20 +69,21 @@ import com.radixdlt.network.p2p.addressbook.AddressBook; public final class AddressBookHandler extends SystemGetJsonHandler { - private final AddressBook addressBook; - private final SystemModelMapper systemModelMapper; + private final AddressBook addressBook; + private final SystemModelMapper systemModelMapper; - @Inject - AddressBookHandler(AddressBook addressBook, SystemModelMapper systemModelMapper) { - this.addressBook = addressBook; - this.systemModelMapper = systemModelMapper; - } + @Inject + AddressBookHandler(AddressBook addressBook, SystemModelMapper systemModelMapper) { + this.addressBook = addressBook; + this.systemModelMapper = systemModelMapper; + } - @Override - public SystemAddressBookResponse handleRequest() { - var response = new SystemAddressBookResponse(); - addressBook.knownPeers() - .forEach((n, entry) -> response.addEntriesItem(systemModelMapper.addressBookEntry(entry))); - return response; - } + @Override + public SystemAddressBookResponse handleRequest() { + var response = new SystemAddressBookResponse(); + addressBook + .knownPeers() + .forEach((n, entry) -> response.addEntriesItem(systemModelMapper.addressBookEntry(entry))); + return response; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/ConfigurationHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/ConfigurationHandler.java index 44cfeb43ea..08c88ef662 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/ConfigurationHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/ConfigurationHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -76,51 +77,46 @@ import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.sync.SyncConfig; - public final class ConfigurationHandler extends SystemGetJsonHandler { - private final long pacemakerTimeout; - private final int bftSyncPatienceMillis; - private final int mempoolMaxSize; - private final long mempoolThrottleMs; - private final SyncConfig syncConfig; - private final P2PConfig p2PConfig; - private final ECPublicKey self; - private final SystemModelMapper systemModelMapper; + private final long pacemakerTimeout; + private final int bftSyncPatienceMillis; + private final int mempoolMaxSize; + private final long mempoolThrottleMs; + private final SyncConfig syncConfig; + private final P2PConfig p2PConfig; + private final ECPublicKey self; + private final SystemModelMapper systemModelMapper; - @Inject - ConfigurationHandler( - @Self ECPublicKey self, - @PacemakerTimeout long pacemakerTimeout, - @BFTSyncPatienceMillis int bftSyncPatienceMillis, - @MempoolMaxSize int mempoolMaxSize, - @MempoolThrottleMs long mempoolThrottleMs, - SyncConfig syncConfig, - P2PConfig p2PConfig, - SystemModelMapper systemModelMapper - ) { - this.self = self; - this.pacemakerTimeout = pacemakerTimeout; - this.bftSyncPatienceMillis = bftSyncPatienceMillis; - this.mempoolMaxSize = mempoolMaxSize; - this.mempoolThrottleMs = mempoolThrottleMs; - this.syncConfig = syncConfig; - this.p2PConfig = p2PConfig; - this.systemModelMapper = systemModelMapper; - } + @Inject + ConfigurationHandler( + @Self ECPublicKey self, + @PacemakerTimeout long pacemakerTimeout, + @BFTSyncPatienceMillis int bftSyncPatienceMillis, + @MempoolMaxSize int mempoolMaxSize, + @MempoolThrottleMs long mempoolThrottleMs, + SyncConfig syncConfig, + P2PConfig p2PConfig, + SystemModelMapper systemModelMapper) { + this.self = self; + this.pacemakerTimeout = pacemakerTimeout; + this.bftSyncPatienceMillis = bftSyncPatienceMillis; + this.mempoolMaxSize = mempoolMaxSize; + this.mempoolThrottleMs = mempoolThrottleMs; + this.syncConfig = syncConfig; + this.p2PConfig = p2PConfig; + this.systemModelMapper = systemModelMapper; + } - @Override - public SystemConfigurationResponse handleRequest() { - return new SystemConfigurationResponse() - .bft(new BFTConfiguration() - .bftSyncPatience(bftSyncPatienceMillis) - .pacemakerTimeout(pacemakerTimeout) - ) - .mempool(new MempoolConfiguration() - .maxSize(mempoolMaxSize) - .throttle(mempoolThrottleMs) - ) - .sync(systemModelMapper.syncConfiguration(syncConfig)) - .networking(systemModelMapper.networkingConfiguration(self, p2PConfig)); - } + @Override + public SystemConfigurationResponse handleRequest() { + return new SystemConfigurationResponse() + .bft( + new BFTConfiguration() + .bftSyncPatience(bftSyncPatienceMillis) + .pacemakerTimeout(pacemakerTimeout)) + .mempool(new MempoolConfiguration().maxSize(mempoolMaxSize).throttle(mempoolThrottleMs)) + .sync(systemModelMapper.syncConfiguration(syncConfig)) + .networking(systemModelMapper.networkingConfiguration(self, p2PConfig)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/HealthHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/HealthHandler.java index 5b986fe815..dc2f1bd725 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/HealthHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/HealthHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,27 +65,28 @@ package com.radixdlt.api.system; import com.google.inject.Inject; -import com.radixdlt.api.system.openapitools.model.HealthResponse; import com.radixdlt.api.system.health.HealthInfoService; +import com.radixdlt.api.system.openapitools.model.HealthResponse; final class HealthHandler extends SystemGetJsonHandler { - private final HealthInfoService healthInfoService; + private final HealthInfoService healthInfoService; - @Inject - HealthHandler(HealthInfoService healthInfoService) { - this.healthInfoService = healthInfoService; - } + @Inject + HealthHandler(HealthInfoService healthInfoService) { + this.healthInfoService = healthInfoService; + } - @Override - public HealthResponse handleRequest() { - var nodeStatus = healthInfoService.nodeStatus(); - var status = switch (nodeStatus) { - case UP -> HealthResponse.StatusEnum.UP; - case BOOTING -> HealthResponse.StatusEnum.BOOTING; - case SYNCING -> HealthResponse.StatusEnum.SYNCING; - case STALLED -> HealthResponse.StatusEnum.STALLED; - case OUT_OF_SYNC -> HealthResponse.StatusEnum.OUT_OF_SYNC; - }; - return new HealthResponse().status(status); - } + @Override + public HealthResponse handleRequest() { + var nodeStatus = healthInfoService.nodeStatus(); + var status = + switch (nodeStatus) { + case UP -> HealthResponse.StatusEnum.UP; + case BOOTING -> HealthResponse.StatusEnum.BOOTING; + case SYNCING -> HealthResponse.StatusEnum.SYNCING; + case STALLED -> HealthResponse.StatusEnum.STALLED; + case OUT_OF_SYNC -> HealthResponse.StatusEnum.OUT_OF_SYNC; + }; + return new HealthResponse().status(status); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/MetricsHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/MetricsHandler.java index 1582aae507..c6e6357054 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/MetricsHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/MetricsHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,24 +69,21 @@ import com.radixdlt.counters.SystemCounters; public final class MetricsHandler extends SystemGetJsonHandler { - private final SystemCounters systemCounters; - private final SystemModelMapper systemModelMapper; + private final SystemCounters systemCounters; + private final SystemModelMapper systemModelMapper; - @Inject - MetricsHandler( - SystemModelMapper systemModelMapper, - SystemCounters systemCounters - ) { - this.systemModelMapper = systemModelMapper; - this.systemCounters = systemCounters; - } + @Inject + MetricsHandler(SystemModelMapper systemModelMapper, SystemCounters systemCounters) { + this.systemModelMapper = systemModelMapper; + this.systemCounters = systemCounters; + } - @Override - public SystemMetricsResponse handleRequest() { - return new SystemMetricsResponse() - .bft(systemModelMapper.bftMetrics(systemCounters)) - .mempool(systemModelMapper.mempoolMetrics(systemCounters)) - .sync(systemModelMapper.syncMetrics(systemCounters)) - .networking(systemModelMapper.networkingMetrics(systemCounters)); - } + @Override + public SystemMetricsResponse handleRequest() { + return new SystemMetricsResponse() + .bft(systemModelMapper.bftMetrics(systemCounters)) + .mempool(systemModelMapper.mempoolMetrics(systemCounters)) + .sync(systemModelMapper.syncMetrics(systemCounters)) + .networking(systemModelMapper.networkingMetrics(systemCounters)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/PeersHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/PeersHandler.java index 8bad187fcb..9846e8c989 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/PeersHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/PeersHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,21 +70,19 @@ public final class PeersHandler extends SystemGetJsonHandler { - private final SystemModelMapper systemModelMapper; - private final PeersView peersView; + private final SystemModelMapper systemModelMapper; + private final PeersView peersView; + + @Inject + PeersHandler(SystemModelMapper systemModelMapper, PeersView peersView) { + this.systemModelMapper = systemModelMapper; + this.peersView = peersView; + } - @Inject - PeersHandler( - SystemModelMapper systemModelMapper, - PeersView peersView - ) { - this.systemModelMapper = systemModelMapper; - this.peersView = peersView; - } - @Override - public SystemPeersResponse handleRequest() { - var response = new SystemPeersResponse(); - peersView.peers().map(systemModelMapper::peer).forEach(response::addPeersItem); - return response; - } + @Override + public SystemPeersResponse handleRequest() { + var response = new SystemPeersResponse(); + peersView.peers().map(systemModelMapper::peer).forEach(response::addPeersItem); + return response; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemApiModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemApiModule.java index 963536a763..f674e83eae 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemApiModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemApiModule.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,40 +70,37 @@ import com.google.inject.multibindings.MapBinder; import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.ProvidesIntoSet; +import com.radixdlt.api.HandlerRoute; import com.radixdlt.api.system.health.HealthInfoService; import com.radixdlt.api.system.health.ScheduledStatsCollecting; import com.radixdlt.api.system.prometheus.PrometheusApiModule; -import com.radixdlt.api.HandlerRoute; import com.radixdlt.environment.EventProcessorOnRunner; import com.radixdlt.environment.LocalEvents; import com.radixdlt.environment.Runners; import io.undertow.server.HttpHandler; public class SystemApiModule extends AbstractModule { - @Override - protected void configure() { - var eventBinder = Multibinder - .newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(ScheduledStatsCollecting.class); - bind(HealthInfoService.class).in(Scopes.SINGLETON); + @Override + protected void configure() { + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(ScheduledStatsCollecting.class); + bind(HealthInfoService.class).in(Scopes.SINGLETON); - var binder = MapBinder.newMapBinder(binder(), HandlerRoute.class, HttpHandler.class); - binder.addBinding(HandlerRoute.get("/system/configuration")).to(ConfigurationHandler.class); - binder.addBinding(HandlerRoute.get("/system/metrics")).to(MetricsHandler.class); - binder.addBinding(HandlerRoute.get("/system/health")).to(HealthHandler.class); - binder.addBinding(HandlerRoute.get("/system/version")).to(VersionHandler.class); - binder.addBinding(HandlerRoute.get("/system/peers")).to(PeersHandler.class); - binder.addBinding(HandlerRoute.get("/system/addressbook")).to(AddressBookHandler.class); - install(new PrometheusApiModule("/prometheus/metrics")); - } + var binder = MapBinder.newMapBinder(binder(), HandlerRoute.class, HttpHandler.class); + binder.addBinding(HandlerRoute.get("/system/configuration")).to(ConfigurationHandler.class); + binder.addBinding(HandlerRoute.get("/system/metrics")).to(MetricsHandler.class); + binder.addBinding(HandlerRoute.get("/system/health")).to(HealthHandler.class); + binder.addBinding(HandlerRoute.get("/system/version")).to(VersionHandler.class); + binder.addBinding(HandlerRoute.get("/system/peers")).to(PeersHandler.class); + binder.addBinding(HandlerRoute.get("/system/addressbook")).to(AddressBookHandler.class); + install(new PrometheusApiModule("/prometheus/metrics")); + } - @ProvidesIntoSet - public EventProcessorOnRunner healthInfoService(HealthInfoService healthInfoService) { - return new EventProcessorOnRunner<>( - Runners.SYSTEM_INFO, - ScheduledStatsCollecting.class, - healthInfoService.updateStats() - ); - } + @ProvidesIntoSet + public EventProcessorOnRunner healthInfoService(HealthInfoService healthInfoService) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, ScheduledStatsCollecting.class, healthInfoService.updateStats()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemGetJsonHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemGetJsonHandler.java index 834b8d6521..317171b266 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemGetJsonHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemGetJsonHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -70,26 +71,26 @@ import io.undertow.util.Headers; abstract class SystemGetJsonHandler implements HttpHandler { - private static final String CONTENT_TYPE_JSON = "application/json"; - private static final long DEFAULT_MAX_REQUEST_SIZE = 1024L * 1024L; + private static final String CONTENT_TYPE_JSON = "application/json"; + private static final long DEFAULT_MAX_REQUEST_SIZE = 1024L * 1024L; - public abstract T handleRequest(); + public abstract T handleRequest(); - @Override - public final void handleRequest(HttpServerExchange exchange) throws Exception { - if (exchange.isInIoThread()) { - exchange.dispatch(this); - return; - } + @Override + public final void handleRequest(HttpServerExchange exchange) throws Exception { + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } - exchange.setMaxEntitySize(DEFAULT_MAX_REQUEST_SIZE); - exchange.startBlocking(); + exchange.setMaxEntitySize(DEFAULT_MAX_REQUEST_SIZE); + exchange.startBlocking(); - var mapper = JSON.getDefault().getMapper(); - var response = handleRequest(); - exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, CONTENT_TYPE_JSON); - exchange.setStatusCode(200); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - exchange.getResponseSender().send(mapper.writeValueAsString(response)); - } + var mapper = JSON.getDefault().getMapper(); + var response = handleRequest(); + exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, CONTENT_TYPE_JSON); + exchange.setStatusCode(200); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + exchange.getResponseSender().send(mapper.writeValueAsString(response)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemModelMapper.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemModelMapper.java index b8710532ab..b7128cb009 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemModelMapper.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/SystemModelMapper.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.system; +import static com.radixdlt.counters.SystemCounters.CounterType.*; + import com.google.inject.Inject; import com.radixdlt.api.system.openapitools.model.Address; import com.radixdlt.api.system.openapitools.model.AddressBookEntry; @@ -79,166 +82,172 @@ import com.radixdlt.api.system.openapitools.model.PeerChannel; import com.radixdlt.api.system.openapitools.model.SyncConfiguration; import com.radixdlt.api.system.openapitools.model.SyncMetrics; -import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry; -import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry.LatestConnectionStatus; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.network.p2p.RadixNodeUri; +import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry; +import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry.LatestConnectionStatus; import com.radixdlt.networks.Addressing; import com.radixdlt.sync.SyncConfig; - import java.math.BigDecimal; import java.time.Instant; -import static com.radixdlt.counters.SystemCounters.CounterType.*; - public final class SystemModelMapper { - private final Addressing addressing; - - @Inject - SystemModelMapper(Addressing addressing) { - this.addressing = addressing; - } - - public NetworkingConfiguration networkingConfiguration(ECPublicKey self, P2PConfig config) { - return new NetworkingConfiguration() - .defaultPort(config.defaultPort()) - .discoveryInterval(config.discoveryInterval()) - .listenAddress(config.listenAddress()) - .listenPort(config.listenPort()) - .broadcastPort(config.broadcastPort()) - .peerConnectionTimeout(config.peerConnectionTimeout()) - .maxInboundChannels(config.maxInboundChannels()) - .maxOutboundChannels(config.maxOutboundChannels()) - .channelBufferSize(config.channelBufferSize()) - .peerLivenessCheckInterval(config.peerLivenessCheckInterval()) - .pingTimeout(config.pingTimeout()) - .seedNodes(config.seedNodes()) - .nodeAddress(addressing.forNodes().of(self)); - } - - public SyncConfiguration syncConfiguration(SyncConfig syncConfig) { - return new SyncConfiguration() - .syncCheckInterval(syncConfig.syncCheckInterval()) - .syncCheckMaxPeers(syncConfig.syncCheckMaxPeers()) - .requestTimeout(syncConfig.syncRequestTimeout()) - .ledgerStatusUpdateMaxPeersToNotify(syncConfig.ledgerStatusUpdateMaxPeersToNotify()) - .maxLedgerUpdatesRate(BigDecimal.valueOf(syncConfig.maxLedgerUpdatesRate())); - } - - public NetworkingMetrics networkingMetrics(SystemCounters counters) { - return new NetworkingMetrics() - .bytesReceived(counters.get(NETWORKING_BYTES_RECEIVED)) - .bytesSent(counters.get(NETWORKING_BYTES_SENT)) - .inbound(networkingInboundMetrics(counters)) - .outbound(networkingOutboundMetrics(counters)); - } - - public NetworkingInboundMetrics networkingInboundMetrics(SystemCounters counters) { - return new NetworkingInboundMetrics() - .discarded(counters.get(MESSAGES_INBOUND_DISCARDED)) - .processed(counters.get(MESSAGES_INBOUND_PROCESSED)) - .received(counters.get(MESSAGES_INBOUND_RECEIVED)); - } - - public NetworkingOutboundMetrics networkingOutboundMetrics(SystemCounters counters) { - return new NetworkingOutboundMetrics() - .aborted(counters.get(MESSAGES_OUTBOUND_ABORTED)) - .processed(counters.get(MESSAGES_OUTBOUND_PROCESSED)) - .pending(counters.get(MESSAGES_OUTBOUND_PENDING)) - .aborted(counters.get(MESSAGES_OUTBOUND_ABORTED)) - .sent(counters.get(MESSAGES_OUTBOUND_SENT)); - } - - public MempoolMetrics mempoolMetrics(SystemCounters counters) { - return new MempoolMetrics() - .addFailure(counters.get(MEMPOOL_ADD_FAILURE)) - .addSuccess(counters.get(MEMPOOL_ADD_SUCCESS)) - .currentSize(counters.get(MEMPOOL_CURRENT_SIZE)) - .relaysSent(counters.get(MEMPOOL_RELAYS_SENT)); - } - - public BFTMetrics bftMetrics(SystemCounters counters) { - return new BFTMetrics() - .committedVertices(counters.get(BFT_COMMITTED_VERTICES)) - .eventsReceived(counters.get(BFT_EVENTS_RECEIVED)) - .noVotesSent(counters.get(BFT_NO_VOTES_SENT)) - .timeoutQuorums(counters.get(BFT_TIMEOUT_QUORUMS)) - .voteQuorums(counters.get(BFT_VOTE_QUORUMS)) - .sync(bftSyncMetrics(counters)) - .pacemaker(bftPacemakerMetrics(counters)) - .vertexStore(bftVertexStoreMetrics(counters)); - } - - public SyncMetrics syncMetrics(SystemCounters counters) { - return new SyncMetrics() - .currentStateVersion(counters.get(SYNC_CURRENT_STATE_VERSION)) - .targetStateVersion(counters.get(SYNC_TARGET_STATE_VERSION)) - .invalidResponsesReceived(counters.get(SYNC_INVALID_RESPONSES_RECEIVED)) - .validResponsesReceived(counters.get(SYNC_VALID_RESPONSES_RECEIVED)) - .remoteRequestsReceived(counters.get(SYNC_REMOTE_REQUESTS_RECEIVED)); - } - - public BFTVertexStoreMetrics bftVertexStoreMetrics(SystemCounters counters) { - return new BFTVertexStoreMetrics() - .forks(counters.get(BFT_VERTEX_STORE_FORKS)) - .indirectParents(counters.get(BFT_VERTEX_STORE_INDIRECT_PARENTS)) - .rebuilds(counters.get(BFT_VERTEX_STORE_REBUILDS)) - .size(counters.get(BFT_VERTEX_STORE_SIZE)); - } - - public BFTPacemakerMetrics bftPacemakerMetrics(SystemCounters counters) { - return new BFTPacemakerMetrics() - .proposalsSent(counters.get(BFT_PACEMAKER_PROPOSALS_SENT)) - .proposedTransactions(counters.get(BFT_PACEMAKER_PROPOSED_TRANSACTIONS)) - .round(counters.get(BFT_PACEMAKER_ROUND)) - .timedOutRounds(counters.get(BFT_PACEMAKER_TIMED_OUT_ROUNDS)) - .timeoutsSent(counters.get(BFT_PACEMAKER_TIMEOUTS_SENT)); - } - - public BFTSyncMetrics bftSyncMetrics(SystemCounters counters) { - return new BFTSyncMetrics() - .requestsReceived(counters.get(BFT_SYNC_REQUESTS_RECEIVED)) - .requestsSent(counters.get(BFT_SYNC_REQUESTS_SENT)) - .requestTimeouts(counters.get(BFT_SYNC_REQUEST_TIMEOUTS)); - } - - public Peer peer(PeersView.PeerInfo peerInfo) { - var peerId = addressing.forNodes().of(peerInfo.getNodeId().getPublicKey()); - var peer = new Peer().peerId(peerId); - - peerInfo.getChannels().forEach(channel -> { - var peerChannel = new PeerChannel() - .type(channel.isOutbound() ? PeerChannel.TypeEnum.OUT : PeerChannel.TypeEnum.IN) - .localPort(channel.getPort()) - .ip(channel.getHost()); - channel.getUri().map(RadixNodeUri::toString).ifPresent(peerChannel::uri); - peer.addChannelsItem(peerChannel); - }); - return peer; - } - - public Address address(PeerAddressEntry entry) { - return new Address() - .uri(entry.getUri().toString()) - .blacklisted(entry.blacklisted()) - .lastConnectionStatus(Address.LastConnectionStatusEnum.fromValue( - entry.getLatestConnectionStatus().map(LatestConnectionStatus::toString).orElse("UNKNOWN") - )); - } - - public AddressBookEntry addressBookEntry(com.radixdlt.network.p2p.addressbook.AddressBookEntry entry) { - var addressBookEntry = new AddressBookEntry() - .peerId(addressing.forNodes().of(entry.getNodeId().getPublicKey())) - .banned(entry.isBanned()); - entry.bannedUntil().map(Instant::toEpochMilli).ifPresent(addressBookEntry::bannedUntil); - entry.getKnownAddresses() - .stream().map(this::address) - .forEach(addressBookEntry::addKnownAddressesItem); - - return addressBookEntry; - } - + private final Addressing addressing; + + @Inject + SystemModelMapper(Addressing addressing) { + this.addressing = addressing; + } + + public NetworkingConfiguration networkingConfiguration(ECPublicKey self, P2PConfig config) { + return new NetworkingConfiguration() + .defaultPort(config.defaultPort()) + .discoveryInterval(config.discoveryInterval()) + .listenAddress(config.listenAddress()) + .listenPort(config.listenPort()) + .broadcastPort(config.broadcastPort()) + .peerConnectionTimeout(config.peerConnectionTimeout()) + .maxInboundChannels(config.maxInboundChannels()) + .maxOutboundChannels(config.maxOutboundChannels()) + .channelBufferSize(config.channelBufferSize()) + .peerLivenessCheckInterval(config.peerLivenessCheckInterval()) + .pingTimeout(config.pingTimeout()) + .seedNodes(config.seedNodes()) + .nodeAddress(addressing.forNodes().of(self)); + } + + public SyncConfiguration syncConfiguration(SyncConfig syncConfig) { + return new SyncConfiguration() + .syncCheckInterval(syncConfig.syncCheckInterval()) + .syncCheckMaxPeers(syncConfig.syncCheckMaxPeers()) + .requestTimeout(syncConfig.syncRequestTimeout()) + .ledgerStatusUpdateMaxPeersToNotify(syncConfig.ledgerStatusUpdateMaxPeersToNotify()) + .maxLedgerUpdatesRate(BigDecimal.valueOf(syncConfig.maxLedgerUpdatesRate())); + } + + public NetworkingMetrics networkingMetrics(SystemCounters counters) { + return new NetworkingMetrics() + .bytesReceived(counters.get(NETWORKING_BYTES_RECEIVED)) + .bytesSent(counters.get(NETWORKING_BYTES_SENT)) + .inbound(networkingInboundMetrics(counters)) + .outbound(networkingOutboundMetrics(counters)); + } + + public NetworkingInboundMetrics networkingInboundMetrics(SystemCounters counters) { + return new NetworkingInboundMetrics() + .discarded(counters.get(MESSAGES_INBOUND_DISCARDED)) + .processed(counters.get(MESSAGES_INBOUND_PROCESSED)) + .received(counters.get(MESSAGES_INBOUND_RECEIVED)); + } + + public NetworkingOutboundMetrics networkingOutboundMetrics(SystemCounters counters) { + return new NetworkingOutboundMetrics() + .aborted(counters.get(MESSAGES_OUTBOUND_ABORTED)) + .processed(counters.get(MESSAGES_OUTBOUND_PROCESSED)) + .pending(counters.get(MESSAGES_OUTBOUND_PENDING)) + .aborted(counters.get(MESSAGES_OUTBOUND_ABORTED)) + .sent(counters.get(MESSAGES_OUTBOUND_SENT)); + } + + public MempoolMetrics mempoolMetrics(SystemCounters counters) { + return new MempoolMetrics() + .addFailure(counters.get(MEMPOOL_ADD_FAILURE)) + .addSuccess(counters.get(MEMPOOL_ADD_SUCCESS)) + .currentSize(counters.get(MEMPOOL_CURRENT_SIZE)) + .relaysSent(counters.get(MEMPOOL_RELAYS_SENT)); + } + + public BFTMetrics bftMetrics(SystemCounters counters) { + return new BFTMetrics() + .committedVertices(counters.get(BFT_COMMITTED_VERTICES)) + .eventsReceived(counters.get(BFT_EVENTS_RECEIVED)) + .noVotesSent(counters.get(BFT_NO_VOTES_SENT)) + .timeoutQuorums(counters.get(BFT_TIMEOUT_QUORUMS)) + .voteQuorums(counters.get(BFT_VOTE_QUORUMS)) + .sync(bftSyncMetrics(counters)) + .pacemaker(bftPacemakerMetrics(counters)) + .vertexStore(bftVertexStoreMetrics(counters)); + } + + public SyncMetrics syncMetrics(SystemCounters counters) { + return new SyncMetrics() + .currentStateVersion(counters.get(SYNC_CURRENT_STATE_VERSION)) + .targetStateVersion(counters.get(SYNC_TARGET_STATE_VERSION)) + .invalidResponsesReceived(counters.get(SYNC_INVALID_RESPONSES_RECEIVED)) + .validResponsesReceived(counters.get(SYNC_VALID_RESPONSES_RECEIVED)) + .remoteRequestsReceived(counters.get(SYNC_REMOTE_REQUESTS_RECEIVED)); + } + + public BFTVertexStoreMetrics bftVertexStoreMetrics(SystemCounters counters) { + return new BFTVertexStoreMetrics() + .forks(counters.get(BFT_VERTEX_STORE_FORKS)) + .indirectParents(counters.get(BFT_VERTEX_STORE_INDIRECT_PARENTS)) + .rebuilds(counters.get(BFT_VERTEX_STORE_REBUILDS)) + .size(counters.get(BFT_VERTEX_STORE_SIZE)); + } + + public BFTPacemakerMetrics bftPacemakerMetrics(SystemCounters counters) { + return new BFTPacemakerMetrics() + .proposalsSent(counters.get(BFT_PACEMAKER_PROPOSALS_SENT)) + .proposedTransactions(counters.get(BFT_PACEMAKER_PROPOSED_TRANSACTIONS)) + .round(counters.get(BFT_PACEMAKER_ROUND)) + .timedOutRounds(counters.get(BFT_PACEMAKER_TIMED_OUT_ROUNDS)) + .timeoutsSent(counters.get(BFT_PACEMAKER_TIMEOUTS_SENT)); + } + + public BFTSyncMetrics bftSyncMetrics(SystemCounters counters) { + return new BFTSyncMetrics() + .requestsReceived(counters.get(BFT_SYNC_REQUESTS_RECEIVED)) + .requestsSent(counters.get(BFT_SYNC_REQUESTS_SENT)) + .requestTimeouts(counters.get(BFT_SYNC_REQUEST_TIMEOUTS)); + } + + public Peer peer(PeersView.PeerInfo peerInfo) { + var peerId = addressing.forNodes().of(peerInfo.getNodeId().getPublicKey()); + var peer = new Peer().peerId(peerId); + + peerInfo + .getChannels() + .forEach( + channel -> { + var peerChannel = + new PeerChannel() + .type( + channel.isOutbound() ? PeerChannel.TypeEnum.OUT : PeerChannel.TypeEnum.IN) + .localPort(channel.getPort()) + .ip(channel.getHost()); + channel.getUri().map(RadixNodeUri::toString).ifPresent(peerChannel::uri); + peer.addChannelsItem(peerChannel); + }); + return peer; + } + + public Address address(PeerAddressEntry entry) { + return new Address() + .uri(entry.getUri().toString()) + .blacklisted(entry.blacklisted()) + .lastConnectionStatus( + Address.LastConnectionStatusEnum.fromValue( + entry + .getLatestConnectionStatus() + .map(LatestConnectionStatus::toString) + .orElse("UNKNOWN"))); + } + + public AddressBookEntry addressBookEntry( + com.radixdlt.network.p2p.addressbook.AddressBookEntry entry) { + var addressBookEntry = + new AddressBookEntry() + .peerId(addressing.forNodes().of(entry.getNodeId().getPublicKey())) + .banned(entry.isBanned()); + entry.bannedUntil().map(Instant::toEpochMilli).ifPresent(addressBookEntry::bannedUntil); + entry.getKnownAddresses().stream() + .map(this::address) + .forEach(addressBookEntry::addKnownAddressesItem); + + return addressBookEntry; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/VersionHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/VersionHandler.java index 7f4be16190..80925fcedf 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/VersionHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/VersionHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,23 +64,24 @@ package com.radixdlt.api.system; +import static org.radix.Radix.SYSTEM_VERSION_KEY; +import static org.radix.Radix.VERSION_STRING_KEY; + import com.google.inject.Inject; import com.radixdlt.api.system.openapitools.model.VersionResponse; import org.radix.Radix; -import static org.radix.Radix.SYSTEM_VERSION_KEY; -import static org.radix.Radix.VERSION_STRING_KEY; - public class VersionHandler extends SystemGetJsonHandler { - private final String version; + private final String version; - @Inject - public VersionHandler() { - this.version = (String) Radix.systemVersionInfo().get(SYSTEM_VERSION_KEY).get(VERSION_STRING_KEY); - } + @Inject + public VersionHandler() { + this.version = + (String) Radix.systemVersionInfo().get(SYSTEM_VERSION_KEY).get(VERSION_STRING_KEY); + } - @Override - public VersionResponse handleRequest() { - return new VersionResponse().version(version); - } + @Override + public VersionResponse handleRequest() { + return new VersionResponse().version(version); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/HealthInfoService.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/HealthInfoService.java index 1c97d38629..4f0377cb58 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/HealthInfoService.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/HealthInfoService.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,119 +64,121 @@ package com.radixdlt.api.system.health; -import com.google.inject.Inject; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.environment.EventProcessor; -import com.radixdlt.environment.ScheduledEventDispatcher; - -import java.util.EnumMap; - +import static com.radixdlt.api.system.health.HealthInfoService.ValueHolder.Type.ABSOLUTE; import static com.radixdlt.api.system.health.NodeStatus.BOOTING; import static com.radixdlt.api.system.health.NodeStatus.OUT_OF_SYNC; import static com.radixdlt.api.system.health.NodeStatus.STALLED; import static com.radixdlt.api.system.health.NodeStatus.SYNCING; import static com.radixdlt.api.system.health.NodeStatus.UP; -import static com.radixdlt.api.system.health.HealthInfoService.ValueHolder.Type.ABSOLUTE; import static com.radixdlt.counters.SystemCounters.CounterType; +import com.google.inject.Inject; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.environment.EventProcessor; +import com.radixdlt.environment.ScheduledEventDispatcher; +import java.util.EnumMap; + public class HealthInfoService { - private static final long THRESHOLD = 3; // Maximum difference between ledger and target - private static final long DEFAULT_COLLECTING_INTERVAL = 1000L; // 1 second - private static final long STATUS_AVERAGING_FACTOR = 3L; // averaging time in multiples of collecting interval - - public static final CounterType LEDGER_KEY = CounterType.LEDGER_STATE_VERSION; - public static final CounterType TARGET_KEY = CounterType.SYNC_TARGET_STATE_VERSION; - - private final SystemCounters systemCounters; - private final ScheduledEventDispatcher scheduledStatsCollecting; - private final EnumMap statistics = new EnumMap<>(CounterType.class); - - @Inject - public HealthInfoService( - SystemCounters systemCounters, - ScheduledEventDispatcher scheduledStatsCollecting - ) { - this.scheduledStatsCollecting = scheduledStatsCollecting; - this.systemCounters = systemCounters; - - statistics.put(LEDGER_KEY, new ValueHolder(STATUS_AVERAGING_FACTOR, ABSOLUTE)); - statistics.put(TARGET_KEY, new ValueHolder(STATUS_AVERAGING_FACTOR, ABSOLUTE)); - - scheduledStatsCollecting.dispatch(ScheduledStatsCollecting.create(), DEFAULT_COLLECTING_INTERVAL); - } - - public NodeStatus nodeStatus() { - if (statistics.get(LEDGER_KEY).lastValue() == 0) { - // Initial status, consensus not started yet - return BOOTING; - } - - if (statistics.get(LEDGER_KEY).isGrowing()) { - // Ledger state version is increasing, so we're completely synced up or catching up - return ledgerIsCloseToTarget() ? UP : SYNCING; - } - - // Ledger is not growing, either node stall or whole network is down or not reachable - return statistics.get(TARGET_KEY).isGrowing() ? STALLED : OUT_OF_SYNC; - } - - public EventProcessor updateStats() { - return flush -> { - collectStats(); - scheduledStatsCollecting.dispatch(ScheduledStatsCollecting.create(), DEFAULT_COLLECTING_INTERVAL); - }; - } - - private void collectStats() { - statistics.forEach((key, value) -> statistics.compute(key, this::updateCounter)); - } - - private ValueHolder updateCounter(CounterType counterType, ValueHolder holder) { - return counterType != null ? holder.update(systemCounters.get(counterType)) : null; - } - - private boolean ledgerIsCloseToTarget() { - return (statistics.get(TARGET_KEY).lastValue() - statistics.get(LEDGER_KEY).lastValue()) < THRESHOLD; - } - - static class ValueHolder { - private final MovingAverage calculator; - private final MovingAverage deltaCalculator; - private final Type type; - private long lastValue; - - public enum Type { - ABSOLUTE, - INCREMENTAL - } - - private ValueHolder(long averagingFactor, Type type) { - calculator = MovingAverage.create(averagingFactor); - deltaCalculator = MovingAverage.create(averagingFactor); - this.type = type; - } - - public ValueHolder update(long newValue) { - var lastDelta = newValue - lastValue; - - lastValue = newValue; - deltaCalculator.update(lastDelta); - - if (type == ABSOLUTE) { - calculator.update(newValue); - } else { - calculator.update(lastDelta); - } - - return this; - } - - public long lastValue() { - return lastValue; - } - - public boolean isGrowing() { - return deltaCalculator.asDouble() > 0.1; - } - } + private static final long THRESHOLD = 3; // Maximum difference between ledger and target + private static final long DEFAULT_COLLECTING_INTERVAL = 1000L; // 1 second + private static final long STATUS_AVERAGING_FACTOR = + 3L; // averaging time in multiples of collecting interval + + public static final CounterType LEDGER_KEY = CounterType.LEDGER_STATE_VERSION; + public static final CounterType TARGET_KEY = CounterType.SYNC_TARGET_STATE_VERSION; + + private final SystemCounters systemCounters; + private final ScheduledEventDispatcher scheduledStatsCollecting; + private final EnumMap statistics = new EnumMap<>(CounterType.class); + + @Inject + public HealthInfoService( + SystemCounters systemCounters, + ScheduledEventDispatcher scheduledStatsCollecting) { + this.scheduledStatsCollecting = scheduledStatsCollecting; + this.systemCounters = systemCounters; + + statistics.put(LEDGER_KEY, new ValueHolder(STATUS_AVERAGING_FACTOR, ABSOLUTE)); + statistics.put(TARGET_KEY, new ValueHolder(STATUS_AVERAGING_FACTOR, ABSOLUTE)); + + scheduledStatsCollecting.dispatch( + ScheduledStatsCollecting.create(), DEFAULT_COLLECTING_INTERVAL); + } + + public NodeStatus nodeStatus() { + if (statistics.get(LEDGER_KEY).lastValue() == 0) { + // Initial status, consensus not started yet + return BOOTING; + } + + if (statistics.get(LEDGER_KEY).isGrowing()) { + // Ledger state version is increasing, so we're completely synced up or catching up + return ledgerIsCloseToTarget() ? UP : SYNCING; + } + + // Ledger is not growing, either node stall or whole network is down or not reachable + return statistics.get(TARGET_KEY).isGrowing() ? STALLED : OUT_OF_SYNC; + } + + public EventProcessor updateStats() { + return flush -> { + collectStats(); + scheduledStatsCollecting.dispatch( + ScheduledStatsCollecting.create(), DEFAULT_COLLECTING_INTERVAL); + }; + } + + private void collectStats() { + statistics.forEach((key, value) -> statistics.compute(key, this::updateCounter)); + } + + private ValueHolder updateCounter(CounterType counterType, ValueHolder holder) { + return counterType != null ? holder.update(systemCounters.get(counterType)) : null; + } + + private boolean ledgerIsCloseToTarget() { + return (statistics.get(TARGET_KEY).lastValue() - statistics.get(LEDGER_KEY).lastValue()) + < THRESHOLD; + } + + static class ValueHolder { + private final MovingAverage calculator; + private final MovingAverage deltaCalculator; + private final Type type; + private long lastValue; + + public enum Type { + ABSOLUTE, + INCREMENTAL + } + + private ValueHolder(long averagingFactor, Type type) { + calculator = MovingAverage.create(averagingFactor); + deltaCalculator = MovingAverage.create(averagingFactor); + this.type = type; + } + + public ValueHolder update(long newValue) { + var lastDelta = newValue - lastValue; + + lastValue = newValue; + deltaCalculator.update(lastDelta); + + if (type == ABSOLUTE) { + calculator.update(newValue); + } else { + calculator.update(lastDelta); + } + + return this; + } + + public long lastValue() { + return lastValue; + } + + public boolean isGrowing() { + return deltaCalculator.asDouble() > 0.1; + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/MovingAverage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/MovingAverage.java index 4bfb504fee..d76a5a1764 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/MovingAverage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/MovingAverage.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,56 +68,56 @@ import java.math.RoundingMode; public class MovingAverage { - private final long averagingFactor; - private BigDecimal average = BigDecimal.ZERO; - private long count = 0; + private final long averagingFactor; + private BigDecimal average = BigDecimal.ZERO; + private long count = 0; - private MovingAverage(long averagingFactor) { - this.averagingFactor = averagingFactor; - } + private MovingAverage(long averagingFactor) { + this.averagingFactor = averagingFactor; + } - public static MovingAverage create(long averagingFactor) { - if (averagingFactor <= 1) { - throw new IllegalArgumentException("Averaging factor must be above 1"); - } - return new MovingAverage(averagingFactor); - } + public static MovingAverage create(long averagingFactor) { + if (averagingFactor <= 1) { + throw new IllegalArgumentException("Averaging factor must be above 1"); + } + return new MovingAverage(averagingFactor); + } - public BigDecimal asBigDecimal() { - return average; - } + public BigDecimal asBigDecimal() { + return average; + } - public int asInteger() { - return average.intValue(); - } + public int asInteger() { + return average.intValue(); + } - public long asLong() { - return average.longValue(); - } + public long asLong() { + return average.longValue(); + } - public double asDouble() { - return average.doubleValue(); - } + public double asDouble() { + return average.doubleValue(); + } - public MovingAverage update(int value) { - return update(BigDecimal.valueOf(value)); - } + public MovingAverage update(int value) { + return update(BigDecimal.valueOf(value)); + } - public MovingAverage update(long value) { - return update(BigDecimal.valueOf(value)); - } + public MovingAverage update(long value) { + return update(BigDecimal.valueOf(value)); + } - public MovingAverage update(double value) { - return update(BigDecimal.valueOf(value)); - } + public MovingAverage update(double value) { + return update(BigDecimal.valueOf(value)); + } - public MovingAverage update(BigDecimal value) { - count++; + public MovingAverage update(BigDecimal value) { + count++; - var divisor = BigDecimal.valueOf(Math.min(count, averagingFactor)); - var delta = value.subtract(average).divide(divisor, 3, RoundingMode.HALF_UP); + var divisor = BigDecimal.valueOf(Math.min(count, averagingFactor)); + var delta = value.subtract(average).divide(divisor, 3, RoundingMode.HALF_UP); - average = average.add(delta); - return this; - } + average = average.add(delta); + return this; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/NodeStatus.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/NodeStatus.java index 7ca7a45f9e..1be9bc8b72 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/NodeStatus.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/NodeStatus.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,9 +65,9 @@ package com.radixdlt.api.system.health; public enum NodeStatus { - BOOTING, - SYNCING, - UP, - STALLED, - OUT_OF_SYNC + BOOTING, + SYNCING, + UP, + STALLED, + OUT_OF_SYNC } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/ScheduledStatsCollecting.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/ScheduledStatsCollecting.java index 8935dad695..60d9b4b6f6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/ScheduledStatsCollecting.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/health/ScheduledStatsCollecting.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,9 +65,9 @@ package com.radixdlt.api.system.health; public enum ScheduledStatsCollecting { - INSTANCE; + INSTANCE; - public static ScheduledStatsCollecting create() { - return INSTANCE; - } + public static ScheduledStatsCollecting create() { + return INSTANCE; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusApiModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusApiModule.java index d47b93594a..f3f20d9a19 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusApiModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusApiModule.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -70,17 +71,17 @@ import io.undertow.server.HttpHandler; public final class PrometheusApiModule extends AbstractModule { - private final String path; + private final String path; - public PrometheusApiModule(String path) { - this.path = path; - } + public PrometheusApiModule(String path) { + this.path = path; + } - @Override - protected void configure() { - bind(PrometheusHandler.class).in(Scopes.SINGLETON); - MapBinder.newMapBinder(binder(), HandlerRoute.class, HttpHandler.class) - .addBinding(HandlerRoute.get(path)) - .to(PrometheusHandler.class); - } + @Override + protected void configure() { + bind(PrometheusHandler.class).in(Scopes.SINGLETON); + MapBinder.newMapBinder(binder(), HandlerRoute.class, HttpHandler.class) + .addBinding(HandlerRoute.get(path)) + .to(PrometheusHandler.class); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusHandler.java index 2a2a2aa480..6a4054fd54 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusHandler.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -69,28 +70,29 @@ import io.undertow.util.Headers; public class PrometheusHandler implements HttpHandler { - // https://prometheus.io/docs/instrumenting/exposition_formats/ - private static final String CONTENT_TYPE_TEXT_PLAIN_004 = "text/plain; version=0.0.4;charset=utf-8"; + // https://prometheus.io/docs/instrumenting/exposition_formats/ + private static final String CONTENT_TYPE_TEXT_PLAIN_004 = + "text/plain; version=0.0.4;charset=utf-8"; - private final PrometheusService metricsService; + private final PrometheusService metricsService; - @Inject - public PrometheusHandler(PrometheusService metricsService) { - this.metricsService = metricsService; - } + @Inject + public PrometheusHandler(PrometheusService metricsService) { + this.metricsService = metricsService; + } - @Override - public void handleRequest(HttpServerExchange exchange) throws Exception { - if (exchange.isInIoThread()) { - exchange.dispatch(this); - return; - } + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } - exchange.startBlocking(); + exchange.startBlocking(); - var text = metricsService.getMetrics(); - exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_004); - exchange.setStatusCode(200); - exchange.getResponseSender().send(text); - } + var text = metricsService.getMetrics(); + exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_004); + exchange.setStatusCode(200); + exchange.getResponseSender().send(text); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusService.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusService.java index 82bb949917..8dc65bbc3d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusService.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/api/system/prometheus/PrometheusService.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,24 +64,22 @@ package com.radixdlt.api.system.prometheus; +import static com.radixdlt.api.system.prometheus.PrometheusService.JmxMetric.jmxMetric; +import static org.radix.Radix.SYSTEM_VERSION_KEY; +import static org.radix.Radix.VERSION_STRING_KEY; + +import com.google.inject.Inject; import com.radixdlt.api.system.health.HealthInfoService; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.Self; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.identifiers.REAddr; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.networks.Addressing; import com.radixdlt.properties.RuntimeProperties; import com.radixdlt.systeminfo.InMemorySystemInfo; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.inject.Inject; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.counters.SystemCounters.CounterType; -import org.radix.Radix; - import java.io.IOException; import java.lang.management.ManagementFactory; import java.util.AbstractCollection; @@ -88,220 +87,242 @@ import java.util.List; import java.util.Locale; import java.util.Map; - import javax.management.InstanceNotFoundException; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.openmbean.CompositeDataSupport; - -import static org.radix.Radix.SYSTEM_VERSION_KEY; -import static org.radix.Radix.VERSION_STRING_KEY; - -import static com.radixdlt.api.system.prometheus.PrometheusService.JmxMetric.jmxMetric; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.radix.Radix; public class PrometheusService { - private static final Logger log = LogManager.getLogger(); - - private static final List EXPORT_LIST = Arrays.asList(CounterType.values()); - - private static final List JMX_METRICS = List.of( - jmxMetric("java.lang:type=MemoryPool,name=G1 Eden Space", "Usage"), - jmxMetric("java.lang:type=MemoryPool,name=G1 Survivor Space", "Usage"), - jmxMetric("java.lang:type=MemoryPool,name=G1 Old Gen", "Usage"), - jmxMetric("java.lang:type=MemoryPool,name=Metaspace", "Usage"), - jmxMetric("java.lang:type=GarbageCollector,name=G1 Old Generation", "Usage"), - jmxMetric("java.lang:type=GarbageCollector,name=G1 Young Generation", "Usage"), - jmxMetric("java.lang:type=OperatingSystem", "SystemCpuLoad", "ProcessCpuLoad", "SystemLoadAverage"), - jmxMetric("java.lang:type=Threading", "ThreadCount", "DaemonThreadCount"), - jmxMetric("java.lang:type=Memory", "HeapMemoryUsage", "NonHeapMemoryUsage"), - jmxMetric("java.lang:type=ClassLoading", "LoadedClassCount") - ); - - private static final String COUNTER = "counter"; - private static final String COUNTER_PREFIX = "info_counters_"; - - private final SystemCounters systemCounters; - private final HealthInfoService healthInfoService; - private final Addressing addressing; - private final InMemorySystemInfo inMemorySystemInfo; - private final BFTNode self; - private final Map endpointStatuses; - private final PeersView peersView; - - @Inject - public PrometheusService( - RuntimeProperties properties, - SystemCounters systemCounters, - PeersView peersView, - HealthInfoService healthInfoService, - InMemorySystemInfo inMemorySystemInfo, - @Self BFTNode self, - Addressing addressing - ) { - boolean enableTransactions = properties.get("api.transactions.enable", false); - this.endpointStatuses = Map.of("transactions", enableTransactions); - this.systemCounters = systemCounters; - this.peersView = peersView; - this.healthInfoService = healthInfoService; - this.inMemorySystemInfo = inMemorySystemInfo; - this.self = self; - this.addressing = addressing; - } - - public String getMetrics() { - var builder = new StringBuilder(); - - exportCounters(builder); - exportSystemInfo(builder); - - return builder.append('\n').toString(); - } - - private void exportSystemInfo(StringBuilder builder) { - var currentEpochView = inMemorySystemInfo.getCurrentView(); - - appendCounter(builder, "info_epochmanager_currentview_view", currentEpochView.getView().number()); - appendCounter(builder, "info_epochmanager_currentview_epoch", currentEpochView.getEpoch()); - appendCounter(builder, "total_peers", peersView.peers().count()); - - var totalValidators = inMemorySystemInfo.getEpochProof().getNextValidatorSet() - .map(BFTValidatorSet::getValidators) - .map(AbstractCollection::size) - .orElse(0); - - appendCounter(builder, "total_validators", totalValidators); - - appendJMXCounters(builder); - - appendCounterExtended( - builder, - prepareNodeInfo(), - "nodeinfo", - "Special metric used to convey information about the current node using labels. Value will always be 0.", - 0.0 - ); - } - - private String prepareNodeInfo() { - var builder = new StringBuilder("nodeinfo{"); - addEndpontStatuses(builder); - appendField(builder, "owner_address", addressing.forAccounts().of(REAddr.ofPubKeyAccount(self.getKey()))); - addBranchAndCommit(builder); - addValidatorAddress(builder); - addAccumulatorState(builder); - appendField(builder, "health", healthInfoService.nodeStatus().name()); - appendField(builder, "key", self.getKey().toHex()); - - return builder.append("}").toString(); - } - - private void addValidatorAddress(StringBuilder builder) { - appendField(builder, "own_validator_address", addressing.forValidators().of(self.getKey())); - - var inSet = inMemorySystemInfo.getEpochProof().getNextValidatorSet() - .map(set -> set.containsNode(self)).orElse(false); - - appendField(builder, "is_in_validator_set", inSet); - } - - private void addBranchAndCommit(StringBuilder builder) { - var branchAndCommit = Radix.systemVersionInfo().get(SYSTEM_VERSION_KEY).get(VERSION_STRING_KEY); - appendField(builder, "branch_and_commit", branchAndCommit); - } - - private void addAccumulatorState(StringBuilder builder) { - var accumulatorState = inMemorySystemInfo.getCurrentProof().getAccumulatorState(); - - appendField( - builder, - "version_accumulator", - accumulatorState.getStateVersion() + "/" + accumulatorState.getAccumulatorHash().toString() - ); - } - - private void addEndpontStatuses(StringBuilder builder) { - endpointStatuses.forEach((name, enabled) -> appendField(builder, name + "_enabled", enabled)); - } - - private void appendField(StringBuilder builder, String name, Object value) { - builder.append(name).append("=\"").append(value).append("\","); - } - - private void exportCounters(StringBuilder builder) { - EXPORT_LIST.forEach(counterType -> generateCounterEntry(counterType, builder)); - } - - private void generateCounterEntry(CounterType counterType, StringBuilder builder) { - var name = COUNTER_PREFIX + counterType.jsonPath().replace('.', '_'); - - long value = systemCounters.get(counterType); - - appendCounter(builder, name, value); - } - - private static void appendCounter(StringBuilder builder, String name, Number value) { - appendCounterExtended(builder, name, name, name, value.doubleValue()); - } - - private static void appendCounterExtended(StringBuilder builder, String name, String type, String help, Object value) { - builder - .append("# HELP ").append(help).append('\n') - .append("# TYPE ").append(type).append(' ').append(COUNTER).append('\n') - .append(name).append(' ').append(value).append('\n'); - } - - static class JmxMetric { - private final String objectNameString; - private final String[] metricAttributes; - - private JmxMetric(String objectNameString, String[] metricAttributes) { - this.objectNameString = objectNameString; - this.metricAttributes = metricAttributes; - } - - static JmxMetric jmxMetric(String objectName, String... attributes) { - return new JmxMetric(objectName, attributes); - } - - void readCounter(MBeanServerConnection connection, StringBuilder builder) { - try { - var objectName = connection.queryNames(new ObjectName(objectNameString), null) - .iterator() - .next(); - - var attributes = connection.getAttributes(objectName, metricAttributes).asList(); - - for (var attribute : attributes) { - var name = attribute.getName(); - - if (name.equals("Usage")) { - name = objectName.getKeyProperty("name"); - } - - var outName = name.toLowerCase(Locale.US) - .replace('.', '_') - .replace(' ', '_'); - - // this might break if more beans are parsed - if (attribute.getValue() instanceof CompositeDataSupport cds) { - appendCounter(builder, outName + "_init", (Number) cds.get("init")); - appendCounter(builder, outName + "_max", (Number) cds.get("max")); - appendCounter(builder, outName + "_committed", (Number) cds.get("committed")); - appendCounter(builder, outName + "_used", (Number) cds.get("used")); - } else { - appendCounter(builder, outName, (Number) attribute.getValue()); - } - } - } catch (InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e) { - log.error("Error while retrieving JMX metric " + objectNameString, e); - } - } - } - - private void appendJMXCounters(StringBuilder builder) { - var connection = ManagementFactory.getPlatformMBeanServer(); - JMX_METRICS.forEach(metric -> metric.readCounter(connection, builder)); - } + private static final Logger log = LogManager.getLogger(); + + private static final List EXPORT_LIST = Arrays.asList(CounterType.values()); + + private static final List JMX_METRICS = + List.of( + jmxMetric("java.lang:type=MemoryPool,name=G1 Eden Space", "Usage"), + jmxMetric("java.lang:type=MemoryPool,name=G1 Survivor Space", "Usage"), + jmxMetric("java.lang:type=MemoryPool,name=G1 Old Gen", "Usage"), + jmxMetric("java.lang:type=MemoryPool,name=Metaspace", "Usage"), + jmxMetric("java.lang:type=GarbageCollector,name=G1 Old Generation", "Usage"), + jmxMetric("java.lang:type=GarbageCollector,name=G1 Young Generation", "Usage"), + jmxMetric( + "java.lang:type=OperatingSystem", + "SystemCpuLoad", + "ProcessCpuLoad", + "SystemLoadAverage"), + jmxMetric("java.lang:type=Threading", "ThreadCount", "DaemonThreadCount"), + jmxMetric("java.lang:type=Memory", "HeapMemoryUsage", "NonHeapMemoryUsage"), + jmxMetric("java.lang:type=ClassLoading", "LoadedClassCount")); + + private static final String COUNTER = "counter"; + private static final String COUNTER_PREFIX = "info_counters_"; + + private final SystemCounters systemCounters; + private final HealthInfoService healthInfoService; + private final Addressing addressing; + private final InMemorySystemInfo inMemorySystemInfo; + private final BFTNode self; + private final Map endpointStatuses; + private final PeersView peersView; + + @Inject + public PrometheusService( + RuntimeProperties properties, + SystemCounters systemCounters, + PeersView peersView, + HealthInfoService healthInfoService, + InMemorySystemInfo inMemorySystemInfo, + @Self BFTNode self, + Addressing addressing) { + boolean enableTransactions = properties.get("api.transactions.enable", false); + this.endpointStatuses = Map.of("transactions", enableTransactions); + this.systemCounters = systemCounters; + this.peersView = peersView; + this.healthInfoService = healthInfoService; + this.inMemorySystemInfo = inMemorySystemInfo; + this.self = self; + this.addressing = addressing; + } + + public String getMetrics() { + var builder = new StringBuilder(); + + exportCounters(builder); + exportSystemInfo(builder); + + return builder.append('\n').toString(); + } + + private void exportSystemInfo(StringBuilder builder) { + var currentEpochView = inMemorySystemInfo.getCurrentView(); + + appendCounter( + builder, "info_epochmanager_currentview_view", currentEpochView.getView().number()); + appendCounter(builder, "info_epochmanager_currentview_epoch", currentEpochView.getEpoch()); + appendCounter(builder, "total_peers", peersView.peers().count()); + + var totalValidators = + inMemorySystemInfo + .getEpochProof() + .getNextValidatorSet() + .map(BFTValidatorSet::getValidators) + .map(AbstractCollection::size) + .orElse(0); + + appendCounter(builder, "total_validators", totalValidators); + + appendJMXCounters(builder); + + appendCounterExtended( + builder, + prepareNodeInfo(), + "nodeinfo", + "Special metric used to convey information about the current node using labels. Value will" + + " always be 0.", + 0.0); + } + + private String prepareNodeInfo() { + var builder = new StringBuilder("nodeinfo{"); + addEndpontStatuses(builder); + appendField( + builder, + "owner_address", + addressing.forAccounts().of(REAddr.ofPubKeyAccount(self.getKey()))); + addBranchAndCommit(builder); + addValidatorAddress(builder); + addAccumulatorState(builder); + appendField(builder, "health", healthInfoService.nodeStatus().name()); + appendField(builder, "key", self.getKey().toHex()); + + return builder.append("}").toString(); + } + + private void addValidatorAddress(StringBuilder builder) { + appendField(builder, "own_validator_address", addressing.forValidators().of(self.getKey())); + + var inSet = + inMemorySystemInfo + .getEpochProof() + .getNextValidatorSet() + .map(set -> set.containsNode(self)) + .orElse(false); + + appendField(builder, "is_in_validator_set", inSet); + } + + private void addBranchAndCommit(StringBuilder builder) { + var branchAndCommit = Radix.systemVersionInfo().get(SYSTEM_VERSION_KEY).get(VERSION_STRING_KEY); + appendField(builder, "branch_and_commit", branchAndCommit); + } + + private void addAccumulatorState(StringBuilder builder) { + var accumulatorState = inMemorySystemInfo.getCurrentProof().getAccumulatorState(); + + appendField( + builder, + "version_accumulator", + accumulatorState.getStateVersion() + + "/" + + accumulatorState.getAccumulatorHash().toString()); + } + + private void addEndpontStatuses(StringBuilder builder) { + endpointStatuses.forEach((name, enabled) -> appendField(builder, name + "_enabled", enabled)); + } + + private void appendField(StringBuilder builder, String name, Object value) { + builder.append(name).append("=\"").append(value).append("\","); + } + + private void exportCounters(StringBuilder builder) { + EXPORT_LIST.forEach(counterType -> generateCounterEntry(counterType, builder)); + } + + private void generateCounterEntry(CounterType counterType, StringBuilder builder) { + var name = COUNTER_PREFIX + counterType.jsonPath().replace('.', '_'); + + long value = systemCounters.get(counterType); + + appendCounter(builder, name, value); + } + + private static void appendCounter(StringBuilder builder, String name, Number value) { + appendCounterExtended(builder, name, name, name, value.doubleValue()); + } + + private static void appendCounterExtended( + StringBuilder builder, String name, String type, String help, Object value) { + builder + .append("# HELP ") + .append(help) + .append('\n') + .append("# TYPE ") + .append(type) + .append(' ') + .append(COUNTER) + .append('\n') + .append(name) + .append(' ') + .append(value) + .append('\n'); + } + + static class JmxMetric { + private final String objectNameString; + private final String[] metricAttributes; + + private JmxMetric(String objectNameString, String[] metricAttributes) { + this.objectNameString = objectNameString; + this.metricAttributes = metricAttributes; + } + + static JmxMetric jmxMetric(String objectName, String... attributes) { + return new JmxMetric(objectName, attributes); + } + + void readCounter(MBeanServerConnection connection, StringBuilder builder) { + try { + var objectName = + connection.queryNames(new ObjectName(objectNameString), null).iterator().next(); + + var attributes = connection.getAttributes(objectName, metricAttributes).asList(); + + for (var attribute : attributes) { + var name = attribute.getName(); + + if (name.equals("Usage")) { + name = objectName.getKeyProperty("name"); + } + + var outName = name.toLowerCase(Locale.US).replace('.', '_').replace(' ', '_'); + + // this might break if more beans are parsed + if (attribute.getValue() instanceof CompositeDataSupport cds) { + appendCounter(builder, outName + "_init", (Number) cds.get("init")); + appendCounter(builder, outName + "_max", (Number) cds.get("max")); + appendCounter(builder, outName + "_committed", (Number) cds.get("committed")); + appendCounter(builder, outName + "_used", (Number) cds.get("used")); + } else { + appendCounter(builder, outName, (Number) attribute.getValue()); + } + } + } catch (InstanceNotFoundException + | ReflectionException + | IOException + | MalformedObjectNameException e) { + log.error("Error while retrieving JMX metric " + objectNameString, e); + } + } + } + + private void appendJMXCounters(StringBuilder builder) { + var connection = ManagementFactory.getPlatformMBeanServer(); + JMX_METRICS.forEach(metric -> metric.readCounter(connection, builder)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTConfiguration.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTConfiguration.java index e6a02eee82..b40573125d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTConfiguration.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTConfiguration.java @@ -67,62 +67,55 @@ import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.VerifiedVertexStoreState; import com.radixdlt.consensus.liveness.ProposerElection; - import java.util.Objects; -/** - * Configuration for a bft instance which should be shared by all validators in the bft. - */ +/** Configuration for a bft instance which should be shared by all validators in the bft. */ public final class BFTConfiguration { - private final ProposerElection proposerElection; - private final BFTValidatorSet validatorSet; - private final VerifiedVertexStoreState vertexStoreState; + private final ProposerElection proposerElection; + private final BFTValidatorSet validatorSet; + private final VerifiedVertexStoreState vertexStoreState; - public BFTConfiguration( - ProposerElection proposerElection, - BFTValidatorSet validatorSet, - VerifiedVertexStoreState vertexStoreState - ) { - this.proposerElection = Objects.requireNonNull(proposerElection); - this.validatorSet = Objects.requireNonNull(validatorSet); - this.vertexStoreState = Objects.requireNonNull(vertexStoreState); - } + public BFTConfiguration( + ProposerElection proposerElection, + BFTValidatorSet validatorSet, + VerifiedVertexStoreState vertexStoreState) { + this.proposerElection = Objects.requireNonNull(proposerElection); + this.validatorSet = Objects.requireNonNull(validatorSet); + this.vertexStoreState = Objects.requireNonNull(vertexStoreState); + } - public ProposerElection getProposerElection() { - return proposerElection; - } + public ProposerElection getProposerElection() { + return proposerElection; + } - public BFTValidatorSet getValidatorSet() { - return validatorSet; - } + public BFTValidatorSet getValidatorSet() { + return validatorSet; + } - public VerifiedVertexStoreState getVertexStoreState() { - return vertexStoreState; - } + public VerifiedVertexStoreState getVertexStoreState() { + return vertexStoreState; + } - @Override - public int hashCode() { - return Objects.hash( - this.proposerElection, - this.validatorSet, - this.vertexStoreState - ); - } + @Override + public int hashCode() { + return Objects.hash(this.proposerElection, this.validatorSet, this.vertexStoreState); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof BFTConfiguration) { - BFTConfiguration that = (BFTConfiguration) obj; - return Objects.equals(this.validatorSet, that.validatorSet) - && Objects.equals(this.vertexStoreState, that.vertexStoreState) - && Objects.equals(this.proposerElection, that.proposerElection); - } - return false; - } + @Override + public boolean equals(Object obj) { + if (obj instanceof BFTConfiguration) { + BFTConfiguration that = (BFTConfiguration) obj; + return Objects.equals(this.validatorSet, that.validatorSet) + && Objects.equals(this.vertexStoreState, that.vertexStoreState) + && Objects.equals(this.proposerElection, that.proposerElection); + } + return false; + } - @Override - public String toString() { - return String.format("%s[validatorSet=%s, vertexStoreState=%s]", - getClass().getSimpleName(), this.validatorSet, this.vertexStoreState); - } + @Override + public String toString() { + return String.format( + "%s[validatorSet=%s, vertexStoreState=%s]", + getClass().getSimpleName(), this.validatorSet, this.vertexStoreState); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventProcessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventProcessor.java index 0ab8ba9e77..6f0d715ec8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventProcessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventProcessor.java @@ -72,49 +72,49 @@ /** * Processor of BFT events. * - * Implementations are not expected to be thread-safe. + *

Implementations are not expected to be thread-safe. */ public interface BFTEventProcessor { - /** - * The initialization call. Must be called first and only once at - * the beginning of the BFT's lifetime. - */ - void start(); + /** + * The initialization call. Must be called first and only once at the beginning of the BFT's + * lifetime. + */ + void start(); - /** - * Process a local view update message. - * - * @param viewUpdate the view update message - */ - void processViewUpdate(ViewUpdate viewUpdate); + /** + * Process a local view update message. + * + * @param viewUpdate the view update message + */ + void processViewUpdate(ViewUpdate viewUpdate); - /** - * Process a consensus vote message. - * - * @param vote the vote message - */ - void processVote(Vote vote); + /** + * Process a consensus vote message. + * + * @param vote the vote message + */ + void processVote(Vote vote); - /** - * Process a consensus proposal message. - * - * @param proposal the proposal message - */ - void processProposal(Proposal proposal); + /** + * Process a consensus proposal message. + * + * @param proposal the proposal message + */ + void processProposal(Proposal proposal); - /** - * Process a local consensus timeout message. - * - * @param scheduledLocalTimeout the view corresponding to the timeout - */ - void processLocalTimeout(ScheduledLocalTimeout scheduledLocalTimeout); + /** + * Process a local consensus timeout message. + * + * @param scheduledLocalTimeout the view corresponding to the timeout + */ + void processLocalTimeout(ScheduledLocalTimeout scheduledLocalTimeout); - /** - * Process a BFT update. - * - * @param update the BFT update - */ - void processBFTUpdate(BFTInsertUpdate update); + /** + * Process a BFT update. + * + * @param update the BFT update + */ + void processBFTUpdate(BFTInsertUpdate update); - void processBFTRebuildUpdate(BFTRebuildUpdate update); + void processBFTRebuildUpdate(BFTRebuildUpdate update); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java index c9469ff753..382afd19e4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java @@ -66,31 +66,28 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTSyncer; +import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.bft.ViewQuorumReached; import com.radixdlt.consensus.bft.ViewUpdate; import com.radixdlt.consensus.liveness.Pacemaker; -import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.safety.SafetyRules; import com.radixdlt.environment.EventProcessor; -/** - * Creates a new bft processor - */ +/** Creates a new bft processor */ public interface BFTFactory { - /** - * Create a new clean BFT processor - * - * @return a new bft processor - */ - BFTEventProcessor create( - BFTNode self, - Pacemaker pacemaker, - VertexStore vertexStore, - BFTSyncer bftSyncer, - EventProcessor viewQuorumReachedEventProcessor, - BFTValidatorSet validatorSet, - ViewUpdate viewUpdate, - SafetyRules safetyRules - ); + /** + * Create a new clean BFT processor + * + * @return a new bft processor + */ + BFTEventProcessor create( + BFTNode self, + Pacemaker pacemaker, + VertexStore vertexStore, + BFTSyncer bftSyncer, + EventProcessor viewQuorumReachedEventProcessor, + BFTValidatorSet validatorSet, + ViewUpdate viewUpdate, + SafetyRules safetyRules); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTHeader.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTHeader.java index 9a5ebc162e..285c552ccc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTHeader.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/BFTHeader.java @@ -1,168 +1,162 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; -import java.util.Objects; - -import static java.util.Objects.requireNonNull; - -/** - * The bft header which gets voted upon by consensus. - */ -@Immutable -@SerializerId2("consensus.bft_header") -public final class BFTHeader { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - private final View view; - - @JsonProperty("vertex_id") - @DsonOutput(Output.ALL) - private final HashCode vertexId; - - @JsonProperty("ledger_header") - @DsonOutput(Output.ALL) - private final LedgerHeader ledgerHeader; - - // TODO: Move command output to a more opaque data structure - public BFTHeader( - View view, // consensus data - HashCode vertexId, // consensus data - LedgerHeader ledgerHeader - ) { - this.view = requireNonNull(view); - this.vertexId = requireNonNull(vertexId); - this.ledgerHeader = requireNonNull(ledgerHeader); - } - - @JsonCreator - public static BFTHeader create( - @JsonProperty("view") long number, - @JsonProperty(value = "vertex_id", required = true) HashCode vertexId, - @JsonProperty(value = "ledger_header", required = true) LedgerHeader ledgerHeader - ) { - return new BFTHeader(View.of(number), vertexId, ledgerHeader); - } - - public static BFTHeader ofGenesisAncestor(LedgerHeader ledgerHeader) { - return new BFTHeader(View.genesis(), HashUtils.zero256(), ledgerHeader); - } - - public LedgerHeader getLedgerHeader() { - return ledgerHeader; - } - - public View getView() { - return view; - } - - public HashCode getVertexId() { - return vertexId; - } - - @JsonProperty("view") - @DsonOutput(Output.ALL) - private long getSerializerView() { - return view.number(); - } - - @Override - public int hashCode() { - return Objects.hash(this.vertexId, this.view, this.ledgerHeader); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - return (o instanceof BFTHeader other) - && Objects.equals(this.view, other.view) - && Objects.equals(this.vertexId, other.vertexId) - && Objects.equals(this.ledgerHeader, other.ledgerHeader); - } - - @Override - public String toString() { - return String.format("%s{view=%s ledger=%s}", - getClass().getSimpleName(), this.view, this.ledgerHeader - ); - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.hash.HashCode; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; +import java.util.Objects; +import javax.annotation.concurrent.Immutable; + +/** The bft header which gets voted upon by consensus. */ +@Immutable +@SerializerId2("consensus.bft_header") +public final class BFTHeader { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + private final View view; + + @JsonProperty("vertex_id") + @DsonOutput(Output.ALL) + private final HashCode vertexId; + + @JsonProperty("ledger_header") + @DsonOutput(Output.ALL) + private final LedgerHeader ledgerHeader; + + // TODO: Move command output to a more opaque data structure + public BFTHeader( + View view, // consensus data + HashCode vertexId, // consensus data + LedgerHeader ledgerHeader) { + this.view = requireNonNull(view); + this.vertexId = requireNonNull(vertexId); + this.ledgerHeader = requireNonNull(ledgerHeader); + } + + @JsonCreator + public static BFTHeader create( + @JsonProperty("view") long number, + @JsonProperty(value = "vertex_id", required = true) HashCode vertexId, + @JsonProperty(value = "ledger_header", required = true) LedgerHeader ledgerHeader) { + return new BFTHeader(View.of(number), vertexId, ledgerHeader); + } + + public static BFTHeader ofGenesisAncestor(LedgerHeader ledgerHeader) { + return new BFTHeader(View.genesis(), HashUtils.zero256(), ledgerHeader); + } + + public LedgerHeader getLedgerHeader() { + return ledgerHeader; + } + + public View getView() { + return view; + } + + public HashCode getVertexId() { + return vertexId; + } + + @JsonProperty("view") + @DsonOutput(Output.ALL) + private long getSerializerView() { + return view.number(); + } + + @Override + public int hashCode() { + return Objects.hash(this.vertexId, this.view, this.ledgerHeader); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + return (o instanceof BFTHeader other) + && Objects.equals(this.view, other.view) + && Objects.equals(this.vertexId, other.vertexId) + && Objects.equals(this.ledgerHeader, other.ledgerHeader); + } + + @Override + public String toString() { + return String.format( + "%s{view=%s ledger=%s}", getClass().getSimpleName(), this.view, this.ledgerHeader); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java index 7061c912d9..8333a6f45a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java @@ -67,35 +67,35 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.View; -/** - * A consensus event which requires syncing to be effectively - * processed - */ -//TODO: make interface sealed +/** A consensus event which requires syncing to be effectively processed */ +// TODO: make interface sealed public interface ConsensusEvent { - /** - * Retrieve the epoch number the consensus message is a part of - * @return the epoch number - */ - long getEpoch(); + /** + * Retrieve the epoch number the consensus message is a part of + * + * @return the epoch number + */ + long getEpoch(); - /** - * Get the view the consensus event is meant for - * @return view of consensus event - */ - View getView(); + /** + * Get the view the consensus event is meant for + * + * @return view of consensus event + */ + View getView(); - /** - * Get the node author of this consensus message - * @return the node author - */ - BFTNode getAuthor(); + /** + * Get the node author of this consensus message + * + * @return the node author + */ + BFTNode getAuthor(); - /** - * Retrieves the {@link HighQC} associated with the event. - * - * @return {@link HighQC} associated with event - */ - HighQC highQC(); + /** + * Retrieves the {@link HighQC} associated with the event. + * + * @return {@link HighQC} associated with event + */ + HighQC highQC(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusHasher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusHasher.java index 855e53ff30..f6ab82584b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusHasher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusHasher.java @@ -66,46 +66,46 @@ import com.google.common.hash.HashCode; import com.radixdlt.crypto.Hasher; - import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; public final class ConsensusHasher { - private ConsensusHasher() { - throw new IllegalStateException(); - } + private ConsensusHasher() { + throw new IllegalStateException(); + } - public static HashCode toHash(HashCode opaque, LedgerHeader header, long nodeTimestamp, Hasher hasher) { - var raw = new ByteArrayOutputStream(); - var outputStream = new DataOutputStream(raw); - try { - outputStream.writeInt(header != null ? 0 : 1); // 4 bytes (Version) - outputStream.write(opaque.asBytes()); // 32 bytes - if (header != null) { - outputStream.write(header.getAccumulatorState().getAccumulatorHash().asBytes()); // 32 bytes - outputStream.writeLong(header.getAccumulatorState().getStateVersion()); // 8 bytes - outputStream.writeLong(header.getEpoch()); // 8 bytes - outputStream.writeLong(header.getView().number()); // 8 bytes - outputStream.writeLong(header.timestamp()); // 8 bytes - if (header.getNextValidatorSet().isPresent()) { - var vset = header.getNextValidatorSet().get(); - outputStream.writeInt(vset.getValidators().size()); // 4 bytes - for (var v : vset.getValidators().asList()) { - var key = v.getNode().getKey().getCompressedBytes(); - outputStream.write(key); - var power = v.getPower(); - outputStream.write(power.toByteArray()); - } - } else { - outputStream.writeInt(0); // 4 bytes - } - } - outputStream.writeLong(nodeTimestamp); // 8 bytes - } catch (IOException e) { - throw new IllegalStateException(); - } - var toHash = raw.toByteArray(); - return hasher.hashBytes(toHash); - } -} \ No newline at end of file + public static HashCode toHash( + HashCode opaque, LedgerHeader header, long nodeTimestamp, Hasher hasher) { + var raw = new ByteArrayOutputStream(); + var outputStream = new DataOutputStream(raw); + try { + outputStream.writeInt(header != null ? 0 : 1); // 4 bytes (Version) + outputStream.write(opaque.asBytes()); // 32 bytes + if (header != null) { + outputStream.write(header.getAccumulatorState().getAccumulatorHash().asBytes()); // 32 bytes + outputStream.writeLong(header.getAccumulatorState().getStateVersion()); // 8 bytes + outputStream.writeLong(header.getEpoch()); // 8 bytes + outputStream.writeLong(header.getView().number()); // 8 bytes + outputStream.writeLong(header.timestamp()); // 8 bytes + if (header.getNextValidatorSet().isPresent()) { + var vset = header.getNextValidatorSet().get(); + outputStream.writeInt(vset.getValidators().size()); // 4 bytes + for (var v : vset.getValidators().asList()) { + var key = v.getNode().getKey().getCompressedBytes(); + outputStream.write(key); + var power = v.getPower(); + outputStream.write(power.toByteArray()); + } + } else { + outputStream.writeInt(0); // 4 bytes + } + } + outputStream.writeLong(nodeTimestamp); // 8 bytes + } catch (IOException e) { + throw new IllegalStateException(); + } + var toHash = raw.toByteArray(); + return hasher.hashBytes(toHash); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashSigner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashSigner.java index d32fae1ad3..6ebd2a4246 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashSigner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashSigner.java @@ -69,27 +69,25 @@ import com.radixdlt.SecurityCritical.SecurityKind; import com.radixdlt.crypto.ECDSASignature; -/** - * Signs a hash. - */ +/** Signs a hash. */ @FunctionalInterface @SecurityCritical(SecurityKind.SIG_SIGN) public interface HashSigner { - /** - * Sign the specified hash with the specified key. - * - * @param hash The hash to sign - * @return The {@link ECDSASignature} - */ - ECDSASignature sign(byte[] hash); + /** + * Sign the specified hash with the specified key. + * + * @param hash The hash to sign + * @return The {@link ECDSASignature} + */ + ECDSASignature sign(byte[] hash); - /** - * Sign the specified hash with the specified key. - * - * @param hash The hash to sign - * @return The {@link ECDSASignature} - */ - default ECDSASignature sign(HashCode hash) { - return sign(hash.asBytes()); - } + /** + * Sign the specified hash with the specified key. + * + * @param hash The hash to sign + * @return The {@link ECDSASignature} + */ + default ECDSASignature sign(HashCode hash) { + return sign(hash.asBytes()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashVerifier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashVerifier.java index e33e316aeb..8d0954fc90 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashVerifier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HashVerifier.java @@ -68,19 +68,16 @@ import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECPublicKey; -/** - * Verifies signatures against hashes. - */ +/** Verifies signatures against hashes. */ @FunctionalInterface public interface HashVerifier { - /** - * Verify the specified signature against the specified hash with - * the specified public key. - * - * @param pubKey The public key to verify with - * @param hash The the hash to verify - * @param sig The signature to verify - * @return {@code true} if the signature matches, {@code false} otherwise - */ - boolean verify(ECPublicKey pubKey, HashCode hash, ECDSASignature sig); + /** + * Verify the specified signature against the specified hash with the specified public key. + * + * @param pubKey The public key to verify with + * @param hash The the hash to verify + * @param sig The signature to verify + * @return {@code true} if the signature matches, {@code false} otherwise + */ + boolean verify(ECPublicKey pubKey, HashCode hash, ECDSASignature sig); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HighQC.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HighQC.java index a2f242574c..ede30c6eeb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HighQC.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/HighQC.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -73,143 +75,136 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.Objects; import java.util.Optional; +import javax.annotation.concurrent.Immutable; -import static java.util.Objects.requireNonNull; - -/** - * Current state of synchronisation for sending node. - */ +/** Current state of synchronisation for sending node. */ @Immutable @SerializerId2("consensus.high_qc") public final class HighQC { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("highest_qc") - @DsonOutput(Output.ALL) - private final QuorumCertificate highestQC; - - @JsonProperty("committed_qc") - @DsonOutput(Output.ALL) - private final QuorumCertificate highestCommittedQC; - - @JsonProperty("highest_tc") - @DsonOutput(Output.ALL) - private final TimeoutCertificate highestTC; - - @JsonCreator - @VisibleForTesting - static HighQC serializerCreate( - @JsonProperty(value = "highest_qc", required = true) QuorumCertificate highestQC, - @JsonProperty("committed_qc") QuorumCertificate highestCommittedQC, - @JsonProperty("highest_tc") TimeoutCertificate highestTC - ) { - return new HighQC(highestQC, highestCommittedQC, highestTC); - } - - private HighQC( - QuorumCertificate highestQC, - QuorumCertificate highestCommittedQC, - TimeoutCertificate highestTC - ) { - this.highestQC = requireNonNull(highestQC); - // Don't include separate committedQC if it is the same as highQC. - // This significantly reduces the serialised size of the object. - if (highestCommittedQC == null || highestQC.equals(highestCommittedQC)) { - this.highestCommittedQC = null; - } else { - this.highestCommittedQC = highestCommittedQC; - } - - // only relevant if it's for a higher view than QC - if (highestTC != null && highestTC.getView().gt(highestQC.getView())) { - this.highestTC = highestTC; - } else { - this.highestTC = null; - } - } - - /** - * Creates a {@link HighQC} from the a QC - * - * @param qc The qc - * @return A new {@link HighQC} - */ - public static HighQC from(QuorumCertificate qc) { - return HighQC.from(qc, qc, Optional.empty()); - } - - /** - * Creates a {@link HighQC} from the specified QCs. - *

- * Note that highestCommittedQC->committed needs to be an ancestor of - * highestQC->proposed, but highestCommittedQC->proposed does not need - * to be an ancestor of highestQC->proposed. - * - * @param highestQC The highest QC we have seen - * @param highestCommittedQC The highest QC we have committed - * @param highestTC The highest timeout certificate - * @return A new {@link HighQC} - */ - public static HighQC from( - QuorumCertificate highestQC, - QuorumCertificate highestCommittedQC, - Optional highestTC - ) { - return new HighQC(highestQC, highestCommittedQC, highestTC.orElse(null)); - } - - public Optional highestTC() { - return Optional.ofNullable(this.highestTC); - } - - public QuorumCertificate highestQC() { - return this.highestQC; - } - - public View getHighestView() { - if (this.highestTC != null && this.highestTC.getView().gt(this.highestQC.getView())) { - return this.highestTC.getView(); - } else { - return this.highestQC.getView(); - } - } - - public QuorumCertificate highestCommittedQC() { - return this.highestCommittedQC == null ? this.highestQC : this.highestCommittedQC; - } - - @VisibleForTesting - QuorumCertificate rawHighestCommittedQC() { - return this.highestCommittedQC; - } - - @Override - public int hashCode() { - return Objects.hash(this.highestQC, this.highestCommittedQC, this.highestTC); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - return (o instanceof HighQC that) - && Objects.equals(this.highestCommittedQC, that.highestCommittedQC) - && Objects.equals(this.highestQC, that.highestQC) - && Objects.equals(this.highestTC, that.highestTC); - } - - @Override - public String toString() { - String highestCommittedString = (this.highestCommittedQC == null) ? "" : this.highestCommittedQC.toString(); - return String.format("%s[highest=%s, highestCommitted=%s, highestTC=%s]", - getClass().getSimpleName(), this.highestQC, highestCommittedString, highestTC); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("highest_qc") + @DsonOutput(Output.ALL) + private final QuorumCertificate highestQC; + + @JsonProperty("committed_qc") + @DsonOutput(Output.ALL) + private final QuorumCertificate highestCommittedQC; + + @JsonProperty("highest_tc") + @DsonOutput(Output.ALL) + private final TimeoutCertificate highestTC; + + @JsonCreator + @VisibleForTesting + static HighQC serializerCreate( + @JsonProperty(value = "highest_qc", required = true) QuorumCertificate highestQC, + @JsonProperty("committed_qc") QuorumCertificate highestCommittedQC, + @JsonProperty("highest_tc") TimeoutCertificate highestTC) { + return new HighQC(highestQC, highestCommittedQC, highestTC); + } + + private HighQC( + QuorumCertificate highestQC, + QuorumCertificate highestCommittedQC, + TimeoutCertificate highestTC) { + this.highestQC = requireNonNull(highestQC); + // Don't include separate committedQC if it is the same as highQC. + // This significantly reduces the serialised size of the object. + if (highestCommittedQC == null || highestQC.equals(highestCommittedQC)) { + this.highestCommittedQC = null; + } else { + this.highestCommittedQC = highestCommittedQC; + } + + // only relevant if it's for a higher view than QC + if (highestTC != null && highestTC.getView().gt(highestQC.getView())) { + this.highestTC = highestTC; + } else { + this.highestTC = null; + } + } + + /** + * Creates a {@link HighQC} from the a QC + * + * @param qc The qc + * @return A new {@link HighQC} + */ + public static HighQC from(QuorumCertificate qc) { + return HighQC.from(qc, qc, Optional.empty()); + } + + /** + * Creates a {@link HighQC} from the specified QCs. + * + *

Note that highestCommittedQC->committed needs to be an ancestor of highestQC->proposed, but + * highestCommittedQC->proposed does not need to be an ancestor of highestQC->proposed. + * + * @param highestQC The highest QC we have seen + * @param highestCommittedQC The highest QC we have committed + * @param highestTC The highest timeout certificate + * @return A new {@link HighQC} + */ + public static HighQC from( + QuorumCertificate highestQC, + QuorumCertificate highestCommittedQC, + Optional highestTC) { + return new HighQC(highestQC, highestCommittedQC, highestTC.orElse(null)); + } + + public Optional highestTC() { + return Optional.ofNullable(this.highestTC); + } + + public QuorumCertificate highestQC() { + return this.highestQC; + } + + public View getHighestView() { + if (this.highestTC != null && this.highestTC.getView().gt(this.highestQC.getView())) { + return this.highestTC.getView(); + } else { + return this.highestQC.getView(); + } + } + + public QuorumCertificate highestCommittedQC() { + return this.highestCommittedQC == null ? this.highestQC : this.highestCommittedQC; + } + + @VisibleForTesting + QuorumCertificate rawHighestCommittedQC() { + return this.highestCommittedQC; + } + + @Override + public int hashCode() { + return Objects.hash(this.highestQC, this.highestCommittedQC, this.highestTC); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + return (o instanceof HighQC that) + && Objects.equals(this.highestCommittedQC, that.highestCommittedQC) + && Objects.equals(this.highestQC, that.highestQC) + && Objects.equals(this.highestTC, that.highestTC); + } + + @Override + public String toString() { + String highestCommittedString = + (this.highestCommittedQC == null) ? "" : this.highestCommittedQC.toString(); + return String.format( + "%s[highest=%s, highestCommitted=%s, highestTC=%s]", + getClass().getSimpleName(), this.highestQC, highestCommittedString, highestTC); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Ledger.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Ledger.java index 680efcbddc..ccd035eb42 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Ledger.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Ledger.java @@ -69,17 +69,15 @@ import java.util.LinkedList; import java.util.Optional; -/** - * A distributed computer which manages the computed state in a BFT. - */ +/** A distributed computer which manages the computed state in a BFT. */ public interface Ledger { - /** - * Given a proposed vertex, executes prepare stage on - * the state computer, the result of which gets persisted on ledger. - * - * @param previous parents of given vertex - * @param vertex vertex to prepare - * @return the results of executing the prepare stage - */ - Optional prepare(LinkedList previous, VerifiedVertex vertex); + /** + * Given a proposed vertex, executes prepare stage on the state computer, the result of which gets + * persisted on ledger. + * + * @param previous parents of given vertex + * @param vertex vertex to prepare + * @return the results of executing the prepare stage + */ + Optional prepare(LinkedList previous, VerifiedVertex vertex); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerHeader.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerHeader.java index 529cc5a5ca..e71f7bf594 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerHeader.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerHeader.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -78,167 +80,165 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.Objects; import java.util.Optional; +import javax.annotation.concurrent.Immutable; -import static java.util.Objects.requireNonNull; - -/** - * Ledger accumulator which gets voted and agreed upon - */ +/** Ledger accumulator which gets voted and agreed upon */ @Immutable @SerializerId2("consensus.ledger_header") public final class LedgerHeader { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("epoch") - @DsonOutput(Output.ALL) - private final long epoch; - - private final View view; - - @JsonProperty("accumulator_state") - @DsonOutput(Output.ALL) - private final AccumulatorState accumulatorState; - - @JsonProperty("timestamp") - @DsonOutput(Output.ALL) - private final long timestamp; // TODO: Move into command accumulator - - @JsonProperty("next_validators") - @DsonOutput(Output.ALL) - private final ImmutableSet nextValidators; - - // TODO: Replace isEndOfEpoch with nextValidatorSet - @JsonCreator - @VisibleForTesting - LedgerHeader( - @JsonProperty("epoch") long epoch, - @JsonProperty("view") long view, - @JsonProperty(value = "accumulator_state", required = true) AccumulatorState accumulatorState, - @JsonProperty("timestamp") long timestamp, - @JsonProperty("next_validators") ImmutableSet nextValidators - ) { - this(epoch, View.of(view), accumulatorState, timestamp, nextValidators); - } - - private LedgerHeader( - long epoch, - View view, - AccumulatorState accumulatorState, - long timestamp, - ImmutableSet nextValidators - ) { - this.epoch = epoch; - - if (epoch < 0) { - throw new IllegalArgumentException("Epoch can't be < 0"); - } - - this.view = view; - this.accumulatorState = requireNonNull(accumulatorState); - this.nextValidators = nextValidators; - this.timestamp = timestamp; - } - - //TODO: used only for tests, move elsewhere https://radixdlt.atlassian.net/browse/NT-2 - public static LedgerHeader mocked() { - return new LedgerHeader(0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0, null); - } - - public static LedgerHeader genesis(AccumulatorState accumulatorState, BFTValidatorSet nextValidators, long timestamp) { - return new LedgerHeader( - 0, View.genesis(), accumulatorState, timestamp, - nextValidators == null ? null : nextValidators.getValidators() - ); - } - - public static LedgerHeader create( - long epoch, - View view, - AccumulatorState accumulatorState, - long timestamp - ) { - return new LedgerHeader(epoch, view, accumulatorState, timestamp, null); - } - - public static LedgerHeader create( - long epoch, - View view, - AccumulatorState accumulatorState, - long timestamp, - BFTValidatorSet validatorSet - ) { - return new LedgerHeader(epoch, view, accumulatorState, timestamp, validatorSet == null ? null : validatorSet.getValidators()); - } - - public LedgerHeader updateViewAndTimestamp(View view, long timestamp) { - return new LedgerHeader( - this.epoch, - view, - this.accumulatorState, - timestamp, - this.nextValidators - ); - } - - @JsonProperty("view") - @DsonOutput(Output.ALL) - private long getSerializerView() { - return view.number(); - } - - public View getView() { - return view; - } - - public Optional getNextValidatorSet() { - return Optional.ofNullable(nextValidators).map(BFTValidatorSet::from); - } - - public AccumulatorState getAccumulatorState() { - return accumulatorState; - } - - public long getEpoch() { - return epoch; - } - - public boolean isEndOfEpoch() { - return nextValidators != null; - } - - public long timestamp() { - return this.timestamp; - } - - @Override - public int hashCode() { - return Objects.hash(this.accumulatorState, this.timestamp, this.epoch, this.view, this.nextValidators); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - return (o instanceof LedgerHeader other) - && this.timestamp == other.timestamp - && Objects.equals(this.accumulatorState, other.accumulatorState) - && this.epoch == other.epoch - && Objects.equals(this.view, other.view) - && Objects.equals(this.nextValidators, other.nextValidators); - } - - @Override - public String toString() { - return String.format("%s{accumulator=%s timestamp=%s epoch=%s view=%s nextValidators=%s}", - getClass().getSimpleName(), this.accumulatorState, this.timestamp, this.epoch, this.view, this.nextValidators - ); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("epoch") + @DsonOutput(Output.ALL) + private final long epoch; + + private final View view; + + @JsonProperty("accumulator_state") + @DsonOutput(Output.ALL) + private final AccumulatorState accumulatorState; + + @JsonProperty("timestamp") + @DsonOutput(Output.ALL) + private final long timestamp; // TODO: Move into command accumulator + + @JsonProperty("next_validators") + @DsonOutput(Output.ALL) + private final ImmutableSet nextValidators; + + // TODO: Replace isEndOfEpoch with nextValidatorSet + @JsonCreator + @VisibleForTesting + LedgerHeader( + @JsonProperty("epoch") long epoch, + @JsonProperty("view") long view, + @JsonProperty(value = "accumulator_state", required = true) AccumulatorState accumulatorState, + @JsonProperty("timestamp") long timestamp, + @JsonProperty("next_validators") ImmutableSet nextValidators) { + this(epoch, View.of(view), accumulatorState, timestamp, nextValidators); + } + + private LedgerHeader( + long epoch, + View view, + AccumulatorState accumulatorState, + long timestamp, + ImmutableSet nextValidators) { + this.epoch = epoch; + + if (epoch < 0) { + throw new IllegalArgumentException("Epoch can't be < 0"); + } + + this.view = view; + this.accumulatorState = requireNonNull(accumulatorState); + this.nextValidators = nextValidators; + this.timestamp = timestamp; + } + + // TODO: used only for tests, move elsewhere https://radixdlt.atlassian.net/browse/NT-2 + public static LedgerHeader mocked() { + return new LedgerHeader( + 0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0, null); + } + + public static LedgerHeader genesis( + AccumulatorState accumulatorState, BFTValidatorSet nextValidators, long timestamp) { + return new LedgerHeader( + 0, + View.genesis(), + accumulatorState, + timestamp, + nextValidators == null ? null : nextValidators.getValidators()); + } + + public static LedgerHeader create( + long epoch, View view, AccumulatorState accumulatorState, long timestamp) { + return new LedgerHeader(epoch, view, accumulatorState, timestamp, null); + } + + public static LedgerHeader create( + long epoch, + View view, + AccumulatorState accumulatorState, + long timestamp, + BFTValidatorSet validatorSet) { + return new LedgerHeader( + epoch, + view, + accumulatorState, + timestamp, + validatorSet == null ? null : validatorSet.getValidators()); + } + + public LedgerHeader updateViewAndTimestamp(View view, long timestamp) { + return new LedgerHeader( + this.epoch, view, this.accumulatorState, timestamp, this.nextValidators); + } + + @JsonProperty("view") + @DsonOutput(Output.ALL) + private long getSerializerView() { + return view.number(); + } + + public View getView() { + return view; + } + + public Optional getNextValidatorSet() { + return Optional.ofNullable(nextValidators).map(BFTValidatorSet::from); + } + + public AccumulatorState getAccumulatorState() { + return accumulatorState; + } + + public long getEpoch() { + return epoch; + } + + public boolean isEndOfEpoch() { + return nextValidators != null; + } + + public long timestamp() { + return this.timestamp; + } + + @Override + public int hashCode() { + return Objects.hash( + this.accumulatorState, this.timestamp, this.epoch, this.view, this.nextValidators); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + return (o instanceof LedgerHeader other) + && this.timestamp == other.timestamp + && Objects.equals(this.accumulatorState, other.accumulatorState) + && this.epoch == other.epoch + && Objects.equals(this.view, other.view) + && Objects.equals(this.nextValidators, other.nextValidators); + } + + @Override + public String toString() { + return String.format( + "%s{accumulator=%s timestamp=%s epoch=%s view=%s nextValidators=%s}", + getClass().getSimpleName(), + this.accumulatorState, + this.timestamp, + this.epoch, + this.view, + this.nextValidators); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerProof.java index 8a891e31ce..515c9735d1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/LedgerProof.java @@ -79,154 +79,139 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.Comparator; import java.util.Objects; import java.util.Optional; +import javax.annotation.concurrent.Immutable; -/** - * Ledger header with proof - */ +/** Ledger header with proof */ @Immutable @SerializerId2("ledger.proof") public final class LedgerProof { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - // proposed - @JsonProperty("opaque") - @DsonOutput(Output.ALL) - private final HashCode opaque; - - // committed ledgerState - @JsonProperty("ledgerState") - @DsonOutput(Output.ALL) - private final LedgerHeader ledgerHeader; - - @JsonProperty("signatures") - @DsonOutput(Output.ALL) - private final TimestampedECDSASignatures signatures; - - @JsonCreator - public LedgerProof( - @JsonProperty(value = "opaque", required = true) HashCode opaque, - @JsonProperty(value = "ledgerState", required = true) LedgerHeader ledgerHeader, - @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures - ) { - this.opaque = Objects.requireNonNull(opaque); - this.ledgerHeader = Objects.requireNonNull(ledgerHeader); - this.signatures = Objects.requireNonNull(signatures); - } - - public static LedgerProof mock() { - var acc = new AccumulatorState(0, HashUtils.zero256()); - var header = LedgerHeader.create(0, View.genesis(), acc, 0); - return new LedgerProof( - HashUtils.zero256(), - header, - new TimestampedECDSASignatures() - ); - } - - public static LedgerProof genesis(AccumulatorState accumulatorState, BFTValidatorSet nextValidators, long timestamp) { - var genesisLedgerHeader = LedgerHeader.genesis(accumulatorState, nextValidators, timestamp); - return new LedgerProof( - HashUtils.zero256(), - genesisLedgerHeader, - new TimestampedECDSASignatures() - ); - } - - public static final class OrderByEpochAndVersionComparator implements Comparator { - @Override - public int compare(LedgerProof p0, LedgerProof p1) { - if (p0.ledgerHeader.getEpoch() != p1.ledgerHeader.getEpoch()) { - return p0.ledgerHeader.getEpoch() > p1.ledgerHeader.getEpoch() ? 1 : -1; - } - - if (p0.ledgerHeader.isEndOfEpoch() != p1.ledgerHeader.isEndOfEpoch()) { - return p0.ledgerHeader.isEndOfEpoch() ? 1 : -1; - } - - return Long.compare( - p0.ledgerHeader.getAccumulatorState().getStateVersion(), - p1.ledgerHeader.getAccumulatorState().getStateVersion() - ); - } - } - - public DtoLedgerProof toDto() { - return new DtoLedgerProof( - opaque, - ledgerHeader, - signatures - ); - } - - public LedgerHeader getRaw() { - return ledgerHeader; - } - - public Optional getNextValidatorSet() { - return ledgerHeader.getNextValidatorSet(); - } - - public long getEpoch() { - return ledgerHeader.getEpoch(); - } - - public View getView() { - return ledgerHeader.getView(); - } - - public AccumulatorState getAccumulatorState() { - return ledgerHeader.getAccumulatorState(); - } - - // TODO: Remove - public long getStateVersion() { - return ledgerHeader.getAccumulatorState().getStateVersion(); - } - - public long timestamp() { - return ledgerHeader.timestamp(); - } - - public boolean isEndOfEpoch() { - return ledgerHeader.isEndOfEpoch(); - } - - public TimestampedECDSASignatures getSignatures() { - return signatures; - } - - public ImmutableList getSignersWithout(BFTNode remove) { - return signatures.getSignatures().keySet().stream() - .filter(n -> !n.equals(remove)) - .collect(ImmutableList.toImmutableList()); - } - - @Override - public int hashCode() { - return Objects.hash(opaque, ledgerHeader, signatures); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof LedgerProof)) { - return false; - } - - LedgerProof other = (LedgerProof) o; - return Objects.equals(this.opaque, other.opaque) - && Objects.equals(this.ledgerHeader, other.ledgerHeader) - && Objects.equals(this.signatures, other.signatures); - } - - @Override - public String toString() { - return String.format("%s{ledger=%s}", this.getClass().getSimpleName(), this.ledgerHeader); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + // proposed + @JsonProperty("opaque") + @DsonOutput(Output.ALL) + private final HashCode opaque; + + // committed ledgerState + @JsonProperty("ledgerState") + @DsonOutput(Output.ALL) + private final LedgerHeader ledgerHeader; + + @JsonProperty("signatures") + @DsonOutput(Output.ALL) + private final TimestampedECDSASignatures signatures; + + @JsonCreator + public LedgerProof( + @JsonProperty(value = "opaque", required = true) HashCode opaque, + @JsonProperty(value = "ledgerState", required = true) LedgerHeader ledgerHeader, + @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures) { + this.opaque = Objects.requireNonNull(opaque); + this.ledgerHeader = Objects.requireNonNull(ledgerHeader); + this.signatures = Objects.requireNonNull(signatures); + } + + public static LedgerProof mock() { + var acc = new AccumulatorState(0, HashUtils.zero256()); + var header = LedgerHeader.create(0, View.genesis(), acc, 0); + return new LedgerProof(HashUtils.zero256(), header, new TimestampedECDSASignatures()); + } + + public static LedgerProof genesis( + AccumulatorState accumulatorState, BFTValidatorSet nextValidators, long timestamp) { + var genesisLedgerHeader = LedgerHeader.genesis(accumulatorState, nextValidators, timestamp); + return new LedgerProof( + HashUtils.zero256(), genesisLedgerHeader, new TimestampedECDSASignatures()); + } + + public static final class OrderByEpochAndVersionComparator implements Comparator { + @Override + public int compare(LedgerProof p0, LedgerProof p1) { + if (p0.ledgerHeader.getEpoch() != p1.ledgerHeader.getEpoch()) { + return p0.ledgerHeader.getEpoch() > p1.ledgerHeader.getEpoch() ? 1 : -1; + } + + if (p0.ledgerHeader.isEndOfEpoch() != p1.ledgerHeader.isEndOfEpoch()) { + return p0.ledgerHeader.isEndOfEpoch() ? 1 : -1; + } + + return Long.compare( + p0.ledgerHeader.getAccumulatorState().getStateVersion(), + p1.ledgerHeader.getAccumulatorState().getStateVersion()); + } + } + + public DtoLedgerProof toDto() { + return new DtoLedgerProof(opaque, ledgerHeader, signatures); + } + + public LedgerHeader getRaw() { + return ledgerHeader; + } + + public Optional getNextValidatorSet() { + return ledgerHeader.getNextValidatorSet(); + } + + public long getEpoch() { + return ledgerHeader.getEpoch(); + } + + public View getView() { + return ledgerHeader.getView(); + } + + public AccumulatorState getAccumulatorState() { + return ledgerHeader.getAccumulatorState(); + } + + // TODO: Remove + public long getStateVersion() { + return ledgerHeader.getAccumulatorState().getStateVersion(); + } + + public long timestamp() { + return ledgerHeader.timestamp(); + } + + public boolean isEndOfEpoch() { + return ledgerHeader.isEndOfEpoch(); + } + + public TimestampedECDSASignatures getSignatures() { + return signatures; + } + + public ImmutableList getSignersWithout(BFTNode remove) { + return signatures.getSignatures().keySet().stream() + .filter(n -> !n.equals(remove)) + .collect(ImmutableList.toImmutableList()); + } + + @Override + public int hashCode() { + return Objects.hash(opaque, ledgerHeader, signatures); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof LedgerProof)) { + return false; + } + + LedgerProof other = (LedgerProof) o; + return Objects.equals(this.opaque, other.opaque) + && Objects.equals(this.ledgerHeader, other.ledgerHeader) + && Objects.equals(this.signatures, other.signatures); + } + + @Override + public String toString() { + return String.format("%s{ledger=%s}", this.getClass().getSimpleName(), this.ledgerHeader); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/PendingVotes.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/PendingVotes.java index a7d46feed4..ce7f62869c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/PendingVotes.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/PendingVotes.java @@ -1,281 +1,279 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus; - -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.View; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -import javax.annotation.concurrent.NotThreadSafe; - -import com.radixdlt.consensus.bft.VoteProcessingResult; -import com.radixdlt.consensus.bft.VoteProcessingResult.VoteRejected.VoteRejectedReason; -import com.radixdlt.consensus.liveness.VoteTimeout; -import com.radixdlt.crypto.Hasher; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Maps; -import com.radixdlt.SecurityCritical; -import com.radixdlt.SecurityCritical.SecurityKind; -import com.radixdlt.consensus.bft.ValidationState; -import com.radixdlt.consensus.bft.BFTValidatorSet; -import com.radixdlt.crypto.ECDSASignature; - -/** - * Manages pending votes for various vertices. - *

- * This class is NOT thread-safe. - *

- * This class is security critical (signature checks, validator set membership checks). - */ -@NotThreadSafe -@SecurityCritical({ SecurityKind.SIG_VERIFY, SecurityKind.GENERAL }) -public final class PendingVotes { - - @VisibleForTesting - // Make sure equals tester can access. - static final class PreviousVote { - private final View view; - private final long epoch; - private final HashCode hash; - private final boolean isTimeout; - - PreviousVote(View view, long epoch, HashCode hash, boolean isTimeout) { - this.view = view; - this.epoch = epoch; - this.hash = hash; - this.isTimeout = isTimeout; - } - - @Override - public int hashCode() { - return Objects.hash(this.view, this.epoch, this.hash, this.isTimeout); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof PreviousVote) { - PreviousVote that = (PreviousVote) obj; - return Objects.equals(this.view, that.view) - && Objects.equals(this.hash, that.hash) - && this.epoch == that.epoch - && this.isTimeout == that.isTimeout; - } - return false; - } - } - - private final Map voteState = Maps.newHashMap(); - private final Map timeoutVoteState = Maps.newHashMap(); - private final Map previousVotes = Maps.newHashMap(); - private final Hasher hasher; - - public PendingVotes(Hasher hasher) { - this.hasher = Objects.requireNonNull(hasher); - } - - /** - * Inserts a vote for a given vertex, - * attempting to form either a quorum certificate for that vertex - * or a timeout certificate. - * A quorum will only be formed if permitted by the {@link BFTValidatorSet}. - * - * @param vote The vote to be inserted - * @return The result of vote processing - */ - public VoteProcessingResult insertVote(Vote vote, BFTValidatorSet validatorSet) { - final BFTNode node = vote.getAuthor(); - final VoteData voteData = vote.getVoteData(); - final HashCode voteDataHash = this.hasher.hash(voteData); - - if (!validatorSet.containsNode(node)) { - return VoteProcessingResult.rejected(VoteRejectedReason.INVALID_AUTHOR); - } - - if (!replacePreviousVote(node, vote, voteDataHash)) { - return VoteProcessingResult.rejected(VoteRejectedReason.DUPLICATE_VOTE); - } - - return processVoteForQC(vote, validatorSet).map(VoteProcessingResult::qcQuorum) - .or(() -> processVoteForTC(vote, validatorSet).map(VoteProcessingResult::tcQuorum)) - .orElseGet(VoteProcessingResult::accepted); - } - - private Optional processVoteForQC(Vote vote, BFTValidatorSet validatorSet) { - final VoteData voteData = vote.getVoteData(); - final HashCode voteDataHash = this.hasher.hash(voteData); - final BFTNode node = vote.getAuthor(); - - final ValidationState validationState = - this.voteState.computeIfAbsent(voteDataHash, k -> validatorSet.newValidationState()); - - final boolean signatureAdded = validationState.addSignature(node, vote.getTimestamp(), vote.getSignature()); - - if (signatureAdded && validationState.complete()) { - return Optional.of(new QuorumCertificate(voteData, validationState.signatures())); - } else { - return Optional.empty(); - } - } - - private Optional processVoteForTC(Vote vote, BFTValidatorSet validatorSet) { - if (!vote.isTimeout()) { - return Optional.empty(); // TC can't be formed if vote is not timed out - } - - final ECDSASignature timeoutSignature = vote.getTimeoutSignature().orElseThrow(); - - final VoteTimeout voteTimeout = VoteTimeout.of(vote); - final HashCode voteTimeoutHash = this.hasher.hash(voteTimeout); - final BFTNode node = vote.getAuthor(); - - final ValidationState validationState = - this.timeoutVoteState.computeIfAbsent(voteTimeoutHash, k -> validatorSet.newValidationState()); - - final boolean signatureAdded = validationState.addSignature(node, vote.getTimestamp(), timeoutSignature); - - if (signatureAdded && validationState.complete()) { - return Optional.of(new TimeoutCertificate( - voteTimeout.getEpoch(), - voteTimeout.getView(), - validationState.signatures())); - } else { - return Optional.empty(); - } - } - - // TODO: Need to rethink whether we should be removing previous signature - // TODO: Could be causing quorum formation to slow down - private boolean replacePreviousVote(BFTNode author, Vote vote, HashCode voteHash) { - final PreviousVote thisVote = new PreviousVote(vote.getView(), vote.getEpoch(), voteHash, vote.isTimeout()); - final PreviousVote previousVote = this.previousVotes.put(author, thisVote); - if (previousVote == null) { - // No previous vote for this author, all good here - return true; - } - - if (thisVote.equals(previousVote)) { - // Just going to ignore this duplicate vote for now. - // However, we can't count duplicate votes multiple times. - return false; - } - - // Prune last pending vote from the pending votes. - // This limits the number of pending vertices that are in the pipeline. - var validationState = this.voteState.get(previousVote.hash); - if (validationState != null) { - validationState.removeSignature(author); - if (validationState.isEmpty()) { - this.voteState.remove(previousVote.hash); - } - } - - if (previousVote.isTimeout) { - final var voteTimeout = new VoteTimeout(previousVote.view, previousVote.epoch); - final var voteTimeoutHash = this.hasher.hash(voteTimeout); - - var timeoutValidationState = this.timeoutVoteState.get(voteTimeoutHash); - if (timeoutValidationState != null) { - timeoutValidationState.removeSignature(author); - if (timeoutValidationState.isEmpty()) { - this.timeoutVoteState.remove(voteTimeoutHash); - } - } - } - - if (vote.getView().equals(previousVote.view)) { - // If the validator already voted in this view for something else, - // then the only valid possibility is a non-timeout vote being replaced by a timeout vote - // on the same vote data, or a byzantine node - return vote.isTimeout() - && !previousVote.isTimeout - && thisVote.hash.equals(previousVote.hash); - } else { - // all good if vote is for a different view - return true; - } - } - - @VisibleForTesting - // Greybox stuff for testing - int voteStateSize() { - return this.voteState.size(); - } - - @VisibleForTesting - // Greybox stuff for testing - int timeoutVoteStateSize() { - return this.timeoutVoteState.size(); - } - - @VisibleForTesting - // Greybox stuff for testing - int previousVotesSize() { - return this.previousVotes.size(); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import com.google.common.hash.HashCode; +import com.radixdlt.SecurityCritical; +import com.radixdlt.SecurityCritical.SecurityKind; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.ValidationState; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.bft.VoteProcessingResult; +import com.radixdlt.consensus.bft.VoteProcessingResult.VoteRejected.VoteRejectedReason; +import com.radixdlt.consensus.liveness.VoteTimeout; +import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.crypto.Hasher; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.concurrent.NotThreadSafe; + +/** + * Manages pending votes for various vertices. + * + *

This class is NOT thread-safe. + * + *

This class is security critical (signature checks, validator set membership checks). + */ +@NotThreadSafe +@SecurityCritical({SecurityKind.SIG_VERIFY, SecurityKind.GENERAL}) +public final class PendingVotes { + + @VisibleForTesting + // Make sure equals tester can access. + static final class PreviousVote { + private final View view; + private final long epoch; + private final HashCode hash; + private final boolean isTimeout; + + PreviousVote(View view, long epoch, HashCode hash, boolean isTimeout) { + this.view = view; + this.epoch = epoch; + this.hash = hash; + this.isTimeout = isTimeout; + } + + @Override + public int hashCode() { + return Objects.hash(this.view, this.epoch, this.hash, this.isTimeout); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PreviousVote) { + PreviousVote that = (PreviousVote) obj; + return Objects.equals(this.view, that.view) + && Objects.equals(this.hash, that.hash) + && this.epoch == that.epoch + && this.isTimeout == that.isTimeout; + } + return false; + } + } + + private final Map voteState = Maps.newHashMap(); + private final Map timeoutVoteState = Maps.newHashMap(); + private final Map previousVotes = Maps.newHashMap(); + private final Hasher hasher; + + public PendingVotes(Hasher hasher) { + this.hasher = Objects.requireNonNull(hasher); + } + + /** + * Inserts a vote for a given vertex, attempting to form either a quorum certificate for that + * vertex or a timeout certificate. A quorum will only be formed if permitted by the {@link + * BFTValidatorSet}. + * + * @param vote The vote to be inserted + * @return The result of vote processing + */ + public VoteProcessingResult insertVote(Vote vote, BFTValidatorSet validatorSet) { + final BFTNode node = vote.getAuthor(); + final VoteData voteData = vote.getVoteData(); + final HashCode voteDataHash = this.hasher.hash(voteData); + + if (!validatorSet.containsNode(node)) { + return VoteProcessingResult.rejected(VoteRejectedReason.INVALID_AUTHOR); + } + + if (!replacePreviousVote(node, vote, voteDataHash)) { + return VoteProcessingResult.rejected(VoteRejectedReason.DUPLICATE_VOTE); + } + + return processVoteForQC(vote, validatorSet) + .map(VoteProcessingResult::qcQuorum) + .or(() -> processVoteForTC(vote, validatorSet).map(VoteProcessingResult::tcQuorum)) + .orElseGet(VoteProcessingResult::accepted); + } + + private Optional processVoteForQC(Vote vote, BFTValidatorSet validatorSet) { + final VoteData voteData = vote.getVoteData(); + final HashCode voteDataHash = this.hasher.hash(voteData); + final BFTNode node = vote.getAuthor(); + + final ValidationState validationState = + this.voteState.computeIfAbsent(voteDataHash, k -> validatorSet.newValidationState()); + + final boolean signatureAdded = + validationState.addSignature(node, vote.getTimestamp(), vote.getSignature()); + + if (signatureAdded && validationState.complete()) { + return Optional.of(new QuorumCertificate(voteData, validationState.signatures())); + } else { + return Optional.empty(); + } + } + + private Optional processVoteForTC(Vote vote, BFTValidatorSet validatorSet) { + if (!vote.isTimeout()) { + return Optional.empty(); // TC can't be formed if vote is not timed out + } + + final ECDSASignature timeoutSignature = vote.getTimeoutSignature().orElseThrow(); + + final VoteTimeout voteTimeout = VoteTimeout.of(vote); + final HashCode voteTimeoutHash = this.hasher.hash(voteTimeout); + final BFTNode node = vote.getAuthor(); + + final ValidationState validationState = + this.timeoutVoteState.computeIfAbsent( + voteTimeoutHash, k -> validatorSet.newValidationState()); + + final boolean signatureAdded = + validationState.addSignature(node, vote.getTimestamp(), timeoutSignature); + + if (signatureAdded && validationState.complete()) { + return Optional.of( + new TimeoutCertificate( + voteTimeout.getEpoch(), voteTimeout.getView(), validationState.signatures())); + } else { + return Optional.empty(); + } + } + + // TODO: Need to rethink whether we should be removing previous signature + // TODO: Could be causing quorum formation to slow down + private boolean replacePreviousVote(BFTNode author, Vote vote, HashCode voteHash) { + final PreviousVote thisVote = + new PreviousVote(vote.getView(), vote.getEpoch(), voteHash, vote.isTimeout()); + final PreviousVote previousVote = this.previousVotes.put(author, thisVote); + if (previousVote == null) { + // No previous vote for this author, all good here + return true; + } + + if (thisVote.equals(previousVote)) { + // Just going to ignore this duplicate vote for now. + // However, we can't count duplicate votes multiple times. + return false; + } + + // Prune last pending vote from the pending votes. + // This limits the number of pending vertices that are in the pipeline. + var validationState = this.voteState.get(previousVote.hash); + if (validationState != null) { + validationState.removeSignature(author); + if (validationState.isEmpty()) { + this.voteState.remove(previousVote.hash); + } + } + + if (previousVote.isTimeout) { + final var voteTimeout = new VoteTimeout(previousVote.view, previousVote.epoch); + final var voteTimeoutHash = this.hasher.hash(voteTimeout); + + var timeoutValidationState = this.timeoutVoteState.get(voteTimeoutHash); + if (timeoutValidationState != null) { + timeoutValidationState.removeSignature(author); + if (timeoutValidationState.isEmpty()) { + this.timeoutVoteState.remove(voteTimeoutHash); + } + } + } + + if (vote.getView().equals(previousVote.view)) { + // If the validator already voted in this view for something else, + // then the only valid possibility is a non-timeout vote being replaced by a timeout vote + // on the same vote data, or a byzantine node + return vote.isTimeout() && !previousVote.isTimeout && thisVote.hash.equals(previousVote.hash); + } else { + // all good if vote is for a different view + return true; + } + } + + @VisibleForTesting + // Greybox stuff for testing + int voteStateSize() { + return this.voteState.size(); + } + + @VisibleForTesting + // Greybox stuff for testing + int timeoutVoteStateSize() { + return this.timeoutVoteState.size(); + } + + @VisibleForTesting + // Greybox stuff for testing + int previousVotesSize() { + return this.previousVotes.size(); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Proposal.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Proposal.java index 38e52691f8..b7513a2dd2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Proposal.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Proposal.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.errorprone.annotations.Immutable; @@ -75,112 +77,107 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; import java.util.Optional; -import static java.util.Objects.requireNonNull; - -/** - * Represents a proposal made by a leader in a round of consensus - */ +/** Represents a proposal made by a leader in a round of consensus */ @SerializerId2("consensus.proposal") @Immutable // author cannot be but is effectively final because of serializer public final class Proposal implements ConsensusEvent { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("vertex") - @DsonOutput(Output.ALL) - private final UnverifiedVertex vertex; - - @JsonProperty("signature") - @DsonOutput(Output.ALL) - private final ECDSASignature signature; - - @JsonProperty("committedQC") - @DsonOutput(Output.ALL) - private final QuorumCertificate committedQC; - - @JsonProperty("highestTC") - @DsonOutput(Output.ALL) - private final TimeoutCertificate highestTC; - - @JsonCreator - Proposal( - @JsonProperty(value = "vertex", required = true) UnverifiedVertex vertex, - @JsonProperty(value = "committedQC", required = true) QuorumCertificate committedQC, - @JsonProperty(value = "signature", required = true) ECDSASignature signature, - @JsonProperty("highestTC") TimeoutCertificate highestTC - ) { - this(vertex, committedQC, signature, Optional.ofNullable(highestTC)); - } - - public Proposal( - UnverifiedVertex vertex, - QuorumCertificate committedQC, - ECDSASignature signature, - Optional highestTC - ) { - this.vertex = requireNonNull(vertex); - this.committedQC = requireNonNull(committedQC); - this.signature = requireNonNull(signature); - - this.highestTC = // only relevant if it's for a higher view than QC - highestTC.filter(tc -> tc.getView().gt(vertex.getQC().getView())).orElse(null); - } - - @Override - public long getEpoch() { - return vertex.getQC().getProposed().getLedgerHeader().getEpoch(); - } - - @Override - public View getView() { - return vertex.getView(); - } - - @Override - public HighQC highQC() { - return HighQC.from(vertex.getQC(), committedQC, Optional.ofNullable(highestTC)); - } - - @Override - public BFTNode getAuthor() { - return vertex.getProposer(); - } - - public UnverifiedVertex getVertex() { - return vertex; - } - - public ECDSASignature getSignature() { - return signature; - } - - @Override - public String toString() { - return String.format("%s{vertex=%s author=%s tc=%s}", getClass().getSimpleName(), vertex, getAuthor(), highestTC); - } - - @Override - public int hashCode() { - return Objects.hash(this.vertex, this.signature, this.committedQC, this.highestTC); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o instanceof Proposal) { - Proposal other = (Proposal) o; - return Objects.equals(this.vertex, other.vertex) - && Objects.equals(this.signature, other.signature) - && Objects.equals(this.committedQC, other.committedQC) - && Objects.equals(this.highestTC, other.highestTC); - } - return false; - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("vertex") + @DsonOutput(Output.ALL) + private final UnverifiedVertex vertex; + + @JsonProperty("signature") + @DsonOutput(Output.ALL) + private final ECDSASignature signature; + + @JsonProperty("committedQC") + @DsonOutput(Output.ALL) + private final QuorumCertificate committedQC; + + @JsonProperty("highestTC") + @DsonOutput(Output.ALL) + private final TimeoutCertificate highestTC; + + @JsonCreator + Proposal( + @JsonProperty(value = "vertex", required = true) UnverifiedVertex vertex, + @JsonProperty(value = "committedQC", required = true) QuorumCertificate committedQC, + @JsonProperty(value = "signature", required = true) ECDSASignature signature, + @JsonProperty("highestTC") TimeoutCertificate highestTC) { + this(vertex, committedQC, signature, Optional.ofNullable(highestTC)); + } + + public Proposal( + UnverifiedVertex vertex, + QuorumCertificate committedQC, + ECDSASignature signature, + Optional highestTC) { + this.vertex = requireNonNull(vertex); + this.committedQC = requireNonNull(committedQC); + this.signature = requireNonNull(signature); + + this.highestTC = // only relevant if it's for a higher view than QC + highestTC.filter(tc -> tc.getView().gt(vertex.getQC().getView())).orElse(null); + } + + @Override + public long getEpoch() { + return vertex.getQC().getProposed().getLedgerHeader().getEpoch(); + } + + @Override + public View getView() { + return vertex.getView(); + } + + @Override + public HighQC highQC() { + return HighQC.from(vertex.getQC(), committedQC, Optional.ofNullable(highestTC)); + } + + @Override + public BFTNode getAuthor() { + return vertex.getProposer(); + } + + public UnverifiedVertex getVertex() { + return vertex; + } + + public ECDSASignature getSignature() { + return signature; + } + + @Override + public String toString() { + return String.format( + "%s{vertex=%s author=%s tc=%s}", + getClass().getSimpleName(), vertex, getAuthor(), highestTC); + } + + @Override + public int hashCode() { + return Objects.hash(this.vertex, this.signature, this.committedQC, this.highestTC); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof Proposal) { + Proposal other = (Proposal) o; + return Objects.equals(this.vertex, other.vertex) + && Objects.equals(this.signature, other.signature) + && Objects.equals(this.committedQC, other.committedQC) + && Objects.equals(this.highestTC, other.highestTC); + } + return false; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/QuorumCertificate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/QuorumCertificate.java index 907122f465..1498820c2b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/QuorumCertificate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/QuorumCertificate.java @@ -76,113 +76,112 @@ import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; import com.radixdlt.utils.Pair; - import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; @SerializerId2("consensus.qc") public final class QuorumCertificate { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("signatures") - @DsonOutput(Output.ALL) - private final TimestampedECDSASignatures signatures; - - @JsonProperty("vote_data") - @DsonOutput(Output.ALL) - private final VoteData voteData; - - @JsonCreator - public QuorumCertificate( - @JsonProperty(value = "vote_data", required = true) VoteData voteData, - @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures - ) { - this.voteData = Objects.requireNonNull(voteData); - this.signatures = Objects.requireNonNull(signatures); - } - - /** - * Create a mocked QC for genesis vertex - * @param genesisVertex the vertex to create a qc for - * @return a mocked QC - */ - public static QuorumCertificate ofGenesis(VerifiedVertex genesisVertex, LedgerHeader ledgerHeader) { - if (!genesisVertex.getView().isGenesis()) { - throw new IllegalArgumentException(String.format("Vertex is not genesis: %s", genesisVertex)); - } - - BFTHeader header = new BFTHeader(genesisVertex.getView(), genesisVertex.getId(), ledgerHeader); - final VoteData voteData = new VoteData(header, header, header); - return new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - } - - public View getView() { - return voteData.getProposed().getView(); - } - - public long getEpoch() { - return this.voteData.getProposed().getLedgerHeader().getEpoch(); - } - - public BFTHeader getProposed() { - return voteData.getProposed(); - } - - public BFTHeader getParent() { - return voteData.getParent(); - } - - public Optional getCommitted() { - return voteData.getCommitted(); - } - - public Optional> getCommittedAndLedgerStateProof(Hasher hasher) { - return voteData.getCommitted().map(committed -> { - var opaque = hasher.hash(voteData); - var ledgerStateProof = new LedgerProof( - opaque, - committed.getLedgerHeader(), - signatures - ); - return Pair.of(committed, ledgerStateProof); - }); - } - - public VoteData getVoteData() { - return voteData; - } - - public TimestampedECDSASignatures getTimestampedSignatures() { - return signatures; - } - - public Stream getSigners() { - return signatures.getSignatures().keySet().stream(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - QuorumCertificate that = (QuorumCertificate) o; - return Objects.equals(signatures, that.signatures) - && Objects.equals(voteData, that.voteData); - } - - @Override - public int hashCode() { - return Objects.hash(signatures, voteData); - } - - @Override - public String toString() { - return String.format("QC{%s:%s}", this.getEpoch(), this.getView()); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("signatures") + @DsonOutput(Output.ALL) + private final TimestampedECDSASignatures signatures; + + @JsonProperty("vote_data") + @DsonOutput(Output.ALL) + private final VoteData voteData; + + @JsonCreator + public QuorumCertificate( + @JsonProperty(value = "vote_data", required = true) VoteData voteData, + @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures) { + this.voteData = Objects.requireNonNull(voteData); + this.signatures = Objects.requireNonNull(signatures); + } + + /** + * Create a mocked QC for genesis vertex + * + * @param genesisVertex the vertex to create a qc for + * @return a mocked QC + */ + public static QuorumCertificate ofGenesis( + VerifiedVertex genesisVertex, LedgerHeader ledgerHeader) { + if (!genesisVertex.getView().isGenesis()) { + throw new IllegalArgumentException(String.format("Vertex is not genesis: %s", genesisVertex)); + } + + BFTHeader header = new BFTHeader(genesisVertex.getView(), genesisVertex.getId(), ledgerHeader); + final VoteData voteData = new VoteData(header, header, header); + return new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + } + + public View getView() { + return voteData.getProposed().getView(); + } + + public long getEpoch() { + return this.voteData.getProposed().getLedgerHeader().getEpoch(); + } + + public BFTHeader getProposed() { + return voteData.getProposed(); + } + + public BFTHeader getParent() { + return voteData.getParent(); + } + + public Optional getCommitted() { + return voteData.getCommitted(); + } + + public Optional> getCommittedAndLedgerStateProof(Hasher hasher) { + return voteData + .getCommitted() + .map( + committed -> { + var opaque = hasher.hash(voteData); + var ledgerStateProof = + new LedgerProof(opaque, committed.getLedgerHeader(), signatures); + return Pair.of(committed, ledgerStateProof); + }); + } + + public VoteData getVoteData() { + return voteData; + } + + public TimestampedECDSASignatures getTimestampedSignatures() { + return signatures; + } + + public Stream getSigners() { + return signatures.getSignatures().keySet().stream(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + QuorumCertificate that = (QuorumCertificate) o; + return Objects.equals(signatures, that.signatures) && Objects.equals(voteData, that.voteData); + } + + @Override + public int hashCode() { + return Objects.hash(signatures, voteData); + } + + @Override + public String toString() { + return String.format("QC{%s:%s}", this.getEpoch(), this.getView()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Sha256Hasher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Sha256Hasher.java index f4b3928149..e80a6380b1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Sha256Hasher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Sha256Hasher.java @@ -71,33 +71,31 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.Serialization; -/** - * A Hasher implementation that uses sha256 hashing algorithm. - */ +/** A Hasher implementation that uses sha256 hashing algorithm. */ public class Sha256Hasher implements Hasher { - private final Serialization serialization; + private final Serialization serialization; - public static Sha256Hasher withDefaultSerialization() { - return new Sha256Hasher(DefaultSerialization.getInstance()); - } + public static Sha256Hasher withDefaultSerialization() { + return new Sha256Hasher(DefaultSerialization.getInstance()); + } - public Sha256Hasher(Serialization serialization) { - this.serialization = serialization; - } + public Sha256Hasher(Serialization serialization) { + this.serialization = serialization; + } - @Override - public int bytes() { - return 32; - } + @Override + public int bytes() { + return 32; + } - @Override - public HashCode hash(Object o) { - return HashUtils.sha256(serialization.toDson(o, DsonOutput.Output.HASH)); - } + @Override + public HashCode hash(Object o) { + return HashUtils.sha256(serialization.toDson(o, DsonOutput.Output.HASH)); + } - @Override - public HashCode hashBytes(byte[] bytes) { - return HashUtils.sha256(bytes); - } + @Override + public HashCode hashBytes(byte[] bytes) { + return HashUtils.sha256(bytes); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimeoutCertificate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimeoutCertificate.java index 77055a89d7..f53a6d67ef 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimeoutCertificate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimeoutCertificate.java @@ -74,93 +74,89 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; import java.util.stream.Stream; -/** - * Represents a timeout certificate for given epoch and view signed by the quorum of validators. - */ +/** Represents a timeout certificate for given epoch and view signed by the quorum of validators. */ @SerializerId2("consensus.tc") public final class TimeoutCertificate { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("epoch") - @DsonOutput(Output.ALL) - private final long epoch; - - private final View view; - - @JsonProperty("signatures") - @DsonOutput(Output.ALL) - private final TimestampedECDSASignatures signatures; - - @JsonCreator - @VisibleForTesting - static TimeoutCertificate serializerCreate( - @JsonProperty("epoch") long epoch, - @JsonProperty("view") long view, - @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures - ) { - return new TimeoutCertificate(epoch, View.of(view), signatures); - } - - public TimeoutCertificate(long epoch, View view, TimestampedECDSASignatures signatures) { - this.epoch = epoch; - - if (epoch < 0) { - throw new IllegalArgumentException("Epoch can't be < 0"); - } - - this.view = Objects.requireNonNull(view); - this.signatures = Objects.requireNonNull(signatures); - } - - public long getEpoch() { - return this.epoch; - } - - public View getView() { - return this.view; - } - - public TimestampedECDSASignatures getTimestampedSignatures() { - return signatures; - } - - public Stream getSigners() { - return signatures.getSignatures().keySet().stream(); - } - - @JsonProperty("view") - @DsonOutput(DsonOutput.Output.ALL) - private Long getSerializerView() { - return this.view.number(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TimeoutCertificate that = (TimeoutCertificate) o; - return Objects.equals(signatures, that.signatures) - && epoch == that.epoch - && Objects.equals(view, that.view); - } - - @Override - public int hashCode() { - return Objects.hash(signatures, epoch, view); - } - - @Override - public String toString() { - return String.format("TC{view=%s epoch=%s}", view, epoch); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("epoch") + @DsonOutput(Output.ALL) + private final long epoch; + + private final View view; + + @JsonProperty("signatures") + @DsonOutput(Output.ALL) + private final TimestampedECDSASignatures signatures; + + @JsonCreator + @VisibleForTesting + static TimeoutCertificate serializerCreate( + @JsonProperty("epoch") long epoch, + @JsonProperty("view") long view, + @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures) { + return new TimeoutCertificate(epoch, View.of(view), signatures); + } + + public TimeoutCertificate(long epoch, View view, TimestampedECDSASignatures signatures) { + this.epoch = epoch; + + if (epoch < 0) { + throw new IllegalArgumentException("Epoch can't be < 0"); + } + + this.view = Objects.requireNonNull(view); + this.signatures = Objects.requireNonNull(signatures); + } + + public long getEpoch() { + return this.epoch; + } + + public View getView() { + return this.view; + } + + public TimestampedECDSASignatures getTimestampedSignatures() { + return signatures; + } + + public Stream getSigners() { + return signatures.getSignatures().keySet().stream(); + } + + @JsonProperty("view") + @DsonOutput(DsonOutput.Output.ALL) + private Long getSerializerView() { + return this.view.number(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeoutCertificate that = (TimeoutCertificate) o; + return Objects.equals(signatures, that.signatures) + && epoch == that.epoch + && Objects.equals(view, that.view); + } + + @Override + public int hashCode() { + return Objects.hash(signatures, epoch, view); + } + + @Override + public String toString() { + return String.format("TC{view=%s epoch=%s}", view, epoch); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignature.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignature.java index 568310a2b2..eb5725beb2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignature.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignature.java @@ -72,9 +72,8 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.Objects; +import javax.annotation.concurrent.Immutable; /** * An ECDSA @@ -83,76 +82,76 @@ @Immutable @SerializerId2("consensus.timestamped_ecdsa_signature") public final class TimestampedECDSASignature { - // Placeholder for the serializer ID - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; + // Placeholder for the serializer ID + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; - @JsonProperty("timestamp") - @DsonOutput(Output.ALL) - private final long timestamp; + @JsonProperty("timestamp") + @DsonOutput(Output.ALL) + private final long timestamp; - @JsonProperty("signature") - @DsonOutput(Output.ALL) - private final ECDSASignature signature; + @JsonProperty("signature") + @DsonOutput(Output.ALL) + private final ECDSASignature signature; - /** - * Create a timestamped signature from a timestamp, weight and signature. - * - * @param timestamp the timestamp - * @param signature the signature - * @return a timestamped signature with the specified properties - */ - @JsonCreator - public static TimestampedECDSASignature from( - @JsonProperty("timestamp") long timestamp, - @JsonProperty(value = "signature", required = true) ECDSASignature signature - ) { - return new TimestampedECDSASignature(timestamp, signature); - } + /** + * Create a timestamped signature from a timestamp, weight and signature. + * + * @param timestamp the timestamp + * @param signature the signature + * @return a timestamped signature with the specified properties + */ + @JsonCreator + public static TimestampedECDSASignature from( + @JsonProperty("timestamp") long timestamp, + @JsonProperty(value = "signature", required = true) ECDSASignature signature) { + return new TimestampedECDSASignature(timestamp, signature); + } - private TimestampedECDSASignature(long timestamp, ECDSASignature signature) { - if (timestamp <= 0) { //we don't expect timestamps before epoch or at the start of the epoch - throw new IllegalArgumentException("Timestamp before epoch: " + timestamp); - } + private TimestampedECDSASignature(long timestamp, ECDSASignature signature) { + if (timestamp <= 0) { // we don't expect timestamps before epoch or at the start of the epoch + throw new IllegalArgumentException("Timestamp before epoch: " + timestamp); + } - this.timestamp = timestamp; - this.signature = Objects.requireNonNull(signature); - } + this.timestamp = timestamp; + this.signature = Objects.requireNonNull(signature); + } - /** - * Returns the timestamp of the signature in milliseconds since epoch. - * @return The timestamp of the signature in milliseconds since epoch - */ - public long timestamp() { - return this.timestamp; - } + /** + * Returns the timestamp of the signature in milliseconds since epoch. + * + * @return The timestamp of the signature in milliseconds since epoch + */ + public long timestamp() { + return this.timestamp; + } - /** - * Returns the signature. - * @return the signature - */ - public ECDSASignature signature() { - return this.signature; - } + /** + * Returns the signature. + * + * @return the signature + */ + public ECDSASignature signature() { + return this.signature; + } - @Override - public boolean equals(Object o) { - if (o instanceof TimestampedECDSASignature) { - TimestampedECDSASignature that = (TimestampedECDSASignature) o; - return this.timestamp == that.timestamp - && Objects.equals(this.signature, that.signature); - } - return false; - } + @Override + public boolean equals(Object o) { + if (o instanceof TimestampedECDSASignature) { + TimestampedECDSASignature that = (TimestampedECDSASignature) o; + return this.timestamp == that.timestamp && Objects.equals(this.signature, that.signature); + } + return false; + } - @Override - public int hashCode() { - return Objects.hash(this.timestamp, this.signature); - } + @Override + public int hashCode() { + return Objects.hash(this.timestamp, this.signature); + } - @Override - public String toString() { - return String.format("%s[%s:%s]", getClass().getSimpleName(), this.timestamp, this.signature); - } -} \ No newline at end of file + @Override + public String toString() { + return String.format("%s[%s:%s]", getClass().getSimpleName(), this.timestamp, this.signature); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignatures.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignatures.java index 6782cf859a..6c57c9c9fd 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignatures.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedECDSASignatures.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; @@ -79,187 +81,184 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; -import org.json.JSONArray; - -import javax.annotation.concurrent.Immutable; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Comparator; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; - -import static java.util.Objects.requireNonNull; +import javax.annotation.concurrent.Immutable; +import org.json.JSONArray; /** * A collection of ECDSA signatures, - * together with vote timestamps. - *

- * Note that the timestamps can be used together with the - * {@link VoteData} in a {@link QuorumCertificate} to reconstruct - * {@link ConsensusHasher} in order to validate signatures. + * Elliptic_Curve_Digital_Signature_Algorithm">ECDSA signatures, together with vote timestamps. + * + *

Note that the timestamps can be used together with the {@link VoteData} in a {@link + * QuorumCertificate} to reconstruct {@link ConsensusHasher} in order to validate signatures. */ @Immutable @SerializerId2("consensus.timestamped_ecdsa_signatures") public final class TimestampedECDSASignatures { - // Placeholder for the serializer ID - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(DsonOutput.Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; + // Placeholder for the serializer ID + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(DsonOutput.Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; - private final Map nodeToTimestampedSignature; + private final Map nodeToTimestampedSignature; - @JsonCreator - public static TimestampedECDSASignatures from( - @JsonProperty("signatures") Map signatures - ) { - if (signatures != null) { - signatures.forEach((key, value) -> { - requireNonNull(key); - requireNonNull(value); - }); - } + @JsonCreator + public static TimestampedECDSASignatures from( + @JsonProperty("signatures") Map signatures) { + if (signatures != null) { + signatures.forEach( + (key, value) -> { + requireNonNull(key); + requireNonNull(value); + }); + } - var signaturesByNode = - signatures == null - ? Map.of() - : signatures.entrySet().stream().collect(Collectors.toMap(e -> toBFTNode(e.getKey()), Map.Entry::getValue)); + var signaturesByNode = + signatures == null + ? Map.of() + : signatures.entrySet().stream() + .collect(Collectors.toMap(e -> toBFTNode(e.getKey()), Map.Entry::getValue)); - return new TimestampedECDSASignatures(signaturesByNode); - } + return new TimestampedECDSASignatures(signaturesByNode); + } - public static TimestampedECDSASignatures fromJSON(JSONArray json) throws DeserializeException { - var builder = ImmutableMap.builder(); - for (int i = 0; i < json.length(); i++) { - var signatureJson = json.getJSONObject(i); + public static TimestampedECDSASignatures fromJSON(JSONArray json) throws DeserializeException { + var builder = ImmutableMap.builder(); + for (int i = 0; i < json.length(); i++) { + var signatureJson = json.getJSONObject(i); - try { - var key = ECPublicKey.fromHex(signatureJson.getString("key")); - var bytes = Bytes.fromHexString(signatureJson.getString("signature")); - var signature = REFieldSerialization.deserializeSignature(ByteBuffer.wrap(bytes)); - var timestamp = signatureJson.getLong("timestamp"); - builder.put(BFTNode.create(key), TimestampedECDSASignature.from(timestamp, signature)); - } catch (PublicKeyException e) { - throw new DeserializeException(e.getMessage()); - } - } - return new TimestampedECDSASignatures(builder.build()); - } + try { + var key = ECPublicKey.fromHex(signatureJson.getString("key")); + var bytes = Bytes.fromHexString(signatureJson.getString("signature")); + var signature = REFieldSerialization.deserializeSignature(ByteBuffer.wrap(bytes)); + var timestamp = signatureJson.getLong("timestamp"); + builder.put(BFTNode.create(key), TimestampedECDSASignature.from(timestamp, signature)); + } catch (PublicKeyException e) { + throw new DeserializeException(e.getMessage()); + } + } + return new TimestampedECDSASignatures(builder.build()); + } - /** - * Returns a new empty instance. - */ - public TimestampedECDSASignatures() { - this.nodeToTimestampedSignature = Map.of(); - } + /** Returns a new empty instance. */ + public TimestampedECDSASignatures() { + this.nodeToTimestampedSignature = Map.of(); + } - /** - * Returns a new instance containing {@code nodeToTimestampAndSignature}. - * - * @param nodeToTimestampAndSignature The map of {@link com.radixdlt.crypto.ECDSASignature}s and their corresponding - * timestamps and {@link com.radixdlt.crypto.ECPublicKey} - */ - public TimestampedECDSASignatures(Map nodeToTimestampAndSignature) { - this.nodeToTimestampedSignature = nodeToTimestampAndSignature == null ? Map.of() : nodeToTimestampAndSignature; - this.nodeToTimestampedSignature.forEach((key, value) -> { - requireNonNull(key); - requireNonNull(value); - }); - } + /** + * Returns a new instance containing {@code nodeToTimestampAndSignature}. + * + * @param nodeToTimestampAndSignature The map of {@link com.radixdlt.crypto.ECDSASignature}s and + * their corresponding timestamps and {@link com.radixdlt.crypto.ECPublicKey} + */ + public TimestampedECDSASignatures( + Map nodeToTimestampAndSignature) { + this.nodeToTimestampedSignature = + nodeToTimestampAndSignature == null ? Map.of() : nodeToTimestampAndSignature; + this.nodeToTimestampedSignature.forEach( + (key, value) -> { + requireNonNull(key); + requireNonNull(value); + }); + } - /** - * Returns signatures and timestamps for each public key - * - * @return Signatures and timestamps for each public key - */ - public Map getSignatures() { - return this.nodeToTimestampedSignature; - } + /** + * Returns signatures and timestamps for each public key + * + * @return Signatures and timestamps for each public key + */ + public Map getSignatures() { + return this.nodeToTimestampedSignature; + } - /** - * Returns the count of signatures. - * - * @return The count of signatures - */ - public int count() { - return this.nodeToTimestampedSignature.size(); - } + /** + * Returns the count of signatures. + * + * @return The count of signatures + */ + public int count() { + return this.nodeToTimestampedSignature.size(); + } - /** - * Returns the weighted timestamp for this set of timestamped signatures. - * - * @return The weighted timestamp, or {@code Long.MIN_VALUE} if a timestamp cannot be computed - */ - public long weightedTimestamp() { - var totalPower = UInt256.ZERO; - var weightedTimes = new ArrayList>(); + /** + * Returns the weighted timestamp for this set of timestamped signatures. + * + * @return The weighted timestamp, or {@code Long.MIN_VALUE} if a timestamp cannot be computed + */ + public long weightedTimestamp() { + var totalPower = UInt256.ZERO; + var weightedTimes = new ArrayList>(); - for (var ts : this.nodeToTimestampedSignature.values()) { - var weight = UInt256.ONE; - totalPower = totalPower.add(weight); - weightedTimes.add(Pair.of(ts.timestamp(), weight)); - } + for (var ts : this.nodeToTimestampedSignature.values()) { + var weight = UInt256.ONE; + totalPower = totalPower.add(weight); + weightedTimes.add(Pair.of(ts.timestamp(), weight)); + } - if (totalPower.isZero()) { - return Long.MIN_VALUE; // Invalid timestamp - } + if (totalPower.isZero()) { + return Long.MIN_VALUE; // Invalid timestamp + } - var median = totalPower.shiftRight(); // Divide by 2 + var median = totalPower.shiftRight(); // Divide by 2 - // Sort ascending by timestamp - weightedTimes.sort(Comparator.comparing(Pair::getFirst)); + // Sort ascending by timestamp + weightedTimes.sort(Comparator.comparing(Pair::getFirst)); - for (var w : weightedTimes) { - var weight = w.getSecond(); + for (var w : weightedTimes) { + var weight = w.getSecond(); - if (median.compareTo(weight) < 0) { - return w.getFirst(); - } + if (median.compareTo(weight) < 0) { + return w.getFirst(); + } - median = median.subtract(weight); - } - throw new IllegalStateException("Logic error in weightedTimestamp"); - } + median = median.subtract(weight); + } + throw new IllegalStateException("Logic error in weightedTimestamp"); + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), this.nodeToTimestampedSignature); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), this.nodeToTimestampedSignature); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof TimestampedECDSASignatures)) { - return false; - } - TimestampedECDSASignatures that = (TimestampedECDSASignatures) o; - return Objects.equals(this.nodeToTimestampedSignature, that.nodeToTimestampedSignature); - } + @Override + public boolean equals(Object o) { + if (!(o instanceof TimestampedECDSASignatures)) { + return false; + } + TimestampedECDSASignatures that = (TimestampedECDSASignatures) o; + return Objects.equals(this.nodeToTimestampedSignature, that.nodeToTimestampedSignature); + } - @Override - public int hashCode() { - return Objects.hash(this.nodeToTimestampedSignature); - } + @Override + public int hashCode() { + return Objects.hash(this.nodeToTimestampedSignature); + } - @JsonProperty("signatures") - @DsonOutput(DsonOutput.Output.ALL) - private Map getSerializerSignatures() { - if (this.nodeToTimestampedSignature != null) { - return this.nodeToTimestampedSignature.entrySet().stream() - .collect(Collectors.toMap(e -> encodePublicKey(e.getKey()), Map.Entry::getValue)); - } - return null; - } + @JsonProperty("signatures") + @DsonOutput(DsonOutput.Output.ALL) + private Map getSerializerSignatures() { + if (this.nodeToTimestampedSignature != null) { + return this.nodeToTimestampedSignature.entrySet().stream() + .collect(Collectors.toMap(e -> encodePublicKey(e.getKey()), Map.Entry::getValue)); + } + return null; + } - private static String encodePublicKey(BFTNode key) { - return Bytes.toHexString(key.getKey().getBytes()); - } + private static String encodePublicKey(BFTNode key) { + return Bytes.toHexString(key.getKey().getBytes()); + } - private static BFTNode toBFTNode(String str) { - try { - return BFTNode.fromPublicKeyBytes(Bytes.fromHexString(str)); - } catch (PublicKeyException e) { - throw new IllegalStateException("Error decoding public key", e); - } - } -} \ No newline at end of file + private static BFTNode toBFTNode(String str) { + try { + return BFTNode.fromPublicKeyBytes(Bytes.fromHexString(str)); + } catch (PublicKeyException e) { + throw new IllegalStateException("Error decoding public key", e); + } + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/UnverifiedVertex.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/UnverifiedVertex.java index 5e32f3b5b5..00efc1a454 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/UnverifiedVertex.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/UnverifiedVertex.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.atom.Txn; @@ -75,154 +77,148 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; - import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import javax.annotation.concurrent.Immutable; -import static java.util.Objects.requireNonNull; - -/** - * Vertex in a Vertex graph - */ +/** Vertex in a Vertex graph */ @Immutable @SerializerId2("consensus.vertex") public final class UnverifiedVertex { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("qc") - @DsonOutput(Output.ALL) - private final QuorumCertificate qc; - - private final View view; - - @JsonProperty("txns") - @DsonOutput(Output.ALL) - private final List txns; - - @JsonProperty("tout") - @DsonOutput(Output.ALL) - private final Boolean proposerTimedOut; - - private final BFTNode proposer; - - private UnverifiedVertex(QuorumCertificate qc, View view, List txns, BFTNode proposer, Boolean proposerTimedOut) { - this.qc = requireNonNull(qc); - this.view = requireNonNull(view); - - if (proposerTimedOut != null && proposerTimedOut && !txns.isEmpty()) { - throw new IllegalArgumentException("Txns must be empty if timeout"); - } - - if (txns != null) { - txns.forEach(Objects::requireNonNull); - } - - this.txns = txns; - this.proposer = proposer; - this.proposerTimedOut = proposerTimedOut; - } - - @JsonCreator - public static UnverifiedVertex create( - @JsonProperty(value = "qc", required = true) QuorumCertificate qc, - @JsonProperty("view") long viewId, - @JsonProperty("txns") List txns, - @JsonProperty("p") byte[] proposer, - @JsonProperty("tout") Boolean proposerTimedOut - ) throws PublicKeyException { - return new UnverifiedVertex( - qc, - View.of(viewId), - txns == null ? List.of() : txns, - proposer != null ? BFTNode.fromPublicKeyBytes(proposer) : null, - proposerTimedOut - ); - } - - public static UnverifiedVertex createGenesis(LedgerHeader ledgerHeader) { - BFTHeader header = BFTHeader.ofGenesisAncestor(ledgerHeader); - final VoteData voteData = new VoteData(header, header, header); - final QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - return new UnverifiedVertex(qc, View.genesis(), null, null, false); - } - - public static UnverifiedVertex createTimeout(QuorumCertificate qc, View view, BFTNode proposer) { - return new UnverifiedVertex(qc, view, List.of(), proposer, true); - } - - public static UnverifiedVertex create( - QuorumCertificate qc, - View view, - List txns, - BFTNode proposer - ) { - if (view.number() == 0) { - throw new IllegalArgumentException("Only genesis can have view 0."); - } - - var txnBytes = txns.stream().map(Txn::getPayload).collect(Collectors.toList()); - - return new UnverifiedVertex(qc, view, txnBytes, proposer, false); - } - - @JsonProperty("p") - @DsonOutput(Output.ALL) - private byte[] getProposerJson() { - return proposer == null ? null : proposer.getKey().getCompressedBytes(); - } - - public BFTNode getProposer() { - return proposer; - } - - public boolean isTimeout() { - return proposerTimedOut != null && proposerTimedOut; - } - - public QuorumCertificate getQC() { - return qc; - } - - public View getView() { - return view; - } - - public List getTxns() { - return txns == null ? List.of() : txns.stream().map(Txn::create).toList(); - } - - @JsonProperty("view") - @DsonOutput(Output.ALL) - private Long getSerializerView() { - return this.view == null ? null : this.view.number(); - } - - @Override - public String toString() { - return String.format("Vertex{view=%s, qc=%s, txns=%s}", view, qc, getTxns()); - } - - @Override - public int hashCode() { - return Objects.hash(qc, proposer, view, txns, proposerTimedOut); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof UnverifiedVertex)) { - return false; - } - - UnverifiedVertex v = (UnverifiedVertex) o; - return Objects.equals(v.view, this.view) - && Objects.equals(v.proposerTimedOut, this.proposerTimedOut) - && Objects.equals(v.proposer, this.proposer) - && Objects.equals(v.getTxns(), this.getTxns()) - && Objects.equals(v.qc, this.qc); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("qc") + @DsonOutput(Output.ALL) + private final QuorumCertificate qc; + + private final View view; + + @JsonProperty("txns") + @DsonOutput(Output.ALL) + private final List txns; + + @JsonProperty("tout") + @DsonOutput(Output.ALL) + private final Boolean proposerTimedOut; + + private final BFTNode proposer; + + private UnverifiedVertex( + QuorumCertificate qc, + View view, + List txns, + BFTNode proposer, + Boolean proposerTimedOut) { + this.qc = requireNonNull(qc); + this.view = requireNonNull(view); + + if (proposerTimedOut != null && proposerTimedOut && !txns.isEmpty()) { + throw new IllegalArgumentException("Txns must be empty if timeout"); + } + + if (txns != null) { + txns.forEach(Objects::requireNonNull); + } + + this.txns = txns; + this.proposer = proposer; + this.proposerTimedOut = proposerTimedOut; + } + + @JsonCreator + public static UnverifiedVertex create( + @JsonProperty(value = "qc", required = true) QuorumCertificate qc, + @JsonProperty("view") long viewId, + @JsonProperty("txns") List txns, + @JsonProperty("p") byte[] proposer, + @JsonProperty("tout") Boolean proposerTimedOut) + throws PublicKeyException { + return new UnverifiedVertex( + qc, + View.of(viewId), + txns == null ? List.of() : txns, + proposer != null ? BFTNode.fromPublicKeyBytes(proposer) : null, + proposerTimedOut); + } + + public static UnverifiedVertex createGenesis(LedgerHeader ledgerHeader) { + BFTHeader header = BFTHeader.ofGenesisAncestor(ledgerHeader); + final VoteData voteData = new VoteData(header, header, header); + final QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + return new UnverifiedVertex(qc, View.genesis(), null, null, false); + } + + public static UnverifiedVertex createTimeout(QuorumCertificate qc, View view, BFTNode proposer) { + return new UnverifiedVertex(qc, view, List.of(), proposer, true); + } + + public static UnverifiedVertex create( + QuorumCertificate qc, View view, List txns, BFTNode proposer) { + if (view.number() == 0) { + throw new IllegalArgumentException("Only genesis can have view 0."); + } + + var txnBytes = txns.stream().map(Txn::getPayload).collect(Collectors.toList()); + + return new UnverifiedVertex(qc, view, txnBytes, proposer, false); + } + + @JsonProperty("p") + @DsonOutput(Output.ALL) + private byte[] getProposerJson() { + return proposer == null ? null : proposer.getKey().getCompressedBytes(); + } + + public BFTNode getProposer() { + return proposer; + } + + public boolean isTimeout() { + return proposerTimedOut != null && proposerTimedOut; + } + + public QuorumCertificate getQC() { + return qc; + } + + public View getView() { + return view; + } + + public List getTxns() { + return txns == null ? List.of() : txns.stream().map(Txn::create).toList(); + } + + @JsonProperty("view") + @DsonOutput(Output.ALL) + private Long getSerializerView() { + return this.view == null ? null : this.view.number(); + } + + @Override + public String toString() { + return String.format("Vertex{view=%s, qc=%s, txns=%s}", view, qc, getTxns()); + } + + @Override + public int hashCode() { + return Objects.hash(qc, proposer, view, txns, proposerTimedOut); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof UnverifiedVertex)) { + return false; + } + + UnverifiedVertex v = (UnverifiedVertex) o; + return Objects.equals(v.view, this.view) + && Objects.equals(v.proposerTimedOut, this.proposerTimedOut) + && Objects.equals(v.proposer, this.proposer) + && Objects.equals(v.getTxns(), this.getTxns()) + && Objects.equals(v.qc, this.qc); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java index 60eb501817..5eae46eead 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -79,173 +81,176 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; import java.util.Optional; -import static java.util.Objects.requireNonNull; - -/** - * Represents a vote on a vertex - */ +/** Represents a vote on a vertex */ @Immutable @SerializerId2("consensus.vote") public final class Vote implements ConsensusEvent { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - SerializerDummy serializer = SerializerDummy.DUMMY; - - private final BFTNode author; - - @JsonProperty("high_qc") - @DsonOutput(Output.ALL) - private final HighQC highQC; - - @JsonProperty("vote_data") - @DsonOutput(Output.ALL) - private final VoteData voteData; - - @JsonProperty("ts") - @DsonOutput(Output.ALL) - private final long timestamp; - - @JsonProperty("signature") - @DsonOutput(Output.ALL) - private final ECDSASignature signature; - - private final Optional timeoutSignature; - - @JsonCreator - @VisibleForTesting - public Vote( - @JsonProperty(value = "author", required = true) byte[] author, - @JsonProperty(value = "vote_data", required = true) VoteData voteData, - @JsonProperty("ts") long timestamp, - @JsonProperty(value = "signature", required = true) ECDSASignature signature, - @JsonProperty(value = "high_qc", required = true) HighQC highQC, - @JsonProperty("timeout_signature") ECDSASignature timeoutSignature - ) throws PublicKeyException { - this( - BFTNode.fromPublicKeyBytes(requireNonNull(author)), voteData, timestamp, - signature, highQC, Optional.ofNullable(timeoutSignature) - ); - } - - public Vote( - BFTNode author, - VoteData voteData, - long timestamp, - ECDSASignature signature, - HighQC highQC, - Optional timeoutSignature - ) { - if (timestamp <= 0) { - throw new IllegalArgumentException("Timestamp before epoch:" + timestamp); - } - - this.author = requireNonNull(author); - this.voteData = requireNonNull(voteData); - this.timestamp = timestamp; - this.signature = requireNonNull(signature); - this.highQC = requireNonNull(highQC); - this.timeoutSignature = requireNonNull(timeoutSignature); - } - - @Override - public long getEpoch() { - return voteData.getProposed().getLedgerHeader().getEpoch(); - } - - @Override - public BFTNode getAuthor() { - return author; - } - - @Override - public HighQC highQC() { - return this.highQC; - } - - @Override - public View getView() { - return getVoteData().getProposed().getView(); - } - - public VoteData getVoteData() { - return voteData; - } - - public static HashCode getHashOfData(Hasher hasher, VoteData voteData, long timestamp) { - var opaque = hasher.hash(voteData); - var header = voteData.getCommitted().map(BFTHeader::getLedgerHeader).orElse(null); - return ConsensusHasher.toHash(opaque, header, timestamp, hasher); - } - - public HashCode getHashOfData(Hasher hasher) { - return getHashOfData(hasher, this.voteData, this.timestamp); - } - - public long getTimestamp() { - return timestamp; - } - - public ECDSASignature getSignature() { - return this.signature; - } - - public Optional getTimeoutSignature() { - return timeoutSignature; - } - - public Vote withTimeoutSignature(ECDSASignature timeoutSignature) { - return new Vote( - this.author, - this.voteData, - this.timestamp, - this.signature, - this.highQC, - Optional.of(timeoutSignature) - ); - } - - public boolean isTimeout() { - return timeoutSignature.isPresent(); - } - - @JsonProperty("author") - @DsonOutput(Output.ALL) - private byte[] getSerializerAuthor() { - return this.author == null ? null : this.author.getKey().getBytes(); - } - - @JsonProperty("timeout_signature") - @DsonOutput(Output.ALL) - private ECDSASignature getSerializerTimeoutSignature() { - return this.timeoutSignature.orElse(null); - } - - @Override - public String toString() { - return String.format("%s{epoch=%s view=%s author=%s timeout?=%s %s}", getClass().getSimpleName(), - getEpoch(), getView(), author, isTimeout(), highQC); - } - - @Override - public int hashCode() { - return Objects.hash(this.author, this.voteData, this.timestamp, this.signature, this.highQC, this.timeoutSignature); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - return (o instanceof Vote other) - && Objects.equals(this.author, other.author) - && Objects.equals(this.voteData, other.voteData) - && this.timestamp == other.timestamp - && Objects.equals(this.signature, other.signature) - && Objects.equals(this.highQC, other.highQC) - && Objects.equals(this.timeoutSignature, other.timeoutSignature); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + SerializerDummy serializer = SerializerDummy.DUMMY; + + private final BFTNode author; + + @JsonProperty("high_qc") + @DsonOutput(Output.ALL) + private final HighQC highQC; + + @JsonProperty("vote_data") + @DsonOutput(Output.ALL) + private final VoteData voteData; + + @JsonProperty("ts") + @DsonOutput(Output.ALL) + private final long timestamp; + + @JsonProperty("signature") + @DsonOutput(Output.ALL) + private final ECDSASignature signature; + + private final Optional timeoutSignature; + + @JsonCreator + @VisibleForTesting + public Vote( + @JsonProperty(value = "author", required = true) byte[] author, + @JsonProperty(value = "vote_data", required = true) VoteData voteData, + @JsonProperty("ts") long timestamp, + @JsonProperty(value = "signature", required = true) ECDSASignature signature, + @JsonProperty(value = "high_qc", required = true) HighQC highQC, + @JsonProperty("timeout_signature") ECDSASignature timeoutSignature) + throws PublicKeyException { + this( + BFTNode.fromPublicKeyBytes(requireNonNull(author)), + voteData, + timestamp, + signature, + highQC, + Optional.ofNullable(timeoutSignature)); + } + + public Vote( + BFTNode author, + VoteData voteData, + long timestamp, + ECDSASignature signature, + HighQC highQC, + Optional timeoutSignature) { + if (timestamp <= 0) { + throw new IllegalArgumentException("Timestamp before epoch:" + timestamp); + } + + this.author = requireNonNull(author); + this.voteData = requireNonNull(voteData); + this.timestamp = timestamp; + this.signature = requireNonNull(signature); + this.highQC = requireNonNull(highQC); + this.timeoutSignature = requireNonNull(timeoutSignature); + } + + @Override + public long getEpoch() { + return voteData.getProposed().getLedgerHeader().getEpoch(); + } + + @Override + public BFTNode getAuthor() { + return author; + } + + @Override + public HighQC highQC() { + return this.highQC; + } + + @Override + public View getView() { + return getVoteData().getProposed().getView(); + } + + public VoteData getVoteData() { + return voteData; + } + + public static HashCode getHashOfData(Hasher hasher, VoteData voteData, long timestamp) { + var opaque = hasher.hash(voteData); + var header = voteData.getCommitted().map(BFTHeader::getLedgerHeader).orElse(null); + return ConsensusHasher.toHash(opaque, header, timestamp, hasher); + } + + public HashCode getHashOfData(Hasher hasher) { + return getHashOfData(hasher, this.voteData, this.timestamp); + } + + public long getTimestamp() { + return timestamp; + } + + public ECDSASignature getSignature() { + return this.signature; + } + + public Optional getTimeoutSignature() { + return timeoutSignature; + } + + public Vote withTimeoutSignature(ECDSASignature timeoutSignature) { + return new Vote( + this.author, + this.voteData, + this.timestamp, + this.signature, + this.highQC, + Optional.of(timeoutSignature)); + } + + public boolean isTimeout() { + return timeoutSignature.isPresent(); + } + + @JsonProperty("author") + @DsonOutput(Output.ALL) + private byte[] getSerializerAuthor() { + return this.author == null ? null : this.author.getKey().getBytes(); + } + + @JsonProperty("timeout_signature") + @DsonOutput(Output.ALL) + private ECDSASignature getSerializerTimeoutSignature() { + return this.timeoutSignature.orElse(null); + } + + @Override + public String toString() { + return String.format( + "%s{epoch=%s view=%s author=%s timeout?=%s %s}", + getClass().getSimpleName(), getEpoch(), getView(), author, isTimeout(), highQC); + } + + @Override + public int hashCode() { + return Objects.hash( + this.author, + this.voteData, + this.timestamp, + this.signature, + this.highQC, + this.timeoutSignature); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + return (o instanceof Vote other) + && Objects.equals(this.author, other.author) + && Objects.equals(this.voteData, other.voteData) + && this.timestamp == other.timestamp + && Objects.equals(this.signature, other.signature) + && Objects.equals(this.highQC, other.highQC) + && Objects.equals(this.timeoutSignature, other.timeoutSignature); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/VoteData.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/VoteData.java index f6b5d5c336..3f574bbecd 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/VoteData.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/VoteData.java @@ -71,75 +71,73 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.Objects; import java.util.Optional; +import javax.annotation.concurrent.Immutable; /** * Serves as data which is voted upon in a round of BFT. In a standard implementation this would * only include a proposed vertex which extends the chain. In this implementation we include data - * for both the parent and any vertex which would be committed if this data reaches a quorum of votes. - * This way, vote processing can be handled statelessly. + * for both the parent and any vertex which would be committed if this data reaches a quorum of + * votes. This way, vote processing can be handled statelessly. */ @Immutable @SerializerId2("consensus.vote_data") public final class VoteData { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; - @JsonProperty("proposed") - @DsonOutput(Output.ALL) - private final BFTHeader proposed; + @JsonProperty("proposed") + @DsonOutput(Output.ALL) + private final BFTHeader proposed; - @JsonProperty("parent") - @DsonOutput(Output.ALL) - private final BFTHeader parent; + @JsonProperty("parent") + @DsonOutput(Output.ALL) + private final BFTHeader parent; - @JsonProperty("committed") - @DsonOutput(Output.ALL) - private final BFTHeader committed; + @JsonProperty("committed") + @DsonOutput(Output.ALL) + private final BFTHeader committed; - @JsonCreator - public VoteData( - @JsonProperty(value = "proposed", required = true) BFTHeader proposed, - @JsonProperty(value = "parent", required = true) BFTHeader parent, - @JsonProperty("committed") BFTHeader committed - ) { - this.proposed = Objects.requireNonNull(proposed); - this.parent = Objects.requireNonNull(parent); - this.committed = committed; - } + @JsonCreator + public VoteData( + @JsonProperty(value = "proposed", required = true) BFTHeader proposed, + @JsonProperty(value = "parent", required = true) BFTHeader parent, + @JsonProperty("committed") BFTHeader committed) { + this.proposed = Objects.requireNonNull(proposed); + this.parent = Objects.requireNonNull(parent); + this.committed = committed; + } - public BFTHeader getProposed() { - return proposed; - } + public BFTHeader getProposed() { + return proposed; + } - public BFTHeader getParent() { - return parent; - } + public BFTHeader getParent() { + return parent; + } - public Optional getCommitted() { - return Optional.ofNullable(committed); - } + public Optional getCommitted() { + return Optional.ofNullable(committed); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - VoteData that = (VoteData) o; - return Objects.equals(proposed, that.proposed) - && Objects.equals(parent, that.parent) - && Objects.equals(committed, that.committed); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VoteData that = (VoteData) o; + return Objects.equals(proposed, that.proposed) + && Objects.equals(parent, that.parent) + && Objects.equals(committed, that.committed); + } - @Override - public int hashCode() { - return Objects.hash(proposed, parent, committed); - } + @Override + public int hashCode() { + return Objects.hash(proposed, parent, committed); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTBuilder.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTBuilder.java index e3971a47a8..383753d498 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTBuilder.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTBuilder.java @@ -68,134 +68,124 @@ import com.radixdlt.consensus.HashVerifier; import com.radixdlt.consensus.PendingVotes; import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.safety.SafetyRules; import com.radixdlt.crypto.Hasher; -import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.environment.RemoteEventDispatcher; -/** - * A helper class to help in constructing a BFT validator state machine - */ +/** A helper class to help in constructing a BFT validator state machine */ public final class BFTBuilder { - // BFT Configuration objects - private BFTValidatorSet validatorSet; - private Hasher hasher; - private HashVerifier verifier; - - // BFT Stateful objects - private Pacemaker pacemaker; - private VertexStore vertexStore; - private BFTSyncer bftSyncer; - private EventDispatcher viewQuorumReachedEventDispatcher; - private EventDispatcher noVoteEventDispatcher; - - // Instance specific objects - private BFTNode self; - - private ViewUpdate viewUpdate; - private RemoteEventDispatcher voteDispatcher; - private SafetyRules safetyRules; - - private BFTBuilder() { - // Just making this inaccessible - } - - public static BFTBuilder create() { - return new BFTBuilder(); - } - - public BFTBuilder self(BFTNode self) { - this.self = self; - return this; - } - - public BFTBuilder viewUpdate(ViewUpdate viewUpdate) { - this.viewUpdate = viewUpdate; - return this; - } - - public BFTBuilder voteDispatcher(RemoteEventDispatcher voteDispatcher) { - this.voteDispatcher = voteDispatcher; - return this; - } - - public BFTBuilder safetyRules(SafetyRules safetyRules) { - this.safetyRules = safetyRules; - return this; - } - - public BFTBuilder hasher(Hasher hasher) { - this.hasher = hasher; - return this; - } - - public BFTBuilder verifier(HashVerifier verifier) { - this.verifier = verifier; - return this; - } - - public BFTBuilder validatorSet(BFTValidatorSet validatorSet) { - this.validatorSet = validatorSet; - return this; - } - - public BFTBuilder pacemaker(Pacemaker pacemaker) { - this.pacemaker = pacemaker; - return this; - } - - public BFTBuilder vertexStore(VertexStore vertexStore) { - this.vertexStore = vertexStore; - return this; - } - - public BFTBuilder bftSyncer(BFTSyncer bftSyncer) { - this.bftSyncer = bftSyncer; - return this; - } - - public BFTBuilder viewQuorumReachedEventDispatcher(EventDispatcher viewQuorumReachedEventDispatcher) { - this.viewQuorumReachedEventDispatcher = viewQuorumReachedEventDispatcher; - return this; - } - - public BFTBuilder noVoteEventDispatcher(EventDispatcher noVoteEventDispatcher) { - this.noVoteEventDispatcher = noVoteEventDispatcher; - return this; - } - - public BFTEventProcessor build() { - if (!validatorSet.containsNode(self)) { - return EmptyBFTEventProcessor.INSTANCE; - } - final PendingVotes pendingVotes = new PendingVotes(hasher); - - BFTEventReducer reducer = new BFTEventReducer( - self, - pacemaker, - vertexStore, - viewQuorumReachedEventDispatcher, - noVoteEventDispatcher, - voteDispatcher, - hasher, - safetyRules, - validatorSet, - pendingVotes, - viewUpdate - ); - - BFTEventPreprocessor preprocessor = new BFTEventPreprocessor( - reducer, - bftSyncer, - viewUpdate - ); - - return new BFTEventVerifier( - validatorSet, - preprocessor, - hasher, - verifier - ); - } + // BFT Configuration objects + private BFTValidatorSet validatorSet; + private Hasher hasher; + private HashVerifier verifier; + + // BFT Stateful objects + private Pacemaker pacemaker; + private VertexStore vertexStore; + private BFTSyncer bftSyncer; + private EventDispatcher viewQuorumReachedEventDispatcher; + private EventDispatcher noVoteEventDispatcher; + + // Instance specific objects + private BFTNode self; + + private ViewUpdate viewUpdate; + private RemoteEventDispatcher voteDispatcher; + private SafetyRules safetyRules; + + private BFTBuilder() { + // Just making this inaccessible + } + + public static BFTBuilder create() { + return new BFTBuilder(); + } + + public BFTBuilder self(BFTNode self) { + this.self = self; + return this; + } + + public BFTBuilder viewUpdate(ViewUpdate viewUpdate) { + this.viewUpdate = viewUpdate; + return this; + } + + public BFTBuilder voteDispatcher(RemoteEventDispatcher voteDispatcher) { + this.voteDispatcher = voteDispatcher; + return this; + } + + public BFTBuilder safetyRules(SafetyRules safetyRules) { + this.safetyRules = safetyRules; + return this; + } + + public BFTBuilder hasher(Hasher hasher) { + this.hasher = hasher; + return this; + } + + public BFTBuilder verifier(HashVerifier verifier) { + this.verifier = verifier; + return this; + } + + public BFTBuilder validatorSet(BFTValidatorSet validatorSet) { + this.validatorSet = validatorSet; + return this; + } + + public BFTBuilder pacemaker(Pacemaker pacemaker) { + this.pacemaker = pacemaker; + return this; + } + + public BFTBuilder vertexStore(VertexStore vertexStore) { + this.vertexStore = vertexStore; + return this; + } + + public BFTBuilder bftSyncer(BFTSyncer bftSyncer) { + this.bftSyncer = bftSyncer; + return this; + } + + public BFTBuilder viewQuorumReachedEventDispatcher( + EventDispatcher viewQuorumReachedEventDispatcher) { + this.viewQuorumReachedEventDispatcher = viewQuorumReachedEventDispatcher; + return this; + } + + public BFTBuilder noVoteEventDispatcher(EventDispatcher noVoteEventDispatcher) { + this.noVoteEventDispatcher = noVoteEventDispatcher; + return this; + } + + public BFTEventProcessor build() { + if (!validatorSet.containsNode(self)) { + return EmptyBFTEventProcessor.INSTANCE; + } + final PendingVotes pendingVotes = new PendingVotes(hasher); + + BFTEventReducer reducer = + new BFTEventReducer( + self, + pacemaker, + vertexStore, + viewQuorumReachedEventDispatcher, + noVoteEventDispatcher, + voteDispatcher, + hasher, + safetyRules, + validatorSet, + pendingVotes, + viewUpdate); + + BFTEventPreprocessor preprocessor = new BFTEventPreprocessor(reducer, bftSyncer, viewUpdate); + + return new BFTEventVerifier(validatorSet, preprocessor, hasher, verifier); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTCommittedUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTCommittedUpdate.java index 12a098184b..2ea8c3b589 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTCommittedUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTCommittedUpdate.java @@ -69,41 +69,38 @@ import com.google.common.hash.HashCode; import java.util.Objects; -/** - * Vertex Store update of committed vertices - */ +/** Vertex Store update of committed vertices */ public final class BFTCommittedUpdate { - private final ImmutableSet pruned; //TODO: remove unused https://radixdlt.atlassian.net/browse/NT-5 - private final ImmutableList committed; - private final VerifiedVertexStoreState vertexStoreState; + private final ImmutableSet + pruned; // TODO: remove unused https://radixdlt.atlassian.net/browse/NT-5 + private final ImmutableList committed; + private final VerifiedVertexStoreState vertexStoreState; - private BFTCommittedUpdate( - ImmutableSet pruned, - ImmutableList committed, - VerifiedVertexStoreState vertexStoreState - ) { - this.pruned = Objects.requireNonNull(pruned); - this.committed = Objects.requireNonNull(committed); - this.vertexStoreState = Objects.requireNonNull(vertexStoreState); - } + private BFTCommittedUpdate( + ImmutableSet pruned, + ImmutableList committed, + VerifiedVertexStoreState vertexStoreState) { + this.pruned = Objects.requireNonNull(pruned); + this.committed = Objects.requireNonNull(committed); + this.vertexStoreState = Objects.requireNonNull(vertexStoreState); + } - public static BFTCommittedUpdate create( - ImmutableSet pruned, - ImmutableList committed, - VerifiedVertexStoreState vertexStoreState - ) { - return new BFTCommittedUpdate(pruned, committed, vertexStoreState); - } + public static BFTCommittedUpdate create( + ImmutableSet pruned, + ImmutableList committed, + VerifiedVertexStoreState vertexStoreState) { + return new BFTCommittedUpdate(pruned, committed, vertexStoreState); + } - public int getVertexStoreSize() { - return vertexStoreState.getVertices().size(); - } + public int getVertexStoreSize() { + return vertexStoreState.getVertices().size(); + } - public ImmutableList getCommitted() { - return committed; - } + public ImmutableList getCommitted() { + return committed; + } - public VerifiedVertexStoreState getVertexStoreState() { - return vertexStoreState; - } + public VerifiedVertexStoreState getVertexStoreState() { + return vertexStoreState; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventPreprocessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventPreprocessor.java index 7a8165db3e..39fa60461e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventPreprocessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventPreprocessor.java @@ -66,12 +66,11 @@ import com.google.common.hash.HashCode; import com.radixdlt.consensus.BFTEventProcessor; -import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.HighQC; +import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTSyncer.SyncResult; - import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; import java.util.HashMap; import java.util.HashSet; @@ -81,217 +80,221 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** - * Preprocesses consensus events and ensures that the vertexStore is synced to - * the correct state before they get forwarded to the actual state reducer. + * Preprocesses consensus events and ensures that the vertexStore is synced to the correct state + * before they get forwarded to the actual state reducer. * - * This class should not be updating any part of the BFT Safety state besides - * the VertexStore. + *

This class should not be updating any part of the BFT Safety state besides the VertexStore. * - * A lot of the queue logic could be done more "cleanly" and functionally using - * lambdas and Functions but the performance impact is too great. + *

A lot of the queue logic could be done more "cleanly" and functionally using lambdas and + * Functions but the performance impact is too great. * - * This class is NOT thread-safe. + *

This class is NOT thread-safe. */ public final class BFTEventPreprocessor implements BFTEventProcessor { - private static final Logger log = LogManager.getLogger(); - - private final BFTEventProcessor forwardTo; - private final BFTSyncer bftSyncer; - private final Set syncingEvents = new HashSet<>(); - private final Map> viewQueues = new HashMap<>(); - private ViewUpdate latestViewUpdate; - - public BFTEventPreprocessor( - BFTEventProcessor forwardTo, - BFTSyncer bftSyncer, - ViewUpdate initialViewUpdate - ) { - this.bftSyncer = Objects.requireNonNull(bftSyncer); - this.forwardTo = forwardTo; - this.latestViewUpdate = Objects.requireNonNull(initialViewUpdate); - } - - @Override - public void processViewUpdate(ViewUpdate viewUpdate) { - final View previousView = this.latestViewUpdate.getCurrentView(); - log.trace("Processing viewUpdate {} cur {}", viewUpdate, previousView); - - // FIXME: Check is required for now since Deterministic tests can randomize local messages - if (viewUpdate.getCurrentView().gt(previousView)) { - this.latestViewUpdate = viewUpdate; - forwardTo.processViewUpdate(viewUpdate); - viewQueues.getOrDefault(viewUpdate.getCurrentView(), new LinkedList<>()) - .forEach(this::processViewCachedEvent); - viewQueues.keySet().removeIf(v -> v.lte(viewUpdate.getCurrentView())); - - syncingEvents.stream() - .filter(e -> e.getView().equals(viewUpdate.getCurrentView())) - .forEach(this::processQueuedConsensusEvent); - - syncingEvents.removeIf(e -> e.getView().lte(viewUpdate.getCurrentView())); - } - } - - private void processViewCachedEvent(ConsensusEvent event) { - if (event instanceof Proposal) { - log.trace("Processing cached proposal {}", event); - processProposal((Proposal) event); - } else if (event instanceof Vote) { - log.trace("Processing cached vote {}", event); - processVote((Vote) event); - } else { - log.error("Ignoring cached ConsensusEvent {}", event); - } - } - - @Override - public void processBFTUpdate(BFTInsertUpdate update) { - HashCode vertexId = update.getInserted().getId(); - log.trace("LOCAL_SYNC: {}", vertexId); - - syncingEvents.stream() - .filter(e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)) - .forEach(this::processQueuedConsensusEvent); - - syncingEvents.removeIf(e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)); - - forwardTo.processBFTUpdate(update); - } - - @Override - public void processBFTRebuildUpdate(BFTRebuildUpdate rebuildUpdate) { - rebuildUpdate.getVertexStoreState().getVertices().forEach(v -> { - HashCode vertexId = v.getId(); - syncingEvents.stream() - .filter(e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)) - .forEach(this::processQueuedConsensusEvent); - - syncingEvents.removeIf(e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)); - }); - } - - @Override - public void processVote(Vote vote) { - log.trace("Vote: PreProcessing {}", vote); - if (!processVoteInternal(vote)) { - log.debug("Vote: Queuing {}, waiting for Sync", vote); - syncingEvents.add(vote); - } - } - - @Override - public void processProposal(Proposal proposal) { - log.trace("Proposal: PreProcessing {}", proposal); - if (!processProposalInternal(proposal)) { - log.debug("Proposal: Queuing {}, waiting for Sync", proposal); - syncingEvents.add(proposal); - } - } - - @Override - public void processLocalTimeout(ScheduledLocalTimeout scheduledLocalTimeout) { - forwardTo.processLocalTimeout(scheduledLocalTimeout); - } - - @Override - public void start() { - forwardTo.start(); - } - - //TODO: rework processQueuedConsensusEvent(), processVoteInternal() and processProposalInternal() - // avoid code duplication and manual forwarding using instanceof - // https://radixdlt.atlassian.net/browse/NT-6 - private boolean processQueuedConsensusEvent(ConsensusEvent event) { - if (event == null) { - return false; - } - - // Explicitly using switch case method here rather than functional method - // to process these events due to much better performance - if (event instanceof Proposal) { - final Proposal proposal = (Proposal) event; - return processProposalInternal(proposal); - } - - if (event instanceof Vote) { - final Vote vote = (Vote) event; - return processVoteInternal(vote); - } - - throw new IllegalStateException("Unexpected consensus event: " + event); - } - - private boolean processVoteInternal(Vote vote) { - final View currentView = this.latestViewUpdate.getCurrentView(); - if (vote.getView().gte(currentView)) { - log.trace("Vote: PreProcessing {}", vote); - return syncUp( - vote.highQC(), - vote.getAuthor(), - () -> processOnCurrentViewOrCache(vote, forwardTo::processVote) - ); - } else { - log.trace("Vote: Ignoring for past view {}, current view is {}", vote, currentView); - return true; - } - } - - private boolean processProposalInternal(Proposal proposal) { - final View currentView = this.latestViewUpdate.getCurrentView(); - if (proposal.getView().gte(currentView)) { - log.trace("Proposal: PreProcessing {}", proposal); - return syncUp( - proposal.highQC(), - proposal.getAuthor(), - () -> processOnCurrentViewOrCache(proposal, forwardTo::processProposal) - ); - } else { - log.trace("Proposal: Ignoring for past view {}, current view is {}", proposal, currentView); - return true; - } - } - - private void processOnCurrentViewOrCache(T event, Consumer processFn) { - if (latestViewUpdate.getCurrentView().equals(event.getView())) { - processFn.accept(event); - } else if (latestViewUpdate.getCurrentView().lt(event.getView())) { - log.trace("Caching {}, current view is {}", event, latestViewUpdate.getCurrentView()); - viewQueues.putIfAbsent(event.getView(), new LinkedList<>()); - viewQueues.get(event.getView()).add(event); - } else { - log.debug("Ignoring {} for past view", event); - } - } - - private boolean syncUp(HighQC highQC, BFTNode author, Runnable whenSynced) { - SyncResult syncResult = this.bftSyncer.syncToQC(highQC, author); - - //TODO: use switch expression and eliminate unnecessary default case - switch (syncResult) { - case SYNCED: - // if already end of epoch then don't need to process - // TODO: need to do the same checks on pacemaker side - // TODO: move this to an epoch preprocessor - final boolean endOfEpoch = highQC.highestCommittedQC() - .getCommitted() - .orElseThrow(() -> new IllegalStateException("Invalid High QC")).getLedgerHeader() - .isEndOfEpoch(); - if (!endOfEpoch) { - whenSynced.run(); - } - - return true; - case INVALID: - return true; - case IN_PROGRESS: - return false; - default: - throw new IllegalStateException("Unknown syncResult " + syncResult); - } - } + private static final Logger log = LogManager.getLogger(); + + private final BFTEventProcessor forwardTo; + private final BFTSyncer bftSyncer; + private final Set syncingEvents = new HashSet<>(); + private final Map> viewQueues = new HashMap<>(); + private ViewUpdate latestViewUpdate; + + public BFTEventPreprocessor( + BFTEventProcessor forwardTo, BFTSyncer bftSyncer, ViewUpdate initialViewUpdate) { + this.bftSyncer = Objects.requireNonNull(bftSyncer); + this.forwardTo = forwardTo; + this.latestViewUpdate = Objects.requireNonNull(initialViewUpdate); + } + + @Override + public void processViewUpdate(ViewUpdate viewUpdate) { + final View previousView = this.latestViewUpdate.getCurrentView(); + log.trace("Processing viewUpdate {} cur {}", viewUpdate, previousView); + + // FIXME: Check is required for now since Deterministic tests can randomize local messages + if (viewUpdate.getCurrentView().gt(previousView)) { + this.latestViewUpdate = viewUpdate; + forwardTo.processViewUpdate(viewUpdate); + viewQueues + .getOrDefault(viewUpdate.getCurrentView(), new LinkedList<>()) + .forEach(this::processViewCachedEvent); + viewQueues.keySet().removeIf(v -> v.lte(viewUpdate.getCurrentView())); + + syncingEvents.stream() + .filter(e -> e.getView().equals(viewUpdate.getCurrentView())) + .forEach(this::processQueuedConsensusEvent); + + syncingEvents.removeIf(e -> e.getView().lte(viewUpdate.getCurrentView())); + } + } + + private void processViewCachedEvent(ConsensusEvent event) { + if (event instanceof Proposal) { + log.trace("Processing cached proposal {}", event); + processProposal((Proposal) event); + } else if (event instanceof Vote) { + log.trace("Processing cached vote {}", event); + processVote((Vote) event); + } else { + log.error("Ignoring cached ConsensusEvent {}", event); + } + } + + @Override + public void processBFTUpdate(BFTInsertUpdate update) { + HashCode vertexId = update.getInserted().getId(); + log.trace("LOCAL_SYNC: {}", vertexId); + + syncingEvents.stream() + .filter(e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)) + .forEach(this::processQueuedConsensusEvent); + + syncingEvents.removeIf( + e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)); + + forwardTo.processBFTUpdate(update); + } + + @Override + public void processBFTRebuildUpdate(BFTRebuildUpdate rebuildUpdate) { + rebuildUpdate + .getVertexStoreState() + .getVertices() + .forEach( + v -> { + HashCode vertexId = v.getId(); + syncingEvents.stream() + .filter(e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)) + .forEach(this::processQueuedConsensusEvent); + + syncingEvents.removeIf( + e -> e.highQC().highestQC().getProposed().getVertexId().equals(vertexId)); + }); + } + + @Override + public void processVote(Vote vote) { + log.trace("Vote: PreProcessing {}", vote); + if (!processVoteInternal(vote)) { + log.debug("Vote: Queuing {}, waiting for Sync", vote); + syncingEvents.add(vote); + } + } + + @Override + public void processProposal(Proposal proposal) { + log.trace("Proposal: PreProcessing {}", proposal); + if (!processProposalInternal(proposal)) { + log.debug("Proposal: Queuing {}, waiting for Sync", proposal); + syncingEvents.add(proposal); + } + } + + @Override + public void processLocalTimeout(ScheduledLocalTimeout scheduledLocalTimeout) { + forwardTo.processLocalTimeout(scheduledLocalTimeout); + } + + @Override + public void start() { + forwardTo.start(); + } + + // TODO: rework processQueuedConsensusEvent(), processVoteInternal() and processProposalInternal() + // avoid code duplication and manual forwarding using instanceof + // https://radixdlt.atlassian.net/browse/NT-6 + private boolean processQueuedConsensusEvent(ConsensusEvent event) { + if (event == null) { + return false; + } + + // Explicitly using switch case method here rather than functional method + // to process these events due to much better performance + if (event instanceof Proposal) { + final Proposal proposal = (Proposal) event; + return processProposalInternal(proposal); + } + + if (event instanceof Vote) { + final Vote vote = (Vote) event; + return processVoteInternal(vote); + } + + throw new IllegalStateException("Unexpected consensus event: " + event); + } + + private boolean processVoteInternal(Vote vote) { + final View currentView = this.latestViewUpdate.getCurrentView(); + if (vote.getView().gte(currentView)) { + log.trace("Vote: PreProcessing {}", vote); + return syncUp( + vote.highQC(), + vote.getAuthor(), + () -> processOnCurrentViewOrCache(vote, forwardTo::processVote)); + } else { + log.trace("Vote: Ignoring for past view {}, current view is {}", vote, currentView); + return true; + } + } + + private boolean processProposalInternal(Proposal proposal) { + final View currentView = this.latestViewUpdate.getCurrentView(); + if (proposal.getView().gte(currentView)) { + log.trace("Proposal: PreProcessing {}", proposal); + return syncUp( + proposal.highQC(), + proposal.getAuthor(), + () -> processOnCurrentViewOrCache(proposal, forwardTo::processProposal)); + } else { + log.trace("Proposal: Ignoring for past view {}, current view is {}", proposal, currentView); + return true; + } + } + + private void processOnCurrentViewOrCache( + T event, Consumer processFn) { + if (latestViewUpdate.getCurrentView().equals(event.getView())) { + processFn.accept(event); + } else if (latestViewUpdate.getCurrentView().lt(event.getView())) { + log.trace("Caching {}, current view is {}", event, latestViewUpdate.getCurrentView()); + viewQueues.putIfAbsent(event.getView(), new LinkedList<>()); + viewQueues.get(event.getView()).add(event); + } else { + log.debug("Ignoring {} for past view", event); + } + } + + private boolean syncUp(HighQC highQC, BFTNode author, Runnable whenSynced) { + SyncResult syncResult = this.bftSyncer.syncToQC(highQC, author); + + // TODO: use switch expression and eliminate unnecessary default case + switch (syncResult) { + case SYNCED: + // if already end of epoch then don't need to process + // TODO: need to do the same checks on pacemaker side + // TODO: move this to an epoch preprocessor + final boolean endOfEpoch = + highQC + .highestCommittedQC() + .getCommitted() + .orElseThrow(() -> new IllegalStateException("Invalid High QC")) + .getLedgerHeader() + .isEndOfEpoch(); + if (!endOfEpoch) { + whenSynced.run(); + } + + return true; + case INVALID: + return true; + case IN_PROGRESS: + return false; + default: + throw new IllegalStateException("Unknown syncResult " + syncResult); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventReducer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventReducer.java index 741c3589e7..796cfa7e26 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventReducer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventReducer.java @@ -69,216 +69,218 @@ import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.liveness.Pacemaker; - import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -import com.radixdlt.environment.EventDispatcher; import com.radixdlt.consensus.safety.SafetyRules; import com.radixdlt.crypto.Hasher; +import com.radixdlt.environment.EventDispatcher; import com.radixdlt.environment.RemoteEventDispatcher; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.Objects; import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * Processes and reduces BFT events to the BFT state based on core - * BFT validation logic, any messages which must be sent to other nodes - * are then forwarded to the BFT sender. + * Processes and reduces BFT events to the BFT state based on core BFT validation logic, any + * messages which must be sent to other nodes are then forwarded to the BFT sender. */ -//TODO: cleanup TODOs https://radixdlt.atlassian.net/browse/NT-7 +// TODO: cleanup TODOs https://radixdlt.atlassian.net/browse/NT-7 public final class BFTEventReducer implements BFTEventProcessor { - private static final Logger log = LogManager.getLogger(); - - private final BFTNode self; - private final VertexStore vertexStore; - private final Pacemaker pacemaker; - private final EventDispatcher viewQuorumReachedEventDispatcher; - private final EventDispatcher noVoteDispatcher; - private final RemoteEventDispatcher voteDispatcher; - private final Hasher hasher; - private final SafetyRules safetyRules; - private final BFTValidatorSet validatorSet; - private final PendingVotes pendingVotes; - - private BFTInsertUpdate latestInsertUpdate; - private ViewUpdate latestViewUpdate; - - /* Indicates whether the quorum (QC or TC) has already been formed for the current view. - * If the quorum has been reached (but view hasn't yet been updated), subsequent votes are ignored. - * TODO: consider moving it to PendingVotes or elsewhere. - */ - private boolean hasReachedQuorum = false; - - private boolean isViewTimedOut = false; - - public BFTEventReducer( - BFTNode self, - Pacemaker pacemaker, - VertexStore vertexStore, - EventDispatcher viewQuorumReachedEventDispatcher, - EventDispatcher noVoteDispatcher, - RemoteEventDispatcher voteDispatcher, - Hasher hasher, - SafetyRules safetyRules, - BFTValidatorSet validatorSet, - PendingVotes pendingVotes, - ViewUpdate initialViewUpdate - ) { - this.self = Objects.requireNonNull(self); - this.pacemaker = Objects.requireNonNull(pacemaker); - this.vertexStore = Objects.requireNonNull(vertexStore); - this.viewQuorumReachedEventDispatcher = Objects.requireNonNull(viewQuorumReachedEventDispatcher); - this.noVoteDispatcher = Objects.requireNonNull(noVoteDispatcher); - this.voteDispatcher = Objects.requireNonNull(voteDispatcher); - this.hasher = Objects.requireNonNull(hasher); - this.safetyRules = Objects.requireNonNull(safetyRules); - this.validatorSet = Objects.requireNonNull(validatorSet); - this.pendingVotes = Objects.requireNonNull(pendingVotes); - this.latestViewUpdate = Objects.requireNonNull(initialViewUpdate); - } - - @Override - public void processBFTUpdate(BFTInsertUpdate update) { - log.trace("BFTUpdate: Processing {}", update); - - final View view = update.getHeader().getView(); - if (view.lt(this.latestViewUpdate.getCurrentView())) { - log.trace("InsertUpdate: Ignoring insert {} for view {}, current view at {}", - update, view, this.latestViewUpdate.getCurrentView()); - return; - } - - this.latestInsertUpdate = update; - this.tryVote(); - - this.pacemaker.processBFTUpdate(update); - } - - @Override - public void processViewUpdate(ViewUpdate viewUpdate) { - this.hasReachedQuorum = false; - this.isViewTimedOut = false; - this.latestViewUpdate = viewUpdate; - this.pacemaker.processViewUpdate(viewUpdate); - this.tryVote(); - } - - private void tryVote() { - BFTInsertUpdate update = this.latestInsertUpdate; - if (update == null) { - return; - } - - if (!Objects.equals(update.getHeader().getView(), this.latestViewUpdate.getCurrentView())) { - return; - } - - // check if already voted in this round - if (this.safetyRules.getLastVote(this.latestViewUpdate.getCurrentView()).isPresent()) { - return; - } - - // don't vote if view has timed out - if (this.isViewTimedOut) { - return; - } - - // TODO: what if insertUpdate occurs before viewUpdate - final BFTNode nextLeader = this.latestViewUpdate.getNextLeader(); - final Optional maybeVote = this.safetyRules.voteFor( - update.getInserted().getVertex(), - update.getHeader(), - update.getInserted().getTimeOfExecution(), - this.latestViewUpdate.getHighQC() - ); - maybeVote.ifPresentOrElse( - vote -> this.voteDispatcher.dispatch(nextLeader, vote), - () -> this.noVoteDispatcher.dispatch(NoVote.create(update.getInserted().getVertex())) - ); - } - - @Override - public void processBFTRebuildUpdate(BFTRebuildUpdate update) { - // No-op - } - - @Override - public void processVote(Vote vote) { - log.trace("Vote: Processing {}", vote); - - final View view = vote.getView(); - - if (view.lt(this.latestViewUpdate.getCurrentView())) { - log.trace("Vote: Ignoring vote from {} for view {}, current view at {}", - vote.getAuthor(), view, this.latestViewUpdate.getCurrentView()); - return; - } - - if (this.hasReachedQuorum) { - log.trace("Vote: Ignoring vote from {} for view {}, quorum has already been reached", - vote.getAuthor(), view); - return; - } - - if (!this.self.equals(this.latestViewUpdate.getNextLeader()) && !vote.isTimeout()) { - log.trace("Vote: Ignoring vote from {} for view {}, unexpected vote", - vote.getAuthor(), view); - return; - } - - //TODO: rework to eliminate uncovered cases and use pattern matching switch - final VoteProcessingResult result = this.pendingVotes.insertVote(vote, this.validatorSet); - - if (result instanceof VoteProcessingResult.VoteAccepted) { - log.trace("Vote has been processed but didn't form a quorum"); - } else if (result instanceof VoteProcessingResult.VoteRejected) { - log.trace("Vote has been rejected because of: {}", - ((VoteProcessingResult.VoteRejected) result).getReason()); - } else if (result instanceof VoteProcessingResult.QuorumReached) { - this.hasReachedQuorum = true; - final ViewVotingResult viewResult = - ((VoteProcessingResult.QuorumReached) result).getViewVotingResult(); - viewQuorumReachedEventDispatcher - .dispatch(new ViewQuorumReached(viewResult, vote.getAuthor())); - } else { - throw new IllegalStateException("Unknown vote processing result type: " + result); - } - } - - @Override - public void processProposal(Proposal proposal) { - log.trace("Proposal: Processing {}", proposal); - - // TODO: Move into preprocessor - final View proposedVertexView = proposal.getView(); - final View currentView = this.latestViewUpdate.getCurrentView(); - if (!currentView.equals(proposedVertexView)) { - log.trace("Proposal: Ignoring view {}, current is: {}", proposedVertexView, currentView); - return; - } - - // TODO: Move insertion and maybe check into BFTSync - var proposedVertex = new VerifiedVertex( - proposal.getVertex(), - this.hasher.hash(proposal.getVertex()) - ); - this.vertexStore.insertVertex(proposedVertex); - } - - @Override - public void processLocalTimeout(ScheduledLocalTimeout scheduledLocalTimeout) { - log.trace("LocalTimeout: Processing {}", scheduledLocalTimeout); - - if (scheduledLocalTimeout.view().equals(this.latestViewUpdate.getCurrentView())) { - this.isViewTimedOut = true; - } - - this.pacemaker.processLocalTimeout(scheduledLocalTimeout); - } - - @Override - public void start() { - this.pacemaker.start(); - } + private static final Logger log = LogManager.getLogger(); + + private final BFTNode self; + private final VertexStore vertexStore; + private final Pacemaker pacemaker; + private final EventDispatcher viewQuorumReachedEventDispatcher; + private final EventDispatcher noVoteDispatcher; + private final RemoteEventDispatcher voteDispatcher; + private final Hasher hasher; + private final SafetyRules safetyRules; + private final BFTValidatorSet validatorSet; + private final PendingVotes pendingVotes; + + private BFTInsertUpdate latestInsertUpdate; + private ViewUpdate latestViewUpdate; + + /* Indicates whether the quorum (QC or TC) has already been formed for the current view. + * If the quorum has been reached (but view hasn't yet been updated), subsequent votes are ignored. + * TODO: consider moving it to PendingVotes or elsewhere. + */ + private boolean hasReachedQuorum = false; + + private boolean isViewTimedOut = false; + + public BFTEventReducer( + BFTNode self, + Pacemaker pacemaker, + VertexStore vertexStore, + EventDispatcher viewQuorumReachedEventDispatcher, + EventDispatcher noVoteDispatcher, + RemoteEventDispatcher voteDispatcher, + Hasher hasher, + SafetyRules safetyRules, + BFTValidatorSet validatorSet, + PendingVotes pendingVotes, + ViewUpdate initialViewUpdate) { + this.self = Objects.requireNonNull(self); + this.pacemaker = Objects.requireNonNull(pacemaker); + this.vertexStore = Objects.requireNonNull(vertexStore); + this.viewQuorumReachedEventDispatcher = + Objects.requireNonNull(viewQuorumReachedEventDispatcher); + this.noVoteDispatcher = Objects.requireNonNull(noVoteDispatcher); + this.voteDispatcher = Objects.requireNonNull(voteDispatcher); + this.hasher = Objects.requireNonNull(hasher); + this.safetyRules = Objects.requireNonNull(safetyRules); + this.validatorSet = Objects.requireNonNull(validatorSet); + this.pendingVotes = Objects.requireNonNull(pendingVotes); + this.latestViewUpdate = Objects.requireNonNull(initialViewUpdate); + } + + @Override + public void processBFTUpdate(BFTInsertUpdate update) { + log.trace("BFTUpdate: Processing {}", update); + + final View view = update.getHeader().getView(); + if (view.lt(this.latestViewUpdate.getCurrentView())) { + log.trace( + "InsertUpdate: Ignoring insert {} for view {}, current view at {}", + update, + view, + this.latestViewUpdate.getCurrentView()); + return; + } + + this.latestInsertUpdate = update; + this.tryVote(); + + this.pacemaker.processBFTUpdate(update); + } + + @Override + public void processViewUpdate(ViewUpdate viewUpdate) { + this.hasReachedQuorum = false; + this.isViewTimedOut = false; + this.latestViewUpdate = viewUpdate; + this.pacemaker.processViewUpdate(viewUpdate); + this.tryVote(); + } + + private void tryVote() { + BFTInsertUpdate update = this.latestInsertUpdate; + if (update == null) { + return; + } + + if (!Objects.equals(update.getHeader().getView(), this.latestViewUpdate.getCurrentView())) { + return; + } + + // check if already voted in this round + if (this.safetyRules.getLastVote(this.latestViewUpdate.getCurrentView()).isPresent()) { + return; + } + + // don't vote if view has timed out + if (this.isViewTimedOut) { + return; + } + + // TODO: what if insertUpdate occurs before viewUpdate + final BFTNode nextLeader = this.latestViewUpdate.getNextLeader(); + final Optional maybeVote = + this.safetyRules.voteFor( + update.getInserted().getVertex(), + update.getHeader(), + update.getInserted().getTimeOfExecution(), + this.latestViewUpdate.getHighQC()); + maybeVote.ifPresentOrElse( + vote -> this.voteDispatcher.dispatch(nextLeader, vote), + () -> this.noVoteDispatcher.dispatch(NoVote.create(update.getInserted().getVertex()))); + } + + @Override + public void processBFTRebuildUpdate(BFTRebuildUpdate update) { + // No-op + } + + @Override + public void processVote(Vote vote) { + log.trace("Vote: Processing {}", vote); + + final View view = vote.getView(); + + if (view.lt(this.latestViewUpdate.getCurrentView())) { + log.trace( + "Vote: Ignoring vote from {} for view {}, current view at {}", + vote.getAuthor(), + view, + this.latestViewUpdate.getCurrentView()); + return; + } + + if (this.hasReachedQuorum) { + log.trace( + "Vote: Ignoring vote from {} for view {}, quorum has already been reached", + vote.getAuthor(), + view); + return; + } + + if (!this.self.equals(this.latestViewUpdate.getNextLeader()) && !vote.isTimeout()) { + log.trace("Vote: Ignoring vote from {} for view {}, unexpected vote", vote.getAuthor(), view); + return; + } + + // TODO: rework to eliminate uncovered cases and use pattern matching switch + final VoteProcessingResult result = this.pendingVotes.insertVote(vote, this.validatorSet); + + if (result instanceof VoteProcessingResult.VoteAccepted) { + log.trace("Vote has been processed but didn't form a quorum"); + } else if (result instanceof VoteProcessingResult.VoteRejected) { + log.trace( + "Vote has been rejected because of: {}", + ((VoteProcessingResult.VoteRejected) result).getReason()); + } else if (result instanceof VoteProcessingResult.QuorumReached) { + this.hasReachedQuorum = true; + final ViewVotingResult viewResult = + ((VoteProcessingResult.QuorumReached) result).getViewVotingResult(); + viewQuorumReachedEventDispatcher.dispatch( + new ViewQuorumReached(viewResult, vote.getAuthor())); + } else { + throw new IllegalStateException("Unknown vote processing result type: " + result); + } + } + + @Override + public void processProposal(Proposal proposal) { + log.trace("Proposal: Processing {}", proposal); + + // TODO: Move into preprocessor + final View proposedVertexView = proposal.getView(); + final View currentView = this.latestViewUpdate.getCurrentView(); + if (!currentView.equals(proposedVertexView)) { + log.trace("Proposal: Ignoring view {}, current is: {}", proposedVertexView, currentView); + return; + } + + // TODO: Move insertion and maybe check into BFTSync + var proposedVertex = + new VerifiedVertex(proposal.getVertex(), this.hasher.hash(proposal.getVertex())); + this.vertexStore.insertVertex(proposedVertex); + } + + @Override + public void processLocalTimeout(ScheduledLocalTimeout scheduledLocalTimeout) { + log.trace("LocalTimeout: Processing {}", scheduledLocalTimeout); + + if (scheduledLocalTimeout.view().equals(this.latestViewUpdate.getCurrentView())) { + this.isViewTimedOut = true; + } + + this.pacemaker.processLocalTimeout(scheduledLocalTimeout); + } + + @Override + public void start() { + this.pacemaker.start(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventVerifier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventVerifier.java index 346b8b4782..0f287a68ae 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventVerifier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventVerifier.java @@ -70,121 +70,124 @@ import com.radixdlt.consensus.BFTEventProcessor; import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.HashVerifier; -import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -import com.radixdlt.consensus.liveness.VoteTimeout; -import com.radixdlt.crypto.Hasher; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; +import com.radixdlt.consensus.liveness.VoteTimeout; import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.crypto.Hasher; import java.util.Objects; import java.util.Optional; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Verifies signatures of BFT messages then forwards to the next processor - */ -@SecurityCritical({ SecurityKind.SIG_VERIFY }) +/** Verifies signatures of BFT messages then forwards to the next processor */ +@SecurityCritical({SecurityKind.SIG_VERIFY}) public final class BFTEventVerifier implements BFTEventProcessor { - private static final Logger log = LogManager.getLogger(); - - private final BFTValidatorSet validatorSet; - private final BFTEventProcessor forwardTo; - private final Hasher hasher; - private final HashVerifier verifier; - - public BFTEventVerifier( - BFTValidatorSet validatorSet, - BFTEventProcessor forwardTo, - Hasher hasher, - HashVerifier verifier - ) { - this.validatorSet = Objects.requireNonNull(validatorSet); - this.hasher = Objects.requireNonNull(hasher); - this.verifier = Objects.requireNonNull(verifier); - this.forwardTo = forwardTo; - } - - @Override - public void start() { - forwardTo.start(); - } - - @Override - public void processViewUpdate(ViewUpdate viewUpdate) { - forwardTo.processViewUpdate(viewUpdate); - } - - @Override - public void processVote(Vote vote) { - validAuthor(vote).ifPresent(node -> { - boolean verifiedVoteData = verifyHash(node, vote.getHashOfData(hasher), vote.getSignature(), vote); - if (!verifiedVoteData) { - log.warn("Ignoring invalid vote data {}", vote); - return; - } - - boolean verifiedTimeoutData = vote.getTimeoutSignature() - .map(timeoutSignature -> verify(node, VoteTimeout.of(vote), timeoutSignature, vote)) - .orElse(true); - - if (!verifiedTimeoutData) { - log.warn("Ignoring invalid timeout data {}", vote); - return; - } - - forwardTo.processVote(vote); - }); - } - - @Override - public void processProposal(Proposal proposal) { - validAuthor(proposal).ifPresent(node -> { - if (verify(node, proposal.getVertex(), proposal.getSignature(), proposal)) { - forwardTo.processProposal(proposal); - } - }); - } - - @Override - public void processLocalTimeout(ScheduledLocalTimeout localTimeout) { - forwardTo.processLocalTimeout(localTimeout); - } - - @Override - public void processBFTUpdate(BFTInsertUpdate update) { - forwardTo.processBFTUpdate(update); - } - - @Override - public void processBFTRebuildUpdate(BFTRebuildUpdate update) { - forwardTo.processBFTRebuildUpdate(update); - } - - private Optional validAuthor(ConsensusEvent event) { - BFTNode node = event.getAuthor(); - if (!validatorSet.containsNode(node)) { - log.warn( - "CONSENSUS_EVENT: {} from author {} not in validator set {}", - event.getClass().getSimpleName(), - node, - this.validatorSet - ); - return Optional.empty(); - } - return Optional.of(node); - } - - private boolean verifyHash(BFTNode author, HashCode hash, ECDSASignature signature, Object what) { - boolean verified = this.verifier.verify(author.getKey(), hash, signature); - if (!verified) { - log.info("Ignoring invalid signature from {} for {}", author, what); - } - return verified; - } - - private boolean verify(BFTNode author, Object hashable, ECDSASignature signature, Object what) { - return verifyHash(author, this.hasher.hash(hashable), signature, what); - } + private static final Logger log = LogManager.getLogger(); + + private final BFTValidatorSet validatorSet; + private final BFTEventProcessor forwardTo; + private final Hasher hasher; + private final HashVerifier verifier; + + public BFTEventVerifier( + BFTValidatorSet validatorSet, + BFTEventProcessor forwardTo, + Hasher hasher, + HashVerifier verifier) { + this.validatorSet = Objects.requireNonNull(validatorSet); + this.hasher = Objects.requireNonNull(hasher); + this.verifier = Objects.requireNonNull(verifier); + this.forwardTo = forwardTo; + } + + @Override + public void start() { + forwardTo.start(); + } + + @Override + public void processViewUpdate(ViewUpdate viewUpdate) { + forwardTo.processViewUpdate(viewUpdate); + } + + @Override + public void processVote(Vote vote) { + validAuthor(vote) + .ifPresent( + node -> { + boolean verifiedVoteData = + verifyHash(node, vote.getHashOfData(hasher), vote.getSignature(), vote); + if (!verifiedVoteData) { + log.warn("Ignoring invalid vote data {}", vote); + return; + } + + boolean verifiedTimeoutData = + vote.getTimeoutSignature() + .map( + timeoutSignature -> + verify(node, VoteTimeout.of(vote), timeoutSignature, vote)) + .orElse(true); + + if (!verifiedTimeoutData) { + log.warn("Ignoring invalid timeout data {}", vote); + return; + } + + forwardTo.processVote(vote); + }); + } + + @Override + public void processProposal(Proposal proposal) { + validAuthor(proposal) + .ifPresent( + node -> { + if (verify(node, proposal.getVertex(), proposal.getSignature(), proposal)) { + forwardTo.processProposal(proposal); + } + }); + } + + @Override + public void processLocalTimeout(ScheduledLocalTimeout localTimeout) { + forwardTo.processLocalTimeout(localTimeout); + } + + @Override + public void processBFTUpdate(BFTInsertUpdate update) { + forwardTo.processBFTUpdate(update); + } + + @Override + public void processBFTRebuildUpdate(BFTRebuildUpdate update) { + forwardTo.processBFTRebuildUpdate(update); + } + + private Optional validAuthor(ConsensusEvent event) { + BFTNode node = event.getAuthor(); + if (!validatorSet.containsNode(node)) { + log.warn( + "CONSENSUS_EVENT: {} from author {} not in validator set {}", + event.getClass().getSimpleName(), + node, + this.validatorSet); + return Optional.empty(); + } + return Optional.of(node); + } + + private boolean verifyHash(BFTNode author, HashCode hash, ECDSASignature signature, Object what) { + boolean verified = this.verifier.verify(author.getKey(), hash, signature); + if (!verified) { + log.info("Ignoring invalid signature from {} for {}", author, what); + } + return verified; + } + + private boolean verify(BFTNode author, Object hashable, ECDSASignature signature, Object what) { + return verifyHash(author, this.hasher.hash(hashable), signature, what); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTHighQCUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTHighQCUpdate.java index f8ce5d9b16..cad40697ea 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTHighQCUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTHighQCUpdate.java @@ -67,45 +67,44 @@ import com.radixdlt.consensus.HighQC; import java.util.Objects; -/** - * An event emitted when the high qc has been updated - */ +/** An event emitted when the high qc has been updated */ public final class BFTHighQCUpdate { - private final VerifiedVertexStoreState vertexStoreState; + private final VerifiedVertexStoreState vertexStoreState; - private BFTHighQCUpdate(VerifiedVertexStoreState vertexStoreState) { - this.vertexStoreState = vertexStoreState; - } + private BFTHighQCUpdate(VerifiedVertexStoreState vertexStoreState) { + this.vertexStoreState = vertexStoreState; + } - public static BFTHighQCUpdate create(VerifiedVertexStoreState vertexStoreState) { - return new BFTHighQCUpdate(vertexStoreState); - } + public static BFTHighQCUpdate create(VerifiedVertexStoreState vertexStoreState) { + return new BFTHighQCUpdate(vertexStoreState); + } - public HighQC getHighQC() { - return vertexStoreState.getHighQC(); - } + public HighQC getHighQC() { + return vertexStoreState.getHighQC(); + } - public VerifiedVertexStoreState getVertexStoreState() { - return vertexStoreState; - } + public VerifiedVertexStoreState getVertexStoreState() { + return vertexStoreState; + } - @Override - public String toString() { - return String.format("%s{highQC=%s}", this.getClass().getSimpleName(), vertexStoreState.getHighQC()); - } + @Override + public String toString() { + return String.format( + "%s{highQC=%s}", this.getClass().getSimpleName(), vertexStoreState.getHighQC()); + } - @Override - public int hashCode() { - return Objects.hash(vertexStoreState); - } + @Override + public int hashCode() { + return Objects.hash(vertexStoreState); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof BFTHighQCUpdate)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof BFTHighQCUpdate)) { + return false; + } - BFTHighQCUpdate other = (BFTHighQCUpdate) o; - return Objects.equals(other.vertexStoreState, this.vertexStoreState); - } + BFTHighQCUpdate other = (BFTHighQCUpdate) o; + return Objects.equals(other.vertexStoreState, this.vertexStoreState); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTInsertUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTInsertUpdate.java index 3177418789..97a6d0abd1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTInsertUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTInsertUpdate.java @@ -67,67 +67,64 @@ import com.radixdlt.consensus.BFTHeader; import java.util.Objects; -/** - * An update emitted when the BFT has inserted a new vertex - */ +/** An update emitted when the BFT has inserted a new vertex */ public final class BFTInsertUpdate { - private final VerifiedVertexStoreState vertexStoreState; - private final PreparedVertex insertedVertex; - private final int siblingsCount; + private final VerifiedVertexStoreState vertexStoreState; + private final PreparedVertex insertedVertex; + private final int siblingsCount; - private BFTInsertUpdate(PreparedVertex insertedVertex, int siblingsCount, VerifiedVertexStoreState vertexStoreState) { - this.insertedVertex = Objects.requireNonNull(insertedVertex); - this.siblingsCount = siblingsCount; - this.vertexStoreState = Objects.requireNonNull(vertexStoreState); - } + private BFTInsertUpdate( + PreparedVertex insertedVertex, int siblingsCount, VerifiedVertexStoreState vertexStoreState) { + this.insertedVertex = Objects.requireNonNull(insertedVertex); + this.siblingsCount = siblingsCount; + this.vertexStoreState = Objects.requireNonNull(vertexStoreState); + } - public static BFTInsertUpdate insertedVertex(PreparedVertex insertedVertex, int siblingsCount, VerifiedVertexStoreState vertexStoreState) { - return new BFTInsertUpdate( - insertedVertex, - siblingsCount, - vertexStoreState - ); - } + public static BFTInsertUpdate insertedVertex( + PreparedVertex insertedVertex, int siblingsCount, VerifiedVertexStoreState vertexStoreState) { + return new BFTInsertUpdate(insertedVertex, siblingsCount, vertexStoreState); + } - public VerifiedVertexStoreState getVertexStoreState() { - return vertexStoreState; - } + public VerifiedVertexStoreState getVertexStoreState() { + return vertexStoreState; + } - public int getSiblingsCount() { - return siblingsCount; - } + public int getSiblingsCount() { + return siblingsCount; + } - public int getVertexStoreSize() { - return vertexStoreState.getVertices().size(); - } + public int getVertexStoreSize() { + return vertexStoreState.getVertices().size(); + } - public BFTHeader getHeader() { - return new BFTHeader(insertedVertex.getView(), insertedVertex.getId(), insertedVertex.getLedgerHeader()); - } + public BFTHeader getHeader() { + return new BFTHeader( + insertedVertex.getView(), insertedVertex.getId(), insertedVertex.getLedgerHeader()); + } - public PreparedVertex getInserted() { - return insertedVertex; - } + public PreparedVertex getInserted() { + return insertedVertex; + } - @Override - public int hashCode() { - return Objects.hash(vertexStoreState, insertedVertex, siblingsCount); - } + @Override + public int hashCode() { + return Objects.hash(vertexStoreState, insertedVertex, siblingsCount); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof BFTInsertUpdate)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof BFTInsertUpdate)) { + return false; + } - BFTInsertUpdate other = (BFTInsertUpdate) o; - return Objects.equals(this.vertexStoreState, other.vertexStoreState) - && Objects.equals(this.insertedVertex, other.insertedVertex) - && this.siblingsCount == other.siblingsCount; - } + BFTInsertUpdate other = (BFTInsertUpdate) o; + return Objects.equals(this.vertexStoreState, other.vertexStoreState) + && Objects.equals(this.insertedVertex, other.insertedVertex) + && this.siblingsCount == other.siblingsCount; + } - @Override - public String toString() { - return String.format("%s{inserted=%s}", getClass().getSimpleName(), insertedVertex); - } + @Override + public String toString() { + return String.format("%s{inserted=%s}", getClass().getSimpleName(), insertedVertex); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTNode.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTNode.java index 09f9eb77ef..6c2a799f1d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTNode.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTNode.java @@ -67,62 +67,61 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.exception.PublicKeyException; - import java.util.Objects; /** * A node in a BFT network which can run BFT validation * - * TODO: turn this into an interface so that an ECPublicKey is not required - * TODO: Serialization of BFT messages are currently what prevent this from happening + *

TODO: turn this into an interface so that an ECPublicKey is not required TODO: Serialization + * of BFT messages are currently what prevent this from happening */ public final class BFTNode { - private final ECPublicKey key; - private final String simpleName; + private final ECPublicKey key; + private final String simpleName; - private BFTNode(ECPublicKey key, String simpleName) { - this.key = Objects.requireNonNull(key); - this.simpleName = Objects.requireNonNull(simpleName); - } + private BFTNode(ECPublicKey key, String simpleName) { + this.key = Objects.requireNonNull(key); + this.simpleName = Objects.requireNonNull(simpleName); + } - public static BFTNode create(ECPublicKey key) { - var shortenedAddress = key.toHex().substring(0, 10); - return new BFTNode(key, shortenedAddress); - } + public static BFTNode create(ECPublicKey key) { + var shortenedAddress = key.toHex().substring(0, 10); + return new BFTNode(key, shortenedAddress); + } - public static BFTNode fromPublicKeyBytes(byte[] key) throws PublicKeyException { - return create(ECPublicKey.fromBytes(key)); - } + public static BFTNode fromPublicKeyBytes(byte[] key) throws PublicKeyException { + return create(ECPublicKey.fromBytes(key)); + } - public static BFTNode random() { - return create(ECKeyPair.generateNew().getPublicKey()); - } + public static BFTNode random() { + return create(ECKeyPair.generateNew().getPublicKey()); + } - public ECPublicKey getKey() { - return key; - } + public ECPublicKey getKey() { + return key; + } - @Override - public int hashCode() { - return Objects.hash(key); - } + @Override + public int hashCode() { + return Objects.hash(key); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof BFTNode)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof BFTNode)) { + return false; + } - BFTNode bftNodeId = (BFTNode) o; - return Objects.equals(bftNodeId.key, this.key); - } + BFTNode bftNodeId = (BFTNode) o; + return Objects.equals(bftNodeId.key, this.key); + } - public String getSimpleName() { - return simpleName; - } + public String getSimpleName() { + return simpleName; + } - @Override - public String toString() { - return simpleName; - } + @Override + public String toString() { + return simpleName; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTRebuildUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTRebuildUpdate.java index 6f1b7c364c..2d18ef8685 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTRebuildUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTRebuildUpdate.java @@ -66,42 +66,40 @@ import java.util.Objects; -/** - * An update emitted when the BFT has been rebuilt - */ +/** An update emitted when the BFT has been rebuilt */ public final class BFTRebuildUpdate { - private final VerifiedVertexStoreState vertexStoreState; - - private BFTRebuildUpdate(VerifiedVertexStoreState vertexStoreState) { - this.vertexStoreState = vertexStoreState; - } + private final VerifiedVertexStoreState vertexStoreState; - public static BFTRebuildUpdate create(VerifiedVertexStoreState vertexStoreState) { - return new BFTRebuildUpdate(vertexStoreState); - } + private BFTRebuildUpdate(VerifiedVertexStoreState vertexStoreState) { + this.vertexStoreState = vertexStoreState; + } - public VerifiedVertexStoreState getVertexStoreState() { - return vertexStoreState; - } + public static BFTRebuildUpdate create(VerifiedVertexStoreState vertexStoreState) { + return new BFTRebuildUpdate(vertexStoreState); + } + public VerifiedVertexStoreState getVertexStoreState() { + return vertexStoreState; + } - @Override - public String toString() { - return String.format("%s{root=%s}", this.getClass().getSimpleName(), vertexStoreState.getRoot()); - } + @Override + public String toString() { + return String.format( + "%s{root=%s}", this.getClass().getSimpleName(), vertexStoreState.getRoot()); + } - @Override - public int hashCode() { - return Objects.hash(vertexStoreState); - } + @Override + public int hashCode() { + return Objects.hash(vertexStoreState); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof BFTRebuildUpdate)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof BFTRebuildUpdate)) { + return false; + } - BFTRebuildUpdate other = (BFTRebuildUpdate) o; - return Objects.equals(other.vertexStoreState, this.vertexStoreState); - } + BFTRebuildUpdate other = (BFTRebuildUpdate) o; + return Objects.equals(other.vertexStoreState, this.vertexStoreState); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTSyncer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTSyncer.java index dba02cf3a5..76b1e03d77 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTSyncer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTSyncer.java @@ -65,29 +65,27 @@ package com.radixdlt.consensus.bft; import com.radixdlt.consensus.HighQC; - import javax.annotation.Nullable; /** - * Synchronizes the state in order for consensus preparation - * TODO: Fix interfaces as it seems like a still rather awkward interface + * Synchronizes the state in order for consensus preparation TODO: Fix interfaces as it seems like a + * still rather awkward interface */ public interface BFTSyncer { - enum SyncResult { - SYNCED, - IN_PROGRESS, - INVALID - } + enum SyncResult { + SYNCED, + IN_PROGRESS, + INVALID + } - /** - * Initiate a sync to a given QC and a committedQC. Returns true if already synced - * otherwise will initiate a syncing process. - * An author is used because the author will most likely have the corresponding vertices - * still in memory. - * - * @param highQC the {@link HighQC} to sync to - * @param author the original author of the qc - * @return {@code SyncResult.SYNCED} if already synced - */ - SyncResult syncToQC(HighQC highQC, @Nullable BFTNode author); + /** + * Initiate a sync to a given QC and a committedQC. Returns true if already synced otherwise will + * initiate a syncing process. An author is used because the author will most likely have the + * corresponding vertices still in memory. + * + * @param highQC the {@link HighQC} to sync to + * @param author the original author of the qc + * @return {@code SyncResult.SYNCED} if already synced + */ + SyncResult syncToQC(HighQC highQC, @Nullable BFTNode author); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidator.java index d2b1f4a2bf..510546f14c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidator.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus.bft; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.crypto.exception.PublicKeyException; @@ -74,95 +76,86 @@ import com.radixdlt.serialization.SerializerId2; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.UInt256; - -import javax.annotation.concurrent.Immutable; import java.util.Objects; +import javax.annotation.concurrent.Immutable; -import static java.util.Objects.requireNonNull; - - -/** - * Represents a validator and their Proof-of-Stake status. - */ +/** Represents a validator and their Proof-of-Stake status. */ @Immutable @SerializerId2("consensus.bft_validator") public final class BFTValidator { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput({Output.ALL}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - // Power associated with each validator, could e.g. be based on staked tokens - @JsonProperty("power") - @DsonOutput({Output.ALL}) - private final UInt256 power; - - // Public key for consensus - private final BFTNode node; - - @JsonCreator - private BFTValidator( - @JsonProperty(value = "node", required = true) String nodeKey, - @JsonProperty(value = "power", required = true) UInt256 power - ) { - this(toBFTNode(requireNonNull(nodeKey)), power); - } - - private BFTValidator( - BFTNode node, - UInt256 power - ) { - this.node = requireNonNull(node); - this.power = requireNonNull(power); - } - - public static BFTValidator from(BFTNode node, UInt256 power) { - return new BFTValidator(node, power); - } - - public BFTNode getNode() { - return node; - } - - public UInt256 getPower() { - return power; - } - - @JsonProperty("node") - @DsonOutput(Output.ALL) - private String getSerializerNodeKey() { - return encodePublicKey(this.node); - } - - private static String encodePublicKey(BFTNode key) { - return Bytes.toHexString(key.getKey().getBytes()); - } - - private static BFTNode toBFTNode(String str) { - try { - return BFTNode.fromPublicKeyBytes(Bytes.fromHexString(str)); - } catch (PublicKeyException e) { - throw new IllegalStateException("Error decoding public key", e); - } - } - - @Override - public int hashCode() { - return Objects.hash(this.node, this.power); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof BFTValidator other) - && Objects.equals(this.node, other.node) - && Objects.equals(this.power, other.power); - } - - @Override - public String toString() { - return String.format("%s{node=%s power=%s}", getClass().getSimpleName(), this.node.getSimpleName(), this.power); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput({Output.ALL}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + // Power associated with each validator, could e.g. be based on staked tokens + @JsonProperty("power") + @DsonOutput({Output.ALL}) + private final UInt256 power; + + // Public key for consensus + private final BFTNode node; + + @JsonCreator + private BFTValidator( + @JsonProperty(value = "node", required = true) String nodeKey, + @JsonProperty(value = "power", required = true) UInt256 power) { + this(toBFTNode(requireNonNull(nodeKey)), power); + } + + private BFTValidator(BFTNode node, UInt256 power) { + this.node = requireNonNull(node); + this.power = requireNonNull(power); + } + + public static BFTValidator from(BFTNode node, UInt256 power) { + return new BFTValidator(node, power); + } + + public BFTNode getNode() { + return node; + } + + public UInt256 getPower() { + return power; + } + + @JsonProperty("node") + @DsonOutput(Output.ALL) + private String getSerializerNodeKey() { + return encodePublicKey(this.node); + } + + private static String encodePublicKey(BFTNode key) { + return Bytes.toHexString(key.getKey().getBytes()); + } + + private static BFTNode toBFTNode(String str) { + try { + return BFTNode.fromPublicKeyBytes(Bytes.fromHexString(str)); + } catch (PublicKeyException e) { + throw new IllegalStateException("Error decoding public key", e); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.node, this.power); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof BFTValidator other) + && Objects.equals(this.node, other.node) + && Objects.equals(this.power, other.power); + } + + @Override + public String toString() { + return String.format( + "%s{node=%s power=%s}", getClass().getSimpleName(), this.node.getSimpleName(), this.power); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidatorSet.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidatorSet.java index 0b0b514220..fa1c118751 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidatorSet.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTValidatorSet.java @@ -75,117 +75,115 @@ import java.util.stream.Stream; /** - * Set of validators for consensus. Only validators with power >= 1 will - * be part of the set. - *

- * Note that this set will validate for set sizes less than 4, - * as long as all validators sign. + * Set of validators for consensus. Only validators with power >= 1 will be part of the set. + * + *

Note that this set will validate for set sizes less than 4, as long as all validators sign. */ public final class BFTValidatorSet { - private final ImmutableBiMap validators; - - // Because we will base power on tokens and because tokens have a max limit - // of 2^256 this should never overflow - private final transient UInt256 totalPower; - - private BFTValidatorSet(Collection validators) { - this(validators.stream()); - } - - private BFTValidatorSet(Stream validators) { - this.validators = validators - .filter(v -> !v.getPower().isZero()) - .collect(ImmutableBiMap.toImmutableBiMap(BFTValidator::getNode, Function.identity())); - this.totalPower = this.validators.values().stream() - .map(BFTValidator::getPower) - .reduce(UInt256::add) - .orElse(UInt256.ZERO); - } - - /** - * Create a validator set from a collection of validators. The sum - * of power of all validator should not exceed UInt256.MAX_VALUE otherwise - * the resulting ValidatorSet will perform in an undefined way. - * This invariant should be upheld within the system due to max number of - * tokens being constrained to UInt256.MAX_VALUE. - * - * @param validators the collection of validators - * @return The new {@code ValidatorSet}. - */ - public static BFTValidatorSet from(Collection validators) { - return new BFTValidatorSet(validators); - } - - /** - * Create a validator set from a stream of validators. The sum - * of power of all validator should not exceed UInt256.MAX_VALUE otherwise - * the resulting ValidatorSet will perform in an undefined way. - * This invariant should be upheld within the system due to max number of - * tokens being constrained to UInt256.MAX_VALUE. - * - * @param validators the stream of validators - * @return The new {@code ValidatorSet}. - */ - public static BFTValidatorSet from(Stream validators) { - return new BFTValidatorSet(validators); - } - - /** - * Create an initial validation state with no signatures for this validator set. - * - * @return An initial validation state with no signatures - */ - public ValidationState newValidationState() { - return ValidationState.forValidatorSet(this); - } - - public boolean containsNode(BFTNode node) { - return validators.containsKey(node); - } - - public UInt256 getPower(BFTNode node) { - return validators.get(node).getPower(); - } - - public UInt256 getTotalPower() { - return totalPower; - } - - public ImmutableSet getValidators() { - return validators.values(); - } - - public ImmutableSet nodes() { - return validators.keySet(); - } - - public ImmutableMap validatorsByKey() { - return validators; - } - - @Override - public int hashCode() { - return Objects.hashCode(this.validators); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof BFTValidatorSet) { - BFTValidatorSet other = (BFTValidatorSet) obj; - return Objects.equals(this.validators, other.validators); - } - return false; - } - - @Override - public String toString() { - final StringJoiner joiner = new StringJoiner(","); - for (BFTValidator validator : this.validators.values()) { - joiner.add(String.format("%s=%s", validator.getNode().getSimpleName(), validator.getPower())); - } - return String.format("%s[%s]", this.getClass().getSimpleName(), joiner.toString()); - } + private final ImmutableBiMap validators; + + // Because we will base power on tokens and because tokens have a max limit + // of 2^256 this should never overflow + private final transient UInt256 totalPower; + + private BFTValidatorSet(Collection validators) { + this(validators.stream()); + } + + private BFTValidatorSet(Stream validators) { + this.validators = + validators + .filter(v -> !v.getPower().isZero()) + .collect(ImmutableBiMap.toImmutableBiMap(BFTValidator::getNode, Function.identity())); + this.totalPower = + this.validators.values().stream() + .map(BFTValidator::getPower) + .reduce(UInt256::add) + .orElse(UInt256.ZERO); + } + + /** + * Create a validator set from a collection of validators. The sum of power of all validator + * should not exceed UInt256.MAX_VALUE otherwise the resulting ValidatorSet will perform in an + * undefined way. This invariant should be upheld within the system due to max number of tokens + * being constrained to UInt256.MAX_VALUE. + * + * @param validators the collection of validators + * @return The new {@code ValidatorSet}. + */ + public static BFTValidatorSet from(Collection validators) { + return new BFTValidatorSet(validators); + } + + /** + * Create a validator set from a stream of validators. The sum of power of all validator should + * not exceed UInt256.MAX_VALUE otherwise the resulting ValidatorSet will perform in an undefined + * way. This invariant should be upheld within the system due to max number of tokens being + * constrained to UInt256.MAX_VALUE. + * + * @param validators the stream of validators + * @return The new {@code ValidatorSet}. + */ + public static BFTValidatorSet from(Stream validators) { + return new BFTValidatorSet(validators); + } + + /** + * Create an initial validation state with no signatures for this validator set. + * + * @return An initial validation state with no signatures + */ + public ValidationState newValidationState() { + return ValidationState.forValidatorSet(this); + } + + public boolean containsNode(BFTNode node) { + return validators.containsKey(node); + } + + public UInt256 getPower(BFTNode node) { + return validators.get(node).getPower(); + } + + public UInt256 getTotalPower() { + return totalPower; + } + + public ImmutableSet getValidators() { + return validators.values(); + } + + public ImmutableSet nodes() { + return validators.keySet(); + } + + public ImmutableMap validatorsByKey() { + return validators; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.validators); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BFTValidatorSet) { + BFTValidatorSet other = (BFTValidatorSet) obj; + return Objects.equals(this.validators, other.validators); + } + return false; + } + + @Override + public String toString() { + final StringJoiner joiner = new StringJoiner(","); + for (BFTValidator validator : this.validators.values()) { + joiner.add(String.format("%s=%s", validator.getNode().getSimpleName(), validator.getPower())); + } + return String.format("%s[%s]", this.getClass().getSimpleName(), joiner.toString()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/EmptyBFTEventProcessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/EmptyBFTEventProcessor.java index 1850a4880f..3bf339a188 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/EmptyBFTEventProcessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/EmptyBFTEventProcessor.java @@ -69,44 +69,42 @@ import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -/** - * An empty BFT event processor - */ +/** An empty BFT event processor */ public enum EmptyBFTEventProcessor implements BFTEventProcessor { - INSTANCE; + INSTANCE; - @Override - public void processVote(Vote vote) { - // No-op - } + @Override + public void processVote(Vote vote) { + // No-op + } - @Override - public void processProposal(Proposal proposal) { - // No-op - } + @Override + public void processProposal(Proposal proposal) { + // No-op + } - @Override - public void processLocalTimeout(ScheduledLocalTimeout timeout) { - // No-op - } + @Override + public void processLocalTimeout(ScheduledLocalTimeout timeout) { + // No-op + } - @Override - public void processBFTUpdate(BFTInsertUpdate update) { - // No-op - } + @Override + public void processBFTUpdate(BFTInsertUpdate update) { + // No-op + } - @Override - public void processBFTRebuildUpdate(BFTRebuildUpdate update) { - // No-op - } + @Override + public void processBFTRebuildUpdate(BFTRebuildUpdate update) { + // No-op + } - @Override - public void start() { - // No-op - } + @Override + public void start() { + // No-op + } - @Override - public void processViewUpdate(ViewUpdate viewUpdate) { - // No-op - } + @Override + public void processViewUpdate(ViewUpdate viewUpdate) { + // No-op + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java index bb710d64e3..1d214a053d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java @@ -68,11 +68,11 @@ import java.util.Objects; /** - * Exception specifying that a vertex cannot be inserted because - * it's parent is missing from the current store. + * Exception specifying that a vertex cannot be inserted because it's parent is missing from the + * current store. */ public class MissingParentException extends RuntimeException { - MissingParentException(HashCode parentId) { - super("Parent Vertex missing: " + Objects.requireNonNull(parentId)); - } + MissingParentException(HashCode parentId) { + super("Parent Vertex missing: " + Objects.requireNonNull(parentId)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/NoVote.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/NoVote.java index 31dfe94245..549fc94afe 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/NoVote.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/NoVote.java @@ -66,41 +66,39 @@ import java.util.Objects; -/** - * An event emitted when the node decides not to vote for a view - */ +/** An event emitted when the node decides not to vote for a view */ public final class NoVote { - private final VerifiedVertex vertex; + private final VerifiedVertex vertex; - private NoVote(VerifiedVertex vertex) { - this.vertex = vertex; - } + private NoVote(VerifiedVertex vertex) { + this.vertex = vertex; + } - public static NoVote create(VerifiedVertex vertex) { - return new NoVote(vertex); - } + public static NoVote create(VerifiedVertex vertex) { + return new NoVote(vertex); + } - public VerifiedVertex getVertex() { - return vertex; - } + public VerifiedVertex getVertex() { + return vertex; + } - @Override - public String toString() { - return String.format("%s{vertex=%s}", this.getClass().getSimpleName(), this.vertex); - } + @Override + public String toString() { + return String.format("%s{vertex=%s}", this.getClass().getSimpleName(), this.vertex); + } - @Override - public int hashCode() { - return Objects.hash(vertex); - } + @Override + public int hashCode() { + return Objects.hash(vertex); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof NoVote)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof NoVote)) { + return false; + } - NoVote other = (NoVote) o; - return Objects.equals(this.vertex, other.vertex); - } + NoVote other = (NoVote) o; + return Objects.equals(this.vertex, other.vertex); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerMaxExponent.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerMaxExponent.java index 54f37488b4..1c02fc8945 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerMaxExponent.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerMaxExponent.java @@ -73,11 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * Pacemaker's max exponent - */ +/** Pacemaker's max exponent */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface PacemakerMaxExponent { -} +public @interface PacemakerMaxExponent {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerRate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerRate.java index c8c5e3cf7f..7718c19ee1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerRate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerRate.java @@ -73,11 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * The rate at which the pacemaker increases timeout - */ +/** The rate at which the pacemaker increases timeout */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface PacemakerRate { -} +public @interface PacemakerRate {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerTimeout.java index a360938f9f..a2a15e82f4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PacemakerTimeout.java @@ -73,12 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * The amount of time the pacemaker will wait until considering - * the round timed out. - */ +/** The amount of time the pacemaker will wait until considering the round timed out. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface PacemakerTimeout { -} +public @interface PacemakerTimeout {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PersistentVertexStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PersistentVertexStore.java index b38b725d50..b841fa1271 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PersistentVertexStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PersistentVertexStore.java @@ -65,9 +65,9 @@ package com.radixdlt.consensus.bft; /** - * Store which saves the Vertex Store State for recovery - * TODO: Remove this interface, integrate with RadixEngine ((RPNV1-718) + * Store which saves the Vertex Store State for recovery TODO: Remove this interface, integrate with + * RadixEngine ((RPNV1-718) */ public interface PersistentVertexStore { - void save(VerifiedVertexStoreState vertexStoreState); + void save(VerifiedVertexStoreState vertexStoreState); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PreparedVertex.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PreparedVertex.java index 9689e38dbe..c0f2eb83a1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PreparedVertex.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/PreparedVertex.java @@ -69,84 +69,85 @@ import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.ledger.StateComputerLedger.PreparedTxn; import com.radixdlt.utils.Pair; - import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; -/** - * Vertex which has been executed in the prepare phase - */ +/** Vertex which has been executed in the prepare phase */ public final class PreparedVertex { - private final long timeOfExecution; - private final VerifiedVertex vertex; - - private final LedgerHeader ledgerHeader; - - private final List preparedTxns; - private final Map commandExceptions; - - PreparedVertex( - VerifiedVertex vertex, - LedgerHeader ledgerHeader, - List preparedTxns, - Map commandExceptions, - long timeOfExecution - ) { - this.vertex = Objects.requireNonNull(vertex); - this.ledgerHeader = Objects.requireNonNull(ledgerHeader); - this.preparedTxns = Objects.requireNonNull(preparedTxns); - this.commandExceptions = Objects.requireNonNull(commandExceptions); - this.timeOfExecution = timeOfExecution; - } - - public long getTimeOfExecution() { - return timeOfExecution; - } - - public HashCode getId() { - return vertex.getId(); - } - - public HashCode getParentId() { - return vertex.getParentId(); - } - - public View getView() { - return vertex.getView(); - } - - public Stream successfulCommands() { - return preparedTxns.stream(); - } - - public Stream> errorCommands() { - return commandExceptions.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue())); - } - - public Stream getTxns() { - return Stream.concat(successfulCommands().map(PreparedTxn::txn), errorCommands().map(Pair::getFirst)); - } - - /** - * Retrieve the resulting header which is to be persisted on ledger - * @return the header - */ - public LedgerHeader getLedgerHeader() { - return ledgerHeader; - } - - /** - * Retrieve the vertex which was executed - * @return the executed vertex - */ - public VerifiedVertex getVertex() { - return vertex; - } - - @Override - public String toString() { - return String.format("%s{vertex=%s ledgerHeader=%s}", this.getClass().getSimpleName(), this.vertex, this.ledgerHeader); - } + private final long timeOfExecution; + private final VerifiedVertex vertex; + + private final LedgerHeader ledgerHeader; + + private final List preparedTxns; + private final Map commandExceptions; + + PreparedVertex( + VerifiedVertex vertex, + LedgerHeader ledgerHeader, + List preparedTxns, + Map commandExceptions, + long timeOfExecution) { + this.vertex = Objects.requireNonNull(vertex); + this.ledgerHeader = Objects.requireNonNull(ledgerHeader); + this.preparedTxns = Objects.requireNonNull(preparedTxns); + this.commandExceptions = Objects.requireNonNull(commandExceptions); + this.timeOfExecution = timeOfExecution; + } + + public long getTimeOfExecution() { + return timeOfExecution; + } + + public HashCode getId() { + return vertex.getId(); + } + + public HashCode getParentId() { + return vertex.getParentId(); + } + + public View getView() { + return vertex.getView(); + } + + public Stream successfulCommands() { + return preparedTxns.stream(); + } + + public Stream> errorCommands() { + return commandExceptions.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue())); + } + + public Stream getTxns() { + return Stream.concat( + successfulCommands().map(PreparedTxn::txn), errorCommands().map(Pair::getFirst)); + } + + /** + * Retrieve the resulting header which is to be persisted on ledger + * + * @return the header + */ + public LedgerHeader getLedgerHeader() { + return ledgerHeader; + } + + /** + * Retrieve the vertex which was executed + * + * @return the executed vertex + */ + public VerifiedVertex getVertex() { + return vertex; + } + + @Override + public String toString() { + return String.format( + "%s{vertex=%s ledgerHeader=%s}", + this.getClass().getSimpleName(), this.vertex, this.ledgerHeader); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/Self.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/Self.java index 235401d6a3..e4ff1c3ca4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/Self.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/Self.java @@ -73,12 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * Identifies that the target concerns the identity of the - * current running node - */ +/** Identifies that the target concerns the identity of the current running node */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface Self { -} +public @interface Self {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ValidationState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ValidationState.java index e03153962b..2297607214 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ValidationState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ValidationState.java @@ -64,141 +64,145 @@ package com.radixdlt.consensus.bft; -import com.radixdlt.utils.UInt256; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.radixdlt.consensus.TimestampedECDSASignature; import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.utils.UInt256; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import javax.annotation.concurrent.NotThreadSafe; /** - * Keeps track of current validation state for a thing that - * needs multiple correct signatures for a quorum. + * Keeps track of current validation state for a thing that needs multiple correct signatures for a + * quorum. */ @NotThreadSafe public final class ValidationState { - private final BFTValidatorSet validatorSet; - private final Map signedNodes; - private transient UInt256 signedPower; - private final transient UInt256 threshold; - - /** - * Construct empty validation state for given hash and set of validator keys. - * - * @param validatorSet The validator set - */ - public static ValidationState forValidatorSet(BFTValidatorSet validatorSet) { - return new ValidationState(validatorSet); - } - - private ValidationState(BFTValidatorSet validatorSet) { - this.validatorSet = Objects.requireNonNull(validatorSet); - this.signedNodes = new HashMap<>(); - this.signedPower = UInt256.ZERO; - this.threshold = threshold(validatorSet.getTotalPower()); - } - - /** - * Removes the signature for the specified key, if present. - * - * @param node the node who's signature is to be removed - */ - public void removeSignature(BFTNode node) { - if (this.validatorSet.containsNode(node)) { - this.signedNodes.computeIfPresent(node, (k, v) -> { - this.signedPower = this.signedPower.subtract(this.validatorSet.getPower(node)); - return null; - }); - } - } - - /** - * Adds key and signature to our list of signing keys and signatures. - * Note that it is assumed that signature validation is performed - * elsewhere. - * - * @param node The node - * @param timestamp The timestamp of the signature - * @param signature The signature to verify - * @return whether the key was added or not - */ - public boolean addSignature(BFTNode node, long timestamp, ECDSASignature signature) { - if (validatorSet.containsNode(node) - && !this.signedNodes.containsKey(node)) { - this.signedNodes.computeIfAbsent(node, k -> { - UInt256 weight = this.validatorSet.getPower(node); - this.signedPower = this.signedPower.add(weight); - return TimestampedECDSASignature.from(timestamp, signature); - }); - return true; - } - return false; - } - - /** - * Return {@code true} if we have not yet accumulated any valid signatures. - * @return {@code true} if we have not accumulated any signatures, {@code false} otherwise. - */ - public boolean isEmpty() { - return this.signedNodes.isEmpty(); - } - - /** - * Returns {@code true} if we have enough valid signatures to form a quorum. - * - * @return {@code true} if we have enough valid signatures to form a quorum, - */ - public boolean complete() { - return signedPower.compareTo(threshold) >= 0; - } - - /** - * Returns an {@link ECDSASignatures} object for our current set of valid signatures. - * - * @return an {@link ECDSASignatures} object for our current set of valid signatures - */ - public TimestampedECDSASignatures signatures() { - return new TimestampedECDSASignatures(ImmutableMap.copyOf(this.signedNodes)); - } - - @VisibleForTesting - static UInt256 threshold(UInt256 n) { - return n.subtract(acceptableFaults(n)); - } - - @VisibleForTesting - static UInt256 acceptableFaults(UInt256 n) { - // Compute acceptable faults based on Byzantine limit n = 3f + 1 - // i.e. f = (n - 1) / 3 - return n.isZero() ? n : n.decrement().divide(UInt256.THREE); - } - - @Override - public int hashCode() { - return Objects.hash(validatorSet, signedNodes); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof ValidationState) { - ValidationState that = (ValidationState) obj; - return Objects.equals(this.validatorSet, that.validatorSet) - && Objects.equals(this.signedNodes, that.signedNodes); - } - return false; - } - - @Override - public String toString() { - return String.format("%s[validatorSet=%s, signedNodes=%s]", - getClass().getSimpleName(), validatorSet, signedNodes); - } + private final BFTValidatorSet validatorSet; + private final Map signedNodes; + private transient UInt256 signedPower; + private final transient UInt256 threshold; + + /** + * Construct empty validation state for given hash and set of validator keys. + * + * @param validatorSet The validator set + */ + public static ValidationState forValidatorSet(BFTValidatorSet validatorSet) { + return new ValidationState(validatorSet); + } + + private ValidationState(BFTValidatorSet validatorSet) { + this.validatorSet = Objects.requireNonNull(validatorSet); + this.signedNodes = new HashMap<>(); + this.signedPower = UInt256.ZERO; + this.threshold = threshold(validatorSet.getTotalPower()); + } + + /** + * Removes the signature for the specified key, if present. + * + * @param node the node who's signature is to be removed + */ + public void removeSignature(BFTNode node) { + if (this.validatorSet.containsNode(node)) { + this.signedNodes.computeIfPresent( + node, + (k, v) -> { + this.signedPower = this.signedPower.subtract(this.validatorSet.getPower(node)); + return null; + }); + } + } + + /** + * Adds key and signature to our list of signing keys and signatures. Note that it is assumed that + * signature validation is performed elsewhere. + * + * @param node The node + * @param timestamp The timestamp of the signature + * @param signature The signature to verify + * @return whether the key was added or not + */ + public boolean addSignature(BFTNode node, long timestamp, ECDSASignature signature) { + if (validatorSet.containsNode(node) && !this.signedNodes.containsKey(node)) { + this.signedNodes.computeIfAbsent( + node, + k -> { + UInt256 weight = this.validatorSet.getPower(node); + this.signedPower = this.signedPower.add(weight); + return TimestampedECDSASignature.from(timestamp, signature); + }); + return true; + } + return false; + } + + /** + * Return {@code true} if we have not yet accumulated any valid signatures. + * + * @return {@code true} if we have not accumulated any signatures, {@code false} otherwise. + */ + public boolean isEmpty() { + return this.signedNodes.isEmpty(); + } + + /** + * Returns {@code true} if we have enough valid signatures to form a quorum. + * + * @return {@code true} if we have enough valid signatures to form a quorum, + */ + public boolean complete() { + return signedPower.compareTo(threshold) >= 0; + } + + /** + * Returns an {@link ECDSASignatures} object for our current set of valid signatures. + * + * @return an {@link ECDSASignatures} object for our current set of valid signatures + */ + public TimestampedECDSASignatures signatures() { + return new TimestampedECDSASignatures(ImmutableMap.copyOf(this.signedNodes)); + } + + @VisibleForTesting + static UInt256 threshold(UInt256 n) { + return n.subtract(acceptableFaults(n)); + } + + @VisibleForTesting + static UInt256 acceptableFaults(UInt256 n) { + // Compute acceptable faults based on Byzantine limit n = 3f + 1 + // i.e. f = (n - 1) / 3 + return n.isZero() ? n : n.decrement().divide(UInt256.THREE); + } + + @Override + public int hashCode() { + return Objects.hash(validatorSet, signedNodes); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ValidationState) { + ValidationState that = (ValidationState) obj; + return Objects.equals(this.validatorSet, that.validatorSet) + && Objects.equals(this.signedNodes, that.signedNodes); + } + return false; + } + + @Override + public String toString() { + return String.format( + "%s[validatorSet=%s, signedNodes=%s]", + getClass().getSimpleName(), validatorSet, signedNodes); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertex.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertex.java index a922bae494..abf6c3c7d2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertex.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertex.java @@ -64,118 +64,112 @@ package com.radixdlt.consensus.bft; +import com.google.common.hash.HashCode; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.UnverifiedVertex; -import com.google.common.hash.HashCode; import com.radixdlt.ledger.StateComputerLedger.PreparedTxn; - import java.util.List; import java.util.Map; import java.util.Objects; -/** - * A vertex which has been verified with hash id - */ +/** A vertex which has been verified with hash id */ public final class VerifiedVertex { - private final UnverifiedVertex vertex; - private final HashCode id; - - public VerifiedVertex(UnverifiedVertex vertex, HashCode id) { - this.vertex = Objects.requireNonNull(vertex); - this.id = Objects.requireNonNull(id); - } - - public BFTNode getProposer() { - return vertex.getProposer(); - } - - public boolean isTimeout() { - return vertex.isTimeout(); - } - - public UnverifiedVertex toSerializable() { - return vertex; - } - - public List getTxns() { - return vertex.getTxns(); - } - - public boolean touchesGenesis() { - return this.getView().isGenesis() - || this.getParentHeader().getView().isGenesis() - || this.getGrandParentHeader().getView().isGenesis(); - } - - public boolean hasDirectParent() { - return this.vertex.getView().equals(this.getParentHeader().getView().next()); - } - - public boolean parentHasDirectParent() { - return this.getParentHeader().getView().equals(this.getGrandParentHeader().getView().next()); - } - - public BFTHeader getParentHeader() { - return vertex.getQC().getProposed(); - } - - public BFTHeader getGrandParentHeader() { - return vertex.getQC().getParent(); - } - - public View getView() { - return vertex.getView(); - } - - public QuorumCertificate getQC() { - return vertex.getQC(); - } - - public HashCode getId() { - return id; - } - - public HashCode getParentId() { - return vertex.getQC().getProposed().getVertexId(); - } - - - public interface PreparedVertexBuilder { - PreparedVertex andTxns( - List preparedTxns, - Map txnExceptions - ); - } - - public PreparedVertexBuilder withHeader(LedgerHeader ledgerHeader, long timeOfExecution) { - return (success, exceptions) -> new PreparedVertex(this, ledgerHeader, success, exceptions, timeOfExecution); - } - - @Override - public int hashCode() { - return Objects.hash(this.vertex, this.id); - } - - @Override - public boolean equals(Object o) { - if (o instanceof VerifiedVertex) { - final var that = (VerifiedVertex) o; - return Objects.equals(this.id, that.id) && Objects.equals(this.vertex, that.vertex); - } - return false; - } - - @Override - public String toString() { - return String.format("%s{epoch=%s view=%s parentView=%s id=%s}", - this.getClass().getSimpleName(), - this.vertex.getQC().getProposed().getLedgerHeader().getEpoch(), - this.vertex.getView(), - this.vertex.getQC().getProposed().getView(), - this.id - ); - } + private final UnverifiedVertex vertex; + private final HashCode id; + + public VerifiedVertex(UnverifiedVertex vertex, HashCode id) { + this.vertex = Objects.requireNonNull(vertex); + this.id = Objects.requireNonNull(id); + } + + public BFTNode getProposer() { + return vertex.getProposer(); + } + + public boolean isTimeout() { + return vertex.isTimeout(); + } + + public UnverifiedVertex toSerializable() { + return vertex; + } + + public List getTxns() { + return vertex.getTxns(); + } + + public boolean touchesGenesis() { + return this.getView().isGenesis() + || this.getParentHeader().getView().isGenesis() + || this.getGrandParentHeader().getView().isGenesis(); + } + + public boolean hasDirectParent() { + return this.vertex.getView().equals(this.getParentHeader().getView().next()); + } + + public boolean parentHasDirectParent() { + return this.getParentHeader().getView().equals(this.getGrandParentHeader().getView().next()); + } + + public BFTHeader getParentHeader() { + return vertex.getQC().getProposed(); + } + + public BFTHeader getGrandParentHeader() { + return vertex.getQC().getParent(); + } + + public View getView() { + return vertex.getView(); + } + + public QuorumCertificate getQC() { + return vertex.getQC(); + } + + public HashCode getId() { + return id; + } + + public HashCode getParentId() { + return vertex.getQC().getProposed().getVertexId(); + } + + public interface PreparedVertexBuilder { + PreparedVertex andTxns(List preparedTxns, Map txnExceptions); + } + + public PreparedVertexBuilder withHeader(LedgerHeader ledgerHeader, long timeOfExecution) { + return (success, exceptions) -> + new PreparedVertex(this, ledgerHeader, success, exceptions, timeOfExecution); + } + + @Override + public int hashCode() { + return Objects.hash(this.vertex, this.id); + } + + @Override + public boolean equals(Object o) { + if (o instanceof VerifiedVertex) { + final var that = (VerifiedVertex) o; + return Objects.equals(this.id, that.id) && Objects.equals(this.vertex, that.vertex); + } + return false; + } + + @Override + public String toString() { + return String.format( + "%s{epoch=%s view=%s parentView=%s id=%s}", + this.getClass().getSimpleName(), + this.vertex.getQC().getProposed().getLedgerHeader().getEpoch(), + this.vertex.getView(), + this.vertex.getQC().getProposed().getView(), + this.id); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexChain.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexChain.java index 449e720536..272df5f19f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexChain.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexChain.java @@ -70,52 +70,50 @@ import java.util.Objects; import javax.annotation.concurrent.Immutable; -/** - * A chain of vertices verified to be consistent - */ +/** A chain of vertices verified to be consistent */ @Immutable public final class VerifiedVertexChain { - private final ImmutableList vertices; + private final ImmutableList vertices; - private VerifiedVertexChain(ImmutableList vertices) { - this.vertices = vertices; - } + private VerifiedVertexChain(ImmutableList vertices) { + this.vertices = vertices; + } - public static VerifiedVertexChain create(List vertices) { - if (vertices.size() >= 2) { - for (int index = 1; index < vertices.size(); index++) { - HashCode parentId = vertices.get(index - 1).getId(); - HashCode parentIdCheck = vertices.get(index).getParentId(); - if (!parentId.equals(parentIdCheck)) { - throw new IllegalArgumentException(String.format("Invalid chain: %s", vertices)); - } - } - } + public static VerifiedVertexChain create(List vertices) { + if (vertices.size() >= 2) { + for (int index = 1; index < vertices.size(); index++) { + HashCode parentId = vertices.get(index - 1).getId(); + HashCode parentIdCheck = vertices.get(index).getParentId(); + if (!parentId.equals(parentIdCheck)) { + throw new IllegalArgumentException(String.format("Invalid chain: %s", vertices)); + } + } + } - return new VerifiedVertexChain(ImmutableList.copyOf(vertices)); - } + return new VerifiedVertexChain(ImmutableList.copyOf(vertices)); + } - public ImmutableList getVertices() { - return vertices; - } + public ImmutableList getVertices() { + return vertices; + } - @Override - public String toString() { - return String.format("%s{vertices=%s}", this.getClass().getSimpleName(), this.vertices); - } + @Override + public String toString() { + return String.format("%s{vertices=%s}", this.getClass().getSimpleName(), this.vertices); + } - @Override - public int hashCode() { - return Objects.hashCode(vertices); - } + @Override + public int hashCode() { + return Objects.hashCode(vertices); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof VerifiedVertexChain)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof VerifiedVertexChain)) { + return false; + } - VerifiedVertexChain other = (VerifiedVertexChain) o; - return Objects.equals(this.vertices, other.vertices); - } + VerifiedVertexChain other = (VerifiedVertexChain) o; + return Objects.equals(this.vertices, other.vertices); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexStoreState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexStoreState.java index 26ad12aab6..6b07b5d8d3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexStoreState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexStoreState.java @@ -69,8 +69,8 @@ import com.google.common.hash.HashCode; import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.crypto.Hasher; import com.radixdlt.store.berkeley.SerializedVertexStoreState; import com.radixdlt.utils.Pair; @@ -79,166 +79,169 @@ import java.util.Optional; import javax.annotation.concurrent.Immutable; -/** - * State of the vertex store which can be serialized. - */ +/** State of the vertex store which can be serialized. */ @Immutable public final class VerifiedVertexStoreState { - private final VerifiedVertex root; - private final LedgerProof rootHeader; - private final HighQC highQC; - // TODO: collapse the following two - private final ImmutableList vertices; - private final ImmutableMap idToVertex; - private Optional highestTC; - - private VerifiedVertexStoreState( - HighQC highQC, - LedgerProof rootHeader, - VerifiedVertex root, - ImmutableMap idToVertex, - ImmutableList vertices, - Optional highestTC - ) { - this.highQC = highQC; - this.rootHeader = rootHeader; - this.root = root; - this.idToVertex = idToVertex; - this.vertices = vertices; - this.highestTC = highestTC; - } - - public static VerifiedVertexStoreState create( - HighQC highQC, - VerifiedVertex root, - Optional highestTC, - Hasher hasher - ) { - return create(highQC, root, ImmutableList.of(), highestTC, hasher); - } - - public static VerifiedVertexStoreState create( - HighQC highQC, - VerifiedVertex root, - ImmutableList vertices, - Optional highestTC, - Hasher hasher - ) { - final Pair headers = highQC.highestCommittedQC() - .getCommittedAndLedgerStateProof(hasher) - .orElseThrow(() -> new IllegalStateException(String.format("highQC=%s does not have commit", highQC))); - LedgerProof rootHeader = headers.getSecond(); - BFTHeader bftHeader = headers.getFirst(); - if (!bftHeader.getVertexId().equals(root.getId())) { - throw new IllegalStateException(String.format("committedHeader=%s does not match rootVertex=%s", bftHeader, root)); - } - - HashMap seen = new HashMap<>(); - seen.put(root.getId(), root); - for (VerifiedVertex v : vertices) { - if (!seen.containsKey(v.getParentId())) { - throw new IllegalStateException(String.format("Missing qc=%s {root=%s vertices=%s}", v.getQC(), root, vertices)); - } - seen.put(v.getId(), v); - } - ImmutableMap idToVertex = ImmutableMap.copyOf(seen); - - if (seen.keySet().stream().noneMatch(highQC.highestCommittedQC().getProposed().getVertexId()::equals)) { - throw new IllegalStateException( - String.format("highQC=%s highCommitted proposed missing {root=%s vertices=%s}", highQC, root, vertices) - ); - } - - if (seen.keySet().stream().noneMatch(highQC.highestCommittedQC().getParent().getVertexId()::equals)) { - throw new IllegalStateException( - String.format("highQC=%s highCommitted parent does not have a corresponding vertex", highQC) - ); - } - - if (seen.keySet().stream().noneMatch(highQC.highestQC().getParent().getVertexId()::equals)) { - throw new IllegalStateException( - String.format("highQC=%s highQC parent does not have a corresponding vertex", highQC) - ); - } - - if (seen.keySet().stream().noneMatch(highQC.highestQC().getProposed().getVertexId()::equals)) { - throw new IllegalStateException( - String.format("highQC=%s highQC proposed does not have a corresponding vertex", highQC) - ); - } - - return new VerifiedVertexStoreState(highQC, rootHeader, root, idToVertex, vertices, highestTC); - } - - public VerifiedVertexStoreState prune(Hasher hasher) { - if (highQC.highestQC().getCommittedAndLedgerStateProof(hasher).isPresent()) { - Pair newHeaders = highQC.highestQC().getCommittedAndLedgerStateProof(hasher).get(); - BFTHeader header = newHeaders.getFirst(); - if (header.getView().gt(root.getView())) { - VerifiedVertex newRoot = idToVertex.get(header.getVertexId()); - ImmutableList newVertices = ImmutableList.of( - idToVertex.get(highQC.highestQC().getParent().getVertexId()), - idToVertex.get(highQC.highestQC().getProposed().getVertexId()) - ); - ImmutableMap idToVertex = ImmutableMap.of( - highQC.highestQC().getParent().getVertexId(), newVertices.get(0), - highQC.highestQC().getProposed().getVertexId(), newVertices.get(1) - ); - HighQC newHighQC = HighQC.from(highQC.highestQC()); - LedgerProof proof = newHeaders.getSecond(); - return new VerifiedVertexStoreState(newHighQC, proof, newRoot, idToVertex, newVertices, highestTC); - } - } - - return this; - } - - public SerializedVertexStoreState toSerialized() { - return new SerializedVertexStoreState( - this.highQC, - this.root.toSerializable(), - this.vertices.stream().map(VerifiedVertex::toSerializable).collect(ImmutableList.toImmutableList()), - this.highestTC.orElse(null) - ); - } - - public HighQC getHighQC() { - return highQC; - } - - public VerifiedVertex getRoot() { - return root; - } - - public ImmutableList getVertices() { - return vertices; - } - - public LedgerProof getRootHeader() { - return rootHeader; - } - - @Override - public int hashCode() { - return Objects.hash(root, rootHeader, highQC, idToVertex, vertices, highestTC); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - if (!(o instanceof VerifiedVertexStoreState)) { - return false; - } - - VerifiedVertexStoreState other = (VerifiedVertexStoreState) o; - return Objects.equals(this.root, other.root) - && Objects.equals(this.rootHeader, other.rootHeader) - && Objects.equals(this.highQC, other.highQC) - && Objects.equals(this.vertices, other.vertices) - && Objects.equals(this.idToVertex, other.idToVertex) - && Objects.equals(this.highestTC, other.highestTC); - } + private final VerifiedVertex root; + private final LedgerProof rootHeader; + private final HighQC highQC; + // TODO: collapse the following two + private final ImmutableList vertices; + private final ImmutableMap idToVertex; + private Optional highestTC; + + private VerifiedVertexStoreState( + HighQC highQC, + LedgerProof rootHeader, + VerifiedVertex root, + ImmutableMap idToVertex, + ImmutableList vertices, + Optional highestTC) { + this.highQC = highQC; + this.rootHeader = rootHeader; + this.root = root; + this.idToVertex = idToVertex; + this.vertices = vertices; + this.highestTC = highestTC; + } + + public static VerifiedVertexStoreState create( + HighQC highQC, VerifiedVertex root, Optional highestTC, Hasher hasher) { + return create(highQC, root, ImmutableList.of(), highestTC, hasher); + } + + public static VerifiedVertexStoreState create( + HighQC highQC, + VerifiedVertex root, + ImmutableList vertices, + Optional highestTC, + Hasher hasher) { + final Pair headers = + highQC + .highestCommittedQC() + .getCommittedAndLedgerStateProof(hasher) + .orElseThrow( + () -> + new IllegalStateException( + String.format("highQC=%s does not have commit", highQC))); + LedgerProof rootHeader = headers.getSecond(); + BFTHeader bftHeader = headers.getFirst(); + if (!bftHeader.getVertexId().equals(root.getId())) { + throw new IllegalStateException( + String.format("committedHeader=%s does not match rootVertex=%s", bftHeader, root)); + } + + HashMap seen = new HashMap<>(); + seen.put(root.getId(), root); + for (VerifiedVertex v : vertices) { + if (!seen.containsKey(v.getParentId())) { + throw new IllegalStateException( + String.format("Missing qc=%s {root=%s vertices=%s}", v.getQC(), root, vertices)); + } + seen.put(v.getId(), v); + } + ImmutableMap idToVertex = ImmutableMap.copyOf(seen); + + if (seen.keySet().stream() + .noneMatch(highQC.highestCommittedQC().getProposed().getVertexId()::equals)) { + throw new IllegalStateException( + String.format( + "highQC=%s highCommitted proposed missing {root=%s vertices=%s}", + highQC, root, vertices)); + } + + if (seen.keySet().stream() + .noneMatch(highQC.highestCommittedQC().getParent().getVertexId()::equals)) { + throw new IllegalStateException( + String.format( + "highQC=%s highCommitted parent does not have a corresponding vertex", highQC)); + } + + if (seen.keySet().stream().noneMatch(highQC.highestQC().getParent().getVertexId()::equals)) { + throw new IllegalStateException( + String.format("highQC=%s highQC parent does not have a corresponding vertex", highQC)); + } + + if (seen.keySet().stream().noneMatch(highQC.highestQC().getProposed().getVertexId()::equals)) { + throw new IllegalStateException( + String.format("highQC=%s highQC proposed does not have a corresponding vertex", highQC)); + } + + return new VerifiedVertexStoreState(highQC, rootHeader, root, idToVertex, vertices, highestTC); + } + + public VerifiedVertexStoreState prune(Hasher hasher) { + if (highQC.highestQC().getCommittedAndLedgerStateProof(hasher).isPresent()) { + Pair newHeaders = + highQC.highestQC().getCommittedAndLedgerStateProof(hasher).get(); + BFTHeader header = newHeaders.getFirst(); + if (header.getView().gt(root.getView())) { + VerifiedVertex newRoot = idToVertex.get(header.getVertexId()); + ImmutableList newVertices = + ImmutableList.of( + idToVertex.get(highQC.highestQC().getParent().getVertexId()), + idToVertex.get(highQC.highestQC().getProposed().getVertexId())); + ImmutableMap idToVertex = + ImmutableMap.of( + highQC.highestQC().getParent().getVertexId(), newVertices.get(0), + highQC.highestQC().getProposed().getVertexId(), newVertices.get(1)); + HighQC newHighQC = HighQC.from(highQC.highestQC()); + LedgerProof proof = newHeaders.getSecond(); + return new VerifiedVertexStoreState( + newHighQC, proof, newRoot, idToVertex, newVertices, highestTC); + } + } + + return this; + } + + public SerializedVertexStoreState toSerialized() { + return new SerializedVertexStoreState( + this.highQC, + this.root.toSerializable(), + this.vertices.stream() + .map(VerifiedVertex::toSerializable) + .collect(ImmutableList.toImmutableList()), + this.highestTC.orElse(null)); + } + + public HighQC getHighQC() { + return highQC; + } + + public VerifiedVertex getRoot() { + return root; + } + + public ImmutableList getVertices() { + return vertices; + } + + public LedgerProof getRootHeader() { + return rootHeader; + } + + @Override + public int hashCode() { + return Objects.hash(root, rootHeader, highQC, idToVertex, vertices, highestTC); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof VerifiedVertexStoreState)) { + return false; + } + + VerifiedVertexStoreState other = (VerifiedVertexStoreState) o; + return Objects.equals(this.root, other.root) + && Objects.equals(this.rootHeader, other.rootHeader) + && Objects.equals(this.highQC, other.highQC) + && Objects.equals(this.vertices, other.vertices) + && Objects.equals(this.idToVertex, other.idToVertex) + && Objects.equals(this.highestTC, other.highestTC); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexInsertionException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexInsertionException.java index 0dfe2dd4c9..e0a34e5ae3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexInsertionException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexInsertionException.java @@ -64,15 +64,13 @@ package com.radixdlt.consensus.bft; -/** - * An exception indicating a failure in inserting a vertex into a VertexStore - */ +/** An exception indicating a failure in inserting a vertex into a VertexStore */ public class VertexInsertionException extends Exception { - VertexInsertionException(String message) { - super(message); - } + VertexInsertionException(String message) { + super(message); + } - VertexInsertionException(String message, Exception cause) { - super(message, cause); - } + VertexInsertionException(String message, Exception cause) { + super(message, cause); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java index 03a63293e7..38e92092b1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java @@ -64,19 +64,17 @@ package com.radixdlt.consensus.bft; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; -import com.radixdlt.consensus.QuorumCertificate; +import com.google.common.hash.HashCode; +import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.Ledger; -import com.radixdlt.consensus.BFTHeader; -import com.google.common.hash.HashCode; - -import com.google.common.collect.ImmutableList; +import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.EventDispatcher; - import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -86,380 +84,378 @@ import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; -/** - * Manages the BFT Vertex chain. - * TODO: Move this logic into ledger package. - */ +/** Manages the BFT Vertex chain. TODO: Move this logic into ledger package. */ @NotThreadSafe public final class VertexStore { - private final EventDispatcher highQCUpdateDispatcher; - private final EventDispatcher bftUpdateDispatcher; - private final EventDispatcher bftRebuildDispatcher; - private final EventDispatcher bftCommittedDispatcher; - - private final Hasher hasher; - private final Ledger ledger; - - private final Map vertices = new HashMap<>(); - private final Map> vertexChildren = new HashMap<>(); - - // These should never be null - private VerifiedVertex rootVertex; - private QuorumCertificate highestQC; - private QuorumCertificate highestCommittedQC; - private Optional highestTC; - - private VertexStore( - Ledger ledger, - Hasher hasher, - VerifiedVertex rootVertex, - QuorumCertificate commitQC, - QuorumCertificate highestQC, - EventDispatcher bftUpdateDispatcher, - EventDispatcher bftRebuildDispatcher, - EventDispatcher highQCUpdateDispatcher, - EventDispatcher bftCommittedDispatcher, - Optional highestTC - ) { - this.ledger = Objects.requireNonNull(ledger); - this.hasher = Objects.requireNonNull(hasher); - this.bftUpdateDispatcher = Objects.requireNonNull(bftUpdateDispatcher); - this.bftRebuildDispatcher = Objects.requireNonNull(bftRebuildDispatcher); - this.highQCUpdateDispatcher = Objects.requireNonNull(highQCUpdateDispatcher); - this.bftCommittedDispatcher = Objects.requireNonNull(bftCommittedDispatcher); - this.rootVertex = Objects.requireNonNull(rootVertex); - this.highestQC = Objects.requireNonNull(highestQC); - this.highestCommittedQC = Objects.requireNonNull(commitQC); - this.vertexChildren.put(rootVertex.getId(), new HashSet<>()); - this.highestTC = Objects.requireNonNull(highestTC); - } - - public static VertexStore create( - VerifiedVertexStoreState vertexStoreState, - Ledger ledger, - Hasher hasher, - EventDispatcher bftUpdateDispatcher, - EventDispatcher bftRebuildDispatcher, - EventDispatcher bftHighQCUpdateDispatcher, - EventDispatcher bftCommittedDispatcher - ) { - VertexStore vertexStore = new VertexStore( - ledger, - hasher, - vertexStoreState.getRoot(), - vertexStoreState.getHighQC().highestCommittedQC(), - vertexStoreState.getHighQC().highestQC(), - bftUpdateDispatcher, - bftRebuildDispatcher, - bftHighQCUpdateDispatcher, - bftCommittedDispatcher, - vertexStoreState.getHighQC().highestTC() - ); - - for (VerifiedVertex vertex : vertexStoreState.getVertices()) { - LinkedList previous = vertexStore.getPathFromRoot(vertex.getParentId()); - Optional preparedVertexMaybe = ledger.prepare(previous, vertex); - if (preparedVertexMaybe.isEmpty()) { - // Try pruning to see if that helps catching up to the ledger - // This can occur if a node crashes between persisting a new QC and committing - // TODO: Cleanup and remove - VerifiedVertexStoreState pruned = vertexStoreState.prune(hasher); - if (!pruned.equals(vertexStoreState)) { - return create( - pruned, - ledger, - hasher, - bftUpdateDispatcher, - bftRebuildDispatcher, - bftHighQCUpdateDispatcher, - bftCommittedDispatcher - ); - } - - // FIXME: If this occurs then it means that our highQC may not have an associated vertex - // FIXME: so should save preparedVertex - break; - } else { - PreparedVertex preparedVertex = preparedVertexMaybe.get(); - vertexStore.vertices.put(preparedVertex.getId(), preparedVertex); - vertexStore.vertexChildren.put(preparedVertex.getId(), new HashSet<>()); - Set siblings = vertexStore.vertexChildren.get(preparedVertex.getParentId()); - siblings.add(preparedVertex.getId()); - } - } - - return vertexStore; - } - - public VerifiedVertex getRoot() { - return rootVertex; - } - - public boolean tryRebuild(VerifiedVertexStoreState vertexStoreState) { - - // FIXME: Currently this assumes vertexStoreState is a chain with no forks which is our only use case at the moment. - LinkedList prepared = new LinkedList<>(); - for (VerifiedVertex vertex : vertexStoreState.getVertices()) { - Optional preparedVertexMaybe = ledger.prepare(prepared, vertex); - if (preparedVertexMaybe.isEmpty()) { - return false; - } - - prepared.add(preparedVertexMaybe.get()); - } - - this.rootVertex = vertexStoreState.getRoot(); - this.highestCommittedQC = vertexStoreState.getHighQC().highestCommittedQC(); - this.highestQC = vertexStoreState.getHighQC().highestQC(); - this.vertices.clear(); - this.vertexChildren.clear(); - this.vertexChildren.put(rootVertex.getId(), new HashSet<>()); - - for (PreparedVertex preparedVertex : prepared) { - this.vertices.put(preparedVertex.getId(), preparedVertex); - this.vertexChildren.put(preparedVertex.getId(), new HashSet<>()); - Set siblings = vertexChildren.get(preparedVertex.getParentId()); - siblings.add(preparedVertex.getId()); - } - - bftRebuildDispatcher.dispatch(BFTRebuildUpdate.create(vertexStoreState)); - return true; - } - - public boolean containsVertex(HashCode vertexId) { - return vertices.containsKey(vertexId) || rootVertex.getId().equals(vertexId); - } - - public void insertVertexChain(VerifiedVertexChain verifiedVertexChain) { - for (VerifiedVertex v: verifiedVertexChain.getVertices()) { - if (!addQC(v.getQC())) { - return; - } - - insertVertex(v); - } - } - - public boolean addQC(QuorumCertificate qc) { - if (!this.containsVertex(qc.getProposed().getVertexId())) { - return false; - } - - if (!vertexChildren.get(qc.getProposed().getVertexId()).isEmpty()) { - // TODO: Check to see if qc's match in case there's a fault - return true; - } - - boolean isHighQC = qc.getView().gt(highestQC.getView()); - boolean isHighCommit = qc.getCommittedAndLedgerStateProof(hasher).isPresent(); - if (!isHighQC && !isHighCommit) { - return true; - } - - if (isHighQC) { - highestQC = qc; - } - - if (isHighCommit) { - qc.getCommitted().ifPresent(header -> this.commit(header, qc)); - } else { - // TODO: we lose all other tail QCs on this save, Not sure if this is okay...investigate... - VerifiedVertexStoreState vertexStoreState = getState(); - this.highQCUpdateDispatcher.dispatch(BFTHighQCUpdate.create(vertexStoreState)); - } - - return true; - } - - private void getChildrenVerticesList(VerifiedVertex parent, ImmutableList.Builder builder) { - Set childrenIds = this.vertexChildren.get(parent.getId()); - if (childrenIds == null) { - return; - } - - for (HashCode childId : childrenIds) { - VerifiedVertex v = vertices.get(childId).getVertex(); - builder.add(v); - getChildrenVerticesList(v, builder); - } - } - - private VerifiedVertexStoreState getState() { - // TODO: store list dynamically rather than recomputing - ImmutableList.Builder verticesBuilder = ImmutableList.builder(); - getChildrenVerticesList(this.rootVertex, verticesBuilder); - return VerifiedVertexStoreState.create( - this.highQC(), - this.rootVertex, - verticesBuilder.build(), - this.highestTC, - hasher - ); - } - - /** - * Inserts a timeout certificate into the store. - * @param timeoutCertificate the timeout certificate - */ - public void insertTimeoutCertificate(TimeoutCertificate timeoutCertificate) { - if (this.highestTC.isEmpty() - || this.highestTC.get().getView().lt(timeoutCertificate.getView())) { - this.highestTC = Optional.of(timeoutCertificate); - } - } - - /** - * Returns the highest inserted timeout certificate. - * @return the highest inserted timeout certificate - */ - public Optional getHighestTimeoutCertificate() { - return this.highestTC; - } - - /** - * Returns the vertex with specified id or empty if not exists. - * @param id the id of a vertex - * @return the specified vertex or empty - */ - // TODO: reimplement in async way - public Optional getPreparedVertex(HashCode id) { - return Optional.ofNullable(vertices.get(id)); - } - - /** - * Inserts a vertex and then attempts to create the next header. - * - * @param vertex vertex to insert - */ - public void insertVertex(VerifiedVertex vertex) { - PreparedVertex v = vertices.get(vertex.getId()); - if (v != null) { - return; - } - - if (!this.containsVertex(vertex.getParentId())) { - throw new MissingParentException(vertex.getParentId()); - } - - insertVertexInternal(vertex); - } - - private void insertVertexInternal(VerifiedVertex vertex) { - LinkedList previous = getPathFromRoot(vertex.getParentId()); - Optional preparedVertexMaybe = ledger.prepare(previous, vertex); - preparedVertexMaybe.ifPresent(preparedVertex -> { - vertices.put(preparedVertex.getId(), preparedVertex); - vertexChildren.put(preparedVertex.getId(), new HashSet<>()); - Set siblings = vertexChildren.get(preparedVertex.getParentId()); - siblings.add(preparedVertex.getId()); - - VerifiedVertexStoreState vertexStoreState = getState(); - BFTInsertUpdate update = BFTInsertUpdate.insertedVertex(preparedVertex, siblings.size(), vertexStoreState); - bftUpdateDispatcher.dispatch(update); - }); - } - - private void removeVertexAndPruneInternal(HashCode vertexId, HashCode skip, Builder prunedVerticesBuilder) { - vertices.remove(vertexId); - - if (this.rootVertex.getId().equals(vertexId)) { - return; - } - - if (skip != null) { - prunedVerticesBuilder.add(vertexId); - } - - Set children = vertexChildren.remove(vertexId); - for (HashCode child : children) { - if (!child.equals(skip)) { - removeVertexAndPruneInternal(child, null, prunedVerticesBuilder); - } - } - } - - /** - * Commit a vertex. Executes the atom and prunes the tree. - * @param header the header to be committed - * @param commitQC the proof of commit - */ - private void commit(BFTHeader header, QuorumCertificate commitQC) { - if (header.getView().compareTo(this.rootVertex.getView()) <= 0) { - return; - } - - final HashCode vertexId = header.getVertexId(); - final VerifiedVertex tipVertex = vertices.get(vertexId).getVertex(); - if (tipVertex == null) { - throw new IllegalStateException("Committing vertex not in store: " + header); - } - - this.rootVertex = tipVertex; - this.highestCommittedQC = commitQC; - Builder prunedSetBuilder = ImmutableSet.builder(); - final ImmutableList path = ImmutableList.copyOf(getPathFromRoot(tipVertex.getId())); - HashCode prev = null; - for (int i = path.size() - 1; i >= 0; i--) { - this.removeVertexAndPruneInternal(path.get(i).getId(), prev, prunedSetBuilder); - prev = path.get(i).getId(); - } - - VerifiedVertexStoreState vertexStoreState = getState(); - ImmutableSet pruned = prunedSetBuilder.build(); - this.bftCommittedDispatcher.dispatch(BFTCommittedUpdate.create(pruned, path, vertexStoreState)); - } - - public LinkedList getPathFromRoot(HashCode vertexId) { - final LinkedList path = new LinkedList<>(); - - PreparedVertex vertex = vertices.get(vertexId); - while (vertex != null) { - path.addFirst(vertex); - vertex = vertices.get(vertex.getParentId()); - } - - return path; - } - - /** - * Retrieves the highest QC and highest committed QC in the store. - * - * @return the highest QCs - */ - public HighQC highQC() { - return HighQC.from(this.highestQC, this.highestCommittedQC, this.highestTC); - } - - /** - * Retrieves list of vertices starting with the given vertexId and - * then proceeding to its ancestors. - * - * if the store does not contain some vertex then will return an empty - * list. - * - * @param vertexId the id of the vertex - * @param count the number of vertices to retrieve - * @return the list of vertices if all found, otherwise an empty list - */ - public Optional> getVertices(HashCode vertexId, int count) { - HashCode nextId = vertexId; - ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(count); - for (int i = 0; i < count; i++) { - final VerifiedVertex verifiedVertex; - if (nextId.equals(rootVertex.getId())) { - verifiedVertex = rootVertex; - } else if (this.vertices.containsKey(nextId)) { - final PreparedVertex preparedVertex = this.vertices.get(nextId); - verifiedVertex = preparedVertex.getVertex(); - } else { - return Optional.empty(); - } - - builder.add(verifiedVertex); - nextId = verifiedVertex.getParentId(); - } - - return Optional.of(builder.build()); - } + private final EventDispatcher highQCUpdateDispatcher; + private final EventDispatcher bftUpdateDispatcher; + private final EventDispatcher bftRebuildDispatcher; + private final EventDispatcher bftCommittedDispatcher; + + private final Hasher hasher; + private final Ledger ledger; + + private final Map vertices = new HashMap<>(); + private final Map> vertexChildren = new HashMap<>(); + + // These should never be null + private VerifiedVertex rootVertex; + private QuorumCertificate highestQC; + private QuorumCertificate highestCommittedQC; + private Optional highestTC; + + private VertexStore( + Ledger ledger, + Hasher hasher, + VerifiedVertex rootVertex, + QuorumCertificate commitQC, + QuorumCertificate highestQC, + EventDispatcher bftUpdateDispatcher, + EventDispatcher bftRebuildDispatcher, + EventDispatcher highQCUpdateDispatcher, + EventDispatcher bftCommittedDispatcher, + Optional highestTC) { + this.ledger = Objects.requireNonNull(ledger); + this.hasher = Objects.requireNonNull(hasher); + this.bftUpdateDispatcher = Objects.requireNonNull(bftUpdateDispatcher); + this.bftRebuildDispatcher = Objects.requireNonNull(bftRebuildDispatcher); + this.highQCUpdateDispatcher = Objects.requireNonNull(highQCUpdateDispatcher); + this.bftCommittedDispatcher = Objects.requireNonNull(bftCommittedDispatcher); + this.rootVertex = Objects.requireNonNull(rootVertex); + this.highestQC = Objects.requireNonNull(highestQC); + this.highestCommittedQC = Objects.requireNonNull(commitQC); + this.vertexChildren.put(rootVertex.getId(), new HashSet<>()); + this.highestTC = Objects.requireNonNull(highestTC); + } + + public static VertexStore create( + VerifiedVertexStoreState vertexStoreState, + Ledger ledger, + Hasher hasher, + EventDispatcher bftUpdateDispatcher, + EventDispatcher bftRebuildDispatcher, + EventDispatcher bftHighQCUpdateDispatcher, + EventDispatcher bftCommittedDispatcher) { + VertexStore vertexStore = + new VertexStore( + ledger, + hasher, + vertexStoreState.getRoot(), + vertexStoreState.getHighQC().highestCommittedQC(), + vertexStoreState.getHighQC().highestQC(), + bftUpdateDispatcher, + bftRebuildDispatcher, + bftHighQCUpdateDispatcher, + bftCommittedDispatcher, + vertexStoreState.getHighQC().highestTC()); + + for (VerifiedVertex vertex : vertexStoreState.getVertices()) { + LinkedList previous = vertexStore.getPathFromRoot(vertex.getParentId()); + Optional preparedVertexMaybe = ledger.prepare(previous, vertex); + if (preparedVertexMaybe.isEmpty()) { + // Try pruning to see if that helps catching up to the ledger + // This can occur if a node crashes between persisting a new QC and committing + // TODO: Cleanup and remove + VerifiedVertexStoreState pruned = vertexStoreState.prune(hasher); + if (!pruned.equals(vertexStoreState)) { + return create( + pruned, + ledger, + hasher, + bftUpdateDispatcher, + bftRebuildDispatcher, + bftHighQCUpdateDispatcher, + bftCommittedDispatcher); + } + + // FIXME: If this occurs then it means that our highQC may not have an associated vertex + // FIXME: so should save preparedVertex + break; + } else { + PreparedVertex preparedVertex = preparedVertexMaybe.get(); + vertexStore.vertices.put(preparedVertex.getId(), preparedVertex); + vertexStore.vertexChildren.put(preparedVertex.getId(), new HashSet<>()); + Set siblings = vertexStore.vertexChildren.get(preparedVertex.getParentId()); + siblings.add(preparedVertex.getId()); + } + } + + return vertexStore; + } + + public VerifiedVertex getRoot() { + return rootVertex; + } + + public boolean tryRebuild(VerifiedVertexStoreState vertexStoreState) { + + // FIXME: Currently this assumes vertexStoreState is a chain with no forks which is our only use + // case at the moment. + LinkedList prepared = new LinkedList<>(); + for (VerifiedVertex vertex : vertexStoreState.getVertices()) { + Optional preparedVertexMaybe = ledger.prepare(prepared, vertex); + if (preparedVertexMaybe.isEmpty()) { + return false; + } + + prepared.add(preparedVertexMaybe.get()); + } + + this.rootVertex = vertexStoreState.getRoot(); + this.highestCommittedQC = vertexStoreState.getHighQC().highestCommittedQC(); + this.highestQC = vertexStoreState.getHighQC().highestQC(); + this.vertices.clear(); + this.vertexChildren.clear(); + this.vertexChildren.put(rootVertex.getId(), new HashSet<>()); + + for (PreparedVertex preparedVertex : prepared) { + this.vertices.put(preparedVertex.getId(), preparedVertex); + this.vertexChildren.put(preparedVertex.getId(), new HashSet<>()); + Set siblings = vertexChildren.get(preparedVertex.getParentId()); + siblings.add(preparedVertex.getId()); + } + + bftRebuildDispatcher.dispatch(BFTRebuildUpdate.create(vertexStoreState)); + return true; + } + + public boolean containsVertex(HashCode vertexId) { + return vertices.containsKey(vertexId) || rootVertex.getId().equals(vertexId); + } + + public void insertVertexChain(VerifiedVertexChain verifiedVertexChain) { + for (VerifiedVertex v : verifiedVertexChain.getVertices()) { + if (!addQC(v.getQC())) { + return; + } + + insertVertex(v); + } + } + + public boolean addQC(QuorumCertificate qc) { + if (!this.containsVertex(qc.getProposed().getVertexId())) { + return false; + } + + if (!vertexChildren.get(qc.getProposed().getVertexId()).isEmpty()) { + // TODO: Check to see if qc's match in case there's a fault + return true; + } + + boolean isHighQC = qc.getView().gt(highestQC.getView()); + boolean isHighCommit = qc.getCommittedAndLedgerStateProof(hasher).isPresent(); + if (!isHighQC && !isHighCommit) { + return true; + } + + if (isHighQC) { + highestQC = qc; + } + + if (isHighCommit) { + qc.getCommitted().ifPresent(header -> this.commit(header, qc)); + } else { + // TODO: we lose all other tail QCs on this save, Not sure if this is okay...investigate... + VerifiedVertexStoreState vertexStoreState = getState(); + this.highQCUpdateDispatcher.dispatch(BFTHighQCUpdate.create(vertexStoreState)); + } + + return true; + } + + private void getChildrenVerticesList( + VerifiedVertex parent, ImmutableList.Builder builder) { + Set childrenIds = this.vertexChildren.get(parent.getId()); + if (childrenIds == null) { + return; + } + + for (HashCode childId : childrenIds) { + VerifiedVertex v = vertices.get(childId).getVertex(); + builder.add(v); + getChildrenVerticesList(v, builder); + } + } + + private VerifiedVertexStoreState getState() { + // TODO: store list dynamically rather than recomputing + ImmutableList.Builder verticesBuilder = ImmutableList.builder(); + getChildrenVerticesList(this.rootVertex, verticesBuilder); + return VerifiedVertexStoreState.create( + this.highQC(), this.rootVertex, verticesBuilder.build(), this.highestTC, hasher); + } + + /** + * Inserts a timeout certificate into the store. + * + * @param timeoutCertificate the timeout certificate + */ + public void insertTimeoutCertificate(TimeoutCertificate timeoutCertificate) { + if (this.highestTC.isEmpty() + || this.highestTC.get().getView().lt(timeoutCertificate.getView())) { + this.highestTC = Optional.of(timeoutCertificate); + } + } + + /** + * Returns the highest inserted timeout certificate. + * + * @return the highest inserted timeout certificate + */ + public Optional getHighestTimeoutCertificate() { + return this.highestTC; + } + + /** + * Returns the vertex with specified id or empty if not exists. + * + * @param id the id of a vertex + * @return the specified vertex or empty + */ + // TODO: reimplement in async way + public Optional getPreparedVertex(HashCode id) { + return Optional.ofNullable(vertices.get(id)); + } + + /** + * Inserts a vertex and then attempts to create the next header. + * + * @param vertex vertex to insert + */ + public void insertVertex(VerifiedVertex vertex) { + PreparedVertex v = vertices.get(vertex.getId()); + if (v != null) { + return; + } + + if (!this.containsVertex(vertex.getParentId())) { + throw new MissingParentException(vertex.getParentId()); + } + + insertVertexInternal(vertex); + } + + private void insertVertexInternal(VerifiedVertex vertex) { + LinkedList previous = getPathFromRoot(vertex.getParentId()); + Optional preparedVertexMaybe = ledger.prepare(previous, vertex); + preparedVertexMaybe.ifPresent( + preparedVertex -> { + vertices.put(preparedVertex.getId(), preparedVertex); + vertexChildren.put(preparedVertex.getId(), new HashSet<>()); + Set siblings = vertexChildren.get(preparedVertex.getParentId()); + siblings.add(preparedVertex.getId()); + + VerifiedVertexStoreState vertexStoreState = getState(); + BFTInsertUpdate update = + BFTInsertUpdate.insertedVertex(preparedVertex, siblings.size(), vertexStoreState); + bftUpdateDispatcher.dispatch(update); + }); + } + + private void removeVertexAndPruneInternal( + HashCode vertexId, HashCode skip, Builder prunedVerticesBuilder) { + vertices.remove(vertexId); + + if (this.rootVertex.getId().equals(vertexId)) { + return; + } + + if (skip != null) { + prunedVerticesBuilder.add(vertexId); + } + + Set children = vertexChildren.remove(vertexId); + for (HashCode child : children) { + if (!child.equals(skip)) { + removeVertexAndPruneInternal(child, null, prunedVerticesBuilder); + } + } + } + + /** + * Commit a vertex. Executes the atom and prunes the tree. + * + * @param header the header to be committed + * @param commitQC the proof of commit + */ + private void commit(BFTHeader header, QuorumCertificate commitQC) { + if (header.getView().compareTo(this.rootVertex.getView()) <= 0) { + return; + } + + final HashCode vertexId = header.getVertexId(); + final VerifiedVertex tipVertex = vertices.get(vertexId).getVertex(); + if (tipVertex == null) { + throw new IllegalStateException("Committing vertex not in store: " + header); + } + + this.rootVertex = tipVertex; + this.highestCommittedQC = commitQC; + Builder prunedSetBuilder = ImmutableSet.builder(); + final ImmutableList path = + ImmutableList.copyOf(getPathFromRoot(tipVertex.getId())); + HashCode prev = null; + for (int i = path.size() - 1; i >= 0; i--) { + this.removeVertexAndPruneInternal(path.get(i).getId(), prev, prunedSetBuilder); + prev = path.get(i).getId(); + } + + VerifiedVertexStoreState vertexStoreState = getState(); + ImmutableSet pruned = prunedSetBuilder.build(); + this.bftCommittedDispatcher.dispatch(BFTCommittedUpdate.create(pruned, path, vertexStoreState)); + } + + public LinkedList getPathFromRoot(HashCode vertexId) { + final LinkedList path = new LinkedList<>(); + + PreparedVertex vertex = vertices.get(vertexId); + while (vertex != null) { + path.addFirst(vertex); + vertex = vertices.get(vertex.getParentId()); + } + + return path; + } + + /** + * Retrieves the highest QC and highest committed QC in the store. + * + * @return the highest QCs + */ + public HighQC highQC() { + return HighQC.from(this.highestQC, this.highestCommittedQC, this.highestTC); + } + + /** + * Retrieves list of vertices starting with the given vertexId and then proceeding to its + * ancestors. + * + *

if the store does not contain some vertex then will return an empty list. + * + * @param vertexId the id of the vertex + * @param count the number of vertices to retrieve + * @return the list of vertices if all found, otherwise an empty list + */ + public Optional> getVertices(HashCode vertexId, int count) { + HashCode nextId = vertexId; + ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(count); + for (int i = 0; i < count; i++) { + final VerifiedVertex verifiedVertex; + if (nextId.equals(rootVertex.getId())) { + verifiedVertex = rootVertex; + } else if (this.vertices.containsKey(nextId)) { + final PreparedVertex preparedVertex = this.vertices.get(nextId); + verifiedVertex = preparedVertex.getVertex(); + } else { + return Optional.empty(); + } + + builder.add(verifiedVertex); + nextId = verifiedVertex.getParentId(); + } + + return Optional.of(builder.build()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/View.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/View.java index acfb3a7dee..9074028a19 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/View.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/View.java @@ -64,92 +64,89 @@ package com.radixdlt.consensus.bft; -/** - * Represents a BFT view used by the Pacemaker of a BFT instance - */ +/** Represents a BFT view used by the Pacemaker of a BFT instance */ public final class View implements Comparable { - private static final View GENESIS_VIEW = View.of(0L); - private final long view; - - private View(long view) { - if (view < 0) { - throw new IllegalArgumentException("view must be >= 0 but was " + view); - } - - this.view = view; - } - - public boolean gte(View other) { - return this.view >= other.view; - } - - public boolean gt(View other) { - return this.view > other.view; - } - - public boolean lt(View other) { - return this.view < other.view; - } - - public boolean lte(View other) { - return this.view <= other.view; - } - - public View previous() { - if (this.view == 0) { - throw new IllegalStateException("View Underflow"); - } - - return new View(view - 1); - } - - public View next() { - if (this.view == Long.MAX_VALUE) { - throw new IllegalStateException("View Overflow"); - } - - return new View(view + 1); - } - - public long number() { - return this.view; - } - - @Override - public int compareTo(View otherView) { - return Long.compare(this.view, otherView.view); - } - - @Override - public int hashCode() { - return Long.hashCode(view); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof View)) { - return false; - } - - View other = (View) o; - return other.view == this.view; - } - - - @Override - public String toString() { - return Long.toString(this.view); - } - - public boolean isGenesis() { - return GENESIS_VIEW.equals(this); - } - - public static View genesis() { - return GENESIS_VIEW; - } - - public static View of(long view) { - return new View(view); - } + private static final View GENESIS_VIEW = View.of(0L); + private final long view; + + private View(long view) { + if (view < 0) { + throw new IllegalArgumentException("view must be >= 0 but was " + view); + } + + this.view = view; + } + + public boolean gte(View other) { + return this.view >= other.view; + } + + public boolean gt(View other) { + return this.view > other.view; + } + + public boolean lt(View other) { + return this.view < other.view; + } + + public boolean lte(View other) { + return this.view <= other.view; + } + + public View previous() { + if (this.view == 0) { + throw new IllegalStateException("View Underflow"); + } + + return new View(view - 1); + } + + public View next() { + if (this.view == Long.MAX_VALUE) { + throw new IllegalStateException("View Overflow"); + } + + return new View(view + 1); + } + + public long number() { + return this.view; + } + + @Override + public int compareTo(View otherView) { + return Long.compare(this.view, otherView.view); + } + + @Override + public int hashCode() { + return Long.hashCode(view); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof View)) { + return false; + } + + View other = (View) o; + return other.view == this.view; + } + + @Override + public String toString() { + return Long.toString(this.view); + } + + public boolean isGenesis() { + return GENESIS_VIEW.equals(this); + } + + public static View genesis() { + return GENESIS_VIEW; + } + + public static View of(long view) { + return new View(view); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewQuorumReached.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewQuorumReached.java index cddcd57c43..dccc909a7d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewQuorumReached.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewQuorumReached.java @@ -66,44 +66,42 @@ import java.util.Objects; -/** - * A local event message indicating that a view quorum (either QC or TC) has been reached. - */ +/** A local event message indicating that a view quorum (either QC or TC) has been reached. */ public final class ViewQuorumReached { - private final ViewVotingResult votingResult; + private final ViewVotingResult votingResult; - // the author of the last received message that lead to forming a quorum - private final BFTNode lastAuthor; + // the author of the last received message that lead to forming a quorum + private final BFTNode lastAuthor; - public ViewQuorumReached(ViewVotingResult votingResult, BFTNode lastAuthor) { - this.votingResult = Objects.requireNonNull(votingResult); - this.lastAuthor = Objects.requireNonNull(lastAuthor); - } + public ViewQuorumReached(ViewVotingResult votingResult, BFTNode lastAuthor) { + this.votingResult = Objects.requireNonNull(votingResult); + this.lastAuthor = Objects.requireNonNull(lastAuthor); + } - public BFTNode lastAuthor() { - return lastAuthor; - } + public BFTNode lastAuthor() { + return lastAuthor; + } - public ViewVotingResult votingResult() { - return votingResult; - } + public ViewVotingResult votingResult() { + return votingResult; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ViewQuorumReached that = (ViewQuorumReached) o; - return Objects.equals(votingResult, that.votingResult) - && Objects.equals(lastAuthor, that.lastAuthor); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ViewQuorumReached that = (ViewQuorumReached) o; + return Objects.equals(votingResult, that.votingResult) + && Objects.equals(lastAuthor, that.lastAuthor); + } - @Override - public int hashCode() { - return Objects.hash(votingResult, lastAuthor); - } + @Override + public int hashCode() { + return Objects.hash(votingResult, lastAuthor); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewUpdate.java index ab44d893f4..29a501dc5e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewUpdate.java @@ -67,80 +67,76 @@ import com.radixdlt.consensus.HighQC; import java.util.Objects; -/** - * Represents an internal (local) view update. - */ +/** Represents an internal (local) view update. */ public final class ViewUpdate { - private final View currentView; - private final HighQC highQC; - - private final BFTNode leader; - private final BFTNode nextLeader; - - private ViewUpdate(View currentView, HighQC highQC, BFTNode leader, BFTNode nextLeader) { - this.currentView = currentView; - this.highQC = highQC; - this.leader = leader; - this.nextLeader = nextLeader; - } - - public static ViewUpdate create(View currentView, HighQC highQC, BFTNode leader, BFTNode nextLeader) { - Objects.requireNonNull(currentView); - Objects.requireNonNull(highQC); - Objects.requireNonNull(leader); - Objects.requireNonNull(nextLeader); - - return new ViewUpdate(currentView, highQC, leader, nextLeader); - } - - public HighQC getHighQC() { - return highQC; - } - - public BFTNode getLeader() { - return leader; - } - - public BFTNode getNextLeader() { - return nextLeader; - } - - public View getCurrentView() { - return currentView; - } - - public long uncommittedViewsCount() { - return Math.max(0L, currentView.number() - highQC.highestCommittedQC().getView().number() - 1); - } - - @Override - public String toString() { - return String.format("%s[%s %s leader=%s next=%s]", - getClass().getSimpleName(), - this.currentView, - this.highQC, - this.leader, - this.nextLeader); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final ViewUpdate that = (ViewUpdate) o; - return Objects.equals(currentView, that.currentView) - && Objects.equals(highQC, that.highQC) - && Objects.equals(leader, that.leader) - && Objects.equals(nextLeader, that.nextLeader); - } - - @Override - public int hashCode() { - return Objects.hash(currentView, highQC, leader, nextLeader); - } + private final View currentView; + private final HighQC highQC; + + private final BFTNode leader; + private final BFTNode nextLeader; + + private ViewUpdate(View currentView, HighQC highQC, BFTNode leader, BFTNode nextLeader) { + this.currentView = currentView; + this.highQC = highQC; + this.leader = leader; + this.nextLeader = nextLeader; + } + + public static ViewUpdate create( + View currentView, HighQC highQC, BFTNode leader, BFTNode nextLeader) { + Objects.requireNonNull(currentView); + Objects.requireNonNull(highQC); + Objects.requireNonNull(leader); + Objects.requireNonNull(nextLeader); + + return new ViewUpdate(currentView, highQC, leader, nextLeader); + } + + public HighQC getHighQC() { + return highQC; + } + + public BFTNode getLeader() { + return leader; + } + + public BFTNode getNextLeader() { + return nextLeader; + } + + public View getCurrentView() { + return currentView; + } + + public long uncommittedViewsCount() { + return Math.max(0L, currentView.number() - highQC.highestCommittedQC().getView().number() - 1); + } + + @Override + public String toString() { + return String.format( + "%s[%s %s leader=%s next=%s]", + getClass().getSimpleName(), this.currentView, this.highQC, this.leader, this.nextLeader); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ViewUpdate that = (ViewUpdate) o; + return Objects.equals(currentView, that.currentView) + && Objects.equals(highQC, that.highQC) + && Objects.equals(leader, that.leader) + && Objects.equals(nextLeader, that.nextLeader); + } + + @Override + public int hashCode() { + return Objects.hash(currentView, highQC, leader, nextLeader); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewVotingResult.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewVotingResult.java index 20d3e6c360..f0229c1064 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewVotingResult.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/ViewVotingResult.java @@ -66,108 +66,101 @@ import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.TimeoutCertificate; - import java.util.Objects; -/** - * The result of a view voting (either QC or TC). - */ -//TODO: DISPATCH: Make interface sealed, check BFTSync::viewQuorumReachedEventProcessor +/** The result of a view voting (either QC or TC). */ +// TODO: DISPATCH: Make interface sealed, check BFTSync::viewQuorumReachedEventProcessor public interface ViewVotingResult { - static FormedQC qc(QuorumCertificate qc) { - return new FormedQC(qc); + static FormedQC qc(QuorumCertificate qc) { + return new FormedQC(qc); + } + + static FormedTC tc(TimeoutCertificate tc) { + return new FormedTC(tc); + } + + View getView(); + + /** Signifies that the view has been completed with a formed quorum certificate. */ + final class FormedQC implements ViewVotingResult { + + private final QuorumCertificate qc; + + public FormedQC(QuorumCertificate qc) { + this.qc = qc; + } + + public QuorumCertificate getQC() { + return this.qc; + } + + @Override + public View getView() { + return this.qc.getView(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FormedQC formedQC = (FormedQC) o; + return Objects.equals(qc, formedQC.qc); + } + + @Override + public int hashCode() { + return Objects.hash(qc); + } + + @Override + public String toString() { + return String.format("FormedQC{qc=%s}", qc); + } + } + + /** Signifies that the view has been completed with a timeout certificate. */ + final class FormedTC implements ViewVotingResult { + + private final TimeoutCertificate tc; + + public FormedTC(TimeoutCertificate tc) { + this.tc = tc; + } + + public TimeoutCertificate getTC() { + return this.tc; + } + + @Override + public View getView() { + return this.tc.getView(); } - static FormedTC tc(TimeoutCertificate tc) { - return new FormedTC(tc); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FormedTC formedTC = (FormedTC) o; + return Objects.equals(tc, formedTC.tc); } - View getView(); - - /** - * Signifies that the view has been completed with a formed quorum certificate. - */ - final class FormedQC implements ViewVotingResult { - - private final QuorumCertificate qc; - - public FormedQC(QuorumCertificate qc) { - this.qc = qc; - } - - public QuorumCertificate getQC() { - return this.qc; - } - - @Override - public View getView() { - return this.qc.getView(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - FormedQC formedQC = (FormedQC) o; - return Objects.equals(qc, formedQC.qc); - } - - @Override - public int hashCode() { - return Objects.hash(qc); - } - - @Override - public String toString() { - return String.format("FormedQC{qc=%s}", qc); - } + @Override + public int hashCode() { + return Objects.hash(tc); } - /** - * Signifies that the view has been completed with a timeout certificate. - */ - final class FormedTC implements ViewVotingResult { - - private final TimeoutCertificate tc; - - public FormedTC(TimeoutCertificate tc) { - this.tc = tc; - } - - public TimeoutCertificate getTC() { - return this.tc; - } - - @Override - public View getView() { - return this.tc.getView(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - FormedTC formedTC = (FormedTC) o; - return Objects.equals(tc, formedTC.tc); - } - - @Override - public int hashCode() { - return Objects.hash(tc); - } - - @Override - public String toString() { - return String.format("FormedTC{tc=%s}", tc); - } + @Override + public String toString() { + return String.format("FormedTC{tc=%s}", tc); } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VoteProcessingResult.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VoteProcessingResult.java index 02887757ad..6e45933928 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VoteProcessingResult.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VoteProcessingResult.java @@ -66,136 +66,127 @@ import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.TimeoutCertificate; - import java.util.Objects; -/** - * The result of processing a received vote. - */ +/** The result of processing a received vote. */ public interface VoteProcessingResult { - static VoteAccepted accepted() { - return VoteAccepted.INSTANCE; - } - - static VoteRejected rejected(VoteRejected.VoteRejectedReason reason) { - return new VoteRejected(reason); - } - - static QuorumReached quorum(ViewVotingResult result) { - return new QuorumReached(result); - } - - static QuorumReached qcQuorum(QuorumCertificate qc) { - return quorum(ViewVotingResult.qc(qc)); - } - - static QuorumReached tcQuorum(TimeoutCertificate tc) { - return quorum(ViewVotingResult.tc(tc)); - } - - /** - * Signifies that a vote has been accepted, but the quorum hasn't been reached. - */ - final class VoteAccepted implements VoteProcessingResult { - - public static final VoteAccepted INSTANCE = new VoteAccepted(); - - private VoteAccepted() { - } - - @Override - public String toString() { - return "VoteAccepted"; - } - - @Override - public boolean equals(Object o) { - return o != null && getClass() == o.getClass(); - } - - @Override - public int hashCode() { - return 1; - } - } - - /** - * Signifies that a vote has been rejected. - */ - final class VoteRejected implements VoteProcessingResult { - public enum VoteRejectedReason { - INVALID_AUTHOR, DUPLICATE_VOTE - } - - private final VoteRejectedReason reason; - - public VoteRejected(VoteRejectedReason reason) { - this.reason = Objects.requireNonNull(reason); - } - - public VoteRejectedReason getReason() { - return this.reason; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - VoteRejected that = (VoteRejected) o; - return reason == that.reason; - } - - @Override - public int hashCode() { - return Objects.hash(reason); - } - - @Override - public String toString() { - return String.format("VoteRejected{reason=%s}", reason); - } - } - - /** - * Signifies that a vote has been accepted and quorum has been reached. - */ - final class QuorumReached implements VoteProcessingResult { - - private final ViewVotingResult viewVotingResult; - - public QuorumReached(ViewVotingResult viewVotingResult) { - this.viewVotingResult = Objects.requireNonNull(viewVotingResult); - } - - public ViewVotingResult getViewVotingResult() { - return this.viewVotingResult; - } - - @Override - public String toString() { - return String.format("QuorumReached{votingResult=%s}", viewVotingResult); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - QuorumReached that = (QuorumReached) o; - return Objects.equals(viewVotingResult, that.viewVotingResult); - } - - @Override - public int hashCode() { - return Objects.hash(viewVotingResult); - } - } + static VoteAccepted accepted() { + return VoteAccepted.INSTANCE; + } + + static VoteRejected rejected(VoteRejected.VoteRejectedReason reason) { + return new VoteRejected(reason); + } + + static QuorumReached quorum(ViewVotingResult result) { + return new QuorumReached(result); + } + + static QuorumReached qcQuorum(QuorumCertificate qc) { + return quorum(ViewVotingResult.qc(qc)); + } + + static QuorumReached tcQuorum(TimeoutCertificate tc) { + return quorum(ViewVotingResult.tc(tc)); + } + + /** Signifies that a vote has been accepted, but the quorum hasn't been reached. */ + final class VoteAccepted implements VoteProcessingResult { + + public static final VoteAccepted INSTANCE = new VoteAccepted(); + + private VoteAccepted() {} + + @Override + public String toString() { + return "VoteAccepted"; + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass(); + } + + @Override + public int hashCode() { + return 1; + } + } + + /** Signifies that a vote has been rejected. */ + final class VoteRejected implements VoteProcessingResult { + public enum VoteRejectedReason { + INVALID_AUTHOR, + DUPLICATE_VOTE + } + + private final VoteRejectedReason reason; + + public VoteRejected(VoteRejectedReason reason) { + this.reason = Objects.requireNonNull(reason); + } + + public VoteRejectedReason getReason() { + return this.reason; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VoteRejected that = (VoteRejected) o; + return reason == that.reason; + } + + @Override + public int hashCode() { + return Objects.hash(reason); + } + + @Override + public String toString() { + return String.format("VoteRejected{reason=%s}", reason); + } + } + + /** Signifies that a vote has been accepted and quorum has been reached. */ + final class QuorumReached implements VoteProcessingResult { + + private final ViewVotingResult viewVotingResult; + + public QuorumReached(ViewVotingResult viewVotingResult) { + this.viewVotingResult = Objects.requireNonNull(viewVotingResult); + } + + public ViewVotingResult getViewVotingResult() { + return this.viewVotingResult; + } + + @Override + public String toString() { + return String.format("QuorumReached{votingResult=%s}", viewVotingResult); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + QuorumReached that = (QuorumReached) o; + return Objects.equals(viewVotingResult, that.viewVotingResult); + } + + @Override + public int hashCode() { + return Objects.hash(viewVotingResult); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncFactory.java index 7240e0209b..b04a5d40ff 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncFactory.java @@ -69,13 +69,8 @@ import com.radixdlt.consensus.liveness.PacemakerState; import com.radixdlt.consensus.sync.BFTSync; -/** - * Creates a new bft sync given a vertex store and pacemaker - */ +/** Creates a new bft sync given a vertex store and pacemaker */ public interface BFTSyncFactory { - BFTSync create( - VertexStore vertexStore, - PacemakerState pacemakerState, - BFTConfiguration configuration - ); + BFTSync create( + VertexStore vertexStore, PacemakerState pacemakerState, BFTConfiguration configuration); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncRequestProcessorFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncRequestProcessorFactory.java index 623f6edada..477ec0cdd9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncRequestProcessorFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/BFTSyncRequestProcessorFactory.java @@ -68,9 +68,7 @@ import com.radixdlt.consensus.sync.GetVerticesRequest; import com.radixdlt.environment.RemoteEventProcessor; -/** - * Creates new instances of sync request processors given a vertex store - */ +/** Creates new instances of sync request processors given a vertex store */ public interface BFTSyncRequestProcessorFactory { - RemoteEventProcessor create(VertexStore vertexStore); + RemoteEventProcessor create(VertexStore vertexStore); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochChange.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochChange.java index 2e1ee352e3..3b2f88a361 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochChange.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochChange.java @@ -68,53 +68,50 @@ import com.radixdlt.consensus.LedgerProof; import java.util.Objects; -/** - * An epoch change message to consensus - */ +/** An epoch change message to consensus */ public final class EpochChange { - private final LedgerProof proof; - private final BFTConfiguration bftConfiguration; + private final LedgerProof proof; + private final BFTConfiguration bftConfiguration; - public EpochChange(LedgerProof proof, BFTConfiguration bftConfiguration) { - this.proof = Objects.requireNonNull(proof); - this.bftConfiguration = Objects.requireNonNull(bftConfiguration); - } + public EpochChange(LedgerProof proof, BFTConfiguration bftConfiguration) { + this.proof = Objects.requireNonNull(proof); + this.bftConfiguration = Objects.requireNonNull(bftConfiguration); + } - public BFTConfiguration getBFTConfiguration() { - return bftConfiguration; - } + public BFTConfiguration getBFTConfiguration() { + return bftConfiguration; + } - public LedgerProof getGenesisHeader() { - return bftConfiguration.getVertexStoreState().getRootHeader(); - } + public LedgerProof getGenesisHeader() { + return bftConfiguration.getVertexStoreState().getRootHeader(); + } - public long getEpoch() { - return proof.getEpoch() + 1; - } + public long getEpoch() { + return proof.getEpoch() + 1; + } - public LedgerProof getProof() { - return proof; - } + public LedgerProof getProof() { + return proof; + } - @Override - public int hashCode() { - return Objects.hash(this.proof, this.bftConfiguration); - } + @Override + public int hashCode() { + return Objects.hash(this.proof, this.bftConfiguration); + } - @Override - public boolean equals(Object o) { - if (o instanceof EpochChange) { - final var that = (EpochChange) o; - return Objects.equals(this.proof, that.proof) - && Objects.equals(this.bftConfiguration, that.bftConfiguration); - } - return false; - } + @Override + public boolean equals(Object o) { + if (o instanceof EpochChange) { + final var that = (EpochChange) o; + return Objects.equals(this.proof, that.proof) + && Objects.equals(this.bftConfiguration, that.bftConfiguration); + } + return false; + } - @Override - public String toString() { - return String.format( - "%s{proof=%s config=%s}", this.getClass().getSimpleName(), proof, bftConfiguration - ); - } + @Override + public String toString() { + return String.format( + "%s{proof=%s config=%s}", this.getClass().getSimpleName(), proof, bftConfiguration); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochManager.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochManager.java index ade100c39e..5c9ec12d1b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochManager.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochManager.java @@ -98,11 +98,6 @@ import com.radixdlt.environment.RemoteEventProcessor; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.concurrent.NotThreadSafe; -import javax.inject.Inject; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -111,326 +106,353 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import javax.annotation.concurrent.NotThreadSafe; +import javax.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Manages Epochs and the BFT instance (which is mostly epoch agnostic) associated with each epoch */ @NotThreadSafe public final class EpochManager { - private static final Logger log = LogManager.getLogger(); - private final BFTNode self; - private final PacemakerFactory pacemakerFactory; - private final VertexStoreFactory vertexStoreFactory; - private final BFTSyncRequestProcessorFactory bftSyncRequestProcessorFactory; - private final BFTSyncFactory bftSyncFactory; - private final Hasher hasher; - private final HashSigner signer; - private final PacemakerTimeoutCalculator timeoutCalculator; - private final SystemCounters counters; - private final Map> queuedEvents; - private final BFTFactory bftFactory; - private final PacemakerStateFactory pacemakerStateFactory; - - private EpochChange currentEpoch; - - private EventProcessor syncTimeoutProcessor; - private EventProcessor syncLedgerUpdateProcessor; - private BFTEventProcessor bftEventProcessor; - - private Set> syncRequestProcessors; - private Set> syncResponseProcessors; - private Set> syncErrorResponseProcessors; - - private Set> bftUpdateProcessors; - private Set> bftRebuildProcessors; - - private final RemoteEventDispatcher ledgerStatusUpdateDispatcher; - - private final PersistentSafetyStateStore persistentSafetyStateStore; - - @Inject - public EpochManager( - @Self BFTNode self, - BFTEventProcessor initialBFTEventProcessor, - VertexStoreBFTSyncRequestProcessor requestProcessor, - BFTSync initialBFTSync, - RemoteEventDispatcher ledgerStatusUpdateDispatcher, - EpochChange initialEpoch, - PacemakerFactory pacemakerFactory, - VertexStoreFactory vertexStoreFactory, - BFTSyncFactory bftSyncFactory, - BFTSyncRequestProcessorFactory bftSyncRequestProcessorFactory, - BFTFactory bftFactory, - SystemCounters counters, - Hasher hasher, - HashSigner signer, - PacemakerTimeoutCalculator timeoutCalculator, - PacemakerStateFactory pacemakerStateFactory, - PersistentSafetyStateStore persistentSafetyStateStore - ) { - var isValidator = initialEpoch.getBFTConfiguration().getValidatorSet().containsNode(self); - // TODO: these should all be removed - if (!isValidator) { - this.bftEventProcessor = EmptyBFTEventProcessor.INSTANCE; - this.syncLedgerUpdateProcessor = update -> { }; - this.syncTimeoutProcessor = timeout -> { }; - } else { - this.bftEventProcessor = Objects.requireNonNull(initialBFTEventProcessor); - this.syncLedgerUpdateProcessor = initialBFTSync.baseLedgerUpdateEventProcessor(); - this.syncTimeoutProcessor = initialBFTSync.vertexRequestTimeoutEventProcessor(); - } - this.syncResponseProcessors = isValidator ? Set.of(initialBFTSync.responseProcessor()) : Set.of(); - this.syncRequestProcessors = isValidator ? Set.of(requestProcessor) : Set.of(); - this.syncErrorResponseProcessors = isValidator ? Set.of(initialBFTSync.errorResponseProcessor()) : Set.of(); - this.bftUpdateProcessors = isValidator - ? Set.of(initialBFTSync::processBFTUpdate, initialBFTEventProcessor::processBFTUpdate) - : Set.of(); - this.bftRebuildProcessors = isValidator - ? Set.of(initialBFTEventProcessor::processBFTRebuildUpdate) - : Set.of(); - - - this.ledgerStatusUpdateDispatcher = Objects.requireNonNull(ledgerStatusUpdateDispatcher); - this.currentEpoch = Objects.requireNonNull(initialEpoch); - this.self = Objects.requireNonNull(self); - this.pacemakerFactory = Objects.requireNonNull(pacemakerFactory); - this.vertexStoreFactory = Objects.requireNonNull(vertexStoreFactory); - this.bftSyncFactory = Objects.requireNonNull(bftSyncFactory); - this.bftSyncRequestProcessorFactory = bftSyncRequestProcessorFactory; - this.hasher = Objects.requireNonNull(hasher); - this.signer = Objects.requireNonNull(signer); - this.timeoutCalculator = Objects.requireNonNull(timeoutCalculator); - this.bftFactory = bftFactory; - this.counters = Objects.requireNonNull(counters); - this.pacemakerStateFactory = Objects.requireNonNull(pacemakerStateFactory); - this.persistentSafetyStateStore = Objects.requireNonNull(persistentSafetyStateStore); - this.queuedEvents = new HashMap<>(); - } - - private void updateEpochState() { - BFTConfiguration config = this.currentEpoch.getBFTConfiguration(); - BFTValidatorSet validatorSet = config.getValidatorSet(); - if (!validatorSet.containsNode(self)) { - this.bftRebuildProcessors = Set.of(); - this.bftUpdateProcessors = Set.of(); - this.syncRequestProcessors = Set.of(); - this.syncResponseProcessors = Set.of(); - this.syncErrorResponseProcessors = Set.of(); - this.bftEventProcessor = EmptyBFTEventProcessor.INSTANCE; - this.syncLedgerUpdateProcessor = update -> { }; - this.syncTimeoutProcessor = timeout -> { }; - return; - } - - final long nextEpoch = this.currentEpoch.getEpoch(); - - // Config - final BFTConfiguration bftConfiguration = this.currentEpoch.getBFTConfiguration(); - final ProposerElection proposerElection = bftConfiguration.getProposerElection(); - HighQC highQC = bftConfiguration.getVertexStoreState().getHighQC(); - View view = highQC.highestQC().getView().next(); - final BFTNode leader = proposerElection.getProposer(view); - final BFTNode nextLeader = proposerElection.getProposer(view.next()); - final ViewUpdate initialViewUpdate = ViewUpdate.create(view, highQC, leader, nextLeader); - - // Mutable Consensus State - final VertexStore vertexStore = vertexStoreFactory.create(bftConfiguration.getVertexStoreState()); - final PacemakerState pacemakerState = pacemakerStateFactory.create(initialViewUpdate, nextEpoch, proposerElection); - - // Consensus Drivers - final SafetyRules safetyRules = new SafetyRules(self, SafetyState.initialState(), persistentSafetyStateStore, hasher, signer); - final Pacemaker pacemaker = pacemakerFactory.create( - validatorSet, - vertexStore, - timeoutCalculator, - safetyRules, - initialViewUpdate, - nextEpoch - ); - final BFTSync bftSync = bftSyncFactory.create( - vertexStore, - pacemakerState, - bftConfiguration - ); - - this.syncLedgerUpdateProcessor = bftSync.baseLedgerUpdateEventProcessor(); - this.syncTimeoutProcessor = bftSync.vertexRequestTimeoutEventProcessor(); - - this.bftEventProcessor = bftFactory.create( - self, - pacemaker, - vertexStore, - bftSync, - bftSync.viewQuorumReachedEventProcessor(), - validatorSet, - initialViewUpdate, - safetyRules - ); - - this.syncResponseProcessors = Set.of(bftSync.responseProcessor()); - this.syncErrorResponseProcessors = Set.of(bftSync.errorResponseProcessor()); - this.syncRequestProcessors = Set.of(bftSyncRequestProcessorFactory.create(vertexStore)); - this.bftRebuildProcessors = ImmutableSet.of(bftEventProcessor::processBFTRebuildUpdate); - this.bftUpdateProcessors = ImmutableSet.of(bftSync::processBFTUpdate, bftEventProcessor::processBFTUpdate); - } - - public void start() { - this.bftEventProcessor.start(); - } - - private long currentEpoch() { - return this.currentEpoch.getEpoch(); - } - - public EventProcessor epochsLedgerUpdateEventProcessor() { - return this::processLedgerUpdate; - } - - private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { - var epochChange = ledgerUpdate.getStateComputerOutput().getInstance(EpochChange.class); - if (epochChange != null) { - this.processEpochChange(epochChange); - } else { - this.syncLedgerUpdateProcessor.process(ledgerUpdate); - } - } - - private void processEpochChange(EpochChange epochChange) { - // Sanity check - if (epochChange.getEpoch() != this.currentEpoch() + 1) { - //safe, as message is internal - throw new IllegalStateException("Bad Epoch change: " + epochChange + " current epoch: " + this.currentEpoch); - } - - if (this.currentEpoch.getBFTConfiguration().getValidatorSet().containsNode(this.self)) { - final ImmutableSet currentAndNextValidators = - ImmutableSet.builder() - .addAll(epochChange.getBFTConfiguration().getValidatorSet().getValidators()) - .addAll(this.currentEpoch.getBFTConfiguration().getValidatorSet().getValidators()) - .build(); - - final var ledgerStatusUpdate = LedgerStatusUpdate.create(epochChange.getGenesisHeader()); - for (BFTValidator validator : currentAndNextValidators) { - if (!validator.getNode().equals(self)) { - this.ledgerStatusUpdateDispatcher.dispatch(validator.getNode(), ledgerStatusUpdate); - } - } - } - - this.currentEpoch = epochChange; - this.updateEpochState(); - this.bftEventProcessor.start(); - - // Execute any queued up consensus events - final List queuedEventsForEpoch = queuedEvents.getOrDefault(epochChange.getEpoch(), Collections.emptyList()); - View highView = queuedEventsForEpoch.stream().map(ConsensusEvent::getView).max(Comparator.naturalOrder()).orElse(View.genesis()); - queuedEventsForEpoch.stream().filter(e -> e.getView().equals(highView)) - .forEach(this::processConsensusEventInternal); - - queuedEvents.remove(epochChange.getEpoch()); - } - - private void processConsensusEventInternal(ConsensusEvent consensusEvent) { - this.counters.increment(CounterType.BFT_EVENTS_RECEIVED); - - //TODO: replace with switch - if (consensusEvent instanceof Proposal) { - bftEventProcessor.processProposal((Proposal) consensusEvent); - } else if (consensusEvent instanceof Vote) { - bftEventProcessor.processVote((Vote) consensusEvent); - } else { - //TODO: add necessary branch once there will be more ConsensusEvent implementations - throw new IllegalStateException("Unknown consensus event: " + consensusEvent); - } - } - - public void processConsensusEvent(ConsensusEvent consensusEvent) { - if (consensusEvent.getEpoch() > this.currentEpoch()) { - log.debug("{}: CONSENSUS_EVENT: Received higher epoch event: {} current epoch: {}", - this.self::getSimpleName, () -> consensusEvent, this::currentEpoch); - - // queue higher epoch events for later processing - // TODO: need to clear this by some rule (e.g. timeout or max size) or else memory leak attack possible - queuedEvents.computeIfAbsent(consensusEvent.getEpoch(), e -> new ArrayList<>()).add(consensusEvent); - counters.increment(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS); - return; - } - - if (consensusEvent.getEpoch() < this.currentEpoch()) { - log.debug("{}: CONSENSUS_EVENT: Ignoring lower epoch event: {} current epoch: {}", - this.self::getSimpleName, () -> consensusEvent, this::currentEpoch - ); - return; - } - - this.processConsensusEventInternal(consensusEvent); - } - - public void processLocalTimeout(Epoched localTimeout) { - if (localTimeout.epoch() != this.currentEpoch()) { - return; - } - - bftEventProcessor.processLocalTimeout(localTimeout.event()); - } - - public EventProcessor epochViewUpdateEventProcessor() { - return epochViewUpdate -> { - if (epochViewUpdate.getEpoch() != this.currentEpoch()) { - return; - } - - log.trace("Processing ViewUpdate: {}", epochViewUpdate); - bftEventProcessor.processViewUpdate(epochViewUpdate.getViewUpdate()); - }; - } - - public void processBFTUpdate(BFTInsertUpdate update) { - bftUpdateProcessors.forEach(p -> p.process(update)); - } - - public EventProcessor bftRebuildUpdateEventProcessor() { - return update -> { - if (update.getVertexStoreState().getRoot().getParentHeader().getLedgerHeader().getEpoch() != this.currentEpoch()) { - return; - } - - bftRebuildProcessors.forEach(p -> p.process(update)); - }; - } - - public RemoteEventProcessor bftSyncRequestProcessor() { - return (node, request) -> syncRequestProcessors.forEach(p -> p.process(node, request)); - } - - public RemoteEventProcessor bftSyncResponseProcessor() { - return (node, resp) -> syncResponseProcessors.forEach(p -> p.process(node, resp)); - } - - public RemoteEventProcessor bftSyncErrorResponseProcessor() { - return (node, err) -> { - log.debug("SYNC_ERROR: Received GetVerticesErrorResponse {}", err); - final var responseEpoch = err.highQC().highestQC().getEpoch(); - if (responseEpoch < this.currentEpoch()) { - log.debug("SYNC_ERROR: Ignoring lower epoch error response: {} current epoch: {}", err, this.currentEpoch()); - return; - } - if (responseEpoch > this.currentEpoch()) { - log.debug("SYNC_ERROR: Received higher epoch error response: {} current epoch: {}", err, this.currentEpoch()); - } else { - // Current epoch - syncErrorResponseProcessors.forEach(p -> p.process(node, err)); - } - }; - } - - public EventProcessor timeoutEventProcessor() { - //Return reference to method rather than syncTimeoutProcessor directly, - // since syncTimeoutProcessor will change over the time - return this::processGetVerticesLocalTimeout; - } - - private void processGetVerticesLocalTimeout(VertexRequestTimeout timeout) { - syncTimeoutProcessor.process(timeout); - } + private static final Logger log = LogManager.getLogger(); + private final BFTNode self; + private final PacemakerFactory pacemakerFactory; + private final VertexStoreFactory vertexStoreFactory; + private final BFTSyncRequestProcessorFactory bftSyncRequestProcessorFactory; + private final BFTSyncFactory bftSyncFactory; + private final Hasher hasher; + private final HashSigner signer; + private final PacemakerTimeoutCalculator timeoutCalculator; + private final SystemCounters counters; + private final Map> queuedEvents; + private final BFTFactory bftFactory; + private final PacemakerStateFactory pacemakerStateFactory; + + private EpochChange currentEpoch; + + private EventProcessor syncTimeoutProcessor; + private EventProcessor syncLedgerUpdateProcessor; + private BFTEventProcessor bftEventProcessor; + + private Set> syncRequestProcessors; + private Set> syncResponseProcessors; + private Set> syncErrorResponseProcessors; + + private Set> bftUpdateProcessors; + private Set> bftRebuildProcessors; + + private final RemoteEventDispatcher ledgerStatusUpdateDispatcher; + + private final PersistentSafetyStateStore persistentSafetyStateStore; + + @Inject + public EpochManager( + @Self BFTNode self, + BFTEventProcessor initialBFTEventProcessor, + VertexStoreBFTSyncRequestProcessor requestProcessor, + BFTSync initialBFTSync, + RemoteEventDispatcher ledgerStatusUpdateDispatcher, + EpochChange initialEpoch, + PacemakerFactory pacemakerFactory, + VertexStoreFactory vertexStoreFactory, + BFTSyncFactory bftSyncFactory, + BFTSyncRequestProcessorFactory bftSyncRequestProcessorFactory, + BFTFactory bftFactory, + SystemCounters counters, + Hasher hasher, + HashSigner signer, + PacemakerTimeoutCalculator timeoutCalculator, + PacemakerStateFactory pacemakerStateFactory, + PersistentSafetyStateStore persistentSafetyStateStore) { + var isValidator = initialEpoch.getBFTConfiguration().getValidatorSet().containsNode(self); + // TODO: these should all be removed + if (!isValidator) { + this.bftEventProcessor = EmptyBFTEventProcessor.INSTANCE; + this.syncLedgerUpdateProcessor = update -> {}; + this.syncTimeoutProcessor = timeout -> {}; + } else { + this.bftEventProcessor = Objects.requireNonNull(initialBFTEventProcessor); + this.syncLedgerUpdateProcessor = initialBFTSync.baseLedgerUpdateEventProcessor(); + this.syncTimeoutProcessor = initialBFTSync.vertexRequestTimeoutEventProcessor(); + } + this.syncResponseProcessors = + isValidator ? Set.of(initialBFTSync.responseProcessor()) : Set.of(); + this.syncRequestProcessors = isValidator ? Set.of(requestProcessor) : Set.of(); + this.syncErrorResponseProcessors = + isValidator ? Set.of(initialBFTSync.errorResponseProcessor()) : Set.of(); + this.bftUpdateProcessors = + isValidator + ? Set.of(initialBFTSync::processBFTUpdate, initialBFTEventProcessor::processBFTUpdate) + : Set.of(); + this.bftRebuildProcessors = + isValidator ? Set.of(initialBFTEventProcessor::processBFTRebuildUpdate) : Set.of(); + + this.ledgerStatusUpdateDispatcher = Objects.requireNonNull(ledgerStatusUpdateDispatcher); + this.currentEpoch = Objects.requireNonNull(initialEpoch); + this.self = Objects.requireNonNull(self); + this.pacemakerFactory = Objects.requireNonNull(pacemakerFactory); + this.vertexStoreFactory = Objects.requireNonNull(vertexStoreFactory); + this.bftSyncFactory = Objects.requireNonNull(bftSyncFactory); + this.bftSyncRequestProcessorFactory = bftSyncRequestProcessorFactory; + this.hasher = Objects.requireNonNull(hasher); + this.signer = Objects.requireNonNull(signer); + this.timeoutCalculator = Objects.requireNonNull(timeoutCalculator); + this.bftFactory = bftFactory; + this.counters = Objects.requireNonNull(counters); + this.pacemakerStateFactory = Objects.requireNonNull(pacemakerStateFactory); + this.persistentSafetyStateStore = Objects.requireNonNull(persistentSafetyStateStore); + this.queuedEvents = new HashMap<>(); + } + + private void updateEpochState() { + BFTConfiguration config = this.currentEpoch.getBFTConfiguration(); + BFTValidatorSet validatorSet = config.getValidatorSet(); + if (!validatorSet.containsNode(self)) { + this.bftRebuildProcessors = Set.of(); + this.bftUpdateProcessors = Set.of(); + this.syncRequestProcessors = Set.of(); + this.syncResponseProcessors = Set.of(); + this.syncErrorResponseProcessors = Set.of(); + this.bftEventProcessor = EmptyBFTEventProcessor.INSTANCE; + this.syncLedgerUpdateProcessor = update -> {}; + this.syncTimeoutProcessor = timeout -> {}; + return; + } + + final long nextEpoch = this.currentEpoch.getEpoch(); + + // Config + final BFTConfiguration bftConfiguration = this.currentEpoch.getBFTConfiguration(); + final ProposerElection proposerElection = bftConfiguration.getProposerElection(); + HighQC highQC = bftConfiguration.getVertexStoreState().getHighQC(); + View view = highQC.highestQC().getView().next(); + final BFTNode leader = proposerElection.getProposer(view); + final BFTNode nextLeader = proposerElection.getProposer(view.next()); + final ViewUpdate initialViewUpdate = ViewUpdate.create(view, highQC, leader, nextLeader); + + // Mutable Consensus State + final VertexStore vertexStore = + vertexStoreFactory.create(bftConfiguration.getVertexStoreState()); + final PacemakerState pacemakerState = + pacemakerStateFactory.create(initialViewUpdate, nextEpoch, proposerElection); + + // Consensus Drivers + final SafetyRules safetyRules = + new SafetyRules( + self, SafetyState.initialState(), persistentSafetyStateStore, hasher, signer); + final Pacemaker pacemaker = + pacemakerFactory.create( + validatorSet, + vertexStore, + timeoutCalculator, + safetyRules, + initialViewUpdate, + nextEpoch); + final BFTSync bftSync = bftSyncFactory.create(vertexStore, pacemakerState, bftConfiguration); + + this.syncLedgerUpdateProcessor = bftSync.baseLedgerUpdateEventProcessor(); + this.syncTimeoutProcessor = bftSync.vertexRequestTimeoutEventProcessor(); + + this.bftEventProcessor = + bftFactory.create( + self, + pacemaker, + vertexStore, + bftSync, + bftSync.viewQuorumReachedEventProcessor(), + validatorSet, + initialViewUpdate, + safetyRules); + + this.syncResponseProcessors = Set.of(bftSync.responseProcessor()); + this.syncErrorResponseProcessors = Set.of(bftSync.errorResponseProcessor()); + this.syncRequestProcessors = Set.of(bftSyncRequestProcessorFactory.create(vertexStore)); + this.bftRebuildProcessors = ImmutableSet.of(bftEventProcessor::processBFTRebuildUpdate); + this.bftUpdateProcessors = + ImmutableSet.of(bftSync::processBFTUpdate, bftEventProcessor::processBFTUpdate); + } + + public void start() { + this.bftEventProcessor.start(); + } + + private long currentEpoch() { + return this.currentEpoch.getEpoch(); + } + + public EventProcessor epochsLedgerUpdateEventProcessor() { + return this::processLedgerUpdate; + } + + private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { + var epochChange = ledgerUpdate.getStateComputerOutput().getInstance(EpochChange.class); + if (epochChange != null) { + this.processEpochChange(epochChange); + } else { + this.syncLedgerUpdateProcessor.process(ledgerUpdate); + } + } + + private void processEpochChange(EpochChange epochChange) { + // Sanity check + if (epochChange.getEpoch() != this.currentEpoch() + 1) { + // safe, as message is internal + throw new IllegalStateException( + "Bad Epoch change: " + epochChange + " current epoch: " + this.currentEpoch); + } + + if (this.currentEpoch.getBFTConfiguration().getValidatorSet().containsNode(this.self)) { + final ImmutableSet currentAndNextValidators = + ImmutableSet.builder() + .addAll(epochChange.getBFTConfiguration().getValidatorSet().getValidators()) + .addAll(this.currentEpoch.getBFTConfiguration().getValidatorSet().getValidators()) + .build(); + + final var ledgerStatusUpdate = LedgerStatusUpdate.create(epochChange.getGenesisHeader()); + for (BFTValidator validator : currentAndNextValidators) { + if (!validator.getNode().equals(self)) { + this.ledgerStatusUpdateDispatcher.dispatch(validator.getNode(), ledgerStatusUpdate); + } + } + } + + this.currentEpoch = epochChange; + this.updateEpochState(); + this.bftEventProcessor.start(); + + // Execute any queued up consensus events + final List queuedEventsForEpoch = + queuedEvents.getOrDefault(epochChange.getEpoch(), Collections.emptyList()); + View highView = + queuedEventsForEpoch.stream() + .map(ConsensusEvent::getView) + .max(Comparator.naturalOrder()) + .orElse(View.genesis()); + queuedEventsForEpoch.stream() + .filter(e -> e.getView().equals(highView)) + .forEach(this::processConsensusEventInternal); + + queuedEvents.remove(epochChange.getEpoch()); + } + + private void processConsensusEventInternal(ConsensusEvent consensusEvent) { + this.counters.increment(CounterType.BFT_EVENTS_RECEIVED); + + // TODO: replace with switch + if (consensusEvent instanceof Proposal) { + bftEventProcessor.processProposal((Proposal) consensusEvent); + } else if (consensusEvent instanceof Vote) { + bftEventProcessor.processVote((Vote) consensusEvent); + } else { + // TODO: add necessary branch once there will be more ConsensusEvent implementations + throw new IllegalStateException("Unknown consensus event: " + consensusEvent); + } + } + + public void processConsensusEvent(ConsensusEvent consensusEvent) { + if (consensusEvent.getEpoch() > this.currentEpoch()) { + log.debug( + "{}: CONSENSUS_EVENT: Received higher epoch event: {} current epoch: {}", + this.self::getSimpleName, + () -> consensusEvent, + this::currentEpoch); + + // queue higher epoch events for later processing + // TODO: need to clear this by some rule (e.g. timeout or max size) or else memory leak attack + // possible + queuedEvents + .computeIfAbsent(consensusEvent.getEpoch(), e -> new ArrayList<>()) + .add(consensusEvent); + counters.increment(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS); + return; + } + + if (consensusEvent.getEpoch() < this.currentEpoch()) { + log.debug( + "{}: CONSENSUS_EVENT: Ignoring lower epoch event: {} current epoch: {}", + this.self::getSimpleName, + () -> consensusEvent, + this::currentEpoch); + return; + } + + this.processConsensusEventInternal(consensusEvent); + } + + public void processLocalTimeout(Epoched localTimeout) { + if (localTimeout.epoch() != this.currentEpoch()) { + return; + } + + bftEventProcessor.processLocalTimeout(localTimeout.event()); + } + + public EventProcessor epochViewUpdateEventProcessor() { + return epochViewUpdate -> { + if (epochViewUpdate.getEpoch() != this.currentEpoch()) { + return; + } + + log.trace("Processing ViewUpdate: {}", epochViewUpdate); + bftEventProcessor.processViewUpdate(epochViewUpdate.getViewUpdate()); + }; + } + + public void processBFTUpdate(BFTInsertUpdate update) { + bftUpdateProcessors.forEach(p -> p.process(update)); + } + + public EventProcessor bftRebuildUpdateEventProcessor() { + return update -> { + if (update.getVertexStoreState().getRoot().getParentHeader().getLedgerHeader().getEpoch() + != this.currentEpoch()) { + return; + } + + bftRebuildProcessors.forEach(p -> p.process(update)); + }; + } + + public RemoteEventProcessor bftSyncRequestProcessor() { + return (node, request) -> syncRequestProcessors.forEach(p -> p.process(node, request)); + } + + public RemoteEventProcessor bftSyncResponseProcessor() { + return (node, resp) -> syncResponseProcessors.forEach(p -> p.process(node, resp)); + } + + public RemoteEventProcessor bftSyncErrorResponseProcessor() { + return (node, err) -> { + log.debug("SYNC_ERROR: Received GetVerticesErrorResponse {}", err); + final var responseEpoch = err.highQC().highestQC().getEpoch(); + if (responseEpoch < this.currentEpoch()) { + log.debug( + "SYNC_ERROR: Ignoring lower epoch error response: {} current epoch: {}", + err, + this.currentEpoch()); + return; + } + if (responseEpoch > this.currentEpoch()) { + log.debug( + "SYNC_ERROR: Received higher epoch error response: {} current epoch: {}", + err, + this.currentEpoch()); + } else { + // Current epoch + syncErrorResponseProcessors.forEach(p -> p.process(node, err)); + } + }; + } + + public EventProcessor timeoutEventProcessor() { + // Return reference to method rather than syncTimeoutProcessor directly, + // since syncTimeoutProcessor will change over the time + return this::processGetVerticesLocalTimeout; + } + + private void processGetVerticesLocalTimeout(VertexRequestTimeout timeout) { + syncTimeoutProcessor.process(timeout); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochView.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochView.java index a40b0d3f91..d21ab059f8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochView.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochView.java @@ -67,59 +67,57 @@ import com.radixdlt.consensus.bft.View; import java.util.Objects; -/** - * A bft view with it's corresponding epoch - */ +/** A bft view with it's corresponding epoch */ public final class EpochView implements Comparable { - private final long epoch; - private final View view; + private final long epoch; + private final View view; - public EpochView(long epoch, View view) { - if (epoch < 0) { - throw new IllegalArgumentException("epoch must be >= 0"); - } - this.epoch = epoch; - this.view = Objects.requireNonNull(view); - } + public EpochView(long epoch, View view) { + if (epoch < 0) { + throw new IllegalArgumentException("epoch must be >= 0"); + } + this.epoch = epoch; + this.view = Objects.requireNonNull(view); + } - public static EpochView of(long epoch, View view) { - return new EpochView(epoch, view); - } + public static EpochView of(long epoch, View view) { + return new EpochView(epoch, view); + } - public long getEpoch() { - return epoch; - } + public long getEpoch() { + return epoch; + } - public View getView() { - return view; - } + public View getView() { + return view; + } - @Override - public String toString() { - return String.format("%s{epoch=%s view=%s}", this.getClass().getSimpleName(), epoch, view); - } + @Override + public String toString() { + return String.format("%s{epoch=%s view=%s}", this.getClass().getSimpleName(), epoch, view); + } - @Override - public int hashCode() { - return Objects.hash(epoch, view); - } + @Override + public int hashCode() { + return Objects.hash(epoch, view); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof EpochView)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof EpochView)) { + return false; + } - EpochView other = (EpochView) o; - return other.epoch == this.epoch && Objects.equals(other.view, this.view); - } + EpochView other = (EpochView) o; + return other.epoch == this.epoch && Objects.equals(other.view, this.view); + } - @Override - public int compareTo(EpochView o) { - if (this.epoch != o.epoch) { - return Long.compare(this.epoch, o.epoch); - } + @Override + public int compareTo(EpochView o) { + if (this.epoch != o.epoch) { + return Long.compare(this.epoch, o.epoch); + } - return this.view.compareTo(o.view); - } + return this.view.compareTo(o.view); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochViewUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochViewUpdate.java index 9210bc6f9d..cc74648253 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochViewUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/EpochViewUpdate.java @@ -65,54 +65,51 @@ package com.radixdlt.consensus.epoch; import com.radixdlt.consensus.bft.ViewUpdate; - import java.util.Objects; -/** - * A wrapper for a ViewUpdate message that also holds epoch. - */ +/** A wrapper for a ViewUpdate message that also holds epoch. */ public final class EpochViewUpdate { - private final long epoch; - private final ViewUpdate viewUpdate; + private final long epoch; + private final ViewUpdate viewUpdate; - public EpochViewUpdate(long epoch, ViewUpdate viewUpdate) { - this.epoch = epoch; - this.viewUpdate = Objects.requireNonNull(viewUpdate); - } + public EpochViewUpdate(long epoch, ViewUpdate viewUpdate) { + this.epoch = epoch; + this.viewUpdate = Objects.requireNonNull(viewUpdate); + } - public EpochView getEpochView() { - return new EpochView(epoch, viewUpdate.getCurrentView()); - } + public EpochView getEpochView() { + return new EpochView(epoch, viewUpdate.getCurrentView()); + } - public long getEpoch() { - return epoch; - } + public long getEpoch() { + return epoch; + } - public ViewUpdate getViewUpdate() { - return viewUpdate; - } + public ViewUpdate getViewUpdate() { + return viewUpdate; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - EpochViewUpdate that = (EpochViewUpdate) o; - return epoch == that.epoch - && Objects.equals(viewUpdate, that.viewUpdate); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EpochViewUpdate that = (EpochViewUpdate) o; + return epoch == that.epoch && Objects.equals(viewUpdate, that.viewUpdate); + } - @Override - public int hashCode() { - return Objects.hash(epoch, viewUpdate); - } + @Override + public int hashCode() { + return Objects.hash(epoch, viewUpdate); + } - @Override - public String toString() { - return String.format("%s{epoch=%s view=%s}", this.getClass().getSimpleName(), epoch, viewUpdate); - } + @Override + public String toString() { + return String.format( + "%s{epoch=%s view=%s}", this.getClass().getSimpleName(), epoch, viewUpdate); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/Epoched.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/Epoched.java index 8bbde42958..cca2027423 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/Epoched.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/Epoched.java @@ -68,58 +68,57 @@ /** * Epoch wrapper for events - * @param event which is wrapped - * TODO: Move other epoch events into this kind of object + * + * @param event which is wrapped TODO: Move other epoch events into this kind of object */ public final class Epoched { - private final long epoch; - private final T event; + private final long epoch; + private final T event; - private Epoched(long epoch, T event) { - this.epoch = epoch; - this.event = event; - } + private Epoched(long epoch, T event) { + this.epoch = epoch; + this.event = event; + } - public static Epoched from(long epoch, T event) { - Objects.requireNonNull(event); - return new Epoched<>(epoch, event); - } + public static Epoched from(long epoch, T event) { + Objects.requireNonNull(event); + return new Epoched<>(epoch, event); + } - public static boolean isInstance(Object event, Class eventClass) { - if (event instanceof Epoched) { - Epoched epoched = (Epoched) event; - return eventClass.isInstance(epoched.event); - } + public static boolean isInstance(Object event, Class eventClass) { + if (event instanceof Epoched) { + Epoched epoched = (Epoched) event; + return eventClass.isInstance(epoched.event); + } - return false; - } + return false; + } - public long epoch() { - return epoch; - } + public long epoch() { + return epoch; + } - public T event() { - return event; - } + public T event() { + return event; + } - @Override - public int hashCode() { - return Objects.hash(epoch, event); - } + @Override + public int hashCode() { + return Objects.hash(epoch, event); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof Epoched)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof Epoched)) { + return false; + } - Epoched other = (Epoched) o; - return Objects.equals(other.event, this.event) - && other.epoch == this.epoch; - } + Epoched other = (Epoched) o; + return Objects.equals(other.event, this.event) && other.epoch == this.epoch; + } - @Override - public String toString() { - return String.format("%s{epoch=%s event=%s}", this.getClass().getSimpleName(), epoch, event); - } + @Override + public String toString() { + return String.format("%s{epoch=%s event=%s}", this.getClass().getSimpleName(), epoch, event); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/VertexStoreFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/VertexStoreFactory.java index a392eff270..f2dba3a775 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/VertexStoreFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/epoch/VertexStoreFactory.java @@ -67,15 +67,14 @@ import com.radixdlt.consensus.bft.VerifiedVertexStoreState; import com.radixdlt.consensus.bft.VertexStore; -/** - * A Vertex Store factory - */ +/** A Vertex Store factory */ public interface VertexStoreFactory { - /** - * Creates a new VertexStore given initial vertex and QC - * @param vertexStoreState the initial vertex store state - * @return a new VertexStore - */ - VertexStore create(VerifiedVertexStoreState vertexStoreState); + /** + * Creates a new VertexStore given initial vertex and QC + * + * @param vertexStoreState the initial vertex store state + * @return a new VertexStore + */ + VertexStore create(VerifiedVertexStoreState vertexStoreState); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/EpochLocalTimeoutOccurrence.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/EpochLocalTimeoutOccurrence.java index 5dd94dfa63..6b8f985b34 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/EpochLocalTimeoutOccurrence.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/EpochLocalTimeoutOccurrence.java @@ -67,36 +67,35 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.epoch.EpochView; -/** - * A timeout which has occurred in the bft node - */ +/** A timeout which has occurred in the bft node */ public final class EpochLocalTimeoutOccurrence { - private final long epoch; - private final LocalTimeoutOccurrence localTimeoutOccurrence; + private final long epoch; + private final LocalTimeoutOccurrence localTimeoutOccurrence; - public EpochLocalTimeoutOccurrence(long epoch, LocalTimeoutOccurrence localTimeoutOccurrence) { - this.epoch = epoch; - this.localTimeoutOccurrence = localTimeoutOccurrence; - } + public EpochLocalTimeoutOccurrence(long epoch, LocalTimeoutOccurrence localTimeoutOccurrence) { + this.epoch = epoch; + this.localTimeoutOccurrence = localTimeoutOccurrence; + } - public EpochView getEpochView() { - return new EpochView(epoch, localTimeoutOccurrence.getView()); - } + public EpochView getEpochView() { + return new EpochView(epoch, localTimeoutOccurrence.getView()); + } - public LocalTimeoutOccurrence getBase() { - return localTimeoutOccurrence; - } + public LocalTimeoutOccurrence getBase() { + return localTimeoutOccurrence; + } - public BFTNode getLeader() { - return localTimeoutOccurrence.getLeader(); - } + public BFTNode getLeader() { + return localTimeoutOccurrence.getLeader(); + } - public BFTNode getNextLeader() { - return localTimeoutOccurrence.getNextLeader(); - } + public BFTNode getNextLeader() { + return localTimeoutOccurrence.getNextLeader(); + } - @Override - public String toString() { - return String.format("%s{epoch=%s event=%s}", this.getClass().getSimpleName(), epoch, localTimeoutOccurrence); - } + @Override + public String toString() { + return String.format( + "%s{epoch=%s event=%s}", this.getClass().getSimpleName(), epoch, localTimeoutOccurrence); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculator.java index babc6c9321..87cfffe2ad 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculator.java @@ -69,43 +69,42 @@ import com.radixdlt.consensus.bft.PacemakerRate; import com.radixdlt.consensus.bft.PacemakerTimeout; -/** - * Timeout calculator which exponentially increases based on number of uncommitted views. - */ +/** Timeout calculator which exponentially increases based on number of uncommitted views. */ public final class ExponentialPacemakerTimeoutCalculator implements PacemakerTimeoutCalculator { - private final long timeoutMilliseconds; - private final double rate; - private final int maxExponent; + private final long timeoutMilliseconds; + private final double rate; + private final int maxExponent; - @Inject - public ExponentialPacemakerTimeoutCalculator( - @PacemakerTimeout long timeoutMilliseconds, - @PacemakerRate double rate, - @PacemakerMaxExponent int maxExponent - ) { - if (timeoutMilliseconds <= 0) { - throw new IllegalArgumentException("timeoutMilliseconds must be > 0 but was " + timeoutMilliseconds); - } - if (rate <= 1.0) { - throw new IllegalArgumentException("rate must be > 1.0, but was " + rate); - } - if (maxExponent < 0) { - throw new IllegalArgumentException("maxExponent must be >= 0, but was " + maxExponent); - } - double maxTimeout = timeoutMilliseconds * Math.pow(rate, maxExponent); - if (maxTimeout > Long.MAX_VALUE) { - throw new IllegalArgumentException("Maximum timeout value of " + maxTimeout + " is too large"); - } + @Inject + public ExponentialPacemakerTimeoutCalculator( + @PacemakerTimeout long timeoutMilliseconds, + @PacemakerRate double rate, + @PacemakerMaxExponent int maxExponent) { + if (timeoutMilliseconds <= 0) { + throw new IllegalArgumentException( + "timeoutMilliseconds must be > 0 but was " + timeoutMilliseconds); + } + if (rate <= 1.0) { + throw new IllegalArgumentException("rate must be > 1.0, but was " + rate); + } + if (maxExponent < 0) { + throw new IllegalArgumentException("maxExponent must be >= 0, but was " + maxExponent); + } + double maxTimeout = timeoutMilliseconds * Math.pow(rate, maxExponent); + if (maxTimeout > Long.MAX_VALUE) { + throw new IllegalArgumentException( + "Maximum timeout value of " + maxTimeout + " is too large"); + } - this.timeoutMilliseconds = timeoutMilliseconds; - this.rate = rate; - this.maxExponent = maxExponent; - } + this.timeoutMilliseconds = timeoutMilliseconds; + this.rate = rate; + this.maxExponent = maxExponent; + } - @Override - public long timeout(long uncommittedViews) { - double exponential = Math.pow(this.rate, Math.min(this.maxExponent, uncommittedViews)); - return Math.round(this.timeoutMilliseconds * exponential); - } + @Override + public long timeout(long uncommittedViews) { + double exponential = Math.pow(this.rate, Math.min(this.maxExponent, uncommittedViews)); + return Math.round(this.timeoutMilliseconds * exponential); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrence.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrence.java index 36de86971e..d2ecf06f9f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrence.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrence.java @@ -68,49 +68,47 @@ import com.radixdlt.consensus.bft.View; import java.util.Objects; -/** - * An actual occurrence (as opposed to a potential) of a local timeout - */ +/** An actual occurrence (as opposed to a potential) of a local timeout */ public final class LocalTimeoutOccurrence { - private final ScheduledLocalTimeout scheduledLocalTimeout; + private final ScheduledLocalTimeout scheduledLocalTimeout; - public LocalTimeoutOccurrence(ScheduledLocalTimeout scheduledLocalTimeout) { - this.scheduledLocalTimeout = Objects.requireNonNull(scheduledLocalTimeout); - } + public LocalTimeoutOccurrence(ScheduledLocalTimeout scheduledLocalTimeout) { + this.scheduledLocalTimeout = Objects.requireNonNull(scheduledLocalTimeout); + } - public ScheduledLocalTimeout timeout() { - return scheduledLocalTimeout; - } + public ScheduledLocalTimeout timeout() { + return scheduledLocalTimeout; + } - public View getView() { - return scheduledLocalTimeout.view(); - } + public View getView() { + return scheduledLocalTimeout.view(); + } - public BFTNode getLeader() { - return scheduledLocalTimeout.viewUpdate().getLeader(); - } + public BFTNode getLeader() { + return scheduledLocalTimeout.viewUpdate().getLeader(); + } - public BFTNode getNextLeader() { - return scheduledLocalTimeout.viewUpdate().getNextLeader(); - } + public BFTNode getNextLeader() { + return scheduledLocalTimeout.viewUpdate().getNextLeader(); + } - @Override - public int hashCode() { - return Objects.hash(scheduledLocalTimeout); - } + @Override + public int hashCode() { + return Objects.hash(scheduledLocalTimeout); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof LocalTimeoutOccurrence)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof LocalTimeoutOccurrence)) { + return false; + } - LocalTimeoutOccurrence other = (LocalTimeoutOccurrence) o; - return Objects.equals(scheduledLocalTimeout, other.scheduledLocalTimeout); - } + LocalTimeoutOccurrence other = (LocalTimeoutOccurrence) o; + return Objects.equals(scheduledLocalTimeout, other.scheduledLocalTimeout); + } - @Override - public String toString() { - return String.format("%s{timeout=%s}", this.getClass().getSimpleName(), scheduledLocalTimeout); - } + @Override + public String toString() { + return String.format("%s{timeout=%s}", this.getClass().getSimpleName(), scheduledLocalTimeout); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/NextTxnsGenerator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/NextTxnsGenerator.java index fadd22679d..05a3546509 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/NextTxnsGenerator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/NextTxnsGenerator.java @@ -67,20 +67,18 @@ import com.radixdlt.atom.Txn; import com.radixdlt.consensus.bft.PreparedVertex; import com.radixdlt.consensus.bft.View; - import java.util.List; -/** - * Generates a new proposed command for a given view - */ +/** Generates a new proposed command for a given view */ public interface NextTxnsGenerator { - /** - * Generates a valid command for the given view - * TODO: Update interface to return an error if already generated a command for a given view - * @param view the view to create the vertex for - * @param prepared vertices with commands which have already been prepared - * @return new command to execute next - */ - List generateNextTxns(View view, List prepared); + /** + * Generates a valid command for the given view TODO: Update interface to return an error if + * already generated a command for a given view + * + * @param view the view to create the vertex for + * @param prepared vertices with commands which have already been prepared + * @return new command to execute next + */ + List generateNextTxns(View view, List prepared); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/Pacemaker.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/Pacemaker.java index bdefa60e35..9e41bccdbb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/Pacemaker.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/Pacemaker.java @@ -68,10 +68,10 @@ import com.google.common.util.concurrent.RateLimiter; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.UnverifiedVertex; -import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTInsertUpdate; import com.radixdlt.consensus.bft.BFTNode; @@ -90,246 +90,246 @@ import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.utils.TimeSupplier; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.List; import java.util.Objects; import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Manages the pacemaker driver. - */ +/** Manages the pacemaker driver. */ public final class Pacemaker { - private static final Logger log = LogManager.getLogger(); - - private final RateLimiter logLimiter = RateLimiter.create(1.0); - - private final BFTNode self; - private final SystemCounters counters; - private final BFTValidatorSet validatorSet; - private final VertexStore vertexStore; - private final SafetyRules safetyRules; - private final ScheduledEventDispatcher timeoutSender; - private final PacemakerTimeoutCalculator timeoutCalculator; - private final NextTxnsGenerator nextTxnsGenerator; - private final Hasher hasher; - private final RemoteEventDispatcher proposalDispatcher; - private final RemoteEventDispatcher voteDispatcher; - private final EventDispatcher timeoutDispatcher; - private final TimeSupplier timeSupplier; - private final SystemCounters systemCounters; - - private ViewUpdate latestViewUpdate; - private boolean isViewTimedOut = false; - private Optional timeoutVoteVertexId = Optional.empty(); - - public Pacemaker( - BFTNode self, - SystemCounters counters, - BFTValidatorSet validatorSet, - VertexStore vertexStore, - SafetyRules safetyRules, - EventDispatcher timeoutDispatcher, - ScheduledEventDispatcher timeoutSender, - PacemakerTimeoutCalculator timeoutCalculator, - NextTxnsGenerator nextTxnsGenerator, - RemoteEventDispatcher proposalDispatcher, - RemoteEventDispatcher voteDispatcher, - Hasher hasher, - TimeSupplier timeSupplier, - ViewUpdate initialViewUpdate, - SystemCounters systemCounters - ) { - this.self = Objects.requireNonNull(self); - this.counters = Objects.requireNonNull(counters); - this.validatorSet = Objects.requireNonNull(validatorSet); - this.vertexStore = Objects.requireNonNull(vertexStore); - this.safetyRules = Objects.requireNonNull(safetyRules); - this.timeoutSender = Objects.requireNonNull(timeoutSender); - this.timeoutDispatcher = Objects.requireNonNull(timeoutDispatcher); - this.timeoutCalculator = Objects.requireNonNull(timeoutCalculator); - this.nextTxnsGenerator = Objects.requireNonNull(nextTxnsGenerator); - this.proposalDispatcher = Objects.requireNonNull(proposalDispatcher); - this.hasher = Objects.requireNonNull(hasher); - this.voteDispatcher = Objects.requireNonNull(voteDispatcher); - this.timeSupplier = Objects.requireNonNull(timeSupplier); - this.latestViewUpdate = Objects.requireNonNull(initialViewUpdate); - this.systemCounters = Objects.requireNonNull(systemCounters); - } - - public void start() { - log.info("Pacemaker Start: {}", latestViewUpdate); - this.startView(); - } - - /** Processes a local view update message **/ - public void processViewUpdate(ViewUpdate viewUpdate) { - log.trace("View Update: {}", viewUpdate); - - final View previousView = this.latestViewUpdate.getCurrentView(); - if (viewUpdate.getCurrentView().lte(previousView)) { - return; - } - this.latestViewUpdate = viewUpdate; - this.systemCounters.set(CounterType.BFT_PACEMAKER_ROUND, viewUpdate.getCurrentView().number()); - - this.startView(); - } - - /** - * Processes a local BFTInsertUpdate message - */ - public void processBFTUpdate(BFTInsertUpdate update) { - /* we only process the insertion of an empty vertex used for timeout vote (see: processLocalTimeout) */ - if (!this.isViewTimedOut - || this.timeoutVoteVertexId.filter(update.getInserted().getId()::equals).isEmpty()) { - return; - } - - this.createAndSendTimeoutVote(update.getInserted()); - } - - private void startView() { - this.isViewTimedOut = false; - this.timeoutVoteVertexId = Optional.empty(); - - long timeout = timeoutCalculator.timeout(latestViewUpdate.uncommittedViewsCount()); - ScheduledLocalTimeout scheduledLocalTimeout = ScheduledLocalTimeout.create(latestViewUpdate, timeout); - this.timeoutSender.dispatch(scheduledLocalTimeout, timeout); - - final BFTNode currentViewProposer = latestViewUpdate.getLeader(); - if (this.self.equals(currentViewProposer)) { - Optional proposalMaybe = generateProposal(latestViewUpdate.getCurrentView()); - proposalMaybe.ifPresent(proposal -> { - log.trace("Broadcasting proposal: {}", proposal); - this.proposalDispatcher.dispatch(this.validatorSet.nodes(), proposal); - this.counters.increment(CounterType.BFT_PACEMAKER_PROPOSALS_SENT); - }); - } - } - - private Optional generateProposal(View view) { - final HighQC highQC = this.latestViewUpdate.getHighQC(); - final QuorumCertificate highestQC = highQC.highestQC(); - - final List nextTxns; - - // Propose null atom in the case that we are at the end of the epoch - // TODO: Remove isEndOfEpoch knowledge from consensus - if (highestQC.getProposed().getLedgerHeader().isEndOfEpoch()) { - nextTxns = List.of(); - } else { - final List preparedVertices = vertexStore.getPathFromRoot(highestQC.getProposed().getVertexId()); - nextTxns = nextTxnsGenerator.generateNextTxns(view, preparedVertices); - systemCounters.add(SystemCounters.CounterType.BFT_PACEMAKER_PROPOSED_TRANSACTIONS, nextTxns.size()); - } - - final UnverifiedVertex proposedVertex = UnverifiedVertex.create(highestQC, view, nextTxns, self); - final VerifiedVertex verifiedVertex = new VerifiedVertex(proposedVertex, hasher.hash(proposedVertex)); - return safetyRules.signProposal( - verifiedVertex, - highQC.highestCommittedQC(), - highQC.highestTC() - ); - } - - /** - * - * Processes a local timeout, causing the pacemaker to - * either broadcast previously sent vote to all nodes - * or broadcast a new vote for a "null" proposal. - * In either case, the sent vote includes a timeout signature, - * which can later be used to form a timeout certificate. - */ - public void processLocalTimeout(ScheduledLocalTimeout scheduledTimeout) { - final var view = scheduledTimeout.view(); - - if (!view.equals(this.latestViewUpdate.getCurrentView())) { - log.trace("LocalTimeout: Ignoring timeout {}, current is {}", scheduledTimeout, this.latestViewUpdate.getCurrentView()); - return; - } - - log.trace("LocalTimeout: {}", scheduledTimeout); - - this.isViewTimedOut = true; - - updateTimeoutCounters(scheduledTimeout); - - this.safetyRules - .getLastVote(view) - .map(this.safetyRules::timeoutVote) - .ifPresentOrElse( - /* if there is a previously sent vote, we time it out and broadcast to all nodes */ - vote -> this.voteDispatcher.dispatch(this.validatorSet.nodes(), vote), - /* otherwise, we asynchronously insert an empty vertex and, when done, - we send a timeout vote on it (see processBFTUpdate) */ - () -> createTimeoutVertexAndSendVote(scheduledTimeout.viewUpdate()) - ); - - rescheduleTimeout(scheduledTimeout); - } - - private void createTimeoutVertexAndSendVote(ViewUpdate viewUpdate) { - if (this.timeoutVoteVertexId.isPresent()) { - return; // vertex for a timeout vote for this view is already inserted - } - - final var highQC = this.latestViewUpdate.getHighQC(); - final var proposedVertex = UnverifiedVertex.createTimeout( - highQC.highestQC(), - viewUpdate.getCurrentView(), - viewUpdate.getLeader() - ); - final var blankVertex = new VerifiedVertex(proposedVertex, hasher.hash(proposedVertex)); - this.timeoutVoteVertexId = Optional.of(blankVertex.getId()); - - // TODO: reimplement in async way - this.vertexStore.getPreparedVertex(blankVertex.getId()).ifPresentOrElse( - this::createAndSendTimeoutVote, // if vertex is already there, send the vote immediately - () -> maybeInsertVertex(blankVertex) // otherwise insert and wait for async bft update msg - ); - } - - // FIXME: This is a temporary fix so that we can continue - // if the vertex store is too far ahead of the pacemaker - private void maybeInsertVertex(VerifiedVertex verifiedVertex) { - try { - this.vertexStore.insertVertex(verifiedVertex); - } catch (MissingParentException e) { - log.debug("Could not insert timeout vertex: {}", e.getMessage()); - } - } - - private void createAndSendTimeoutVote(PreparedVertex preparedVertex) { - final BFTHeader bftHeader = - new BFTHeader(preparedVertex.getView(), preparedVertex.getId(), preparedVertex.getLedgerHeader()); - - final Vote baseVote = this.safetyRules.createVote( - preparedVertex.getVertex(), - bftHeader, - this.timeSupplier.currentTime(), - this.latestViewUpdate.getHighQC()); - - final Vote timeoutVote = this.safetyRules.timeoutVote(baseVote); - - this.voteDispatcher.dispatch(this.validatorSet.nodes(), timeoutVote); - } - - private void updateTimeoutCounters(ScheduledLocalTimeout scheduledTimeout) { - if (scheduledTimeout.count() == 0) { - counters.increment(CounterType.BFT_PACEMAKER_TIMED_OUT_ROUNDS); - } - counters.increment(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); - } - - private void rescheduleTimeout(ScheduledLocalTimeout scheduledTimeout) { - final LocalTimeoutOccurrence localTimeoutOccurrence = new LocalTimeoutOccurrence(scheduledTimeout); - this.timeoutDispatcher.dispatch(localTimeoutOccurrence); - - final long timeout = timeoutCalculator.timeout(latestViewUpdate.uncommittedViewsCount()); - final ScheduledLocalTimeout nextTimeout = scheduledTimeout.nextRetry(timeout); - this.timeoutSender.dispatch(nextTimeout, timeout); - } + private static final Logger log = LogManager.getLogger(); + + private final RateLimiter logLimiter = RateLimiter.create(1.0); + + private final BFTNode self; + private final SystemCounters counters; + private final BFTValidatorSet validatorSet; + private final VertexStore vertexStore; + private final SafetyRules safetyRules; + private final ScheduledEventDispatcher timeoutSender; + private final PacemakerTimeoutCalculator timeoutCalculator; + private final NextTxnsGenerator nextTxnsGenerator; + private final Hasher hasher; + private final RemoteEventDispatcher proposalDispatcher; + private final RemoteEventDispatcher voteDispatcher; + private final EventDispatcher timeoutDispatcher; + private final TimeSupplier timeSupplier; + private final SystemCounters systemCounters; + + private ViewUpdate latestViewUpdate; + private boolean isViewTimedOut = false; + private Optional timeoutVoteVertexId = Optional.empty(); + + public Pacemaker( + BFTNode self, + SystemCounters counters, + BFTValidatorSet validatorSet, + VertexStore vertexStore, + SafetyRules safetyRules, + EventDispatcher timeoutDispatcher, + ScheduledEventDispatcher timeoutSender, + PacemakerTimeoutCalculator timeoutCalculator, + NextTxnsGenerator nextTxnsGenerator, + RemoteEventDispatcher proposalDispatcher, + RemoteEventDispatcher voteDispatcher, + Hasher hasher, + TimeSupplier timeSupplier, + ViewUpdate initialViewUpdate, + SystemCounters systemCounters) { + this.self = Objects.requireNonNull(self); + this.counters = Objects.requireNonNull(counters); + this.validatorSet = Objects.requireNonNull(validatorSet); + this.vertexStore = Objects.requireNonNull(vertexStore); + this.safetyRules = Objects.requireNonNull(safetyRules); + this.timeoutSender = Objects.requireNonNull(timeoutSender); + this.timeoutDispatcher = Objects.requireNonNull(timeoutDispatcher); + this.timeoutCalculator = Objects.requireNonNull(timeoutCalculator); + this.nextTxnsGenerator = Objects.requireNonNull(nextTxnsGenerator); + this.proposalDispatcher = Objects.requireNonNull(proposalDispatcher); + this.hasher = Objects.requireNonNull(hasher); + this.voteDispatcher = Objects.requireNonNull(voteDispatcher); + this.timeSupplier = Objects.requireNonNull(timeSupplier); + this.latestViewUpdate = Objects.requireNonNull(initialViewUpdate); + this.systemCounters = Objects.requireNonNull(systemCounters); + } + + public void start() { + log.info("Pacemaker Start: {}", latestViewUpdate); + this.startView(); + } + + /** Processes a local view update message * */ + public void processViewUpdate(ViewUpdate viewUpdate) { + log.trace("View Update: {}", viewUpdate); + + final View previousView = this.latestViewUpdate.getCurrentView(); + if (viewUpdate.getCurrentView().lte(previousView)) { + return; + } + this.latestViewUpdate = viewUpdate; + this.systemCounters.set(CounterType.BFT_PACEMAKER_ROUND, viewUpdate.getCurrentView().number()); + + this.startView(); + } + + /** Processes a local BFTInsertUpdate message */ + public void processBFTUpdate(BFTInsertUpdate update) { + /* we only process the insertion of an empty vertex used for timeout vote (see: processLocalTimeout) */ + if (!this.isViewTimedOut + || this.timeoutVoteVertexId.filter(update.getInserted().getId()::equals).isEmpty()) { + return; + } + + this.createAndSendTimeoutVote(update.getInserted()); + } + + private void startView() { + this.isViewTimedOut = false; + this.timeoutVoteVertexId = Optional.empty(); + + long timeout = timeoutCalculator.timeout(latestViewUpdate.uncommittedViewsCount()); + ScheduledLocalTimeout scheduledLocalTimeout = + ScheduledLocalTimeout.create(latestViewUpdate, timeout); + this.timeoutSender.dispatch(scheduledLocalTimeout, timeout); + + final BFTNode currentViewProposer = latestViewUpdate.getLeader(); + if (this.self.equals(currentViewProposer)) { + Optional proposalMaybe = generateProposal(latestViewUpdate.getCurrentView()); + proposalMaybe.ifPresent( + proposal -> { + log.trace("Broadcasting proposal: {}", proposal); + this.proposalDispatcher.dispatch(this.validatorSet.nodes(), proposal); + this.counters.increment(CounterType.BFT_PACEMAKER_PROPOSALS_SENT); + }); + } + } + + private Optional generateProposal(View view) { + final HighQC highQC = this.latestViewUpdate.getHighQC(); + final QuorumCertificate highestQC = highQC.highestQC(); + + final List nextTxns; + + // Propose null atom in the case that we are at the end of the epoch + // TODO: Remove isEndOfEpoch knowledge from consensus + if (highestQC.getProposed().getLedgerHeader().isEndOfEpoch()) { + nextTxns = List.of(); + } else { + final List preparedVertices = + vertexStore.getPathFromRoot(highestQC.getProposed().getVertexId()); + nextTxns = nextTxnsGenerator.generateNextTxns(view, preparedVertices); + systemCounters.add( + SystemCounters.CounterType.BFT_PACEMAKER_PROPOSED_TRANSACTIONS, nextTxns.size()); + } + + final UnverifiedVertex proposedVertex = + UnverifiedVertex.create(highestQC, view, nextTxns, self); + final VerifiedVertex verifiedVertex = + new VerifiedVertex(proposedVertex, hasher.hash(proposedVertex)); + return safetyRules.signProposal( + verifiedVertex, highQC.highestCommittedQC(), highQC.highestTC()); + } + + /** + * Processes a local timeout, causing the pacemaker to either broadcast previously sent vote to + * all nodes or broadcast a new vote for a "null" proposal. In either case, the sent vote includes + * a timeout signature, which can later be used to form a timeout certificate. + */ + public void processLocalTimeout(ScheduledLocalTimeout scheduledTimeout) { + final var view = scheduledTimeout.view(); + + if (!view.equals(this.latestViewUpdate.getCurrentView())) { + log.trace( + "LocalTimeout: Ignoring timeout {}, current is {}", + scheduledTimeout, + this.latestViewUpdate.getCurrentView()); + return; + } + + log.trace("LocalTimeout: {}", scheduledTimeout); + + this.isViewTimedOut = true; + + updateTimeoutCounters(scheduledTimeout); + + this.safetyRules + .getLastVote(view) + .map(this.safetyRules::timeoutVote) + .ifPresentOrElse( + /* if there is a previously sent vote, we time it out and broadcast to all nodes */ + vote -> this.voteDispatcher.dispatch(this.validatorSet.nodes(), vote), + /* otherwise, we asynchronously insert an empty vertex and, when done, + we send a timeout vote on it (see processBFTUpdate) */ + () -> createTimeoutVertexAndSendVote(scheduledTimeout.viewUpdate())); + + rescheduleTimeout(scheduledTimeout); + } + + private void createTimeoutVertexAndSendVote(ViewUpdate viewUpdate) { + if (this.timeoutVoteVertexId.isPresent()) { + return; // vertex for a timeout vote for this view is already inserted + } + + final var highQC = this.latestViewUpdate.getHighQC(); + final var proposedVertex = + UnverifiedVertex.createTimeout( + highQC.highestQC(), viewUpdate.getCurrentView(), viewUpdate.getLeader()); + final var blankVertex = new VerifiedVertex(proposedVertex, hasher.hash(proposedVertex)); + this.timeoutVoteVertexId = Optional.of(blankVertex.getId()); + + // TODO: reimplement in async way + this.vertexStore + .getPreparedVertex(blankVertex.getId()) + .ifPresentOrElse( + this::createAndSendTimeoutVote, // if vertex is already there, send the vote immediately + () -> + maybeInsertVertex(blankVertex) // otherwise insert and wait for async bft update msg + ); + } + + // FIXME: This is a temporary fix so that we can continue + // if the vertex store is too far ahead of the pacemaker + private void maybeInsertVertex(VerifiedVertex verifiedVertex) { + try { + this.vertexStore.insertVertex(verifiedVertex); + } catch (MissingParentException e) { + log.debug("Could not insert timeout vertex: {}", e.getMessage()); + } + } + + private void createAndSendTimeoutVote(PreparedVertex preparedVertex) { + final BFTHeader bftHeader = + new BFTHeader( + preparedVertex.getView(), preparedVertex.getId(), preparedVertex.getLedgerHeader()); + + final Vote baseVote = + this.safetyRules.createVote( + preparedVertex.getVertex(), + bftHeader, + this.timeSupplier.currentTime(), + this.latestViewUpdate.getHighQC()); + + final Vote timeoutVote = this.safetyRules.timeoutVote(baseVote); + + this.voteDispatcher.dispatch(this.validatorSet.nodes(), timeoutVote); + } + + private void updateTimeoutCounters(ScheduledLocalTimeout scheduledTimeout) { + if (scheduledTimeout.count() == 0) { + counters.increment(CounterType.BFT_PACEMAKER_TIMED_OUT_ROUNDS); + } + counters.increment(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); + } + + private void rescheduleTimeout(ScheduledLocalTimeout scheduledTimeout) { + final LocalTimeoutOccurrence localTimeoutOccurrence = + new LocalTimeoutOccurrence(scheduledTimeout); + this.timeoutDispatcher.dispatch(localTimeoutOccurrence); + + final long timeout = timeoutCalculator.timeout(latestViewUpdate.uncommittedViewsCount()); + final ScheduledLocalTimeout nextTimeout = scheduledTimeout.nextRetry(timeout); + this.timeoutSender.dispatch(nextTimeout, timeout); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerFactory.java index 1a97da76ff..e64926344d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerFactory.java @@ -69,22 +69,19 @@ import com.radixdlt.consensus.bft.ViewUpdate; import com.radixdlt.consensus.safety.SafetyRules; -/** - * Pacemaker factory - */ +/** Pacemaker factory */ public interface PacemakerFactory { - /** - * Creates a new clean pacemaker. - * - * @return a new pacemaker - */ - Pacemaker create( - BFTValidatorSet validatorSet, - VertexStore vertexStore, - PacemakerTimeoutCalculator timeoutCalculator, - SafetyRules safetyRules, - ViewUpdate initialViewUpdate, - long nextEpoch - ); + /** + * Creates a new clean pacemaker. + * + * @return a new pacemaker + */ + Pacemaker create( + BFTValidatorSet validatorSet, + VertexStore vertexStore, + PacemakerTimeoutCalculator timeoutCalculator, + SafetyRules safetyRules, + ViewUpdate initialViewUpdate, + long nextEpoch); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerReducer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerReducer.java index 1a720a6fc5..b3248ca55e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerReducer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerReducer.java @@ -68,22 +68,22 @@ import com.radixdlt.consensus.bft.View; /** - * Reduces state for a pacemaker given some events - * TODO: This is currently hack, should move to a more message based interface + * Reduces state for a pacemaker given some events TODO: This is currently hack, should move to a + * more message based interface */ public interface PacemakerReducer { - /** - * Signifies to the pacemaker that a quorum has agreed that a view has - * been completed. - * - * @param highQC the sync info for the view - */ - void processQC(HighQC highQC); + /** + * Signifies to the pacemaker that a quorum has agreed that a view has been completed. + * + * @param highQC the sync info for the view + */ + void processQC(HighQC highQC); - /** - * Signifies to the pacemaker that it should move to the next view - * TODO: Replace or combine with processQC on implementation of timeout quorum - * @param nextView the view to move to - */ - void updateView(View nextView); + /** + * Signifies to the pacemaker that it should move to the next view TODO: Replace or combine with + * processQC on implementation of timeout quorum + * + * @param nextView the view to move to + */ + void updateView(View nextView); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerState.java index 1711a24a23..0af90b06af 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerState.java @@ -70,65 +70,56 @@ import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.bft.ViewUpdate; import com.radixdlt.environment.EventDispatcher; +import java.util.Objects; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Objects; - /** - * This class is responsible for keeping track of current consensus view state. - * It sends an internal ViewUpdate message on a transition to next view. + * This class is responsible for keeping track of current consensus view state. It sends an internal + * ViewUpdate message on a transition to next view. */ public class PacemakerState implements PacemakerReducer { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - private final EventDispatcher viewUpdateSender; - private final ProposerElection proposerElection; + private final EventDispatcher viewUpdateSender; + private final ProposerElection proposerElection; - private View currentView; - private HighQC highQC; + private View currentView; + private HighQC highQC; - @Inject - public PacemakerState( - ViewUpdate viewUpdate, - ProposerElection proposerElection, - EventDispatcher viewUpdateSender - ) { - this.proposerElection = Objects.requireNonNull(proposerElection); - this.viewUpdateSender = Objects.requireNonNull(viewUpdateSender); - this.highQC = viewUpdate.getHighQC(); - this.currentView = viewUpdate.getCurrentView(); - } + @Inject + public PacemakerState( + ViewUpdate viewUpdate, + ProposerElection proposerElection, + EventDispatcher viewUpdateSender) { + this.proposerElection = Objects.requireNonNull(proposerElection); + this.viewUpdateSender = Objects.requireNonNull(viewUpdateSender); + this.highQC = viewUpdate.getHighQC(); + this.currentView = viewUpdate.getCurrentView(); + } - @Override - public void processQC(HighQC highQC) { - log.trace("QuorumCertificate: {}", highQC); + @Override + public void processQC(HighQC highQC) { + log.trace("QuorumCertificate: {}", highQC); - final View view = highQC.getHighestView(); - if (view.gte(this.currentView)) { - this.highQC = highQC; - this.updateView(view.next()); - } else { - log.trace("Ignoring QC for view {}: current view is {}", view, this.currentView); - } - } + final View view = highQC.getHighestView(); + if (view.gte(this.currentView)) { + this.highQC = highQC; + this.updateView(view.next()); + } else { + log.trace("Ignoring QC for view {}: current view is {}", view, this.currentView); + } + } - @Override - public void updateView(View nextView) { - if (nextView.lte(this.currentView)) { - return; - } + @Override + public void updateView(View nextView) { + if (nextView.lte(this.currentView)) { + return; + } - final BFTNode leader = this.proposerElection.getProposer(nextView); - final BFTNode nextLeader = this.proposerElection.getProposer(nextView.next()); - this.currentView = nextView; - viewUpdateSender.dispatch( - ViewUpdate.create( - this.currentView, - this.highQC, - leader, - nextLeader - ) - ); - } + final BFTNode leader = this.proposerElection.getProposer(nextView); + final BFTNode nextLeader = this.proposerElection.getProposer(nextView.next()); + this.currentView = nextView; + viewUpdateSender.dispatch(ViewUpdate.create(this.currentView, this.highQC, leader, nextLeader)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerStateFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerStateFactory.java index eb6eaaaeee..6f44ec68bc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerStateFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerStateFactory.java @@ -66,15 +66,13 @@ import com.radixdlt.consensus.bft.ViewUpdate; -/** - * Pacemaker state factory - */ +/** Pacemaker state factory */ public interface PacemakerStateFactory { - /** - * Creates a new clean pacemaker state. - * - * @return a new pacemaker state - */ - PacemakerState create(ViewUpdate initialView, long epoch, ProposerElection proposerElection); + /** + * Creates a new clean pacemaker state. + * + * @return a new pacemaker state + */ + PacemakerState create(ViewUpdate initialView, long epoch, ProposerElection proposerElection); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerTimeoutCalculator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerTimeoutCalculator.java index a376c61529..0924bb6344 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerTimeoutCalculator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/PacemakerTimeoutCalculator.java @@ -66,11 +66,11 @@ public interface PacemakerTimeoutCalculator { - /** - * Calculates the pacemaker view timeout. - * - * @param uncommittedViews the number of uncommitted views - * @return pacemaker view timeout in milliseconds - */ - long timeout(long uncommittedViews); + /** + * Calculates the pacemaker view timeout. + * + * @param uncommittedViews the number of uncommitted views + * @return pacemaker view timeout in milliseconds + */ + long timeout(long uncommittedViews); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ProposerElection.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ProposerElection.java index fa298e7d0f..e57af21ceb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ProposerElection.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ProposerElection.java @@ -1,80 +1,79 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus.liveness; - -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.View; - -/** - * Represents the election for valid proposers - */ -public interface ProposerElection { - /** - * Retrieve the deterministic proposer for a given view - * @param view the view to get the proposer for - * @return the EUID of the proposer - */ - BFTNode getProposer(View view); -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus.liveness; + +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.View; + +/** Represents the election for valid proposers */ +public interface ProposerElection { + /** + * Retrieve the deterministic proposer for a given view + * + * @param view the view to get the proposer for + * @return the EUID of the proposer + */ + BFTNode getProposer(View view); +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeout.java index 64834cc8aa..7ce6efbe8a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeout.java @@ -68,71 +68,62 @@ import com.radixdlt.consensus.bft.ViewUpdate; import java.util.Objects; -/** - * A potential timeout that is scheduled - */ +/** A potential timeout that is scheduled */ public final class ScheduledLocalTimeout { - private final ViewUpdate viewUpdate; - private final long millisecondsWaitTime; - private final int count; + private final ViewUpdate viewUpdate; + private final long millisecondsWaitTime; + private final int count; - private ScheduledLocalTimeout( - ViewUpdate viewUpdate, - long millisecondsWaitTime, - int count - ) { - this.viewUpdate = viewUpdate; - this.millisecondsWaitTime = millisecondsWaitTime; - this.count = count; - } + private ScheduledLocalTimeout(ViewUpdate viewUpdate, long millisecondsWaitTime, int count) { + this.viewUpdate = viewUpdate; + this.millisecondsWaitTime = millisecondsWaitTime; + this.count = count; + } - public static ScheduledLocalTimeout create(ViewUpdate viewUpdate, long millisecondsWaitTime) { - return new ScheduledLocalTimeout(viewUpdate, millisecondsWaitTime, 0); - } + public static ScheduledLocalTimeout create(ViewUpdate viewUpdate, long millisecondsWaitTime) { + return new ScheduledLocalTimeout(viewUpdate, millisecondsWaitTime, 0); + } - public ScheduledLocalTimeout nextRetry(long millisecondsWaitTime) { - return new ScheduledLocalTimeout( - viewUpdate, - millisecondsWaitTime, - this.count + 1 - ); - } + public ScheduledLocalTimeout nextRetry(long millisecondsWaitTime) { + return new ScheduledLocalTimeout(viewUpdate, millisecondsWaitTime, this.count + 1); + } - public int count() { - return count; - } + public int count() { + return count; + } - public ViewUpdate viewUpdate() { - return viewUpdate; - } + public ViewUpdate viewUpdate() { + return viewUpdate; + } - public View view() { - return viewUpdate.getCurrentView(); - } + public View view() { + return viewUpdate.getCurrentView(); + } - public long millisecondsWaitTime() { - return millisecondsWaitTime; - } + public long millisecondsWaitTime() { + return millisecondsWaitTime; + } - @Override - public int hashCode() { - return Objects.hash(viewUpdate, millisecondsWaitTime, count); - } + @Override + public int hashCode() { + return Objects.hash(viewUpdate, millisecondsWaitTime, count); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ScheduledLocalTimeout)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ScheduledLocalTimeout)) { + return false; + } - ScheduledLocalTimeout other = (ScheduledLocalTimeout) o; - return Objects.equals(other.viewUpdate, this.viewUpdate) - && other.millisecondsWaitTime == this.millisecondsWaitTime - && other.count == this.count; - } + ScheduledLocalTimeout other = (ScheduledLocalTimeout) o; + return Objects.equals(other.viewUpdate, this.viewUpdate) + && other.millisecondsWaitTime == this.millisecondsWaitTime + && other.count == this.count; + } - @Override - public String toString() { - return String.format("%s{view=%s count=%s}", this.getClass().getSimpleName(), viewUpdate, count); - } + @Override + public String toString() { + return String.format( + "%s{view=%s count=%s}", this.getClass().getSimpleName(), viewUpdate, count); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/VoteTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/VoteTimeout.java index 79aa883697..11f22e06f6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/VoteTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/VoteTimeout.java @@ -73,81 +73,73 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; -/** - * Represents a vote timeout; data to sign for a timeout signature. - */ +/** Represents a vote timeout; data to sign for a timeout signature. */ @Immutable @SerializerId2("consensus.vote_timeout") public final class VoteTimeout { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {DsonOutput.Output.API, DsonOutput.Output.WIRE, DsonOutput.Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {DsonOutput.Output.API, DsonOutput.Output.WIRE, DsonOutput.Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; - private final View view; + private final View view; - @JsonProperty("epoch") - @DsonOutput(DsonOutput.Output.ALL) - private final long epoch; + @JsonProperty("epoch") + @DsonOutput(DsonOutput.Output.ALL) + private final long epoch; - @JsonCreator - public VoteTimeout( - @JsonProperty("view") long view, - @JsonProperty("epoch") long epoch - ) { - this(View.of(view), epoch); - } + @JsonCreator + public VoteTimeout(@JsonProperty("view") long view, @JsonProperty("epoch") long epoch) { + this(View.of(view), epoch); + } - public VoteTimeout(View view, long epoch) { - if (epoch < 0) { - throw new IllegalArgumentException("Epoch can't be < 0"); - } - - this.view = Objects.requireNonNull(view); - this.epoch = epoch; + public VoteTimeout(View view, long epoch) { + if (epoch < 0) { + throw new IllegalArgumentException("Epoch can't be < 0"); } - public static VoteTimeout of(Vote vote) { - return new VoteTimeout(vote.getView(), vote.getEpoch()); - } + this.view = Objects.requireNonNull(view); + this.epoch = epoch; + } - public View getView() { - return view; - } + public static VoteTimeout of(Vote vote) { + return new VoteTimeout(vote.getView(), vote.getEpoch()); + } - public long getEpoch() { - return epoch; - } + public View getView() { + return view; + } - @JsonProperty("view") - @DsonOutput(DsonOutput.Output.ALL) - private Long getSerializerView() { - return this.view.number(); - } + public long getEpoch() { + return epoch; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - VoteTimeout that = (VoteTimeout) o; - return epoch == that.epoch - && Objects.equals(view, that.view); - } + @JsonProperty("view") + @DsonOutput(DsonOutput.Output.ALL) + private Long getSerializerView() { + return this.view.number(); + } - @Override - public int hashCode() { - return Objects.hash(view, epoch); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public String toString() { - return String.format("%s{epoch=%s view=%s}", - getClass().getSimpleName(), getEpoch(), getView()); + if (o == null || getClass() != o.getClass()) { + return false; } + VoteTimeout that = (VoteTimeout) o; + return epoch == that.epoch && Objects.equals(view, that.view); + } + + @Override + public int hashCode() { + return Objects.hash(view, epoch); + } + + @Override + public String toString() { + return String.format("%s{epoch=%s view=%s}", getClass().getSimpleName(), getEpoch(), getView()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/WeightedRotatingLeaders.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/WeightedRotatingLeaders.java index 6fa9f6efb6..f92329c798 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/WeightedRotatingLeaders.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/liveness/WeightedRotatingLeaders.java @@ -64,10 +64,10 @@ package com.radixdlt.consensus.liveness; -import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidator; import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.View; import com.radixdlt.utils.KeyComparator; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt256s; @@ -79,154 +79,156 @@ import java.util.Map.Entry; /** - * Rotates leaders with those having more power being proposed more often - * in proportion to the amount of power they have. + * Rotates leaders with those having more power being proposed more often in proportion to the + * amount of power they have. * - * Calculation of the next leader is dependent on the weight state of the - * previous view and thus computing the leader for an arbitrary view can - * be quite expensive. + *

Calculation of the next leader is dependent on the weight state of the previous view and thus + * computing the leader for an arbitrary view can be quite expensive. * - * We resolve this by keeping a cache of some given size of the previous - * views closest to the highest view calculated. + *

We resolve this by keeping a cache of some given size of the previous views closest to the + * highest view calculated. * - * This class stateful and is NOT thread-safe. + *

This class stateful and is NOT thread-safe. */ public final class WeightedRotatingLeaders implements ProposerElection { - private static final int DEFAULT_CACHE_SIZE = 10; - private static final UInt384 POW_2_256 = UInt384.from(UInt256.MAX_VALUE).increment(); - - private final BFTValidatorSet validatorSet; - private final Comparator> weightsComparator; - private final CachingNextLeaderComputer nextLeaderComputer; - - public WeightedRotatingLeaders(BFTValidatorSet validatorSet) { - this(validatorSet, DEFAULT_CACHE_SIZE); - } - - public WeightedRotatingLeaders(BFTValidatorSet validatorSet, int cacheSize) { - this.validatorSet = validatorSet; - this.weightsComparator = Comparator - .comparing(Entry::getValue) - .thenComparing(v -> v.getKey().getNode().getKey(), KeyComparator.instance().reversed()); - this.nextLeaderComputer = new CachingNextLeaderComputer(validatorSet, weightsComparator, cacheSize); - } - - private static class CachingNextLeaderComputer { - private final BFTValidatorSet validatorSet; - private final Comparator> weightsComparator; - private final Map weights; - private final BFTValidator[] cache; - private final Long lcm; - private View curView; - - private CachingNextLeaderComputer( - BFTValidatorSet validatorSet, - Comparator> weightsComparator, - int cacheSize - ) { - this.validatorSet = validatorSet; - this.weightsComparator = weightsComparator; - this.weights = new HashMap<>(); - this.cache = new BFTValidator[cacheSize]; - - UInt256[] powerArray = validatorSet.getValidators().stream().map(BFTValidator::getPower).toArray(UInt256[]::new); - // after cappedLCM is executed, the following invariant will be true: - // (lcm > 0 && lcm < 2^63 -1 ) || lcm == null - // This is due to use of 2^63 - 1 cap and also the invariant from ValidatorSet - // that powerArray will always be non-zero - UInt256 lcm256 = UInt256s.cappedLCM(UInt256.from(Long.MAX_VALUE), powerArray); - this.lcm = lcm256 == null ? null : lcm256.getLow().getLow(); - - this.resetToView(View.of(0)); - } - - private BFTValidator computeHeaviest() { - final Entry max = weights.entrySet().stream() - .max(weightsComparator) - .orElseThrow(() -> new IllegalStateException("Weights cannot be empty")); - return max.getKey(); - } - - private void computeNext() { - // Reset current leader by subtracting total power - final int curIndex = (int) (this.curView.number() % cache.length); - final BFTValidator curLeader = cache[curIndex]; - weights.merge(curLeader, UInt384.from(validatorSet.getTotalPower()), UInt384::subtract); - - // Add weights relative to each validator's power - for (BFTValidator validator : validatorSet.getValidators()) { - weights.merge(validator, UInt384.from(validator.getPower()), UInt384::add); - } - - // Compute next leader by getting heaviest validator - this.curView = this.curView.next(); - int index = (int) (this.curView.number() % cache.length); - cache[index] = computeHeaviest(); - } - - private BFTValidator checkCacheForProposer(View view) { - if (view.compareTo(curView) <= 0 && view.number() > curView.number() - cache.length) { - final int index = (int) (view.number() % cache.length); - return cache[index]; - } - - return null; - } - - private void computeToView(View view) { - while (view.compareTo(curView) > 0) { - computeNext(); - } - } - - private BFTValidator resetToView(View view) { - // reset if view isn't in cache - if (curView == null || view.number() < curView.number() - cache.length) { - if (lcm == null || lcm > view.number()) { - curView = View.genesis(); - } else { - long multipleOfLCM = view.number() / lcm; - curView = View.of(multipleOfLCM * lcm); - } - - for (BFTValidator validator : validatorSet.getValidators()) { - weights.put(validator, POW_2_256.subtract(validator.getPower())); - } - cache[0] = computeHeaviest(); - } - - // compute to view - computeToView(view); - - // guaranteed to return non-null; - return cache[(int) (view.number() % cache.length)]; - } - - @Override - public String toString() { - return String.format("%s %s %s", this.curView, Arrays.toString(this.cache), this.weights); - } - } - - @Override - public BFTNode getProposer(View view) { - nextLeaderComputer.computeToView(view); - - // validator will only be null if the view supplied is before the cache - // window - BFTValidator validator = nextLeaderComputer.checkCacheForProposer(view); - if (validator != null) { - // dynamic program cache successful - return validator.getNode(); - } else { - // cache doesn't have value, do the expensive operation - CachingNextLeaderComputer computer = new CachingNextLeaderComputer(validatorSet, weightsComparator, 1); - return computer.resetToView(view).getNode(); - } - } - - @Override - public String toString() { - return String.format("%s %s", this.getClass().getSimpleName(), this.nextLeaderComputer); - } -} \ No newline at end of file + private static final int DEFAULT_CACHE_SIZE = 10; + private static final UInt384 POW_2_256 = UInt384.from(UInt256.MAX_VALUE).increment(); + + private final BFTValidatorSet validatorSet; + private final Comparator> weightsComparator; + private final CachingNextLeaderComputer nextLeaderComputer; + + public WeightedRotatingLeaders(BFTValidatorSet validatorSet) { + this(validatorSet, DEFAULT_CACHE_SIZE); + } + + public WeightedRotatingLeaders(BFTValidatorSet validatorSet, int cacheSize) { + this.validatorSet = validatorSet; + this.weightsComparator = + Comparator.comparing(Entry::getValue) + .thenComparing(v -> v.getKey().getNode().getKey(), KeyComparator.instance().reversed()); + this.nextLeaderComputer = + new CachingNextLeaderComputer(validatorSet, weightsComparator, cacheSize); + } + + private static class CachingNextLeaderComputer { + private final BFTValidatorSet validatorSet; + private final Comparator> weightsComparator; + private final Map weights; + private final BFTValidator[] cache; + private final Long lcm; + private View curView; + + private CachingNextLeaderComputer( + BFTValidatorSet validatorSet, + Comparator> weightsComparator, + int cacheSize) { + this.validatorSet = validatorSet; + this.weightsComparator = weightsComparator; + this.weights = new HashMap<>(); + this.cache = new BFTValidator[cacheSize]; + + UInt256[] powerArray = + validatorSet.getValidators().stream().map(BFTValidator::getPower).toArray(UInt256[]::new); + // after cappedLCM is executed, the following invariant will be true: + // (lcm > 0 && lcm < 2^63 -1 ) || lcm == null + // This is due to use of 2^63 - 1 cap and also the invariant from ValidatorSet + // that powerArray will always be non-zero + UInt256 lcm256 = UInt256s.cappedLCM(UInt256.from(Long.MAX_VALUE), powerArray); + this.lcm = lcm256 == null ? null : lcm256.getLow().getLow(); + + this.resetToView(View.of(0)); + } + + private BFTValidator computeHeaviest() { + final Entry max = + weights.entrySet().stream() + .max(weightsComparator) + .orElseThrow(() -> new IllegalStateException("Weights cannot be empty")); + return max.getKey(); + } + + private void computeNext() { + // Reset current leader by subtracting total power + final int curIndex = (int) (this.curView.number() % cache.length); + final BFTValidator curLeader = cache[curIndex]; + weights.merge(curLeader, UInt384.from(validatorSet.getTotalPower()), UInt384::subtract); + + // Add weights relative to each validator's power + for (BFTValidator validator : validatorSet.getValidators()) { + weights.merge(validator, UInt384.from(validator.getPower()), UInt384::add); + } + + // Compute next leader by getting heaviest validator + this.curView = this.curView.next(); + int index = (int) (this.curView.number() % cache.length); + cache[index] = computeHeaviest(); + } + + private BFTValidator checkCacheForProposer(View view) { + if (view.compareTo(curView) <= 0 && view.number() > curView.number() - cache.length) { + final int index = (int) (view.number() % cache.length); + return cache[index]; + } + + return null; + } + + private void computeToView(View view) { + while (view.compareTo(curView) > 0) { + computeNext(); + } + } + + private BFTValidator resetToView(View view) { + // reset if view isn't in cache + if (curView == null || view.number() < curView.number() - cache.length) { + if (lcm == null || lcm > view.number()) { + curView = View.genesis(); + } else { + long multipleOfLCM = view.number() / lcm; + curView = View.of(multipleOfLCM * lcm); + } + + for (BFTValidator validator : validatorSet.getValidators()) { + weights.put(validator, POW_2_256.subtract(validator.getPower())); + } + cache[0] = computeHeaviest(); + } + + // compute to view + computeToView(view); + + // guaranteed to return non-null; + return cache[(int) (view.number() % cache.length)]; + } + + @Override + public String toString() { + return String.format("%s %s %s", this.curView, Arrays.toString(this.cache), this.weights); + } + } + + @Override + public BFTNode getProposer(View view) { + nextLeaderComputer.computeToView(view); + + // validator will only be null if the view supplied is before the cache + // window + BFTValidator validator = nextLeaderComputer.checkCacheForProposer(view); + if (validator != null) { + // dynamic program cache successful + return validator.getNode(); + } else { + // cache doesn't have value, do the expensive operation + CachingNextLeaderComputer computer = + new CachingNextLeaderComputer(validatorSet, weightsComparator, 1); + return computer.resetToView(view).getNode(); + } + } + + @Override + public String toString() { + return String.format("%s %s", this.getClass().getSimpleName(), this.nextLeaderComputer); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/PersistentSafetyStateStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/PersistentSafetyStateStore.java index a6175c249a..afc9344943 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/PersistentSafetyStateStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/PersistentSafetyStateStore.java @@ -66,11 +66,11 @@ import java.util.Optional; -/** - * Responsible for synchronously persisting safety state - */ +/** Responsible for synchronously persisting safety state */ public interface PersistentSafetyStateStore { - void commitState(SafetyState safetyState); - void close(); - Optional get(); + void commitState(SafetyState safetyState); + + void close(); + + Optional get(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyRules.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyRules.java index 67c1d80c85..8ea4ffa6c2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyRules.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyRules.java @@ -66,194 +66,182 @@ import com.google.common.hash.HashCode; import com.google.inject.Inject; -import com.radixdlt.consensus.TimeoutCertificate; -import com.radixdlt.consensus.bft.Self; -import com.radixdlt.consensus.bft.VerifiedVertex; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.liveness.VoteTimeout; -import com.radixdlt.crypto.Hasher; +import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HashSigner; +import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.VoteData; import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.Self; +import com.radixdlt.consensus.bft.VerifiedVertex; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.liveness.VoteTimeout; import com.radixdlt.consensus.safety.SafetyState.Builder; import com.radixdlt.crypto.ECDSASignature; - +import com.radixdlt.crypto.Hasher; import java.util.Objects; import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Manages safety of the protocol. - */ +/** Manages safety of the protocol. */ public final class SafetyRules { - private static final Logger logger = LogManager.getLogger(); - - private final BFTNode self; - private final Hasher hasher; - private final HashSigner signer; - private final PersistentSafetyStateStore persistentSafetyStateStore; - - private SafetyState state; - - @Inject - public SafetyRules( - @Self BFTNode self, - SafetyState initialState, - PersistentSafetyStateStore persistentSafetyStateStore, - Hasher hasher, - HashSigner signer - ) { - this.self = self; - this.state = Objects.requireNonNull(initialState); - this.persistentSafetyStateStore = Objects.requireNonNull(persistentSafetyStateStore); - this.hasher = Objects.requireNonNull(hasher); - this.signer = Objects.requireNonNull(signer); - } - - private boolean checkLastVoted(VerifiedVertex proposedVertex) { - // ensure vertex does not violate earlier votes - if (proposedVertex.getView().lte(this.state.getLastVotedView())) { - logger.warn("Safety warning: Vertex {} violates earlier vote at view {}", - proposedVertex, - this.state.getLastVotedView() - ); - return false; - } else { - return true; - } - } - - private boolean checkLocked(VerifiedVertex proposedVertex, Builder nextStateBuilder) { - if (proposedVertex.getParentHeader().getView().lt(this.state.getLockedView())) { - logger.warn("Safety warning: Vertex {} does not respect locked view {}", - proposedVertex, - this.state.getLockedView() - ); - return false; - } - - // pre-commit phase on consecutive qc's proposed vertex - if (proposedVertex.getGrandParentHeader().getView().compareTo(this.state.getLockedView()) > 0) { - nextStateBuilder.lockedView(proposedVertex.getGrandParentHeader().getView()); - } - return true; - } - - /** - * Create a signed proposal from a vertex - * @param proposedVertex vertex to sign - * @param highestCommittedQC highest known committed QC - * @param highestTC highest known TC - * @return signed proposal object for consensus - */ - public Optional signProposal( - VerifiedVertex proposedVertex, - QuorumCertificate highestCommittedQC, - Optional highestTC - ) { - final Builder safetyStateBuilder = this.state.toBuilder(); - if (!checkLocked(proposedVertex, safetyStateBuilder)) { - return Optional.empty(); - } - - this.state = safetyStateBuilder.build(); - - final ECDSASignature signature = this.signer.sign(proposedVertex.getId()); - return Optional.of(new Proposal( - proposedVertex.toSerializable(), - highestCommittedQC, - signature, - highestTC - )); - } - - private static VoteData constructVoteData(VerifiedVertex proposedVertex, BFTHeader proposedHeader) { - final BFTHeader parent = proposedVertex.getParentHeader(); - - // Add a vertex to commit if creating a quorum for the proposed vertex would - // create three consecutive qcs. - final BFTHeader toCommit; - if (proposedVertex.touchesGenesis() - || !proposedVertex.hasDirectParent() - || !proposedVertex.parentHasDirectParent() - ) { - toCommit = null; - } else { - toCommit = proposedVertex.getGrandParentHeader(); - } - - return new VoteData(proposedHeader, parent, toCommit); - } - - /** - * Vote for a proposed vertex while ensuring that safety invariants are upheld. - * - * @param proposedVertex The proposed vertex - * @param proposedHeader results of vertex execution - * @param timestamp timestamp to use for the vote in milliseconds since epoch - * @param highQC our current sync state - * @return A vote result containing the vote and any committed vertices - */ - public Optional voteFor(VerifiedVertex proposedVertex, BFTHeader proposedHeader, long timestamp, HighQC highQC) { - Builder safetyStateBuilder = this.state.toBuilder(); - - if (!checkLastVoted(proposedVertex)) { - return Optional.empty(); - } - - if (!checkLocked(proposedVertex, safetyStateBuilder)) { - return Optional.empty(); - } - - final Vote vote = createVote(proposedVertex, proposedHeader, timestamp, highQC); - - safetyStateBuilder.lastVote(vote); - - this.state = safetyStateBuilder.build(); - this.persistentSafetyStateStore.commitState(this.state); - - return Optional.of(vote); - } - - public Vote timeoutVote(Vote vote) { - if (vote.isTimeout()) { // vote is already timed out - return vote; - } - - final VoteTimeout voteTimeout = VoteTimeout.of(vote); - final HashCode voteTimeoutHash = hasher.hash(voteTimeout); - - final ECDSASignature timeoutSignature = this.signer.sign(voteTimeoutHash); - final Vote timeoutVote = vote.withTimeoutSignature(timeoutSignature); - - this.state = this.state.toBuilder().lastVote(timeoutVote).build(); - this.persistentSafetyStateStore.commitState(this.state); - - return timeoutVote; - } - - public Vote createVote( - VerifiedVertex proposedVertex, - BFTHeader proposedHeader, - long timestamp, - HighQC highQC - ) { - final VoteData voteData = constructVoteData(proposedVertex, proposedHeader); - final var voteHash = Vote.getHashOfData(hasher, voteData, timestamp); - - // TODO make signing more robust by including author in signed hash - final ECDSASignature signature = this.signer.sign(voteHash); - return new Vote(this.self, voteData, timestamp, signature, highQC, Optional.empty()); - } - - public Optional getLastVote(View view) { - return this.state.getLastVote() - .filter(lastVote -> lastVote.getView().equals(view)); - } + private static final Logger logger = LogManager.getLogger(); + + private final BFTNode self; + private final Hasher hasher; + private final HashSigner signer; + private final PersistentSafetyStateStore persistentSafetyStateStore; + + private SafetyState state; + + @Inject + public SafetyRules( + @Self BFTNode self, + SafetyState initialState, + PersistentSafetyStateStore persistentSafetyStateStore, + Hasher hasher, + HashSigner signer) { + this.self = self; + this.state = Objects.requireNonNull(initialState); + this.persistentSafetyStateStore = Objects.requireNonNull(persistentSafetyStateStore); + this.hasher = Objects.requireNonNull(hasher); + this.signer = Objects.requireNonNull(signer); + } + + private boolean checkLastVoted(VerifiedVertex proposedVertex) { + // ensure vertex does not violate earlier votes + if (proposedVertex.getView().lte(this.state.getLastVotedView())) { + logger.warn( + "Safety warning: Vertex {} violates earlier vote at view {}", + proposedVertex, + this.state.getLastVotedView()); + return false; + } else { + return true; + } + } + + private boolean checkLocked(VerifiedVertex proposedVertex, Builder nextStateBuilder) { + if (proposedVertex.getParentHeader().getView().lt(this.state.getLockedView())) { + logger.warn( + "Safety warning: Vertex {} does not respect locked view {}", + proposedVertex, + this.state.getLockedView()); + return false; + } + + // pre-commit phase on consecutive qc's proposed vertex + if (proposedVertex.getGrandParentHeader().getView().compareTo(this.state.getLockedView()) > 0) { + nextStateBuilder.lockedView(proposedVertex.getGrandParentHeader().getView()); + } + return true; + } + + /** + * Create a signed proposal from a vertex + * + * @param proposedVertex vertex to sign + * @param highestCommittedQC highest known committed QC + * @param highestTC highest known TC + * @return signed proposal object for consensus + */ + public Optional signProposal( + VerifiedVertex proposedVertex, + QuorumCertificate highestCommittedQC, + Optional highestTC) { + final Builder safetyStateBuilder = this.state.toBuilder(); + if (!checkLocked(proposedVertex, safetyStateBuilder)) { + return Optional.empty(); + } + + this.state = safetyStateBuilder.build(); + + final ECDSASignature signature = this.signer.sign(proposedVertex.getId()); + return Optional.of( + new Proposal(proposedVertex.toSerializable(), highestCommittedQC, signature, highestTC)); + } + + private static VoteData constructVoteData( + VerifiedVertex proposedVertex, BFTHeader proposedHeader) { + final BFTHeader parent = proposedVertex.getParentHeader(); + + // Add a vertex to commit if creating a quorum for the proposed vertex would + // create three consecutive qcs. + final BFTHeader toCommit; + if (proposedVertex.touchesGenesis() + || !proposedVertex.hasDirectParent() + || !proposedVertex.parentHasDirectParent()) { + toCommit = null; + } else { + toCommit = proposedVertex.getGrandParentHeader(); + } + + return new VoteData(proposedHeader, parent, toCommit); + } + + /** + * Vote for a proposed vertex while ensuring that safety invariants are upheld. + * + * @param proposedVertex The proposed vertex + * @param proposedHeader results of vertex execution + * @param timestamp timestamp to use for the vote in milliseconds since epoch + * @param highQC our current sync state + * @return A vote result containing the vote and any committed vertices + */ + public Optional voteFor( + VerifiedVertex proposedVertex, BFTHeader proposedHeader, long timestamp, HighQC highQC) { + Builder safetyStateBuilder = this.state.toBuilder(); + + if (!checkLastVoted(proposedVertex)) { + return Optional.empty(); + } + + if (!checkLocked(proposedVertex, safetyStateBuilder)) { + return Optional.empty(); + } + + final Vote vote = createVote(proposedVertex, proposedHeader, timestamp, highQC); + + safetyStateBuilder.lastVote(vote); + + this.state = safetyStateBuilder.build(); + this.persistentSafetyStateStore.commitState(this.state); + + return Optional.of(vote); + } + + public Vote timeoutVote(Vote vote) { + if (vote.isTimeout()) { // vote is already timed out + return vote; + } + + final VoteTimeout voteTimeout = VoteTimeout.of(vote); + final HashCode voteTimeoutHash = hasher.hash(voteTimeout); + + final ECDSASignature timeoutSignature = this.signer.sign(voteTimeoutHash); + final Vote timeoutVote = vote.withTimeoutSignature(timeoutSignature); + + this.state = this.state.toBuilder().lastVote(timeoutVote).build(); + this.persistentSafetyStateStore.commitState(this.state); + + return timeoutVote; + } + + public Vote createVote( + VerifiedVertex proposedVertex, BFTHeader proposedHeader, long timestamp, HighQC highQC) { + final VoteData voteData = constructVoteData(proposedVertex, proposedHeader); + final var voteHash = Vote.getHashOfData(hasher, voteData, timestamp); + + // TODO make signing more robust by including author in signed hash + final ECDSASignature signature = this.signer.sign(voteHash); + return new Vote(this.self, voteData, timestamp, signature, highQC, Optional.empty()); + } + + public Optional getLastVote(View view) { + return this.state.getLastVote().filter(lastVote -> lastVote.getView().equals(view)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyState.java index 0e21a9e1fa..5f6975afef 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/safety/SafetyState.java @@ -1,206 +1,195 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus.safety; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.inject.Inject; -import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; -import java.util.Objects; -import java.util.Optional; - -/** - * The state maintained to ensure the safety of the consensus system. - */ -@Immutable -@SerializerId2("consensus.safety_state") -public final class SafetyState { - - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(DsonOutput.Output.ALL) - SerializerDummy serializer = SerializerDummy.DUMMY; - - private final View lockedView; // the highest 2-chain head - - private final Optional lastVote; - - @Inject - public SafetyState() { - this(View.genesis(), Optional.empty()); - } - - @JsonCreator - public SafetyState( - @JsonProperty("locked_view") Long lockedView, - @JsonProperty("last_vote") Vote lastVote - ) { - this(View.of(lockedView), Optional.ofNullable(lastVote)); - } - - public SafetyState( - View lockedView, - Optional lastVote - ) { - this.lockedView = Objects.requireNonNull(lockedView); - this.lastVote = Objects.requireNonNull(lastVote); - } - - static class Builder { - private final SafetyState original; - private View lockedView; - private Vote lastVote; - private boolean changed = false; - - private Builder(SafetyState safetyState) { - this.original = safetyState; - } - - public Builder lockedView(View lockedView) { - this.lockedView = lockedView; - this.changed = true; - return this; - } - - public Builder lastVote(Vote vote) { - this.lastVote = vote; - this.changed = true; - return this; - } - - public SafetyState build() { - if (changed) { - return new SafetyState( - lockedView == null ? original.lockedView : lockedView, - lastVote == null ? original.lastVote : Optional.of(lastVote) - ); - } else { - return original; - } - } - } - - public Builder toBuilder() { - return new Builder(this); - } - - public static SafetyState initialState() { - return new SafetyState(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SafetyState that = (SafetyState) o; - return Objects.equals(lockedView, that.lockedView) - && Objects.equals(lastVote, that.lastVote); - } - - @Override - public int hashCode() { - return Objects.hash(lockedView, lastVote); - } - - @Override - public String toString() { - return String.format("SafetyState{lockedView=%s, lastVote=%s}", - lockedView, lastVote); - } - - public View getLastVotedView() { - return getLastVote().map(Vote::getView).orElse(View.genesis()); - } - - public View getLockedView() { - return lockedView; - } - - public Optional getLastVote() { - return lastVote; - } - - @JsonProperty("locked_view") - @DsonOutput(DsonOutput.Output.ALL) - private Long getSerializerLockedView() { - return this.lockedView == null ? null : this.lockedView.number(); - } - - @JsonProperty("last_vote") - @DsonOutput(DsonOutput.Output.ALL) - public Vote getSerializerLastVote() { - return lastVote.orElse(null); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus.safety; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.inject.Inject; +import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.concurrent.Immutable; + +/** The state maintained to ensure the safety of the consensus system. */ +@Immutable +@SerializerId2("consensus.safety_state") +public final class SafetyState { + + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(DsonOutput.Output.ALL) + SerializerDummy serializer = SerializerDummy.DUMMY; + + private final View lockedView; // the highest 2-chain head + + private final Optional lastVote; + + @Inject + public SafetyState() { + this(View.genesis(), Optional.empty()); + } + + @JsonCreator + public SafetyState( + @JsonProperty("locked_view") Long lockedView, @JsonProperty("last_vote") Vote lastVote) { + this(View.of(lockedView), Optional.ofNullable(lastVote)); + } + + public SafetyState(View lockedView, Optional lastVote) { + this.lockedView = Objects.requireNonNull(lockedView); + this.lastVote = Objects.requireNonNull(lastVote); + } + + static class Builder { + private final SafetyState original; + private View lockedView; + private Vote lastVote; + private boolean changed = false; + + private Builder(SafetyState safetyState) { + this.original = safetyState; + } + + public Builder lockedView(View lockedView) { + this.lockedView = lockedView; + this.changed = true; + return this; + } + + public Builder lastVote(Vote vote) { + this.lastVote = vote; + this.changed = true; + return this; + } + + public SafetyState build() { + if (changed) { + return new SafetyState( + lockedView == null ? original.lockedView : lockedView, + lastVote == null ? original.lastVote : Optional.of(lastVote)); + } else { + return original; + } + } + } + + public Builder toBuilder() { + return new Builder(this); + } + + public static SafetyState initialState() { + return new SafetyState(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SafetyState that = (SafetyState) o; + return Objects.equals(lockedView, that.lockedView) && Objects.equals(lastVote, that.lastVote); + } + + @Override + public int hashCode() { + return Objects.hash(lockedView, lastVote); + } + + @Override + public String toString() { + return String.format("SafetyState{lockedView=%s, lastVote=%s}", lockedView, lastVote); + } + + public View getLastVotedView() { + return getLastVote().map(Vote::getView).orElse(View.genesis()); + } + + public View getLockedView() { + return lockedView; + } + + public Optional getLastVote() { + return lastVote; + } + + @JsonProperty("locked_view") + @DsonOutput(DsonOutput.Output.ALL) + private Long getSerializerLockedView() { + return this.lockedView == null ? null : this.lockedView.number(); + } + + @JsonProperty("last_vote") + @DsonOutput(DsonOutput.Output.ALL) + public Vote getSerializerLastVote() { + return lastVote.orElse(null); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSync.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSync.java index fde8e8cf38..5a2a9f30ca 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSync.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSync.java @@ -64,6 +64,8 @@ package com.radixdlt.consensus.sync; +import static java.util.function.Predicate.not; + import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; import com.google.common.util.concurrent.RateLimiter; @@ -87,508 +89,530 @@ import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.utils.Pair; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import static java.util.function.Predicate.not; - -/** - * Manages keeping the VertexStore and pacemaker in sync for consensus - */ +/** Manages keeping the VertexStore and pacemaker in sync for consensus */ public final class BFTSync implements BFTSyncer { - private enum SyncStage { - PREPARING, - GET_COMMITTED_VERTICES, - LEDGER_SYNC, - GET_QC_VERTICES - } - - private static class SyncRequestState { - private final List syncIds = new ArrayList<>(); - private final ImmutableList authors; - private final View view; - - SyncRequestState(ImmutableList authors, View view) { - this.authors = Objects.requireNonNull(authors); - this.view = Objects.requireNonNull(view); - } - } - - private static class SyncState { - private final HashCode localSyncId; - private final HighQC highQC; - private final BFTHeader committedHeader; - private final LedgerProof committedProof; - private final BFTNode author; - private SyncStage syncStage; - private final LinkedList fetched = new LinkedList<>(); - - SyncState(HighQC highQC, BFTNode author, Hasher hasher) { - this.localSyncId = highQC.highestQC().getProposed().getVertexId(); - Pair pair = highQC.highestCommittedQC() - .getCommittedAndLedgerStateProof(hasher) - .orElseThrow(() -> new IllegalStateException("committedQC must have a commit")); - this.committedHeader = pair.getFirst(); - this.committedProof = pair.getSecond(); - this.highQC = highQC; - this.author = author; - this.syncStage = SyncStage.PREPARING; - } - - void setSyncStage(SyncStage syncStage) { - this.syncStage = syncStage; - } - - HighQC highQC() { - return this.highQC; - } - - @Override - public String toString() { - return String.format("%s{%s syncState=%s}", this.getClass().getSimpleName(), highQC, syncStage); - } - } - - private static final Comparator> syncPriority = - Comparator.comparing((Map.Entry e) -> e.getValue().view) - .reversed(); // Prioritise by highest view - - - private static final Logger log = LogManager.getLogger(); - private final BFTNode self; - private final VertexStore vertexStore; - private final Hasher hasher; - private final PacemakerReducer pacemakerReducer; - private final Map syncing = new HashMap<>(); - private final TreeMap> ledgerSyncing; - private final Map bftSyncing = new HashMap<>(); - private final RemoteEventDispatcher requestSender; - private final EventDispatcher localSyncRequestEventDispatcher; - private final ScheduledEventDispatcher timeoutDispatcher; - private final Random random; - private final int bftSyncPatienceMillis; - private final SystemCounters systemCounters; - private LedgerProof currentLedgerHeader; - - // TODO: remove once we figure that out - private final Set runOnThreads = Collections.newSetFromMap(new ConcurrentHashMap<>(2)); - - // FIXME: Remove this once sync is fixed - private final RateLimiter syncRequestRateLimiter; - - public BFTSync( - @Self BFTNode self, - RateLimiter syncRequestRateLimiter, - VertexStore vertexStore, - Hasher hasher, - PacemakerReducer pacemakerReducer, - Comparator ledgerHeaderComparator, - RemoteEventDispatcher requestSender, - EventDispatcher localSyncRequestEventDispatcher, - ScheduledEventDispatcher timeoutDispatcher, - LedgerProof currentLedgerHeader, - Random random, - int bftSyncPatienceMillis, - SystemCounters systemCounters - ) { - this.self = self; - this.syncRequestRateLimiter = Objects.requireNonNull(syncRequestRateLimiter); - this.vertexStore = vertexStore; - this.hasher = Objects.requireNonNull(hasher); - this.pacemakerReducer = pacemakerReducer; - this.ledgerSyncing = new TreeMap<>(ledgerHeaderComparator); - this.requestSender = requestSender; - this.localSyncRequestEventDispatcher = Objects.requireNonNull(localSyncRequestEventDispatcher); - this.timeoutDispatcher = Objects.requireNonNull(timeoutDispatcher); - this.currentLedgerHeader = Objects.requireNonNull(currentLedgerHeader); - this.random = random; - this.bftSyncPatienceMillis = bftSyncPatienceMillis; - this.systemCounters = Objects.requireNonNull(systemCounters); - } - - public EventProcessor viewQuorumReachedEventProcessor() { - return viewQuorumReached -> { - this.runOnThreads.add(Thread.currentThread().getName()); - - final HighQC highQC; - //TODO: extract into dedicated method, this method has too many responsibilities - if (viewQuorumReached.votingResult() instanceof FormedQC) { - highQC = HighQC.from( - ((FormedQC) viewQuorumReached.votingResult()).getQC(), - this.vertexStore.highQC().highestCommittedQC(), - this.vertexStore.getHighestTimeoutCertificate()); - } else if (viewQuorumReached.votingResult() instanceof FormedTC) { - highQC = HighQC.from( - this.vertexStore.highQC().highestQC(), - this.vertexStore.highQC().highestCommittedQC(), - Optional.of(((FormedTC) viewQuorumReached.votingResult()).getTC())); - } else { - //TODO: cleanup this mess - throw new IllegalStateException("Unknown voting result: " + viewQuorumReached.votingResult()); - } - - syncToQC(highQC, viewQuorumReached.lastAuthor()); - }; - } - - @Override - public SyncResult syncToQC(HighQC highQC, @Nullable BFTNode author) { - this.runOnThreads.add(Thread.currentThread().getName()); - final QuorumCertificate qc = highQC.highestQC(); - final HashCode vertexId = qc.getProposed().getVertexId(); - - if (qc.getProposed().getView().compareTo(vertexStore.getRoot().getView()) < 0) { - return SyncResult.INVALID; - } - - if (qc.getProposed().getView().compareTo(this.currentLedgerHeader.getView()) < 0) { - return SyncResult.INVALID; - } - - highQC.highestTC().ifPresent(vertexStore::insertTimeoutCertificate); - - if (vertexStore.addQC(qc)) { - // TODO: check if already sent highest - // TODO: Move pacemaker outside of sync - this.pacemakerReducer.processQC(vertexStore.highQC()); - return SyncResult.SYNCED; - } - - // TODO: Move this check into pre-check - // Bad genesis qc, ignore... - if (qc.getView().isGenesis()) { - log.warn("SYNC_TO_QC: Bad Genesis: {}", highQC); - return SyncResult.INVALID; - } - - log.trace("SYNC_TO_QC: Need sync: {}", highQC); - - if (syncing.containsKey(vertexId)) { - return SyncResult.IN_PROGRESS; - } - - if (author == null) { - throw new IllegalStateException("Syncing required but author wasn't provided."); - } - - startSync(highQC, author); - - return SyncResult.IN_PROGRESS; - } - - private boolean requiresLedgerSync(SyncState syncState) { - final BFTHeader committedHeader = syncState.committedHeader; - if (!vertexStore.containsVertex(committedHeader.getVertexId())) { - View rootView = vertexStore.getRoot().getView(); - return rootView.compareTo(committedHeader.getView()) < 0; - } - - return false; - } - - private void startSync(HighQC highQC, BFTNode author) { - final SyncState syncState = new SyncState(highQC, author, hasher); - syncing.put(syncState.localSyncId, syncState); - if (requiresLedgerSync(syncState)) { - this.doCommittedSync(syncState); - } else { - this.doQCSync(syncState); - } - } - - private void doQCSync(SyncState syncState) { - syncState.setSyncStage(SyncStage.GET_QC_VERTICES); - log.debug("SYNC_VERTICES: QC: Sending initial GetVerticesRequest for sync={}", syncState); - final var authors = Stream - .concat( - Stream.of(syncState.author), - syncState.highQC().highestQC().getSigners().filter(n -> !n.equals(syncState.author)) - ) - .filter(not(n -> n.equals(this.self))) - .collect(ImmutableList.toImmutableList()); - - final var qc = syncState.highQC().highestQC(); - this.sendBFTSyncRequest(qc.getView(), qc.getProposed().getVertexId(), 1, authors, syncState.localSyncId); - } - - private void doCommittedSync(SyncState syncState) { - final HashCode committedQCId = syncState.highQC().highestCommittedQC().getProposed().getVertexId(); - final var commitedView = syncState.highQC().highestCommittedQC().getView(); - syncState.setSyncStage(SyncStage.GET_COMMITTED_VERTICES); - log.debug("SYNC_VERTICES: Committed: Sending initial GetVerticesRequest for sync={}", syncState); - // Retrieve the 3 vertices preceding the committedQC so we can create a valid committed root - - final var authors = Stream - .concat( - Stream.of(syncState.author), - syncState.highQC().highestCommittedQC().getSigners().filter(n -> !n.equals(syncState.author)) - ) - .filter(not(n -> n.equals(this.self))) - .collect(ImmutableList.toImmutableList()); - - this.sendBFTSyncRequest(commitedView, committedQCId, 3, authors, syncState.localSyncId); - } - - public EventProcessor vertexRequestTimeoutEventProcessor() { - return this::processGetVerticesLocalTimeout; - } - - private void processGetVerticesLocalTimeout(VertexRequestTimeout timeout) { - this.runOnThreads.add(Thread.currentThread().getName()); - - final GetVerticesRequest request = highestQCRequest(this.bftSyncing.entrySet()); - - SyncRequestState syncRequestState = bftSyncing.remove(request); - if (syncRequestState == null) { - return; - } - - if (syncRequestState.authors.isEmpty()) { - throw new IllegalStateException("Request contains no authors except ourselves"); - } - - var syncIds = syncRequestState.syncIds.stream() - .filter(syncing::containsKey) - .distinct() - .toList(); - - //noinspection UnstableApiUsage - for (var syncId : syncIds) { - systemCounters.increment(CounterType.BFT_SYNC_REQUEST_TIMEOUTS); - SyncState syncState = syncing.remove(syncId); - if (syncState == null) { - // TODO: remove once we figure this out - final var msg = new StringBuilder(); - msg - .append("Got a null value from \"syncing\" map. SyncId=") - .append(syncId) - .append(" SyncIds=") - .append(syncIds) - .append(" Map=") - .append(syncing) - .append(" Contains=") - .append(syncing.containsKey(syncId)) - .append(" Thread=") - .append(Thread.currentThread().getName()) - .append(" OtherThreads=") - .append(String.join(",", runOnThreads)); - log.error(msg.toString()); - throw new IllegalStateException( - "Inconsistent sync state, please contact Radix team member on Discord. (" + msg + ")" - ); - } else { - syncToQC(syncState.highQC, randomFrom(syncRequestState.authors)); - } - } - } - - private GetVerticesRequest highestQCRequest(Collection> requests) { - return requests.stream() - .sorted(syncPriority) - .findFirst() - .map(Map.Entry::getKey) - .orElse(null); - } - - private T randomFrom(List elements) { - final var size = elements.size(); - if (size <= 0) { - return null; - } - int nextIndex = random.nextInt(size); - return elements.get(nextIndex); - } - - private void sendBFTSyncRequest(View view, HashCode vertexId, int count, ImmutableList authors, HashCode syncId) { - GetVerticesRequest request = new GetVerticesRequest(vertexId, count); - SyncRequestState syncRequestState = bftSyncing.getOrDefault(request, new SyncRequestState(authors, view)); - if (syncRequestState.syncIds.isEmpty()) { - if (this.syncRequestRateLimiter.tryAcquire()) { - VertexRequestTimeout scheduledTimeout = VertexRequestTimeout.create(request); - this.timeoutDispatcher.dispatch(scheduledTimeout, bftSyncPatienceMillis); - this.requestSender.dispatch(authors.get(0), request); - } else { - log.warn("RATE_LIMIT: Request dropped"); - } - this.bftSyncing.put(request, syncRequestState); - } - syncRequestState.syncIds.add(syncId); - } - - private void rebuildAndSyncQC(SyncState syncState) { - log.debug("SYNC_STATE: Rebuilding and syncing QC: sync={} curRoot={}", syncState, vertexStore.getRoot()); - - // TODO: check if there are any vertices which haven't been local sync processed yet - if (requiresLedgerSync(syncState)) { - syncState.fetched.sort(Comparator.comparing(VerifiedVertex::getView)); - ImmutableList nonRootVertices = syncState.fetched.stream() - .skip(1) - .collect(ImmutableList.toImmutableList()); - var vertexStoreState = VerifiedVertexStoreState.create( - HighQC.from(syncState.highQC().highestCommittedQC()), - syncState.fetched.get(0), - nonRootVertices, - vertexStore.getHighestTimeoutCertificate(), - hasher - ); - if (vertexStore.tryRebuild(vertexStoreState)) { - // TODO: Move pacemaker outside of sync - pacemakerReducer.processQC(vertexStoreState.getHighQC()); - } - } else { - log.debug("SYNC_STATE: skipping rebuild"); - } - - // At this point we are guaranteed to be in sync with the committed state - // Retry sync - this.syncing.remove(syncState.localSyncId); - this.syncToQC(syncState.highQC(), syncState.author); - } - - private void processVerticesResponseForCommittedSync(SyncState syncState, BFTNode sender, GetVerticesResponse response) { - log.debug("SYNC_STATE: Processing vertices {} View {} From {} CurrentLedgerHeader {}", - syncState, response.getVertices().get(0).getView(), sender, this.currentLedgerHeader - ); - - syncState.fetched.addAll(response.getVertices()); - - // TODO: verify actually extends rather than just state version comparison - if (syncState.committedProof.getStateVersion() <= this.currentLedgerHeader.getStateVersion()) { - rebuildAndSyncQC(syncState); - } else { - ImmutableList signers = syncState.committedProof.getSignersWithout(self); - syncState.setSyncStage(SyncStage.LEDGER_SYNC); - ledgerSyncing.compute(syncState.committedProof.getRaw(), (header, syncing) -> { - if (syncing == null) { - syncing = new ArrayList<>(); - } - syncing.add(syncState.localSyncId); - return syncing; - }); - LocalSyncRequest localSyncRequest = new LocalSyncRequest( - syncState.committedProof, - signers - ); - - localSyncRequestEventDispatcher.dispatch(localSyncRequest); - } - } - - private void processVerticesResponseForQCSync(SyncState syncState, GetVerticesResponse response) { - VerifiedVertex vertex = response.getVertices().get(0); - syncState.fetched.addFirst(vertex); - HashCode parentId = vertex.getParentId(); - - if (vertexStore.containsVertex(parentId)) { - vertexStore.insertVertexChain(VerifiedVertexChain.create(syncState.fetched)); - // Finish it off - this.syncing.remove(syncState.localSyncId); - this.syncToQC(syncState.highQC, syncState.author); - } else { - log.debug("SYNC_VERTICES: Sending further GetVerticesRequest for {} fetched={} root={}", - syncState.highQC(), syncState.fetched.size(), vertexStore.getRoot()); - - final var authors = Stream - .concat( - Stream.of(syncState.author), - vertex.getQC().getSigners().filter(n -> !n.equals(syncState.author)) - ) - .filter(not(n -> n.equals(this.self))) - .collect(ImmutableList.toImmutableList()); - - this.sendBFTSyncRequest(syncState.highQC.highestQC().getView(), parentId, 1, authors, syncState.localSyncId); - } - } - - private void processGetVerticesErrorResponse(BFTNode sender, GetVerticesErrorResponse response) { - this.runOnThreads.add(Thread.currentThread().getName()); - - // TODO: check response - final var request = response.request(); - final var syncRequestState = bftSyncing.get(request); - if (syncRequestState != null) { - log.debug("SYNC_VERTICES: Received GetVerticesErrorResponse: {} highQC: {}", response, vertexStore.highQC()); - if (response.highQC().highestQC().getView().compareTo(vertexStore.highQC().highestQC().getView()) > 0) { - // error response indicates that the node has moved on from last sync so try and sync to a new qc - syncToQC(response.highQC(), sender); - } - } - } - - public RemoteEventProcessor errorResponseProcessor() { - return this::processGetVerticesErrorResponse; - } - - public RemoteEventProcessor responseProcessor() { - return this::processGetVerticesResponse; - } - - private void processGetVerticesResponse(BFTNode sender, GetVerticesResponse response) { - this.runOnThreads.add(Thread.currentThread().getName()); - - // TODO: check response - - log.debug("SYNC_VERTICES: Received GetVerticesResponse {}", response); - - VerifiedVertex firstVertex = response.getVertices().get(0); - GetVerticesRequest requestInfo = new GetVerticesRequest(firstVertex.getId(), response.getVertices().size()); - SyncRequestState syncRequestState = bftSyncing.remove(requestInfo); - if (syncRequestState != null) { - for (HashCode syncTo : syncRequestState.syncIds) { - SyncState syncState = syncing.get(syncTo); - if (syncState == null) { - continue; // sync requirements already satisfied by another sync - } - //TODO: replace with enhanced switch - switch (syncState.syncStage) { - case GET_COMMITTED_VERTICES: - processVerticesResponseForCommittedSync(syncState, sender, response); - break; - case GET_QC_VERTICES: - processVerticesResponseForQCSync(syncState, response); - break; - default: - throw new IllegalStateException("Unknown sync stage: " + syncState.syncStage); - } - } - } - } - - public void processBFTUpdate(BFTInsertUpdate update) { - } - - public EventProcessor baseLedgerUpdateEventProcessor() { - return this::processLedgerUpdate; - } - - // TODO: Verify headers match - private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { - this.runOnThreads.add(Thread.currentThread().getName()); - - log.trace("SYNC_STATE: update {}", ledgerUpdate.getTail()); - - this.currentLedgerHeader = ledgerUpdate.getTail(); - - Collection> listeners = this.ledgerSyncing.headMap( - ledgerUpdate.getTail().getRaw(), true - ).values(); - Iterator> listenersIterator = listeners.iterator(); - while (listenersIterator.hasNext()) { - List syncs = listenersIterator.next(); - for (HashCode syncTo : syncs) { - SyncState syncState = syncing.get(syncTo); - if (syncState != null) { - rebuildAndSyncQC(syncState); - } - } - listenersIterator.remove(); - } - - syncing.entrySet().removeIf(e -> e.getValue().highQC.highestQC().getView().lte(ledgerUpdate.getTail().getView())); - } + private enum SyncStage { + PREPARING, + GET_COMMITTED_VERTICES, + LEDGER_SYNC, + GET_QC_VERTICES + } + + private static class SyncRequestState { + private final List syncIds = new ArrayList<>(); + private final ImmutableList authors; + private final View view; + + SyncRequestState(ImmutableList authors, View view) { + this.authors = Objects.requireNonNull(authors); + this.view = Objects.requireNonNull(view); + } + } + + private static class SyncState { + private final HashCode localSyncId; + private final HighQC highQC; + private final BFTHeader committedHeader; + private final LedgerProof committedProof; + private final BFTNode author; + private SyncStage syncStage; + private final LinkedList fetched = new LinkedList<>(); + + SyncState(HighQC highQC, BFTNode author, Hasher hasher) { + this.localSyncId = highQC.highestQC().getProposed().getVertexId(); + Pair pair = + highQC + .highestCommittedQC() + .getCommittedAndLedgerStateProof(hasher) + .orElseThrow(() -> new IllegalStateException("committedQC must have a commit")); + this.committedHeader = pair.getFirst(); + this.committedProof = pair.getSecond(); + this.highQC = highQC; + this.author = author; + this.syncStage = SyncStage.PREPARING; + } + + void setSyncStage(SyncStage syncStage) { + this.syncStage = syncStage; + } + + HighQC highQC() { + return this.highQC; + } + + @Override + public String toString() { + return String.format( + "%s{%s syncState=%s}", this.getClass().getSimpleName(), highQC, syncStage); + } + } + + private static final Comparator> syncPriority = + Comparator.comparing((Map.Entry e) -> e.getValue().view) + .reversed(); // Prioritise by highest view + + private static final Logger log = LogManager.getLogger(); + private final BFTNode self; + private final VertexStore vertexStore; + private final Hasher hasher; + private final PacemakerReducer pacemakerReducer; + private final Map syncing = new HashMap<>(); + private final TreeMap> ledgerSyncing; + private final Map bftSyncing = new HashMap<>(); + private final RemoteEventDispatcher requestSender; + private final EventDispatcher localSyncRequestEventDispatcher; + private final ScheduledEventDispatcher timeoutDispatcher; + private final Random random; + private final int bftSyncPatienceMillis; + private final SystemCounters systemCounters; + private LedgerProof currentLedgerHeader; + + // TODO: remove once we figure that out + private final Set runOnThreads = Collections.newSetFromMap(new ConcurrentHashMap<>(2)); + + // FIXME: Remove this once sync is fixed + private final RateLimiter syncRequestRateLimiter; + + public BFTSync( + @Self BFTNode self, + RateLimiter syncRequestRateLimiter, + VertexStore vertexStore, + Hasher hasher, + PacemakerReducer pacemakerReducer, + Comparator ledgerHeaderComparator, + RemoteEventDispatcher requestSender, + EventDispatcher localSyncRequestEventDispatcher, + ScheduledEventDispatcher timeoutDispatcher, + LedgerProof currentLedgerHeader, + Random random, + int bftSyncPatienceMillis, + SystemCounters systemCounters) { + this.self = self; + this.syncRequestRateLimiter = Objects.requireNonNull(syncRequestRateLimiter); + this.vertexStore = vertexStore; + this.hasher = Objects.requireNonNull(hasher); + this.pacemakerReducer = pacemakerReducer; + this.ledgerSyncing = new TreeMap<>(ledgerHeaderComparator); + this.requestSender = requestSender; + this.localSyncRequestEventDispatcher = Objects.requireNonNull(localSyncRequestEventDispatcher); + this.timeoutDispatcher = Objects.requireNonNull(timeoutDispatcher); + this.currentLedgerHeader = Objects.requireNonNull(currentLedgerHeader); + this.random = random; + this.bftSyncPatienceMillis = bftSyncPatienceMillis; + this.systemCounters = Objects.requireNonNull(systemCounters); + } + + public EventProcessor viewQuorumReachedEventProcessor() { + return viewQuorumReached -> { + this.runOnThreads.add(Thread.currentThread().getName()); + + final HighQC highQC; + // TODO: extract into dedicated method, this method has too many responsibilities + if (viewQuorumReached.votingResult() instanceof FormedQC) { + highQC = + HighQC.from( + ((FormedQC) viewQuorumReached.votingResult()).getQC(), + this.vertexStore.highQC().highestCommittedQC(), + this.vertexStore.getHighestTimeoutCertificate()); + } else if (viewQuorumReached.votingResult() instanceof FormedTC) { + highQC = + HighQC.from( + this.vertexStore.highQC().highestQC(), + this.vertexStore.highQC().highestCommittedQC(), + Optional.of(((FormedTC) viewQuorumReached.votingResult()).getTC())); + } else { + // TODO: cleanup this mess + throw new IllegalStateException( + "Unknown voting result: " + viewQuorumReached.votingResult()); + } + + syncToQC(highQC, viewQuorumReached.lastAuthor()); + }; + } + + @Override + public SyncResult syncToQC(HighQC highQC, @Nullable BFTNode author) { + this.runOnThreads.add(Thread.currentThread().getName()); + final QuorumCertificate qc = highQC.highestQC(); + final HashCode vertexId = qc.getProposed().getVertexId(); + + if (qc.getProposed().getView().compareTo(vertexStore.getRoot().getView()) < 0) { + return SyncResult.INVALID; + } + + if (qc.getProposed().getView().compareTo(this.currentLedgerHeader.getView()) < 0) { + return SyncResult.INVALID; + } + + highQC.highestTC().ifPresent(vertexStore::insertTimeoutCertificate); + + if (vertexStore.addQC(qc)) { + // TODO: check if already sent highest + // TODO: Move pacemaker outside of sync + this.pacemakerReducer.processQC(vertexStore.highQC()); + return SyncResult.SYNCED; + } + + // TODO: Move this check into pre-check + // Bad genesis qc, ignore... + if (qc.getView().isGenesis()) { + log.warn("SYNC_TO_QC: Bad Genesis: {}", highQC); + return SyncResult.INVALID; + } + + log.trace("SYNC_TO_QC: Need sync: {}", highQC); + + if (syncing.containsKey(vertexId)) { + return SyncResult.IN_PROGRESS; + } + + if (author == null) { + throw new IllegalStateException("Syncing required but author wasn't provided."); + } + + startSync(highQC, author); + + return SyncResult.IN_PROGRESS; + } + + private boolean requiresLedgerSync(SyncState syncState) { + final BFTHeader committedHeader = syncState.committedHeader; + if (!vertexStore.containsVertex(committedHeader.getVertexId())) { + View rootView = vertexStore.getRoot().getView(); + return rootView.compareTo(committedHeader.getView()) < 0; + } + + return false; + } + + private void startSync(HighQC highQC, BFTNode author) { + final SyncState syncState = new SyncState(highQC, author, hasher); + syncing.put(syncState.localSyncId, syncState); + if (requiresLedgerSync(syncState)) { + this.doCommittedSync(syncState); + } else { + this.doQCSync(syncState); + } + } + + private void doQCSync(SyncState syncState) { + syncState.setSyncStage(SyncStage.GET_QC_VERTICES); + log.debug("SYNC_VERTICES: QC: Sending initial GetVerticesRequest for sync={}", syncState); + final var authors = + Stream.concat( + Stream.of(syncState.author), + syncState + .highQC() + .highestQC() + .getSigners() + .filter(n -> !n.equals(syncState.author))) + .filter(not(n -> n.equals(this.self))) + .collect(ImmutableList.toImmutableList()); + + final var qc = syncState.highQC().highestQC(); + this.sendBFTSyncRequest( + qc.getView(), qc.getProposed().getVertexId(), 1, authors, syncState.localSyncId); + } + + private void doCommittedSync(SyncState syncState) { + final HashCode committedQCId = + syncState.highQC().highestCommittedQC().getProposed().getVertexId(); + final var commitedView = syncState.highQC().highestCommittedQC().getView(); + syncState.setSyncStage(SyncStage.GET_COMMITTED_VERTICES); + log.debug( + "SYNC_VERTICES: Committed: Sending initial GetVerticesRequest for sync={}", syncState); + // Retrieve the 3 vertices preceding the committedQC so we can create a valid committed root + + final var authors = + Stream.concat( + Stream.of(syncState.author), + syncState + .highQC() + .highestCommittedQC() + .getSigners() + .filter(n -> !n.equals(syncState.author))) + .filter(not(n -> n.equals(this.self))) + .collect(ImmutableList.toImmutableList()); + + this.sendBFTSyncRequest(commitedView, committedQCId, 3, authors, syncState.localSyncId); + } + + public EventProcessor vertexRequestTimeoutEventProcessor() { + return this::processGetVerticesLocalTimeout; + } + + private void processGetVerticesLocalTimeout(VertexRequestTimeout timeout) { + this.runOnThreads.add(Thread.currentThread().getName()); + + final GetVerticesRequest request = highestQCRequest(this.bftSyncing.entrySet()); + + SyncRequestState syncRequestState = bftSyncing.remove(request); + if (syncRequestState == null) { + return; + } + + if (syncRequestState.authors.isEmpty()) { + throw new IllegalStateException("Request contains no authors except ourselves"); + } + + var syncIds = + syncRequestState.syncIds.stream().filter(syncing::containsKey).distinct().toList(); + + //noinspection UnstableApiUsage + for (var syncId : syncIds) { + systemCounters.increment(CounterType.BFT_SYNC_REQUEST_TIMEOUTS); + SyncState syncState = syncing.remove(syncId); + if (syncState == null) { + // TODO: remove once we figure this out + final var msg = new StringBuilder(); + msg.append("Got a null value from \"syncing\" map. SyncId=") + .append(syncId) + .append(" SyncIds=") + .append(syncIds) + .append(" Map=") + .append(syncing) + .append(" Contains=") + .append(syncing.containsKey(syncId)) + .append(" Thread=") + .append(Thread.currentThread().getName()) + .append(" OtherThreads=") + .append(String.join(",", runOnThreads)); + log.error(msg.toString()); + throw new IllegalStateException( + "Inconsistent sync state, please contact Radix team member on Discord. (" + msg + ")"); + } else { + syncToQC(syncState.highQC, randomFrom(syncRequestState.authors)); + } + } + } + + private GetVerticesRequest highestQCRequest( + Collection> requests) { + return requests.stream().sorted(syncPriority).findFirst().map(Map.Entry::getKey).orElse(null); + } + + private T randomFrom(List elements) { + final var size = elements.size(); + if (size <= 0) { + return null; + } + int nextIndex = random.nextInt(size); + return elements.get(nextIndex); + } + + private void sendBFTSyncRequest( + View view, HashCode vertexId, int count, ImmutableList authors, HashCode syncId) { + GetVerticesRequest request = new GetVerticesRequest(vertexId, count); + SyncRequestState syncRequestState = + bftSyncing.getOrDefault(request, new SyncRequestState(authors, view)); + if (syncRequestState.syncIds.isEmpty()) { + if (this.syncRequestRateLimiter.tryAcquire()) { + VertexRequestTimeout scheduledTimeout = VertexRequestTimeout.create(request); + this.timeoutDispatcher.dispatch(scheduledTimeout, bftSyncPatienceMillis); + this.requestSender.dispatch(authors.get(0), request); + } else { + log.warn("RATE_LIMIT: Request dropped"); + } + this.bftSyncing.put(request, syncRequestState); + } + syncRequestState.syncIds.add(syncId); + } + + private void rebuildAndSyncQC(SyncState syncState) { + log.debug( + "SYNC_STATE: Rebuilding and syncing QC: sync={} curRoot={}", + syncState, + vertexStore.getRoot()); + + // TODO: check if there are any vertices which haven't been local sync processed yet + if (requiresLedgerSync(syncState)) { + syncState.fetched.sort(Comparator.comparing(VerifiedVertex::getView)); + ImmutableList nonRootVertices = + syncState.fetched.stream().skip(1).collect(ImmutableList.toImmutableList()); + var vertexStoreState = + VerifiedVertexStoreState.create( + HighQC.from(syncState.highQC().highestCommittedQC()), + syncState.fetched.get(0), + nonRootVertices, + vertexStore.getHighestTimeoutCertificate(), + hasher); + if (vertexStore.tryRebuild(vertexStoreState)) { + // TODO: Move pacemaker outside of sync + pacemakerReducer.processQC(vertexStoreState.getHighQC()); + } + } else { + log.debug("SYNC_STATE: skipping rebuild"); + } + + // At this point we are guaranteed to be in sync with the committed state + // Retry sync + this.syncing.remove(syncState.localSyncId); + this.syncToQC(syncState.highQC(), syncState.author); + } + + private void processVerticesResponseForCommittedSync( + SyncState syncState, BFTNode sender, GetVerticesResponse response) { + log.debug( + "SYNC_STATE: Processing vertices {} View {} From {} CurrentLedgerHeader {}", + syncState, + response.getVertices().get(0).getView(), + sender, + this.currentLedgerHeader); + + syncState.fetched.addAll(response.getVertices()); + + // TODO: verify actually extends rather than just state version comparison + if (syncState.committedProof.getStateVersion() <= this.currentLedgerHeader.getStateVersion()) { + rebuildAndSyncQC(syncState); + } else { + ImmutableList signers = syncState.committedProof.getSignersWithout(self); + syncState.setSyncStage(SyncStage.LEDGER_SYNC); + ledgerSyncing.compute( + syncState.committedProof.getRaw(), + (header, syncing) -> { + if (syncing == null) { + syncing = new ArrayList<>(); + } + syncing.add(syncState.localSyncId); + return syncing; + }); + LocalSyncRequest localSyncRequest = new LocalSyncRequest(syncState.committedProof, signers); + + localSyncRequestEventDispatcher.dispatch(localSyncRequest); + } + } + + private void processVerticesResponseForQCSync(SyncState syncState, GetVerticesResponse response) { + VerifiedVertex vertex = response.getVertices().get(0); + syncState.fetched.addFirst(vertex); + HashCode parentId = vertex.getParentId(); + + if (vertexStore.containsVertex(parentId)) { + vertexStore.insertVertexChain(VerifiedVertexChain.create(syncState.fetched)); + // Finish it off + this.syncing.remove(syncState.localSyncId); + this.syncToQC(syncState.highQC, syncState.author); + } else { + log.debug( + "SYNC_VERTICES: Sending further GetVerticesRequest for {} fetched={} root={}", + syncState.highQC(), + syncState.fetched.size(), + vertexStore.getRoot()); + + final var authors = + Stream.concat( + Stream.of(syncState.author), + vertex.getQC().getSigners().filter(n -> !n.equals(syncState.author))) + .filter(not(n -> n.equals(this.self))) + .collect(ImmutableList.toImmutableList()); + + this.sendBFTSyncRequest( + syncState.highQC.highestQC().getView(), parentId, 1, authors, syncState.localSyncId); + } + } + + private void processGetVerticesErrorResponse(BFTNode sender, GetVerticesErrorResponse response) { + this.runOnThreads.add(Thread.currentThread().getName()); + + // TODO: check response + final var request = response.request(); + final var syncRequestState = bftSyncing.get(request); + if (syncRequestState != null) { + log.debug( + "SYNC_VERTICES: Received GetVerticesErrorResponse: {} highQC: {}", + response, + vertexStore.highQC()); + if (response + .highQC() + .highestQC() + .getView() + .compareTo(vertexStore.highQC().highestQC().getView()) + > 0) { + // error response indicates that the node has moved on from last sync so try and sync to a + // new qc + syncToQC(response.highQC(), sender); + } + } + } + + public RemoteEventProcessor errorResponseProcessor() { + return this::processGetVerticesErrorResponse; + } + + public RemoteEventProcessor responseProcessor() { + return this::processGetVerticesResponse; + } + + private void processGetVerticesResponse(BFTNode sender, GetVerticesResponse response) { + this.runOnThreads.add(Thread.currentThread().getName()); + + // TODO: check response + + log.debug("SYNC_VERTICES: Received GetVerticesResponse {}", response); + + VerifiedVertex firstVertex = response.getVertices().get(0); + GetVerticesRequest requestInfo = + new GetVerticesRequest(firstVertex.getId(), response.getVertices().size()); + SyncRequestState syncRequestState = bftSyncing.remove(requestInfo); + if (syncRequestState != null) { + for (HashCode syncTo : syncRequestState.syncIds) { + SyncState syncState = syncing.get(syncTo); + if (syncState == null) { + continue; // sync requirements already satisfied by another sync + } + // TODO: replace with enhanced switch + switch (syncState.syncStage) { + case GET_COMMITTED_VERTICES: + processVerticesResponseForCommittedSync(syncState, sender, response); + break; + case GET_QC_VERTICES: + processVerticesResponseForQCSync(syncState, response); + break; + default: + throw new IllegalStateException("Unknown sync stage: " + syncState.syncStage); + } + } + } + } + + public void processBFTUpdate(BFTInsertUpdate update) {} + + public EventProcessor baseLedgerUpdateEventProcessor() { + return this::processLedgerUpdate; + } + + // TODO: Verify headers match + private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { + this.runOnThreads.add(Thread.currentThread().getName()); + + log.trace("SYNC_STATE: update {}", ledgerUpdate.getTail()); + + this.currentLedgerHeader = ledgerUpdate.getTail(); + + Collection> listeners = + this.ledgerSyncing.headMap(ledgerUpdate.getTail().getRaw(), true).values(); + Iterator> listenersIterator = listeners.iterator(); + while (listenersIterator.hasNext()) { + List syncs = listenersIterator.next(); + for (HashCode syncTo : syncs) { + SyncState syncState = syncing.get(syncTo); + if (syncState != null) { + rebuildAndSyncQC(syncState); + } + } + listenersIterator.remove(); + } + + syncing + .entrySet() + .removeIf( + e -> e.getValue().highQC.highestQC().getView().lte(ledgerUpdate.getTail().getView())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSyncPatienceMillis.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSyncPatienceMillis.java index 0fa58a4fb1..93793105d5 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSyncPatienceMillis.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/BFTSyncPatienceMillis.java @@ -73,12 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * The amount of milliseconds to wait until a retry for another sync - * request is made - */ +/** The amount of milliseconds to wait until a retry for another sync request is made */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface BFTSyncPatienceMillis { -} +public @interface BFTSyncPatienceMillis {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesErrorResponse.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesErrorResponse.java index 0eb185343e..178a8c324f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesErrorResponse.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesErrorResponse.java @@ -67,28 +67,26 @@ import com.radixdlt.consensus.HighQC; import java.util.Objects; -/** - * An error response to the GetVertices call - */ +/** An error response to the GetVertices call */ public final class GetVerticesErrorResponse { - private final HighQC highQC; - private final GetVerticesRequest request; + private final HighQC highQC; + private final GetVerticesRequest request; - public GetVerticesErrorResponse(HighQC highQC, GetVerticesRequest request) { - this.highQC = Objects.requireNonNull(highQC); - this.request = Objects.requireNonNull(request); - } + public GetVerticesErrorResponse(HighQC highQC, GetVerticesRequest request) { + this.highQC = Objects.requireNonNull(highQC); + this.request = Objects.requireNonNull(request); + } - public HighQC highQC() { - return this.highQC; - } + public HighQC highQC() { + return this.highQC; + } - public GetVerticesRequest request() { - return this.request; - } + public GetVerticesRequest request() { + return this.request; + } - @Override - public String toString() { - return String.format("%s{%s}", this.getClass().getSimpleName(), this.highQC); - } + @Override + public String toString() { + return String.format("%s{%s}", this.getClass().getSimpleName(), this.highQC); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesRequest.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesRequest.java index c770b39bb8..ed93d8d251 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesRequest.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesRequest.java @@ -67,44 +67,42 @@ import com.google.common.hash.HashCode; import java.util.Objects; -/** - * Parameters for a local get vertices request - */ +/** Parameters for a local get vertices request */ public final class GetVerticesRequest { - private final HashCode vertexId; - private final int count; + private final HashCode vertexId; + private final int count; - public GetVerticesRequest(HashCode vertexId, int count) { - this.vertexId = Objects.requireNonNull(vertexId); - this.count = count; - } + public GetVerticesRequest(HashCode vertexId, int count) { + this.vertexId = Objects.requireNonNull(vertexId); + this.count = count; + } - public HashCode getVertexId() { - return vertexId; - } + public HashCode getVertexId() { + return vertexId; + } - public int getCount() { - return count; - } + public int getCount() { + return count; + } - @Override - public int hashCode() { - return Objects.hash(vertexId, count); - } + @Override + public int hashCode() { + return Objects.hash(vertexId, count); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof GetVerticesRequest)) { - return false; - } - GetVerticesRequest other = (GetVerticesRequest) o; + @Override + public boolean equals(Object o) { + if (!(o instanceof GetVerticesRequest)) { + return false; + } + GetVerticesRequest other = (GetVerticesRequest) o; - return Objects.equals(other.vertexId, this.vertexId) - && other.count == this.count; - } + return Objects.equals(other.vertexId, this.vertexId) && other.count == this.count; + } - @Override - public String toString() { - return String.format("%s{id=%s count=%s}", this.getClass().getSimpleName(), this.vertexId, this.count); - } + @Override + public String toString() { + return String.format( + "%s{id=%s count=%s}", this.getClass().getSimpleName(), this.vertexId, this.count); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesResponse.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesResponse.java index 80a7181ac5..ba62b396f4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesResponse.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/GetVerticesResponse.java @@ -68,22 +68,20 @@ import java.util.List; import java.util.Objects; -/** - * An RPC response - */ +/** An RPC response */ public final class GetVerticesResponse { - private final List vertices; + private final List vertices; - public GetVerticesResponse(List vertices) { - this.vertices = Objects.requireNonNull(vertices); - } + public GetVerticesResponse(List vertices) { + this.vertices = Objects.requireNonNull(vertices); + } - public List getVertices() { - return vertices; - } + public List getVertices() { + return vertices; + } - @Override - public String toString() { - return String.format("%s{vertices=%s}", this.getClass().getSimpleName(), vertices); - } + @Override + public String toString() { + return String.format("%s{vertices=%s}", this.getClass().getSimpleName(), vertices); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexRequestTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexRequestTimeout.java index 2213c4ad7e..1840d368a1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexRequestTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexRequestTimeout.java @@ -66,36 +66,34 @@ import java.util.Objects; -/** - * A scheduled timeout for a given vertex request - */ +/** A scheduled timeout for a given vertex request */ public final class VertexRequestTimeout { - private final GetVerticesRequest request; + private final GetVerticesRequest request; - private VertexRequestTimeout(GetVerticesRequest request) { - this.request = request; - } + private VertexRequestTimeout(GetVerticesRequest request) { + this.request = request; + } - public static VertexRequestTimeout create(GetVerticesRequest request) { - return new VertexRequestTimeout(request); - } + public static VertexRequestTimeout create(GetVerticesRequest request) { + return new VertexRequestTimeout(request); + } - public GetVerticesRequest getRequest() { - return request; - } + public GetVerticesRequest getRequest() { + return request; + } - @Override - public int hashCode() { - return Objects.hash(request); - } + @Override + public int hashCode() { + return Objects.hash(request); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof VertexRequestTimeout)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof VertexRequestTimeout)) { + return false; + } - VertexRequestTimeout other = (VertexRequestTimeout) o; - return Objects.equals(this.request, other.request); - } + VertexRequestTimeout other = (VertexRequestTimeout) o; + return Objects.equals(this.request, other.request); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexStoreBFTSyncRequestProcessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexStoreBFTSyncRequestProcessor.java index e29c7c43a3..06606dad49 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexStoreBFTSyncRequestProcessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/sync/VertexStoreBFTSyncRequestProcessor.java @@ -74,45 +74,43 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Processor of sync requests and responds with info from a VertexStore - */ -public final class VertexStoreBFTSyncRequestProcessor implements RemoteEventProcessor { - private static final Logger log = LogManager.getLogger(); - private final VertexStore vertexStore; - private final RemoteEventDispatcher errorResponseDispatcher; - private final RemoteEventDispatcher responseDispatcher; - private final SystemCounters systemCounters; +/** Processor of sync requests and responds with info from a VertexStore */ +public final class VertexStoreBFTSyncRequestProcessor + implements RemoteEventProcessor { + private static final Logger log = LogManager.getLogger(); + private final VertexStore vertexStore; + private final RemoteEventDispatcher errorResponseDispatcher; + private final RemoteEventDispatcher responseDispatcher; + private final SystemCounters systemCounters; - @Inject - public VertexStoreBFTSyncRequestProcessor( - VertexStore vertexStore, - RemoteEventDispatcher errorResponseDispatcher, - RemoteEventDispatcher responseDispatcher, - SystemCounters systemCounters - ) { - this.vertexStore = Objects.requireNonNull(vertexStore); - this.errorResponseDispatcher = Objects.requireNonNull(errorResponseDispatcher); - this.responseDispatcher = Objects.requireNonNull(responseDispatcher); - this.systemCounters = systemCounters; - } + @Inject + public VertexStoreBFTSyncRequestProcessor( + VertexStore vertexStore, + RemoteEventDispatcher errorResponseDispatcher, + RemoteEventDispatcher responseDispatcher, + SystemCounters systemCounters) { + this.vertexStore = Objects.requireNonNull(vertexStore); + this.errorResponseDispatcher = Objects.requireNonNull(errorResponseDispatcher); + this.responseDispatcher = Objects.requireNonNull(responseDispatcher); + this.systemCounters = systemCounters; + } - @Override - public void process(BFTNode sender, GetVerticesRequest request) { - // TODO: Handle nodes trying to DDOS this endpoint - systemCounters.increment(SystemCounters.CounterType.BFT_SYNC_REQUESTS_RECEIVED); + @Override + public void process(BFTNode sender, GetVerticesRequest request) { + // TODO: Handle nodes trying to DDOS this endpoint + systemCounters.increment(SystemCounters.CounterType.BFT_SYNC_REQUESTS_RECEIVED); - log.debug("SYNC_VERTICES: Received GetVerticesRequest {}", request); - var verticesMaybe = vertexStore.getVertices(request.getVertexId(), request.getCount()); - verticesMaybe.ifPresentOrElse( - fetched -> { - log.debug("SYNC_VERTICES: Sending Response {}", fetched); - this.responseDispatcher.dispatch(sender, new GetVerticesResponse(fetched)); - }, - () -> { - log.debug("SYNC_VERTICES: Sending error response {}", vertexStore.highQC()); - this.errorResponseDispatcher.dispatch(sender, new GetVerticesErrorResponse(vertexStore.highQC(), request)); - } - ); - } + log.debug("SYNC_VERTICES: Received GetVerticesRequest {}", request); + var verticesMaybe = vertexStore.getVertices(request.getVertexId(), request.getCount()); + verticesMaybe.ifPresentOrElse( + fetched -> { + log.debug("SYNC_VERTICES: Sending Response {}", fetched); + this.responseDispatcher.dispatch(sender, new GetVerticesResponse(fetched)); + }, + () -> { + log.debug("SYNC_VERTICES: Sending error response {}", vertexStore.highQC()); + this.errorResponseDispatcher.dispatch( + sender, new GetVerticesErrorResponse(vertexStore.highQC(), request)); + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java index ec703f8aa4..94797e9680 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java @@ -66,216 +66,211 @@ import java.util.Map; -/** - * System counters interface. - */ +/** System counters interface. */ public interface SystemCounters { - enum CounterType { - // Please keep these sorted - - BFT_EVENTS_RECEIVED("bft.events_received"), - BFT_COMMITTED_VERTICES("bft.committed_vertices"), - /** Number of proposals rejected. */ - BFT_NO_VOTES_SENT("bft.no_votes_sent"), - /** Number of vote quorums formed. */ - BFT_VOTE_QUORUMS("bft.vote_quorums"), - /** Number of view-timeout quorums formed. */ - BFT_TIMEOUT_QUORUMS("bft.timeout_quorums"), - - /** Number of times a view-timeout message was broadcast. */ - BFT_PACEMAKER_TIMEOUTS_SENT("bft.pacemaker.timeouts_sent"), - BFT_PACEMAKER_ROUND("bft.pacemaker.round"), - BFT_PACEMAKER_PROPOSED_TRANSACTIONS("bft.pacemaker.proposed_transactions"), - BFT_PACEMAKER_PROPOSALS_SENT("bft.pacemaker.proposals_sent"), - BFT_PACEMAKER_TIMED_OUT_ROUNDS("bft.pacemaker.timed_out_rounds"), - - BFT_SYNC_REQUESTS_SENT("bft.sync.requests_sent"), - BFT_SYNC_REQUESTS_RECEIVED("bft.sync.requests_received"), - BFT_SYNC_REQUEST_TIMEOUTS("bft.sync.request_timeouts"), - - /** Number of views that timed out. Rescheduled timeouts of the same view are not counted */ - BFT_VERTEX_STORE_SIZE("bft.vertex_store.size"), - BFT_VERTEX_STORE_FORKS("bft.vertex_store.forks"), - BFT_VERTEX_STORE_REBUILDS("bft.vertex_store.rebuilds"), - BFT_VERTEX_STORE_INDIRECT_PARENTS("bft.vertex_store.indirect_parents"), - - - // Count of database accesses - COUNT_BDB_LEDGER_COMMIT("count.bdb.ledger.commit"), - COUNT_BDB_LEDGER_CREATE_TX("count.bdb.ledger.create_tx"), - COUNT_BDB_LEDGER_CONTAINS("count.bdb.ledger.contains"), - COUNT_BDB_LEDGER_CONTAINS_TX("count.bdb.ledger.contains_tx"), - COUNT_BDB_LEDGER_ENTRIES("count.bdb.ledger.entries"), - COUNT_BDB_LEDGER_GET_FIRST("count.bdb.ledger.get_first"), - COUNT_BDB_LEDGER_GET_LAST("count.bdb.ledger.get_last"), - COUNT_BDB_LEDGER_GET_NEXT("count.bdb.ledger.get_next"), - COUNT_BDB_LEDGER_GET_PREV("count.bdb.ledger.get_prev"), - COUNT_BDB_LEDGER_STORE("count.bdb.ledger.store"), - COUNT_BDB_LEDGER_LAST_COMMITTED("count.bdb.ledger.last_committed"), - COUNT_BDB_LEDGER_LAST_VERTEX("count.bdb.ledger.last_vertex"), - COUNT_BDB_LEDGER_SAVE("count.bdb.ledger.save"), - COUNT_BDB_LEDGER_SEARCH("count.bdb.ledger.search"), - COUNT_BDB_LEDGER_TOTAL("count.bdb.ledger.total"), - COUNT_BDB_LEDGER_BYTES_READ("count.bdb.ledger.bytes.read"), - COUNT_BDB_LEDGER_BYTES_WRITE("count.bdb.ledger.bytes.write"), - COUNT_BDB_LEDGER_DELETES("count.bdb.ledger.deletes"), - COUNT_BDB_LEDGER_PROOFS_ADDED("count.bdb.ledger.proofs.added"), - COUNT_BDB_LEDGER_PROOFS_REMOVED("count.bdb.ledger.proofs.removed"), - - COUNT_BDB_ADDRESS_BOOK_TOTAL("count.bdb.address_book.total"), - COUNT_BDB_ADDRESS_BOOK_BYTES_READ("count.bdb.address_book.bytes.read"), - COUNT_BDB_ADDRESS_BOOK_BYTES_WRITE("count.bdb.address_book.bytes.write"), - COUNT_BDB_ADDRESS_BOOK_DELETES("count.bdb.address_book.deletes"), - - COUNT_BDB_SAFETY_STATE_TOTAL("count.bdb.safety_state.total"), - COUNT_BDB_SAFETY_STATE_BYTES_READ("count.bdb.safety_state.bytes.read"), - COUNT_BDB_SAFETY_STATE_BYTES_WRITE("count.bdb.safety_state.bytes.write"), - - COUNT_BDB_HEADER_BYTES_WRITE("count.bdb.header.bytes.write"), - - // Total elapsed time for database access, in microseconds - ELAPSED_APIDB_BALANCE_READ("elapsed.apidb.balance.read"), - ELAPSED_APIDB_BALANCE_WRITE("elapsed.apidb.balance.write"), - - ELAPSED_APIDB_TOKEN_READ("elapsed.apidb.token.read"), - ELAPSED_APIDB_TOKEN_WRITE("elapsed.apidb.token.write"), - - ELAPSED_APIDB_TRANSACTION_READ("elapsed.apidb.transaction.read"), - ELAPSED_APIDB_TRANSACTION_WRITE("elapsed.apidb.transaction.write"), - - ELAPSED_APIDB_FLUSH_TIME("elapsed.apidb.flush.time"), - - ELAPSED_BDB_ADDRESS_BOOK("elapsed.bdb.address_book"), - - ELAPSED_BDB_LEDGER_COMMIT("elapsed.bdb.ledger.commit"), - ELAPSED_BDB_LEDGER_CREATE_TX("elapsed.bdb.ledger.create_tx"), - ELAPSED_BDB_LEDGER_CONTAINS_TX("elapsed.bdb.ledger.contains_tx"), - ELAPSED_BDB_LEDGER_ENTRIES("elapsed.bdb.ledger.entries"), - ELAPSED_BDB_LEDGER_GET("elapsed.bdb.ledger.get"), - ELAPSED_BDB_LEDGER_GET_FIRST("elapsed.bdb.ledger.get_first"), - ELAPSED_BDB_LEDGER_GET_LAST("elapsed.bdb.ledger.get_last"), - ELAPSED_BDB_LEDGER_STORE("elapsed.bdb.ledger.store"), - ELAPSED_BDB_LEDGER_LAST_COMMITTED("elapsed.bdb.ledger.last_committed"), - ELAPSED_BDB_LEDGER_LAST_VERTEX("elapsed.bdb.ledger.last_vertex"), - ELAPSED_BDB_LEDGER_SAVE("elapsed.bdb.ledger.save"), - ELAPSED_BDB_LEDGER_SEARCH("elapsed.bdb.ledger.search"), - ELAPSED_BDB_LEDGER_TOTAL("elapsed.bdb.ledger.total"), - - ELAPSED_BDB_SAFETY_STATE("elapsed.bdb.safety_state"), - - PERSISTENCE_VERTEX_STORE_SAVES("persistence.vertex_store_saves"), - PERSISTENCE_SAFETY_STORE_SAVES("persistence.safety_store_saves"), - - PERSISTENCE_ATOM_LOG_WRITE_BYTES("persistence.atom_log.write_bytes"), - PERSISTENCE_ATOM_LOG_WRITE_COMPRESSED("persistence.atom_log.write_compressed"), - - EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS("epoch_manager.queued_consensus_events"), - - STARTUP_TIME_MS("startup.time_ms"), - - HASHED_BYTES("hashed.bytes"), - - LEDGER_STATE_VERSION("ledger.state_version"), - LEDGER_SYNC_COMMANDS_PROCESSED("ledger.sync_commands_processed"), - LEDGER_BFT_COMMANDS_PROCESSED("ledger.bft_commands_processed"), - - SYNC_INVALID_RESPONSES_RECEIVED("sync.invalid_responses_received"), - SYNC_VALID_RESPONSES_RECEIVED("sync.valid_responses_received"), - SYNC_REMOTE_REQUESTS_RECEIVED("sync.remote_requests_received"), - SYNC_CURRENT_STATE_VERSION("sync.current_state_version"), - SYNC_TARGET_STATE_VERSION("sync.target_state_version"), - - MEMPOOL_CURRENT_SIZE("mempool.current_size"), - MEMPOOL_RELAYS_SENT("mempool.relays_sent"), - MEMPOOL_ADD_SUCCESS("mempool.add_success"), - MEMPOOL_ADD_FAILURE("mempool.add_failure"), - - RADIX_ENGINE_INVALID_PROPOSED_COMMANDS("radix_engine.invalid_proposed_commands"), - RADIX_ENGINE_USER_TRANSACTIONS("radix_engine.user_transactions"), - RADIX_ENGINE_SYSTEM_TRANSACTIONS("radix_engine.system_transactions"), - - MESSAGES_INBOUND_AVG_QUEUED_TIME("messages.inbound.avg_queued_time"), - MESSAGES_INBOUND_TOTAL_QUEUED_TIME("messages.inbound.total_queued_time"), - MESSAGES_INBOUND_AVG_PROCESSING_TIME("messages.inbound.avg_processing_time"), - MESSAGES_INBOUND_TOTAL_PROCESSING_TIME("messages.inbound.total_processing_time"), - MESSAGES_INBOUND_RECEIVED("messages.inbound.received"), - MESSAGES_INBOUND_PROCESSED("messages.inbound.processed"), - MESSAGES_INBOUND_DISCARDED("messages.inbound.discarded"), - MESSAGES_OUTBOUND_ABORTED("messages.outbound.aborted"), - MESSAGES_OUTBOUND_PENDING("messages.outbound.pending"), - MESSAGES_OUTBOUND_PROCESSED("messages.outbound.processed"), - MESSAGES_OUTBOUND_SENT("messages.outbound.sent"), - - NETWORKING_TCP_DROPPED_MESSAGES("networking.tcp.dropped_messages"), - NETWORKING_BYTES_SENT("networking.bytes_sent"), - NETWORKING_BYTES_RECEIVED("networking.bytes_received"), - NETWORKING_P2P_ACTIVE_INBOUND_CHANNELS("networking.p2p.active_inbound_channels"), - NETWORKING_P2P_ACTIVE_OUTBOUND_CHANNELS("networking.p2p.active_outbound_channels"), - NETWORKING_P2P_ACTIVE_CHANNELS("networking.p2p.active_channels"), - NETWORKING_P2P_CHANNELS_INITIALIZED("networking.p2p.channels_initialized"), - - SIGNATURES_SIGNED("signatures.signed"), - SIGNATURES_VERIFIED("signatures.verified"), - TIME_DURATION("time.duration"); - - private final String jsonPath; - - CounterType(String jsonPath) { - this.jsonPath = jsonPath; - } - - public String jsonPath() { - return jsonPath; - } - } - - /** - * Increments the specified counter, returning the new value. - * - * @param counterType The counter to increment - * @return The new incremented value - */ - long increment(CounterType counterType); - - /** - * Increments the specified counter by the specified amount, - * returning the new value. - * - * @param counterType The counter to increment - * @return The new incremented value - */ - long add(CounterType counterType, long amount); - - /** - * Sets the specified counter to the specified value, - * returning the previous value. - * - * @param counterType The counter to increment - * @return The previous value - */ - long set(CounterType counterType, long value); - - /** - * Returns the current value of the specified counter. - * - * @param counterType The counter value to return - * @return The current value of the counter - */ - long get(CounterType counterType); - - /** - * Set a group of values. Values are updates in such - * a way as to prevent read-tearing when {@link #toMap()} - * is called. - * - * @param newValues The values to update. - */ - void setAll(Map newValues); - - /** - * Returns the current values as a map. - * @return the current values as a map - */ - Map toMap(); -} \ No newline at end of file + enum CounterType { + // Please keep these sorted + + BFT_EVENTS_RECEIVED("bft.events_received"), + BFT_COMMITTED_VERTICES("bft.committed_vertices"), + /** Number of proposals rejected. */ + BFT_NO_VOTES_SENT("bft.no_votes_sent"), + /** Number of vote quorums formed. */ + BFT_VOTE_QUORUMS("bft.vote_quorums"), + /** Number of view-timeout quorums formed. */ + BFT_TIMEOUT_QUORUMS("bft.timeout_quorums"), + + /** Number of times a view-timeout message was broadcast. */ + BFT_PACEMAKER_TIMEOUTS_SENT("bft.pacemaker.timeouts_sent"), + BFT_PACEMAKER_ROUND("bft.pacemaker.round"), + BFT_PACEMAKER_PROPOSED_TRANSACTIONS("bft.pacemaker.proposed_transactions"), + BFT_PACEMAKER_PROPOSALS_SENT("bft.pacemaker.proposals_sent"), + BFT_PACEMAKER_TIMED_OUT_ROUNDS("bft.pacemaker.timed_out_rounds"), + + BFT_SYNC_REQUESTS_SENT("bft.sync.requests_sent"), + BFT_SYNC_REQUESTS_RECEIVED("bft.sync.requests_received"), + BFT_SYNC_REQUEST_TIMEOUTS("bft.sync.request_timeouts"), + + /** Number of views that timed out. Rescheduled timeouts of the same view are not counted */ + BFT_VERTEX_STORE_SIZE("bft.vertex_store.size"), + BFT_VERTEX_STORE_FORKS("bft.vertex_store.forks"), + BFT_VERTEX_STORE_REBUILDS("bft.vertex_store.rebuilds"), + BFT_VERTEX_STORE_INDIRECT_PARENTS("bft.vertex_store.indirect_parents"), + + // Count of database accesses + COUNT_BDB_LEDGER_COMMIT("count.bdb.ledger.commit"), + COUNT_BDB_LEDGER_CREATE_TX("count.bdb.ledger.create_tx"), + COUNT_BDB_LEDGER_CONTAINS("count.bdb.ledger.contains"), + COUNT_BDB_LEDGER_CONTAINS_TX("count.bdb.ledger.contains_tx"), + COUNT_BDB_LEDGER_ENTRIES("count.bdb.ledger.entries"), + COUNT_BDB_LEDGER_GET_FIRST("count.bdb.ledger.get_first"), + COUNT_BDB_LEDGER_GET_LAST("count.bdb.ledger.get_last"), + COUNT_BDB_LEDGER_GET_NEXT("count.bdb.ledger.get_next"), + COUNT_BDB_LEDGER_GET_PREV("count.bdb.ledger.get_prev"), + COUNT_BDB_LEDGER_STORE("count.bdb.ledger.store"), + COUNT_BDB_LEDGER_LAST_COMMITTED("count.bdb.ledger.last_committed"), + COUNT_BDB_LEDGER_LAST_VERTEX("count.bdb.ledger.last_vertex"), + COUNT_BDB_LEDGER_SAVE("count.bdb.ledger.save"), + COUNT_BDB_LEDGER_SEARCH("count.bdb.ledger.search"), + COUNT_BDB_LEDGER_TOTAL("count.bdb.ledger.total"), + COUNT_BDB_LEDGER_BYTES_READ("count.bdb.ledger.bytes.read"), + COUNT_BDB_LEDGER_BYTES_WRITE("count.bdb.ledger.bytes.write"), + COUNT_BDB_LEDGER_DELETES("count.bdb.ledger.deletes"), + COUNT_BDB_LEDGER_PROOFS_ADDED("count.bdb.ledger.proofs.added"), + COUNT_BDB_LEDGER_PROOFS_REMOVED("count.bdb.ledger.proofs.removed"), + + COUNT_BDB_ADDRESS_BOOK_TOTAL("count.bdb.address_book.total"), + COUNT_BDB_ADDRESS_BOOK_BYTES_READ("count.bdb.address_book.bytes.read"), + COUNT_BDB_ADDRESS_BOOK_BYTES_WRITE("count.bdb.address_book.bytes.write"), + COUNT_BDB_ADDRESS_BOOK_DELETES("count.bdb.address_book.deletes"), + + COUNT_BDB_SAFETY_STATE_TOTAL("count.bdb.safety_state.total"), + COUNT_BDB_SAFETY_STATE_BYTES_READ("count.bdb.safety_state.bytes.read"), + COUNT_BDB_SAFETY_STATE_BYTES_WRITE("count.bdb.safety_state.bytes.write"), + + COUNT_BDB_HEADER_BYTES_WRITE("count.bdb.header.bytes.write"), + + // Total elapsed time for database access, in microseconds + ELAPSED_APIDB_BALANCE_READ("elapsed.apidb.balance.read"), + ELAPSED_APIDB_BALANCE_WRITE("elapsed.apidb.balance.write"), + + ELAPSED_APIDB_TOKEN_READ("elapsed.apidb.token.read"), + ELAPSED_APIDB_TOKEN_WRITE("elapsed.apidb.token.write"), + + ELAPSED_APIDB_TRANSACTION_READ("elapsed.apidb.transaction.read"), + ELAPSED_APIDB_TRANSACTION_WRITE("elapsed.apidb.transaction.write"), + + ELAPSED_APIDB_FLUSH_TIME("elapsed.apidb.flush.time"), + + ELAPSED_BDB_ADDRESS_BOOK("elapsed.bdb.address_book"), + + ELAPSED_BDB_LEDGER_COMMIT("elapsed.bdb.ledger.commit"), + ELAPSED_BDB_LEDGER_CREATE_TX("elapsed.bdb.ledger.create_tx"), + ELAPSED_BDB_LEDGER_CONTAINS_TX("elapsed.bdb.ledger.contains_tx"), + ELAPSED_BDB_LEDGER_ENTRIES("elapsed.bdb.ledger.entries"), + ELAPSED_BDB_LEDGER_GET("elapsed.bdb.ledger.get"), + ELAPSED_BDB_LEDGER_GET_FIRST("elapsed.bdb.ledger.get_first"), + ELAPSED_BDB_LEDGER_GET_LAST("elapsed.bdb.ledger.get_last"), + ELAPSED_BDB_LEDGER_STORE("elapsed.bdb.ledger.store"), + ELAPSED_BDB_LEDGER_LAST_COMMITTED("elapsed.bdb.ledger.last_committed"), + ELAPSED_BDB_LEDGER_LAST_VERTEX("elapsed.bdb.ledger.last_vertex"), + ELAPSED_BDB_LEDGER_SAVE("elapsed.bdb.ledger.save"), + ELAPSED_BDB_LEDGER_SEARCH("elapsed.bdb.ledger.search"), + ELAPSED_BDB_LEDGER_TOTAL("elapsed.bdb.ledger.total"), + + ELAPSED_BDB_SAFETY_STATE("elapsed.bdb.safety_state"), + + PERSISTENCE_VERTEX_STORE_SAVES("persistence.vertex_store_saves"), + PERSISTENCE_SAFETY_STORE_SAVES("persistence.safety_store_saves"), + + PERSISTENCE_ATOM_LOG_WRITE_BYTES("persistence.atom_log.write_bytes"), + PERSISTENCE_ATOM_LOG_WRITE_COMPRESSED("persistence.atom_log.write_compressed"), + + EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS("epoch_manager.queued_consensus_events"), + + STARTUP_TIME_MS("startup.time_ms"), + + HASHED_BYTES("hashed.bytes"), + + LEDGER_STATE_VERSION("ledger.state_version"), + LEDGER_SYNC_COMMANDS_PROCESSED("ledger.sync_commands_processed"), + LEDGER_BFT_COMMANDS_PROCESSED("ledger.bft_commands_processed"), + + SYNC_INVALID_RESPONSES_RECEIVED("sync.invalid_responses_received"), + SYNC_VALID_RESPONSES_RECEIVED("sync.valid_responses_received"), + SYNC_REMOTE_REQUESTS_RECEIVED("sync.remote_requests_received"), + SYNC_CURRENT_STATE_VERSION("sync.current_state_version"), + SYNC_TARGET_STATE_VERSION("sync.target_state_version"), + + MEMPOOL_CURRENT_SIZE("mempool.current_size"), + MEMPOOL_RELAYS_SENT("mempool.relays_sent"), + MEMPOOL_ADD_SUCCESS("mempool.add_success"), + MEMPOOL_ADD_FAILURE("mempool.add_failure"), + + RADIX_ENGINE_INVALID_PROPOSED_COMMANDS("radix_engine.invalid_proposed_commands"), + RADIX_ENGINE_USER_TRANSACTIONS("radix_engine.user_transactions"), + RADIX_ENGINE_SYSTEM_TRANSACTIONS("radix_engine.system_transactions"), + + MESSAGES_INBOUND_AVG_QUEUED_TIME("messages.inbound.avg_queued_time"), + MESSAGES_INBOUND_TOTAL_QUEUED_TIME("messages.inbound.total_queued_time"), + MESSAGES_INBOUND_AVG_PROCESSING_TIME("messages.inbound.avg_processing_time"), + MESSAGES_INBOUND_TOTAL_PROCESSING_TIME("messages.inbound.total_processing_time"), + MESSAGES_INBOUND_RECEIVED("messages.inbound.received"), + MESSAGES_INBOUND_PROCESSED("messages.inbound.processed"), + MESSAGES_INBOUND_DISCARDED("messages.inbound.discarded"), + MESSAGES_OUTBOUND_ABORTED("messages.outbound.aborted"), + MESSAGES_OUTBOUND_PENDING("messages.outbound.pending"), + MESSAGES_OUTBOUND_PROCESSED("messages.outbound.processed"), + MESSAGES_OUTBOUND_SENT("messages.outbound.sent"), + + NETWORKING_TCP_DROPPED_MESSAGES("networking.tcp.dropped_messages"), + NETWORKING_BYTES_SENT("networking.bytes_sent"), + NETWORKING_BYTES_RECEIVED("networking.bytes_received"), + NETWORKING_P2P_ACTIVE_INBOUND_CHANNELS("networking.p2p.active_inbound_channels"), + NETWORKING_P2P_ACTIVE_OUTBOUND_CHANNELS("networking.p2p.active_outbound_channels"), + NETWORKING_P2P_ACTIVE_CHANNELS("networking.p2p.active_channels"), + NETWORKING_P2P_CHANNELS_INITIALIZED("networking.p2p.channels_initialized"), + + SIGNATURES_SIGNED("signatures.signed"), + SIGNATURES_VERIFIED("signatures.verified"), + TIME_DURATION("time.duration"); + + private final String jsonPath; + + CounterType(String jsonPath) { + this.jsonPath = jsonPath; + } + + public String jsonPath() { + return jsonPath; + } + } + + /** + * Increments the specified counter, returning the new value. + * + * @param counterType The counter to increment + * @return The new incremented value + */ + long increment(CounterType counterType); + + /** + * Increments the specified counter by the specified amount, returning the new value. + * + * @param counterType The counter to increment + * @return The new incremented value + */ + long add(CounterType counterType, long amount); + + /** + * Sets the specified counter to the specified value, returning the previous value. + * + * @param counterType The counter to increment + * @return The previous value + */ + long set(CounterType counterType, long value); + + /** + * Returns the current value of the specified counter. + * + * @param counterType The counter value to return + * @return The current value of the counter + */ + long get(CounterType counterType); + + /** + * Set a group of values. Values are updates in such a way as to prevent read-tearing when {@link + * #toMap()} is called. + * + * @param newValues The values to update. + */ + void setAll(Map newValues); + + /** + * Returns the current values as a map. + * + * @return the current values as a map + */ + Map toMap(); +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCountersImpl.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCountersImpl.java index c195a4882c..fa6d4c476b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCountersImpl.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/counters/SystemCountersImpl.java @@ -64,103 +64,102 @@ package com.radixdlt.counters; +import com.google.common.collect.Maps; import java.time.Instant; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; -import com.google.common.collect.Maps; - -/** - * Event counting utility class. - */ +/** Event counting utility class. */ public final class SystemCountersImpl implements SystemCounters { - private static final List COUNTER_LIST = List.of(CounterType.values()); - - private final EnumMap counters = new EnumMap<>(CounterType.class); - private final String since; - - public SystemCountersImpl() { - this(System.currentTimeMillis()); - } - - public SystemCountersImpl(long startTime) { - COUNTER_LIST.stream() - .filter(counterType -> counterType != CounterType.TIME_DURATION) - .forEach(counterType -> counters.put(counterType, new AtomicLong(0))); - - //This one is special, kinda "self ticking" - counters.put(CounterType.TIME_DURATION, new AtomicLong() { - @Override - public long longValue() { - return System.currentTimeMillis() - startTime; - } - }); - - since = Instant.ofEpochMilli(startTime).toString(); - } - - @Override - public long increment(CounterType counterType) { - return counters.get(counterType).incrementAndGet(); - } - - @Override - public long add(CounterType counterType, long amount) { - return counters.get(counterType).addAndGet(amount); - } - - @Override - public long set(CounterType counterType, long value) { - return counters.get(counterType).getAndSet(value); - } - - @Override - public long get(CounterType counterType) { - return counters.get(counterType).longValue(); - } - - @Override - public void setAll(Map newValues) { - // Note that this only prevents read tearing - // Lost updates are still possible - synchronized (counters) { - for (Map.Entry e : newValues.entrySet()) { - counters.get(e.getKey()).set(e.getValue()); - } - } - } - - @Override - public Map toMap() { - var output = Maps.newTreeMap(); - - synchronized (counters) { - COUNTER_LIST.forEach(counter -> addValue(output, makePath(counter.jsonPath()), get(counter))); - } - - addValue(output, makePath("time.since"), since); - - return output; - } - - @SuppressWarnings("unchecked") - private void addValue(Map values, String[] path, Object value) { - for (int i = 0; i < path.length - 1; ++i) { - // Needs exhaustive testing to ensure correctness. - // Will fail if there is a counter called foo.bar and a counter called foo.bar.baz. - values = (Map) values.computeIfAbsent(path[i], k -> Maps.newTreeMap()); - } - values.put(path[path.length - 1], value); - } - - private String[] makePath(String jsonPath) { - return jsonPath.split("\\."); - } - - @Override - public String toString() { - return String.format("SystemCountersImpl[%s]", toMap()); - } + private static final List COUNTER_LIST = List.of(CounterType.values()); + + private final EnumMap counters = new EnumMap<>(CounterType.class); + private final String since; + + public SystemCountersImpl() { + this(System.currentTimeMillis()); + } + + public SystemCountersImpl(long startTime) { + COUNTER_LIST.stream() + .filter(counterType -> counterType != CounterType.TIME_DURATION) + .forEach(counterType -> counters.put(counterType, new AtomicLong(0))); + + // This one is special, kinda "self ticking" + counters.put( + CounterType.TIME_DURATION, + new AtomicLong() { + @Override + public long longValue() { + return System.currentTimeMillis() - startTime; + } + }); + + since = Instant.ofEpochMilli(startTime).toString(); + } + + @Override + public long increment(CounterType counterType) { + return counters.get(counterType).incrementAndGet(); + } + + @Override + public long add(CounterType counterType, long amount) { + return counters.get(counterType).addAndGet(amount); + } + + @Override + public long set(CounterType counterType, long value) { + return counters.get(counterType).getAndSet(value); + } + + @Override + public long get(CounterType counterType) { + return counters.get(counterType).longValue(); + } + + @Override + public void setAll(Map newValues) { + // Note that this only prevents read tearing + // Lost updates are still possible + synchronized (counters) { + for (Map.Entry e : newValues.entrySet()) { + counters.get(e.getKey()).set(e.getValue()); + } + } + } + + @Override + public Map toMap() { + var output = Maps.newTreeMap(); + + synchronized (counters) { + COUNTER_LIST.forEach(counter -> addValue(output, makePath(counter.jsonPath()), get(counter))); + } + + addValue(output, makePath("time.since"), since); + + return output; + } + + @SuppressWarnings("unchecked") + private void addValue(Map values, String[] path, Object value) { + for (int i = 0; i < path.length - 1; ++i) { + // Needs exhaustive testing to ensure correctness. + // Will fail if there is a counter called foo.bar and a counter called foo.bar.baz. + values = (Map) values.computeIfAbsent(path[i], k -> Maps.newTreeMap()); + } + values.put(path[path.length - 1], value); + } + + private String[] makePath(String jsonPath) { + return jsonPath.split("\\."); + } + + @Override + public String toString() { + return String.format("SystemCountersImpl[%s]", toMap()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Dispatchers.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Dispatchers.java index 4d71eecde8..8268132f8d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Dispatchers.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Dispatchers.java @@ -70,159 +70,146 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; import com.radixdlt.counters.SystemCounters; - -import javax.annotation.Nullable; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.Nullable; -/** - * Helper class to set up environment with dispatched events - */ -//TODO: get rid of field injection https://radixdlt.atlassian.net/browse/NT-3 +/** Helper class to set up environment with dispatched events */ +// TODO: get rid of field injection https://radixdlt.atlassian.net/browse/NT-3 public final class Dispatchers { - private Dispatchers() { - throw new IllegalStateException("Cannot instantiate."); - } - - private static class DispatcherProvider implements Provider> { - @Inject - private Provider environmentProvider; - - @Inject - private SystemCounters systemCounters; - - @Inject - private Set> onDispatchProcessors; - - private final Class c; - private final Function counterTypeMapper; - - DispatcherProvider( - Class c, - @Nullable Function counterTypeMapper - ) { - this.c = c; - this.counterTypeMapper = counterTypeMapper; - } - - @Override - public EventDispatcher get() { - final EventDispatcher dispatcher = environmentProvider.get().getDispatcher(c); - final Set> processors = onDispatchProcessors.stream() - .flatMap(p -> p.getProcessor(c).stream()) - .collect(Collectors.toSet()); - return e -> { - dispatcher.dispatch(e); - processors.forEach(p -> p.process(e)); - if (counterTypeMapper != null) { - systemCounters.increment(counterTypeMapper.apply(e)); - } - }; - } - } - - private static final class ScheduledDispatcherProvider implements Provider> { - @Inject - private Provider environmentProvider; - private final Class eventClass; - private final TypeLiteral eventLiteral; - - ScheduledDispatcherProvider(Class eventClass) { - this.eventClass = eventClass; - this.eventLiteral = null; - } - - ScheduledDispatcherProvider(TypeLiteral eventLiteral) { - this.eventClass = null; - this.eventLiteral = eventLiteral; - } - - @Override - public ScheduledEventDispatcher get() { - Environment e = environmentProvider.get(); - if (eventClass != null) { - return e.getScheduledDispatcher(eventClass); - } else { - return e.getScheduledDispatcher(eventLiteral); - } - } - } - - private static final class RemoteDispatcherProvider implements Provider> { - @Inject - private Provider environmentProvider; - - @Inject - private SystemCounters systemCounters; - - @Inject - @Self - private BFTNode self; - - @Inject - private Set> onDispatchProcessors; - - private final SystemCounters.CounterType counterType; - private final Class c; - - RemoteDispatcherProvider(Class c) { - this(c, null); - } - - RemoteDispatcherProvider( - Class c, - @Nullable SystemCounters.CounterType counterType - ) { - this.c = c; - this.counterType = counterType; - } - - @Override - public RemoteEventDispatcher get() { - var remoteDispatcher = environmentProvider.get().getRemoteDispatcher(c); - var localDispatcher = environmentProvider.get().getDispatcher(c); - final Set> onDispatch = onDispatchProcessors.stream() - .flatMap(p -> p.getProcessor(c).stream()) - .collect(Collectors.toSet()); - return (node, e) -> { - if (node.equals(self)) { - localDispatcher.dispatch(e); - } else { - remoteDispatcher.dispatch(node, e); - } - onDispatch.forEach(p -> p.process(e)); - if (counterType != null) { - systemCounters.increment(counterType); - } - }; - } - } - - public static Provider> dispatcherProvider(Class c) { - return new DispatcherProvider<>(c, null); - } - - public static Provider> dispatcherProvider( - Class c, - Function counterTypeMapper - ) { - return new DispatcherProvider<>(c, counterTypeMapper); - } - - public static Provider> scheduledDispatcherProvider(Class c) { - return new ScheduledDispatcherProvider<>(c); - } - - public static Provider> scheduledDispatcherProvider(TypeLiteral t) { - return new ScheduledDispatcherProvider<>(t); - } - - public static Provider> remoteDispatcherProvider(Class c) { - return new RemoteDispatcherProvider<>(c); - } - - public static Provider> remoteDispatcherProvider(Class c, SystemCounters.CounterType counterType) { - return new RemoteDispatcherProvider<>(c, counterType); - } + private Dispatchers() { + throw new IllegalStateException("Cannot instantiate."); + } + + private static class DispatcherProvider implements Provider> { + @Inject private Provider environmentProvider; + + @Inject private SystemCounters systemCounters; + + @Inject private Set> onDispatchProcessors; + + private final Class c; + private final Function counterTypeMapper; + + DispatcherProvider( + Class c, @Nullable Function counterTypeMapper) { + this.c = c; + this.counterTypeMapper = counterTypeMapper; + } + + @Override + public EventDispatcher get() { + final EventDispatcher dispatcher = environmentProvider.get().getDispatcher(c); + final Set> processors = + onDispatchProcessors.stream() + .flatMap(p -> p.getProcessor(c).stream()) + .collect(Collectors.toSet()); + return e -> { + dispatcher.dispatch(e); + processors.forEach(p -> p.process(e)); + if (counterTypeMapper != null) { + systemCounters.increment(counterTypeMapper.apply(e)); + } + }; + } + } + + private static final class ScheduledDispatcherProvider + implements Provider> { + @Inject private Provider environmentProvider; + private final Class eventClass; + private final TypeLiteral eventLiteral; + + ScheduledDispatcherProvider(Class eventClass) { + this.eventClass = eventClass; + this.eventLiteral = null; + } + + ScheduledDispatcherProvider(TypeLiteral eventLiteral) { + this.eventClass = null; + this.eventLiteral = eventLiteral; + } + + @Override + public ScheduledEventDispatcher get() { + Environment e = environmentProvider.get(); + if (eventClass != null) { + return e.getScheduledDispatcher(eventClass); + } else { + return e.getScheduledDispatcher(eventLiteral); + } + } + } + + private static final class RemoteDispatcherProvider + implements Provider> { + @Inject private Provider environmentProvider; + + @Inject private SystemCounters systemCounters; + + @Inject @Self private BFTNode self; + + @Inject private Set> onDispatchProcessors; + + private final SystemCounters.CounterType counterType; + private final Class c; + + RemoteDispatcherProvider(Class c) { + this(c, null); + } + + RemoteDispatcherProvider(Class c, @Nullable SystemCounters.CounterType counterType) { + this.c = c; + this.counterType = counterType; + } + + @Override + public RemoteEventDispatcher get() { + var remoteDispatcher = environmentProvider.get().getRemoteDispatcher(c); + var localDispatcher = environmentProvider.get().getDispatcher(c); + final Set> onDispatch = + onDispatchProcessors.stream() + .flatMap(p -> p.getProcessor(c).stream()) + .collect(Collectors.toSet()); + return (node, e) -> { + if (node.equals(self)) { + localDispatcher.dispatch(e); + } else { + remoteDispatcher.dispatch(node, e); + } + onDispatch.forEach(p -> p.process(e)); + if (counterType != null) { + systemCounters.increment(counterType); + } + }; + } + } + + public static Provider> dispatcherProvider(Class c) { + return new DispatcherProvider<>(c, null); + } + + public static Provider> dispatcherProvider( + Class c, Function counterTypeMapper) { + return new DispatcherProvider<>(c, counterTypeMapper); + } + + public static Provider> scheduledDispatcherProvider(Class c) { + return new ScheduledDispatcherProvider<>(c); + } + + public static Provider> scheduledDispatcherProvider( + TypeLiteral t) { + return new ScheduledDispatcherProvider<>(t); + } + + public static Provider> remoteDispatcherProvider(Class c) { + return new RemoteDispatcherProvider<>(c); + } + + public static Provider> remoteDispatcherProvider( + Class c, SystemCounters.CounterType counterType) { + return new RemoteDispatcherProvider<>(c, counterType); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Environment.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Environment.java index f0979a8c39..14f3fe6c48 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Environment.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Environment.java @@ -67,13 +67,15 @@ import com.google.inject.TypeLiteral; /** - * The environment where events get dispatched to. The implementing - * environment is then responsible for getting dispatched events to their - * processors. + * The environment where events get dispatched to. The implementing environment is then responsible + * for getting dispatched events to their processors. */ public interface Environment { - EventDispatcher getDispatcher(Class eventClass); - ScheduledEventDispatcher getScheduledDispatcher(Class eventClass); - ScheduledEventDispatcher getScheduledDispatcher(TypeLiteral typeLiteral); - RemoteEventDispatcher getRemoteDispatcher(Class eventClass); + EventDispatcher getDispatcher(Class eventClass); + + ScheduledEventDispatcher getScheduledDispatcher(Class eventClass); + + ScheduledEventDispatcher getScheduledDispatcher(TypeLiteral typeLiteral); + + RemoteEventDispatcher getRemoteDispatcher(Class eventClass); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventDispatcher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventDispatcher.java index 9e63bdd1b2..b1eae613f8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventDispatcher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventDispatcher.java @@ -70,5 +70,5 @@ * @param the event class */ public interface EventDispatcher { - void dispatch(T t); + void dispatch(T t); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessor.java index c66504e5a7..08ae0ea124 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessor.java @@ -65,11 +65,10 @@ package com.radixdlt.environment; /** - * Processes an event. An environment is responsible for making - * the call into processors. + * Processes an event. An environment is responsible for making the call into processors. * * @param the event class */ public interface EventProcessor { - void process(T t); + void process(T t); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnDispatch.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnDispatch.java index a60e0cc919..e345f8d2c3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnDispatch.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnDispatch.java @@ -69,26 +69,27 @@ /** * A processor which is to process an event synchronously at time of dispatch + * * @param the class of the event */ public final class EventProcessorOnDispatch { - private final Class eventClass; - private final EventProcessor processor; + private final Class eventClass; + private final EventProcessor processor; - public EventProcessorOnDispatch(Class eventClass, EventProcessor processor) { - this.eventClass = Objects.requireNonNull(eventClass); - this.processor = Objects.requireNonNull(processor); - } + public EventProcessorOnDispatch(Class eventClass, EventProcessor processor) { + this.eventClass = Objects.requireNonNull(eventClass); + this.processor = Objects.requireNonNull(processor); + } - public Optional> getProcessor(Class c) { - if (c.equals(eventClass)) { - return Optional.of((EventProcessor) processor); - } + public Optional> getProcessor(Class c) { + if (c.equals(eventClass)) { + return Optional.of((EventProcessor) processor); + } - return Optional.empty(); - } + return Optional.empty(); + } - public Class getEventClass() { - return eventClass; - } + public Class getEventClass() { + return eventClass; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnRunner.java index d57e90b841..45cff88109 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnRunner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/EventProcessorOnRunner.java @@ -65,7 +65,6 @@ package com.radixdlt.environment; import com.google.inject.TypeLiteral; - import java.util.Objects; import java.util.Optional; @@ -75,70 +74,72 @@ * @param The event class */ public final class EventProcessorOnRunner { - private final String runnerName; - private final Class eventClass; - private final TypeLiteral typeLiteral; - private final EventProcessor processor; - private final long rateLimitDelayMs; - - public EventProcessorOnRunner(String runnerName, Class eventClass, EventProcessor processor) { - this(runnerName, eventClass, null, processor, 0); - } - - public EventProcessorOnRunner(String runnerName, TypeLiteral typeLiteral, EventProcessor processor) { - this(runnerName, null, typeLiteral, processor, 0); - } - - public EventProcessorOnRunner(String runnerName, Class eventClass, EventProcessor processor, long rateLimitDelayMs) { - this(runnerName, eventClass, null, processor, rateLimitDelayMs); - } - - private EventProcessorOnRunner( - String runnerName, - Class eventClass, - TypeLiteral typeLiteral, - EventProcessor processor, - long rateLimitDelayMs - ) { - this.runnerName = Objects.requireNonNull(runnerName); - this.eventClass = eventClass; - this.typeLiteral = typeLiteral; - this.processor = Objects.requireNonNull(processor); - if (rateLimitDelayMs < 0) { - throw new IllegalArgumentException("rateLimitDelayMs must be >= 0."); - } - this.rateLimitDelayMs = rateLimitDelayMs; - } - - public long getRateLimitDelayMs() { - return rateLimitDelayMs; - } - - public String getRunnerName() { - return runnerName; - } - - public Optional> getProcessor(Class c) { - if (eventClass != null && eventClass.isAssignableFrom(c)) { - return Optional.of((EventProcessor) processor); - } - - return Optional.empty(); - } - - public Optional> getProcessor(TypeLiteral c) { - if (c.equals(typeLiteral)) { - return Optional.of((EventProcessor) processor); - } - - return Optional.empty(); - } - - public Optional> getEventClass() { - return Optional.ofNullable(eventClass); - } - - public Optional> getTypeLiteral() { - return Optional.ofNullable(typeLiteral); - } + private final String runnerName; + private final Class eventClass; + private final TypeLiteral typeLiteral; + private final EventProcessor processor; + private final long rateLimitDelayMs; + + public EventProcessorOnRunner( + String runnerName, Class eventClass, EventProcessor processor) { + this(runnerName, eventClass, null, processor, 0); + } + + public EventProcessorOnRunner( + String runnerName, TypeLiteral typeLiteral, EventProcessor processor) { + this(runnerName, null, typeLiteral, processor, 0); + } + + public EventProcessorOnRunner( + String runnerName, Class eventClass, EventProcessor processor, long rateLimitDelayMs) { + this(runnerName, eventClass, null, processor, rateLimitDelayMs); + } + + private EventProcessorOnRunner( + String runnerName, + Class eventClass, + TypeLiteral typeLiteral, + EventProcessor processor, + long rateLimitDelayMs) { + this.runnerName = Objects.requireNonNull(runnerName); + this.eventClass = eventClass; + this.typeLiteral = typeLiteral; + this.processor = Objects.requireNonNull(processor); + if (rateLimitDelayMs < 0) { + throw new IllegalArgumentException("rateLimitDelayMs must be >= 0."); + } + this.rateLimitDelayMs = rateLimitDelayMs; + } + + public long getRateLimitDelayMs() { + return rateLimitDelayMs; + } + + public String getRunnerName() { + return runnerName; + } + + public Optional> getProcessor(Class c) { + if (eventClass != null && eventClass.isAssignableFrom(c)) { + return Optional.of((EventProcessor) processor); + } + + return Optional.empty(); + } + + public Optional> getProcessor(TypeLiteral c) { + if (c.equals(typeLiteral)) { + return Optional.of((EventProcessor) processor); + } + + return Optional.empty(); + } + + public Optional> getEventClass() { + return Optional.ofNullable(eventClass); + } + + public Optional> getTypeLiteral() { + return Optional.ofNullable(typeLiteral); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/LocalEvents.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/LocalEvents.java index 3791b6fa7a..f3e640752c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/LocalEvents.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/LocalEvents.java @@ -64,21 +64,19 @@ package com.radixdlt.environment; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + /** - * Classes which are used as local events - * TODO: Remove and infer LocalEvents from ProcessorOnRunners + * Classes which are used as local events TODO: Remove and infer LocalEvents from ProcessorOnRunners */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface LocalEvents { -} +public @interface LocalEvents {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessOnDispatch.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessOnDispatch.java index 926b7847be..35ff6af894 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessOnDispatch.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessOnDispatch.java @@ -74,12 +74,10 @@ import javax.inject.Qualifier; /** - * To be used with EventProcessor, this annotation is used to - * indicate that the event should be processed synchronously to - * the original dispatch. + * To be used with EventProcessor, this annotation is used to indicate that the event should be + * processed synchronously to the original dispatch. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface ProcessOnDispatch { -} +public @interface ProcessOnDispatch {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventDispatcher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventDispatcher.java index fc202ea831..87d3cb92bb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventDispatcher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventDispatcher.java @@ -72,9 +72,9 @@ * @param the event class */ public interface RemoteEventDispatcher { - void dispatch(BFTNode receiver, T t); + void dispatch(BFTNode receiver, T t); - default void dispatch(Iterable receivers, T t) { - receivers.forEach(r -> dispatch(r, t)); - } + default void dispatch(Iterable receivers, T t) { + receivers.forEach(r -> dispatch(r, t)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessor.java index bc2bf5856b..751b753d79 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessor.java @@ -73,9 +73,9 @@ * @param the event class */ public interface RemoteEventProcessor { - void process(BFTNode sender, T t); + void process(BFTNode sender, T t); - default void process(RemoteEvent event) { - process(event.getOrigin(), event.getEvent()); - } + default void process(RemoteEvent event) { + process(event.getOrigin(), event.getEvent()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessorOnRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessorOnRunner.java index f28fe0489c..2ad841e810 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessorOnRunner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/RemoteEventProcessorOnRunner.java @@ -69,50 +69,56 @@ /** * Registration for a remote event processor to run on a certain runner + * * @param the class of the remote event */ public final class RemoteEventProcessorOnRunner { - private final String runnerName; - private final Class eventClass; - private final RemoteEventProcessor processor; - private final long rateLimitDelayMs; - - public RemoteEventProcessorOnRunner(String runnerName, Class eventClass, RemoteEventProcessor processor) { - this(runnerName, eventClass, processor, 0); - } + private final String runnerName; + private final Class eventClass; + private final RemoteEventProcessor processor; + private final long rateLimitDelayMs; - public RemoteEventProcessorOnRunner(String runnerName, Class eventClass, RemoteEventProcessor processor, long rateLimitDelayMs) { - this.runnerName = Objects.requireNonNull(runnerName); - this.eventClass = Objects.requireNonNull(eventClass); - this.processor = Objects.requireNonNull(processor); - if (rateLimitDelayMs < 0) { - throw new IllegalArgumentException("rateLimitDelayMs must be >= 0."); - } - this.rateLimitDelayMs = rateLimitDelayMs; - } + public RemoteEventProcessorOnRunner( + String runnerName, Class eventClass, RemoteEventProcessor processor) { + this(runnerName, eventClass, processor, 0); + } - public long getRateLimitDelayMs() { - return rateLimitDelayMs; + public RemoteEventProcessorOnRunner( + String runnerName, + Class eventClass, + RemoteEventProcessor processor, + long rateLimitDelayMs) { + this.runnerName = Objects.requireNonNull(runnerName); + this.eventClass = Objects.requireNonNull(eventClass); + this.processor = Objects.requireNonNull(processor); + if (rateLimitDelayMs < 0) { + throw new IllegalArgumentException("rateLimitDelayMs must be >= 0."); } + this.rateLimitDelayMs = rateLimitDelayMs; + } - public String getRunnerName() { - return runnerName; - } + public long getRateLimitDelayMs() { + return rateLimitDelayMs; + } - public Optional> getProcessor(Class c) { - if (c.isAssignableFrom(eventClass)) { - return Optional.of((RemoteEventProcessor) processor); - } + public String getRunnerName() { + return runnerName; + } - return Optional.empty(); + public Optional> getProcessor(Class c) { + if (c.isAssignableFrom(eventClass)) { + return Optional.of((RemoteEventProcessor) processor); } - public Class getEventClass() { - return eventClass; - } + return Optional.empty(); + } - @Override - public String toString() { - return String.format("%s Processor %s", this.runnerName, this.processor); - } + public Class getEventClass() { + return eventClass; + } + + @Override + public String toString() { + return String.format("%s Processor %s", this.runnerName, this.processor); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Runners.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Runners.java index d6005d41a5..60b4e39d27 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Runners.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/Runners.java @@ -65,12 +65,11 @@ package com.radixdlt.environment; public final class Runners { - public static final String P2P_NETWORK = "p2p_network"; - public static final String SYNC = "sync"; - public static final String MEMPOOL = "mempool"; - public static final String CONSENSUS = "consensus"; - public static final String SYSTEM_INFO = "info"; + public static final String P2P_NETWORK = "p2p_network"; + public static final String SYNC = "sync"; + public static final String MEMPOOL = "mempool"; + public static final String CONSENSUS = "consensus"; + public static final String SYSTEM_INFO = "info"; - private Runners() { - } + private Runners() {} } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventDispatcher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventDispatcher.java index 7b42dc50a8..c5cc76847d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventDispatcher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventDispatcher.java @@ -70,5 +70,5 @@ * @param the event class */ public interface ScheduledEventDispatcher { - void dispatch(T t, long milliseconds); + void dispatch(T t, long milliseconds); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventProducerOnRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventProducerOnRunner.java index 04a63d1e5c..5c5c2b854f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventProducerOnRunner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ScheduledEventProducerOnRunner.java @@ -68,47 +68,44 @@ import java.util.Objects; import java.util.function.Supplier; -/** - * An event producer registered to run on a runner. - */ +/** An event producer registered to run on a runner. */ public final class ScheduledEventProducerOnRunner { - private final String runnerName; - private final EventDispatcher eventDispatcher; - private final Supplier eventSupplier; - private final Duration initialDelay; - private final Duration interval; + private final String runnerName; + private final EventDispatcher eventDispatcher; + private final Supplier eventSupplier; + private final Duration initialDelay; + private final Duration interval; - public ScheduledEventProducerOnRunner( - String runnerName, - EventDispatcher eventDispatcher, - Supplier eventSupplier, - Duration initialDelay, - Duration interval - ) { - this.runnerName = Objects.requireNonNull(runnerName); - this.eventDispatcher = Objects.requireNonNull(eventDispatcher); - this.eventSupplier = Objects.requireNonNull(eventSupplier); - this.initialDelay = initialDelay; - this.interval = interval; - } + public ScheduledEventProducerOnRunner( + String runnerName, + EventDispatcher eventDispatcher, + Supplier eventSupplier, + Duration initialDelay, + Duration interval) { + this.runnerName = Objects.requireNonNull(runnerName); + this.eventDispatcher = Objects.requireNonNull(eventDispatcher); + this.eventSupplier = Objects.requireNonNull(eventSupplier); + this.initialDelay = initialDelay; + this.interval = interval; + } - public String getRunnerName() { - return runnerName; - } + public String getRunnerName() { + return runnerName; + } - public EventDispatcher getEventDispatcher() { - return eventDispatcher; - } + public EventDispatcher getEventDispatcher() { + return eventDispatcher; + } - public Supplier getEventSupplier() { - return eventSupplier; - } + public Supplier getEventSupplier() { + return eventSupplier; + } - public Duration getInitialDelay() { - return initialDelay; - } + public Duration getInitialDelay() { + return initialDelay; + } - public Duration getInterval() { - return interval; - } + public Duration getInterval() { + return interval; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessor.java index bcde14cd8d..30b11a168d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessor.java @@ -65,5 +65,5 @@ package com.radixdlt.environment; public interface StartProcessor { - void start(); + void start(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessorOnRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessorOnRunner.java index 72f52ab456..016824693c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessorOnRunner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/StartProcessorOnRunner.java @@ -67,19 +67,19 @@ import java.util.Objects; public class StartProcessorOnRunner { - private final String runnerName; - private final StartProcessor processor; + private final String runnerName; + private final StartProcessor processor; - public StartProcessorOnRunner(String runnerName, StartProcessor processor) { - this.runnerName = Objects.requireNonNull(runnerName); - this.processor = Objects.requireNonNull(processor); - } + public StartProcessorOnRunner(String runnerName, StartProcessor processor) { + this.runnerName = Objects.requireNonNull(runnerName); + this.processor = Objects.requireNonNull(processor); + } - public String getRunnerName() { - return runnerName; - } + public String getRunnerName() { + return runnerName; + } - public StartProcessor getProcessor() { - return processor; - } + public StartProcessor getProcessor() { + return processor; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ModuleRunnerImpl.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ModuleRunnerImpl.java index 1156bdd246..3362adb65c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ModuleRunnerImpl.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ModuleRunnerImpl.java @@ -77,9 +77,6 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.time.Duration; import java.util.HashSet; import java.util.List; @@ -90,165 +87,166 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Executes chaos related events - */ +/** Executes chaos related events */ public final class ModuleRunnerImpl implements ModuleRunner { - private static final Logger logger = LogManager.getLogger(); - private Scheduler singleThreadScheduler; - private ScheduledExecutorService executorService; - private final String threadName; - private final Object startLock = new Object(); - private CompositeDisposable compositeDisposable; - - private final Set startProcessors; - private final List> subscriptions; - private final ImmutableList> onStart; - - private static class Subscription { - final Observable o; - final EventProcessor p; - - Subscription(Observable o, EventProcessor p) { - this.o = o; - this.p = p; - } - - Disposable subscribe(Scheduler s) { - return o.observeOn(s).subscribe(p::process, e -> { - // TODO: Implement better error handling especially against Byzantine nodes. - // TODO: Exit process for now. - e.printStackTrace(); - Thread.sleep(1000); - System.exit(-1); - }); - } - } - - private ModuleRunnerImpl( - String threadName, - Set startProcessors, // TODO: combine with onStart - List> subscriptions, - ImmutableList> onStart - ) { - this.threadName = threadName; - this.startProcessors = startProcessors; - this.subscriptions = subscriptions; - this.onStart = onStart; - } - - public static class Builder { - private HashSet startProcessors = new HashSet<>(); - private ImmutableList.Builder> subscriptionsBuilder = ImmutableList.builder(); - private ImmutableList.Builder> onStartBuilder = new ImmutableList.Builder<>(); - - public Builder add(StartProcessor startProcessor) { - startProcessors.add(startProcessor); - return this; - } - - public Builder add(Observable o, EventProcessor p) { - subscriptionsBuilder.add(new Subscription<>(o, p)); - return this; - } - - public Builder add(Flowable o, EventProcessor p) { - subscriptionsBuilder.add(new Subscription<>(o.toObservable(), p)); - return this; - } - - public Builder add(Flowable> o, RemoteEventProcessor p) { - subscriptionsBuilder.add(new Subscription<>(o.toObservable(), p::process)); - return this; - } - - public Builder scheduleWithFixedDelay( - EventDispatcher eventDispatcher, - Supplier eventSupplier, - Duration initialDelay, - Duration interval - ) { - return onStart(executor -> - executor.scheduleWithFixedDelay( - () -> eventDispatcher.dispatch(eventSupplier.get()), - initialDelay.toMillis(), - interval.toMillis(), - TimeUnit.MILLISECONDS - ) - ); - } - - public Builder onStart(Consumer fn) { - this.onStartBuilder.add(fn); - return this; - } - - public ModuleRunnerImpl build(String threadName) { - return new ModuleRunnerImpl( - threadName, - Set.copyOf(startProcessors), - subscriptionsBuilder.build(), - onStartBuilder.build() - ); - } - } - - public static Builder builder() { - return new Builder(); - } - - - @Override - public void start() { - synchronized (this.startLock) { - if (this.compositeDisposable != null) { - return; - } - - this.executorService = Executors.newSingleThreadScheduledExecutor(ThreadFactories.daemonThreads(threadName)); - this.singleThreadScheduler = Schedulers.from(this.executorService); - - logger.info("Starting Runner: {}", this.threadName); - - this.executorService.submit(() -> startProcessors.forEach(StartProcessor::start)); - final var disposables = this.subscriptions.stream() - .map(s -> s.subscribe(singleThreadScheduler)) - .collect(Collectors.toList()); - this.compositeDisposable = new CompositeDisposable(disposables); - - this.onStart.forEach(f -> f.accept(this.executorService)); - } - } - - @Override - public void stop() { - synchronized (this.startLock) { - if (compositeDisposable != null) { - compositeDisposable.dispose(); - compositeDisposable = null; - - this.shutdownAndAwaitTermination(); - } - } - } - - private void shutdownAndAwaitTermination() { - this.executorService.shutdown(); // Disable new tasks from being submitted - try { - // Wait a while for existing tasks to terminate - if (!this.executorService.awaitTermination(2, TimeUnit.SECONDS)) { - this.executorService.shutdownNow(); // Cancel currently executing tasks - // Wait a while for tasks to respond to being cancelled - if (!this.executorService.awaitTermination(2, TimeUnit.SECONDS)) { - System.err.println("Pool " + this.threadName + " did not terminate"); - } - } - } catch (InterruptedException ie) { - // (Re-)Cancel if current thread also interrupted - this.executorService.shutdownNow(); - // Preserve interrupt status - Thread.currentThread().interrupt(); - } - } + private static final Logger logger = LogManager.getLogger(); + private Scheduler singleThreadScheduler; + private ScheduledExecutorService executorService; + private final String threadName; + private final Object startLock = new Object(); + private CompositeDisposable compositeDisposable; + + private final Set startProcessors; + private final List> subscriptions; + private final ImmutableList> onStart; + + private static class Subscription { + final Observable o; + final EventProcessor p; + + Subscription(Observable o, EventProcessor p) { + this.o = o; + this.p = p; + } + + Disposable subscribe(Scheduler s) { + return o.observeOn(s) + .subscribe( + p::process, + e -> { + // TODO: Implement better error handling especially against Byzantine nodes. + // TODO: Exit process for now. + e.printStackTrace(); + Thread.sleep(1000); + System.exit(-1); + }); + } + } + + private ModuleRunnerImpl( + String threadName, + Set startProcessors, // TODO: combine with onStart + List> subscriptions, + ImmutableList> onStart) { + this.threadName = threadName; + this.startProcessors = startProcessors; + this.subscriptions = subscriptions; + this.onStart = onStart; + } + + public static class Builder { + private HashSet startProcessors = new HashSet<>(); + private ImmutableList.Builder> subscriptionsBuilder = ImmutableList.builder(); + private ImmutableList.Builder> onStartBuilder = + new ImmutableList.Builder<>(); + + public Builder add(StartProcessor startProcessor) { + startProcessors.add(startProcessor); + return this; + } + + public Builder add(Observable o, EventProcessor p) { + subscriptionsBuilder.add(new Subscription<>(o, p)); + return this; + } + + public Builder add(Flowable o, EventProcessor p) { + subscriptionsBuilder.add(new Subscription<>(o.toObservable(), p)); + return this; + } + + public Builder add(Flowable> o, RemoteEventProcessor p) { + subscriptionsBuilder.add(new Subscription<>(o.toObservable(), p::process)); + return this; + } + + public Builder scheduleWithFixedDelay( + EventDispatcher eventDispatcher, + Supplier eventSupplier, + Duration initialDelay, + Duration interval) { + return onStart( + executor -> + executor.scheduleWithFixedDelay( + () -> eventDispatcher.dispatch(eventSupplier.get()), + initialDelay.toMillis(), + interval.toMillis(), + TimeUnit.MILLISECONDS)); + } + + public Builder onStart(Consumer fn) { + this.onStartBuilder.add(fn); + return this; + } + + public ModuleRunnerImpl build(String threadName) { + return new ModuleRunnerImpl( + threadName, + Set.copyOf(startProcessors), + subscriptionsBuilder.build(), + onStartBuilder.build()); + } + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public void start() { + synchronized (this.startLock) { + if (this.compositeDisposable != null) { + return; + } + + this.executorService = + Executors.newSingleThreadScheduledExecutor(ThreadFactories.daemonThreads(threadName)); + this.singleThreadScheduler = Schedulers.from(this.executorService); + + logger.info("Starting Runner: {}", this.threadName); + + this.executorService.submit(() -> startProcessors.forEach(StartProcessor::start)); + final var disposables = + this.subscriptions.stream() + .map(s -> s.subscribe(singleThreadScheduler)) + .collect(Collectors.toList()); + this.compositeDisposable = new CompositeDisposable(disposables); + + this.onStart.forEach(f -> f.accept(this.executorService)); + } + } + + @Override + public void stop() { + synchronized (this.startLock) { + if (compositeDisposable != null) { + compositeDisposable.dispose(); + compositeDisposable = null; + + this.shutdownAndAwaitTermination(); + } + } + } + + private void shutdownAndAwaitTermination() { + this.executorService.shutdown(); // Disable new tasks from being submitted + try { + // Wait a while for existing tasks to terminate + if (!this.executorService.awaitTermination(2, TimeUnit.SECONDS)) { + this.executorService.shutdownNow(); // Cancel currently executing tasks + // Wait a while for tasks to respond to being cancelled + if (!this.executorService.awaitTermination(2, TimeUnit.SECONDS)) { + System.err.println("Pool " + this.threadName + " did not terminate"); + } + } + } catch (InterruptedException ie) { + // (Re-)Cancel if current thread also interrupted + this.executorService.shutdownNow(); + // Preserve interrupt status + Thread.currentThread().interrupt(); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ObservableProvider.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ObservableProvider.java index 379e8cc947..464848ba78 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ObservableProvider.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/ObservableProvider.java @@ -68,35 +68,31 @@ import com.google.inject.Provider; import com.google.inject.TypeLiteral; import io.reactivex.rxjava3.core.Observable; - import java.util.Objects; -/** - * Helper class to hook up observables to the environment - */ +/** Helper class to hook up observables to the environment */ public final class ObservableProvider implements Provider> { - @Inject - private Provider rxEnvironmentProvider; - private final Class c; - private final TypeLiteral t; + @Inject private Provider rxEnvironmentProvider; + private final Class c; + private final TypeLiteral t; - ObservableProvider(Class c) { - this.c = Objects.requireNonNull(c); - this.t = null; - } + ObservableProvider(Class c) { + this.c = Objects.requireNonNull(c); + this.t = null; + } - ObservableProvider(TypeLiteral t) { - this.t = Objects.requireNonNull(t); - this.c = null; - } + ObservableProvider(TypeLiteral t) { + this.t = Objects.requireNonNull(t); + this.c = null; + } - @Override - public Observable get() { - RxEnvironment e = rxEnvironmentProvider.get(); - if (c != null) { - return e.getObservable(c); - } else { - return e.getObservable(t); - } + @Override + public Observable get() { + RxEnvironment e = rxEnvironmentProvider.get(); + if (c != null) { + return e.getObservable(c); + } else { + return e.getObservable(t); } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RemoteEvent.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RemoteEvent.java index dae3c5b384..988059125a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RemoteEvent.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RemoteEvent.java @@ -68,37 +68,36 @@ import java.util.Objects; /** - * A helper class which contains remote event and the - * origin node. + * A helper class which contains remote event and the origin node. * * @param the event class */ public final class RemoteEvent { - private final T event; - private final BFTNode origin; + private final T event; + private final BFTNode origin; - private RemoteEvent(BFTNode origin, T event) { - this.origin = origin; - this.event = event; - } + private RemoteEvent(BFTNode origin, T event) { + this.origin = origin; + this.event = event; + } - public static RemoteEvent create(BFTNode origin, T event) { - Objects.requireNonNull(origin); - Objects.requireNonNull(event); + public static RemoteEvent create(BFTNode origin, T event) { + Objects.requireNonNull(origin); + Objects.requireNonNull(event); - return new RemoteEvent<>(origin, event); - } + return new RemoteEvent<>(origin, event); + } - public BFTNode getOrigin() { - return origin; - } + public BFTNode getOrigin() { + return origin; + } - public T getEvent() { - return event; - } + public T getEvent() { + return event; + } - @Override - public String toString() { - return String.format("%s[%s->%s]", getClass().getSimpleName(), this.origin, this.event); - } + @Override + public String toString() { + return String.format("%s[%s->%s]", getClass().getSimpleName(), this.origin, this.event); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironment.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironment.java index af3dae1690..93ac17306d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironment.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironment.java @@ -72,7 +72,6 @@ import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.subjects.ReplaySubject; import io.reactivex.rxjava3.subjects.Subject; - import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -81,79 +80,84 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -/** - * Environment which utilizes RXJava to distribute events from - * dispatchers to processors. - */ +/** Environment which utilizes RXJava to distribute events from dispatchers to processors. */ public final class RxEnvironment implements Environment { - private final Map, Subject> subjects; - private final Map, Subject> typeLiteralSubjects; - private final ScheduledExecutorService executorService; - private final Map, RxRemoteDispatcher> remoteDispatchers; - - public RxEnvironment( - Set> localEventTypeLiterals, - Set> localEventClasses, - ScheduledExecutorService executorService, - Set> remoteDispatchers - ) { - this.typeLiteralSubjects = localEventTypeLiterals.stream() - .collect(Collectors.toMap(c -> c, c -> ReplaySubject.createWithSize(5).toSerialized())); - this.subjects = localEventClasses.stream() - .collect(Collectors.toMap(c -> c, c -> ReplaySubject.createWithSize(5).toSerialized())); - this.executorService = Objects.requireNonNull(executorService); - this.remoteDispatchers = remoteDispatchers.stream() - .collect(Collectors.toMap(RxRemoteDispatcher::eventClass, d -> d)); - } - - private Optional> getSubject(TypeLiteral t) { - @SuppressWarnings("unchecked") - Subject eventDispatcher = (Subject) typeLiteralSubjects.get(t); - - return Optional.ofNullable(eventDispatcher); - } - - private Optional> getSubject(Class eventClass) { - @SuppressWarnings("unchecked") - Subject eventDispatcher = (Subject) subjects.get(eventClass); - - return Optional.ofNullable(eventDispatcher); - } - - @Override - public EventDispatcher getDispatcher(Class eventClass) { - return getSubject(eventClass).>map(s -> s::onNext).orElse(e -> { }); - } - - @Override - public ScheduledEventDispatcher getScheduledDispatcher(Class eventClass) { - return (e, millis) -> getSubject(eventClass) - .ifPresent(s -> executorService.schedule(() -> s.onNext(e), millis, TimeUnit.MILLISECONDS)); - } - - @Override - public ScheduledEventDispatcher getScheduledDispatcher(TypeLiteral typeLiteral) { - return (e, millis) -> getSubject(typeLiteral) - .ifPresent(s -> executorService.schedule(() -> s.onNext(e), millis, TimeUnit.MILLISECONDS)); - } - - @Override - public RemoteEventDispatcher getRemoteDispatcher(Class eventClass) { - if (!remoteDispatchers.containsKey(eventClass)) { - throw new IllegalStateException("No dispatcher for " + eventClass); - } - - @SuppressWarnings("unchecked") - final RemoteEventDispatcher dispatcher = (RemoteEventDispatcher) remoteDispatchers.get(eventClass).dispatcher(); - return dispatcher; - } - - public Observable getObservable(Class eventClass) { - return getSubject(eventClass) - .orElseThrow(() -> new IllegalStateException(eventClass + " not registered as observable.")); - } - - public Observable getObservable(TypeLiteral t) { - return getSubject(t).orElseThrow(); - } + private final Map, Subject> subjects; + private final Map, Subject> typeLiteralSubjects; + private final ScheduledExecutorService executorService; + private final Map, RxRemoteDispatcher> remoteDispatchers; + + public RxEnvironment( + Set> localEventTypeLiterals, + Set> localEventClasses, + ScheduledExecutorService executorService, + Set> remoteDispatchers) { + this.typeLiteralSubjects = + localEventTypeLiterals.stream() + .collect(Collectors.toMap(c -> c, c -> ReplaySubject.createWithSize(5).toSerialized())); + this.subjects = + localEventClasses.stream() + .collect(Collectors.toMap(c -> c, c -> ReplaySubject.createWithSize(5).toSerialized())); + this.executorService = Objects.requireNonNull(executorService); + this.remoteDispatchers = + remoteDispatchers.stream() + .collect(Collectors.toMap(RxRemoteDispatcher::eventClass, d -> d)); + } + + private Optional> getSubject(TypeLiteral t) { + @SuppressWarnings("unchecked") + Subject eventDispatcher = (Subject) typeLiteralSubjects.get(t); + + return Optional.ofNullable(eventDispatcher); + } + + private Optional> getSubject(Class eventClass) { + @SuppressWarnings("unchecked") + Subject eventDispatcher = (Subject) subjects.get(eventClass); + + return Optional.ofNullable(eventDispatcher); + } + + @Override + public EventDispatcher getDispatcher(Class eventClass) { + return getSubject(eventClass).>map(s -> s::onNext).orElse(e -> {}); + } + + @Override + public ScheduledEventDispatcher getScheduledDispatcher(Class eventClass) { + return (e, millis) -> + getSubject(eventClass) + .ifPresent( + s -> executorService.schedule(() -> s.onNext(e), millis, TimeUnit.MILLISECONDS)); + } + + @Override + public ScheduledEventDispatcher getScheduledDispatcher(TypeLiteral typeLiteral) { + return (e, millis) -> + getSubject(typeLiteral) + .ifPresent( + s -> executorService.schedule(() -> s.onNext(e), millis, TimeUnit.MILLISECONDS)); + } + + @Override + public RemoteEventDispatcher getRemoteDispatcher(Class eventClass) { + if (!remoteDispatchers.containsKey(eventClass)) { + throw new IllegalStateException("No dispatcher for " + eventClass); + } + + @SuppressWarnings("unchecked") + final RemoteEventDispatcher dispatcher = + (RemoteEventDispatcher) remoteDispatchers.get(eventClass).dispatcher(); + return dispatcher; + } + + public Observable getObservable(Class eventClass) { + return getSubject(eventClass) + .orElseThrow( + () -> new IllegalStateException(eventClass + " not registered as observable.")); + } + + public Observable getObservable(TypeLiteral t) { + return getSubject(t).orElseThrow(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironmentModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironmentModule.java index 12eaf05d7d..5d95e29db8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironmentModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxEnvironmentModule.java @@ -86,7 +86,10 @@ import com.radixdlt.environment.StartProcessorOnRunner; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.utils.ThreadFactories; - +import io.reactivex.rxjava3.core.BackpressureOverflowStrategy; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Observable; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -94,264 +97,263 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import io.reactivex.rxjava3.core.BackpressureOverflowStrategy; -import io.reactivex.rxjava3.core.BackpressureStrategy; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.core.Observable; - -/** - * Environment utilizing RxJava - */ +/** Environment utilizing RxJava */ public final class RxEnvironmentModule extends AbstractModule { - @Override - public void configure() { - ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(ThreadFactories.daemonThreads("TimeoutSender")); - bind(Environment.class).to(RxEnvironment.class); - bind(ScheduledExecutorService.class).toInstance(ses); + @Override + public void configure() { + ScheduledExecutorService ses = + Executors.newSingleThreadScheduledExecutor(ThreadFactories.daemonThreads("TimeoutSender")); + bind(Environment.class).to(RxEnvironment.class); + bind(ScheduledExecutorService.class).toInstance(ses); - // TODO: Remove, still required by SimulationNodes.java - bind(new TypeLiteral>() { }).toProvider(new ObservableProvider<>(LedgerUpdate.class)); - bind(new TypeLiteral>() { }).toProvider(new ObservableProvider<>(BFTHighQCUpdate.class)); + // TODO: Remove, still required by SimulationNodes.java + bind(new TypeLiteral>() {}) + .toProvider(new ObservableProvider<>(LedgerUpdate.class)); + bind(new TypeLiteral>() {}) + .toProvider(new ObservableProvider<>(BFTHighQCUpdate.class)); - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }); - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }); - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }); - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }); - } + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}); + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}); + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}); + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}); + } - @Provides - @Singleton - private RxEnvironment rxEnvironment( - ScheduledExecutorService ses, - Set> dispatchers, - @LocalEvents Set> localProcessedEventClasses // TODO: remove, infer from ProcessorOnRunners - ) { - return new RxEnvironment( - Set.of(new TypeLiteral>() { }), - localProcessedEventClasses, - ses, - dispatchers - ); - } + @Provides + @Singleton + private RxEnvironment rxEnvironment( + ScheduledExecutorService ses, + Set> dispatchers, + @LocalEvents + Set> localProcessedEventClasses // TODO: remove, infer from ProcessorOnRunners + ) { + return new RxEnvironment( + Set.of(new TypeLiteral>() {}), + localProcessedEventClasses, + ses, + dispatchers); + } - @ProvidesIntoMap - @StringMapKey(Runners.CONSENSUS) - @Singleton - public ModuleRunner consensusRunner( - @Self String name, - Set> processors, - RxEnvironment rxEnvironment, - Set> remoteProcessors, - RxRemoteEnvironment rxRemoteEnvironment, - Set> scheduledEventProducers, - Set startProcessors - ) { - final var runnerName = Runners.CONSENSUS; - final var builder = ModuleRunnerImpl.builder(); - addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); - addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); - addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); - addStartProcessorsOnRunner(startProcessors, runnerName, builder); - return builder.build("BFT " + name); - } + @ProvidesIntoMap + @StringMapKey(Runners.CONSENSUS) + @Singleton + public ModuleRunner consensusRunner( + @Self String name, + Set> processors, + RxEnvironment rxEnvironment, + Set> remoteProcessors, + RxRemoteEnvironment rxRemoteEnvironment, + Set> scheduledEventProducers, + Set startProcessors) { + final var runnerName = Runners.CONSENSUS; + final var builder = ModuleRunnerImpl.builder(); + addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); + addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); + addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); + addStartProcessorsOnRunner(startProcessors, runnerName, builder); + return builder.build("BFT " + name); + } - @ProvidesIntoMap - @StringMapKey(Runners.SYSTEM_INFO) - @Singleton - public ModuleRunner systemInfoRunner( - @Self String name, - Set> processors, - RxEnvironment rxEnvironment - ) { - final var runnerName = Runners.SYSTEM_INFO; - final var builder = ModuleRunnerImpl.builder(); - addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); - return builder.build("SystemInfo " + name); - } + @ProvidesIntoMap + @StringMapKey(Runners.SYSTEM_INFO) + @Singleton + public ModuleRunner systemInfoRunner( + @Self String name, Set> processors, RxEnvironment rxEnvironment) { + final var runnerName = Runners.SYSTEM_INFO; + final var builder = ModuleRunnerImpl.builder(); + addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); + return builder.build("SystemInfo " + name); + } - @ProvidesIntoMap - @StringMapKey(Runners.MEMPOOL) - @Singleton - public ModuleRunner mempoolRunner( - @Self String name, - Set> processors, - RxEnvironment rxEnvironment, - Set> remoteProcessors, - RxRemoteEnvironment rxRemoteEnvironment, - Set> scheduledEventProducers - ) { - final var runnerName = Runners.MEMPOOL; - final var builder = ModuleRunnerImpl.builder(); - addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); - addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); - addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); - return builder.build("MempoolRunner " + name); - } + @ProvidesIntoMap + @StringMapKey(Runners.MEMPOOL) + @Singleton + public ModuleRunner mempoolRunner( + @Self String name, + Set> processors, + RxEnvironment rxEnvironment, + Set> remoteProcessors, + RxRemoteEnvironment rxRemoteEnvironment, + Set> scheduledEventProducers) { + final var runnerName = Runners.MEMPOOL; + final var builder = ModuleRunnerImpl.builder(); + addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); + addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); + addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); + return builder.build("MempoolRunner " + name); + } - @ProvidesIntoMap - @StringMapKey(Runners.SYNC) - @Singleton - public ModuleRunner syncRunner( - @Self String name, - Set> processors, - RxEnvironment rxEnvironment, - Set> remoteProcessors, - RxRemoteEnvironment rxRemoteEnvironment, - Set> scheduledEventProducers - ) { + @ProvidesIntoMap + @StringMapKey(Runners.SYNC) + @Singleton + public ModuleRunner syncRunner( + @Self String name, + Set> processors, + RxEnvironment rxEnvironment, + Set> remoteProcessors, + RxRemoteEnvironment rxRemoteEnvironment, + Set> scheduledEventProducers) { - final var runnerName = Runners.SYNC; - final var builder = ModuleRunnerImpl.builder(); - addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); - addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); - addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); - return builder.build("SyncRunner " + name); - } + final var runnerName = Runners.SYNC; + final var builder = ModuleRunnerImpl.builder(); + addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); + addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); + addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); + return builder.build("SyncRunner " + name); + } - @ProvidesIntoMap - @StringMapKey(Runners.P2P_NETWORK) - @Singleton - public ModuleRunner p2pNetworkRunner( - @Self String name, - Set> processors, - RxEnvironment rxEnvironment, - Set> remoteProcessors, - RxRemoteEnvironment rxRemoteEnvironment, - Set> scheduledEventProducers - ) { - final var runnerName = Runners.P2P_NETWORK; - final var builder = ModuleRunnerImpl.builder(); - addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); - addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); - addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); - return builder.build("P2PNetworkRunner " + name); - } + @ProvidesIntoMap + @StringMapKey(Runners.P2P_NETWORK) + @Singleton + public ModuleRunner p2pNetworkRunner( + @Self String name, + Set> processors, + RxEnvironment rxEnvironment, + Set> remoteProcessors, + RxRemoteEnvironment rxRemoteEnvironment, + Set> scheduledEventProducers) { + final var runnerName = Runners.P2P_NETWORK; + final var builder = ModuleRunnerImpl.builder(); + addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); + addRemoteProcessorsOnRunner(remoteProcessors, rxRemoteEnvironment, runnerName, builder); + addScheduledEventProducersOnRunner(scheduledEventProducers, runnerName, builder); + return builder.build("P2PNetworkRunner " + name); + } - private static void addToBuilder( - Class eventClass, - RxRemoteEnvironment rxEnvironment, - RemoteEventProcessorOnRunner processor, - ModuleRunnerImpl.Builder builder - ) { - final Flowable> events; - if (processor.getRateLimitDelayMs() > 0) { - events = rxEnvironment.remoteEvents(eventClass) - .onBackpressureBuffer(100, null, BackpressureOverflowStrategy.DROP_LATEST) - .concatMap(e -> Flowable.timer(processor.getRateLimitDelayMs(), TimeUnit.MILLISECONDS).map(l -> e)); - } else { - events = rxEnvironment.remoteEvents(eventClass); - } + private static void addToBuilder( + Class eventClass, + RxRemoteEnvironment rxEnvironment, + RemoteEventProcessorOnRunner processor, + ModuleRunnerImpl.Builder builder) { + final Flowable> events; + if (processor.getRateLimitDelayMs() > 0) { + events = + rxEnvironment + .remoteEvents(eventClass) + .onBackpressureBuffer(100, null, BackpressureOverflowStrategy.DROP_LATEST) + .concatMap( + e -> + Flowable.timer(processor.getRateLimitDelayMs(), TimeUnit.MILLISECONDS) + .map(l -> e)); + } else { + events = rxEnvironment.remoteEvents(eventClass); + } - processor.getProcessor(eventClass).ifPresent(p -> builder.add(events, p)); - } + processor.getProcessor(eventClass).ifPresent(p -> builder.add(events, p)); + } - private static void addToBuilder( - TypeLiteral typeLiteral, - RxEnvironment rxEnvironment, - EventProcessorOnRunner processor, - ModuleRunnerImpl.Builder builder - ) { - if (processor.getRateLimitDelayMs() > 0) { - final Flowable events = rxEnvironment.getObservable(typeLiteral) - .toFlowable(BackpressureStrategy.DROP) - .onBackpressureBuffer(100, null, BackpressureOverflowStrategy.DROP_LATEST) - .concatMap(e -> Flowable.timer(processor.getRateLimitDelayMs(), TimeUnit.MILLISECONDS).map(l -> e)); - processor.getProcessor(typeLiteral).ifPresent(p -> builder.add(events, p)); - } else { - final Observable events = rxEnvironment.getObservable(typeLiteral); - processor.getProcessor(typeLiteral).ifPresent(p -> builder.add(events, p)); - } - } + private static void addToBuilder( + TypeLiteral typeLiteral, + RxEnvironment rxEnvironment, + EventProcessorOnRunner processor, + ModuleRunnerImpl.Builder builder) { + if (processor.getRateLimitDelayMs() > 0) { + final Flowable events = + rxEnvironment + .getObservable(typeLiteral) + .toFlowable(BackpressureStrategy.DROP) + .onBackpressureBuffer(100, null, BackpressureOverflowStrategy.DROP_LATEST) + .concatMap( + e -> + Flowable.timer(processor.getRateLimitDelayMs(), TimeUnit.MILLISECONDS) + .map(l -> e)); + processor.getProcessor(typeLiteral).ifPresent(p -> builder.add(events, p)); + } else { + final Observable events = rxEnvironment.getObservable(typeLiteral); + processor.getProcessor(typeLiteral).ifPresent(p -> builder.add(events, p)); + } + } - private static void addToBuilder( - Class eventClass, - RxEnvironment rxEnvironment, - EventProcessorOnRunner processor, - ModuleRunnerImpl.Builder builder - ) { - if (processor.getRateLimitDelayMs() > 0) { - final Flowable events = rxEnvironment.getObservable(eventClass) - .toFlowable(BackpressureStrategy.DROP) - .onBackpressureBuffer(100, null, BackpressureOverflowStrategy.DROP_LATEST) - .concatMap(e -> Flowable.timer(processor.getRateLimitDelayMs(), TimeUnit.MILLISECONDS).map(l -> e)); - processor.getProcessor(eventClass).ifPresent(p -> builder.add(events, p)); - } else { - final Observable events = rxEnvironment.getObservable(eventClass); - processor.getProcessor(eventClass).ifPresent(p -> builder.add(events, p)); - } - } + private static void addToBuilder( + Class eventClass, + RxEnvironment rxEnvironment, + EventProcessorOnRunner processor, + ModuleRunnerImpl.Builder builder) { + if (processor.getRateLimitDelayMs() > 0) { + final Flowable events = + rxEnvironment + .getObservable(eventClass) + .toFlowable(BackpressureStrategy.DROP) + .onBackpressureBuffer(100, null, BackpressureOverflowStrategy.DROP_LATEST) + .concatMap( + e -> + Flowable.timer(processor.getRateLimitDelayMs(), TimeUnit.MILLISECONDS) + .map(l -> e)); + processor.getProcessor(eventClass).ifPresent(p -> builder.add(events, p)); + } else { + final Observable events = rxEnvironment.getObservable(eventClass); + processor.getProcessor(eventClass).ifPresent(p -> builder.add(events, p)); + } + } - @SuppressWarnings("unchecked") - private void addScheduledEventProducersOnRunner( - Set> allScheduledEventProducers, - String runnerName, - ModuleRunnerImpl.Builder builder - ) { - allScheduledEventProducers.stream() - .filter(p -> p.getRunnerName().equals(runnerName)) - .forEach(scheduledEventProducer -> - builder.scheduleWithFixedDelay( - (EventDispatcher) scheduledEventProducer.getEventDispatcher(), - (Supplier) scheduledEventProducer.getEventSupplier(), - scheduledEventProducer.getInitialDelay(), - scheduledEventProducer.getInterval() - ) - ); - } + @SuppressWarnings("unchecked") + private void addScheduledEventProducersOnRunner( + Set> allScheduledEventProducers, + String runnerName, + ModuleRunnerImpl.Builder builder) { + allScheduledEventProducers.stream() + .filter(p -> p.getRunnerName().equals(runnerName)) + .forEach( + scheduledEventProducer -> + builder.scheduleWithFixedDelay( + (EventDispatcher) scheduledEventProducer.getEventDispatcher(), + (Supplier) scheduledEventProducer.getEventSupplier(), + scheduledEventProducer.getInitialDelay(), + scheduledEventProducer.getInterval())); + } - private void addStartProcessorsOnRunner( - Set allStartProcessors, - String runnerName, - ModuleRunnerImpl.Builder builder - ) { - allStartProcessors.stream() - .filter(p -> p.getRunnerName().equals(runnerName)) - .map(StartProcessorOnRunner::getProcessor) - .forEach(builder::add); - } + private void addStartProcessorsOnRunner( + Set allStartProcessors, + String runnerName, + ModuleRunnerImpl.Builder builder) { + allStartProcessors.stream() + .filter(p -> p.getRunnerName().equals(runnerName)) + .map(StartProcessorOnRunner::getProcessor) + .forEach(builder::add); + } - private void addRemoteProcessorsOnRunner( - Set> allRemoteProcessors, - RxRemoteEnvironment rxRemoteEnvironment, - String runnerName, - ModuleRunnerImpl.Builder builder - ) { - final var remoteEventClasses = allRemoteProcessors.stream() - .filter(p -> p.getRunnerName().equals(runnerName)) - .map(RemoteEventProcessorOnRunner::getEventClass) - .collect(Collectors.toSet()); - remoteEventClasses.forEach(eventClass -> - allRemoteProcessors - .stream() - .filter(p -> p.getRunnerName().equals(runnerName)) - .forEach(p -> addToBuilder(eventClass, rxRemoteEnvironment, p, builder)) - ); - } + private void addRemoteProcessorsOnRunner( + Set> allRemoteProcessors, + RxRemoteEnvironment rxRemoteEnvironment, + String runnerName, + ModuleRunnerImpl.Builder builder) { + final var remoteEventClasses = + allRemoteProcessors.stream() + .filter(p -> p.getRunnerName().equals(runnerName)) + .map(RemoteEventProcessorOnRunner::getEventClass) + .collect(Collectors.toSet()); + remoteEventClasses.forEach( + eventClass -> + allRemoteProcessors.stream() + .filter(p -> p.getRunnerName().equals(runnerName)) + .forEach(p -> addToBuilder(eventClass, rxRemoteEnvironment, p, builder))); + } - private void addProcessorsOnRunner( - Set> allProcessors, - RxEnvironment rxEnvironment, - String runnerName, - ModuleRunnerImpl.Builder builder - ) { - final var runnerProcessors = allProcessors.stream() - .filter(p -> p.getRunnerName().equals(runnerName)) - .collect(Collectors.toSet()); + private void addProcessorsOnRunner( + Set> allProcessors, + RxEnvironment rxEnvironment, + String runnerName, + ModuleRunnerImpl.Builder builder) { + final var runnerProcessors = + allProcessors.stream() + .filter(p -> p.getRunnerName().equals(runnerName)) + .collect(Collectors.toSet()); - final var eventClasses = runnerProcessors.stream() - .filter(e -> e.getEventClass().isPresent()) - .map(e -> e.getEventClass().get()) - .collect(Collectors.toSet()); - eventClasses.forEach(eventClass -> - runnerProcessors.forEach(p -> addToBuilder(eventClass, rxEnvironment, p, builder)) - ); + final var eventClasses = + runnerProcessors.stream() + .filter(e -> e.getEventClass().isPresent()) + .map(e -> e.getEventClass().get()) + .collect(Collectors.toSet()); + eventClasses.forEach( + eventClass -> + runnerProcessors.forEach(p -> addToBuilder(eventClass, rxEnvironment, p, builder))); - final var typeLiterals = runnerProcessors.stream() - .filter(e -> e.getTypeLiteral().isPresent()) - .map(e -> e.getTypeLiteral().get()) - .collect(Collectors.toSet()); - typeLiterals.forEach(typeLiteral -> - runnerProcessors.forEach(p -> addToBuilder(typeLiteral, rxEnvironment, p, builder)) - ); - } + final var typeLiterals = + runnerProcessors.stream() + .filter(e -> e.getTypeLiteral().isPresent()) + .map(e -> e.getTypeLiteral().get()) + .collect(Collectors.toSet()); + typeLiterals.forEach( + typeLiteral -> + runnerProcessors.forEach(p -> addToBuilder(typeLiteral, rxEnvironment, p, builder))); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteDispatcher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteDispatcher.java index c3ce286815..b34b5e0ae7 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteDispatcher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteDispatcher.java @@ -69,29 +69,29 @@ /** * Remote event dispatcher for rx environment + * * @param the event class */ public final class RxRemoteDispatcher { - private final Class eventClass; - private final RemoteEventDispatcher dispatcher; + private final Class eventClass; + private final RemoteEventDispatcher dispatcher; - private RxRemoteDispatcher(Class eventClass, RemoteEventDispatcher dispatcher) { - this.eventClass = eventClass; - this.dispatcher = dispatcher; - } + private RxRemoteDispatcher(Class eventClass, RemoteEventDispatcher dispatcher) { + this.eventClass = eventClass; + this.dispatcher = dispatcher; + } - public Class eventClass() { - return eventClass; - } + public Class eventClass() { + return eventClass; + } - public RemoteEventDispatcher dispatcher() { - return dispatcher; - } + public RemoteEventDispatcher dispatcher() { + return dispatcher; + } - public static RxRemoteDispatcher create(Class eventClass, RemoteEventDispatcher dispatcher) { - return new RxRemoteDispatcher<>( - Objects.requireNonNull(eventClass), - Objects.requireNonNull(dispatcher) - ); - } + public static RxRemoteDispatcher create( + Class eventClass, RemoteEventDispatcher dispatcher) { + return new RxRemoteDispatcher<>( + Objects.requireNonNull(eventClass), Objects.requireNonNull(dispatcher)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteEnvironment.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteEnvironment.java index 6276ba58ae..f0e6090d64 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteEnvironment.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/rx/RxRemoteEnvironment.java @@ -66,9 +66,7 @@ import io.reactivex.rxjava3.core.Flowable; -/** - * Provides remote events - */ +/** Provides remote events */ public interface RxRemoteEnvironment { - Flowable> remoteEvents(Class remoteEventClass); + Flowable> remoteEvents(Class remoteEventClass); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochsLocalSyncService.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochsLocalSyncService.java index 833b308203..16eb76b5f6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochsLocalSyncService.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochsLocalSyncService.java @@ -71,131 +71,134 @@ import com.radixdlt.environment.RemoteEventProcessor; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.sync.LocalSyncService; -import com.radixdlt.sync.messages.local.SyncLedgerUpdateTimeout; -import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; -import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; import com.radixdlt.sync.messages.local.LocalSyncRequest; -import javax.annotation.concurrent.NotThreadSafe; - import com.radixdlt.sync.messages.local.SyncCheckReceiveStatusTimeout; import com.radixdlt.sync.messages.local.SyncCheckTrigger; +import com.radixdlt.sync.messages.local.SyncLedgerUpdateTimeout; import com.radixdlt.sync.messages.local.SyncRequestTimeout; +import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncResponse; +import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Manages the syncing service across epochs - */ +/** Manages the syncing service across epochs */ @NotThreadSafe public class EpochsLocalSyncService { - private static final Logger log = LogManager.getLogger(); - - private final LocalSyncServiceFactory localSyncServiceFactory; - - private EpochChange currentEpoch; - private LocalSyncService localSyncService; - - @Inject - public EpochsLocalSyncService( - LocalSyncService initialLocalSyncService, - EpochChange initialEpoch, - LocalSyncServiceFactory localSyncServiceFactory - ) { - this.currentEpoch = initialEpoch; - this.localSyncService = initialLocalSyncService; - - this.localSyncServiceFactory = localSyncServiceFactory; - } - - public EventProcessor epochsLedgerUpdateEventProcessor() { - return this::processLedgerUpdate; - } - - private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { - var epochChange = ledgerUpdate.getStateComputerOutput().getInstance(EpochChange.class); - if (epochChange != null) { - this.currentEpoch = epochChange; - - this.localSyncService = localSyncServiceFactory.create( - new RemoteSyncResponseValidatorSetVerifier( - epochChange.getBFTConfiguration().getValidatorSet() - ), - this.localSyncService.getSyncState() - ); - } - this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdate); - } - - public EventProcessor localSyncRequestEventProcessor() { - return this::processLocalSyncRequest; - } - - private void processLocalSyncRequest(LocalSyncRequest request) { - final long targetEpoch = request.getTarget().getEpoch(); - - if (targetEpoch < currentEpoch.getEpoch()) { - log.trace("Request epoch {} is lower from current {} ignoring: {}", targetEpoch, currentEpoch.getEpoch(), request); - return; - } - - localSyncService.localSyncRequestEventProcessor().process(request); - } - - public EventProcessor syncCheckTriggerEventProcessor() { - return this::processSyncCheckTrigger; - } - - private void processSyncCheckTrigger(SyncCheckTrigger syncCheckTrigger) { - this.localSyncService.syncCheckTriggerEventProcessor().process(syncCheckTrigger); - } - - public EventProcessor syncCheckReceiveStatusTimeoutEventProcessor() { - return this::processSyncCheckReceiveStatusTimeout; - } - - public EventProcessor syncLedgerUpdateTimeoutProcessor() { - return this::processSyncLedgerUpdateTimeout; - } - - private void processSyncLedgerUpdateTimeout(SyncLedgerUpdateTimeout syncLedgerUpdateTimeout) { - this.localSyncService.syncLedgerUpdateTimeoutProcessor().process(syncLedgerUpdateTimeout); - } - - private void processSyncCheckReceiveStatusTimeout(SyncCheckReceiveStatusTimeout syncCheckReceiveStatusTimeout) { - this.localSyncService.syncCheckReceiveStatusTimeoutEventProcessor().process(syncCheckReceiveStatusTimeout); - } - - public EventProcessor syncRequestTimeoutEventProcessor() { - return this::processSyncRequestTimeout; - } - - private void processSyncRequestTimeout(SyncRequestTimeout syncRequestTimeout) { - this.localSyncService.syncRequestTimeoutEventProcessor().process(syncRequestTimeout); - } - - public RemoteEventProcessor statusResponseEventProcessor() { - return this::processStatusResponse; - } - - private void processStatusResponse(BFTNode sender, StatusResponse statusResponse) { - this.localSyncService.statusResponseEventProcessor().process(sender, statusResponse); - } - - public RemoteEventProcessor syncResponseEventProcessor() { - return this::processSyncResponse; - } - - private void processSyncResponse(BFTNode sender, SyncResponse syncResponse) { - this.localSyncService.syncResponseEventProcessor().process(sender, syncResponse); - } - - public RemoteEventProcessor ledgerStatusUpdateEventProcessor() { - return this::processLedgerStatusUpdate; - } - - private void processLedgerStatusUpdate(BFTNode sender, LedgerStatusUpdate ledgerStatusUpdate) { - this.localSyncService.ledgerStatusUpdateEventProcessor().process(sender, ledgerStatusUpdate); - } + private static final Logger log = LogManager.getLogger(); + + private final LocalSyncServiceFactory localSyncServiceFactory; + + private EpochChange currentEpoch; + private LocalSyncService localSyncService; + + @Inject + public EpochsLocalSyncService( + LocalSyncService initialLocalSyncService, + EpochChange initialEpoch, + LocalSyncServiceFactory localSyncServiceFactory) { + this.currentEpoch = initialEpoch; + this.localSyncService = initialLocalSyncService; + + this.localSyncServiceFactory = localSyncServiceFactory; + } + + public EventProcessor epochsLedgerUpdateEventProcessor() { + return this::processLedgerUpdate; + } + + private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { + var epochChange = ledgerUpdate.getStateComputerOutput().getInstance(EpochChange.class); + if (epochChange != null) { + this.currentEpoch = epochChange; + + this.localSyncService = + localSyncServiceFactory.create( + new RemoteSyncResponseValidatorSetVerifier( + epochChange.getBFTConfiguration().getValidatorSet()), + this.localSyncService.getSyncState()); + } + this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdate); + } + + public EventProcessor localSyncRequestEventProcessor() { + return this::processLocalSyncRequest; + } + + private void processLocalSyncRequest(LocalSyncRequest request) { + final long targetEpoch = request.getTarget().getEpoch(); + + if (targetEpoch < currentEpoch.getEpoch()) { + log.trace( + "Request epoch {} is lower from current {} ignoring: {}", + targetEpoch, + currentEpoch.getEpoch(), + request); + return; + } + + localSyncService.localSyncRequestEventProcessor().process(request); + } + + public EventProcessor syncCheckTriggerEventProcessor() { + return this::processSyncCheckTrigger; + } + + private void processSyncCheckTrigger(SyncCheckTrigger syncCheckTrigger) { + this.localSyncService.syncCheckTriggerEventProcessor().process(syncCheckTrigger); + } + + public EventProcessor + syncCheckReceiveStatusTimeoutEventProcessor() { + return this::processSyncCheckReceiveStatusTimeout; + } + + public EventProcessor syncLedgerUpdateTimeoutProcessor() { + return this::processSyncLedgerUpdateTimeout; + } + + private void processSyncLedgerUpdateTimeout(SyncLedgerUpdateTimeout syncLedgerUpdateTimeout) { + this.localSyncService.syncLedgerUpdateTimeoutProcessor().process(syncLedgerUpdateTimeout); + } + + private void processSyncCheckReceiveStatusTimeout( + SyncCheckReceiveStatusTimeout syncCheckReceiveStatusTimeout) { + this.localSyncService + .syncCheckReceiveStatusTimeoutEventProcessor() + .process(syncCheckReceiveStatusTimeout); + } + + public EventProcessor syncRequestTimeoutEventProcessor() { + return this::processSyncRequestTimeout; + } + + private void processSyncRequestTimeout(SyncRequestTimeout syncRequestTimeout) { + this.localSyncService.syncRequestTimeoutEventProcessor().process(syncRequestTimeout); + } + + public RemoteEventProcessor statusResponseEventProcessor() { + return this::processStatusResponse; + } + + private void processStatusResponse(BFTNode sender, StatusResponse statusResponse) { + this.localSyncService.statusResponseEventProcessor().process(sender, statusResponse); + } + + public RemoteEventProcessor syncResponseEventProcessor() { + return this::processSyncResponse; + } + + private void processSyncResponse(BFTNode sender, SyncResponse syncResponse) { + this.localSyncService.syncResponseEventProcessor().process(sender, syncResponse); + } + + public RemoteEventProcessor ledgerStatusUpdateEventProcessor() { + return this::processLedgerStatusUpdate; + } + + private void processLedgerStatusUpdate(BFTNode sender, LedgerStatusUpdate ledgerStatusUpdate) { + this.localSyncService.ledgerStatusUpdateEventProcessor().process(sender, ledgerStatusUpdate); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/LocalSyncServiceFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/LocalSyncServiceFactory.java index 38ae4be83b..7c3b638fcc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/LocalSyncServiceFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/LocalSyncServiceFactory.java @@ -65,15 +65,12 @@ package com.radixdlt.epochs; import com.radixdlt.sync.LocalSyncService; -import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; import com.radixdlt.sync.SyncState; +import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; -/** - * A factory for creating LocalSyncService instances. - */ +/** A factory for creating LocalSyncService instances. */ public interface LocalSyncServiceFactory { - LocalSyncService create( - RemoteSyncResponseValidatorSetVerifier remoteSyncResponseValidatorSetVerifier, - SyncState syncState - ); + LocalSyncService create( + RemoteSyncResponseValidatorSetVerifier remoteSyncResponseValidatorSetVerifier, + SyncState syncState); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keygen/KeyGenerator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keygen/KeyGenerator.java index 6e259b4f00..47242cec3e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keygen/KeyGenerator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keygen/KeyGenerator.java @@ -64,134 +64,148 @@ package com.radixdlt.keygen; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.crypto.RadixKeyStore; -import com.radixdlt.utils.functional.Failure; -import com.radixdlt.utils.functional.Result; - -import java.io.File; -import java.security.Security; - +import static com.radixdlt.errors.ApiErrors.MISSING_PARAMETER; import static com.radixdlt.errors.InternalErrors.GENERAL; import static com.radixdlt.errors.InternalErrors.MISSING_KEYSTORE_FILE; import static com.radixdlt.errors.InternalErrors.UNABLE_TO_LOAD_KEYSTORE; import static com.radixdlt.errors.InternalErrors.UNABLE_TO_PARSE_COMMAND_LINE; -import static com.radixdlt.errors.ApiErrors.MISSING_PARAMETER; import static com.radixdlt.utils.functional.Failure.failure; import static com.radixdlt.utils.functional.Result.allOf; import static com.radixdlt.utils.functional.Result.fromOptional; - import static java.util.Optional.ofNullable; -/** - * Command line utility for key generation. - */ +import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.crypto.RadixKeyStore; +import com.radixdlt.utils.functional.Failure; +import com.radixdlt.utils.functional.Result; +import java.io.File; +import java.security.Security; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** Command line utility for key generation. */ public class KeyGenerator { - private static final String DEFAULT_KEYPAIR_NAME = "node"; - - static { - Security.insertProviderAt(new BouncyCastleProvider(), 1); - } - - private final Options options; - - private KeyGenerator() { - options = new Options() - .addOption("h", "help", false, "Show usage information (this message)") - .addOption("k", "keystore", true, "Keystore name") - .addOption("p", "password", true, "Password for keystore") - .addOption("n", "keypair-name", true, "Key pair name (optional, default name is 'node')") - .addOption("pk", "show-public-key", false, "Prints the public key of an existing " - + "keypair and exits"); - } - - public static void main(String[] args) { - var rc = new KeyGenerator().run(args).fold(Failure::code, __ -> 0); - System.exit(rc); - } - - private Result run(String[] args) { - return parseParameters(args) - .filter(commandLine -> !commandLine.hasOption("h"), irrelevant()) - .filter(commandLine -> commandLine.getOptions().length != 0, irrelevant()) - .flatMap(cli -> allOf(parseKeystore(cli), parsePassword(cli), parseKeypair(cli), parseShowPk(cli)) - .flatMap(this::generateKeypair)) - .onFailure(failure -> usage(failure.message())) - .onSuccessDo(() -> System.out.println("Done")); - } - - private Failure irrelevant() { - return failure(0, ""); - } - - private void usage(String message) { - if (!message.isEmpty()) { - System.out.println("ERROR: " + message); - } - new HelpFormatter().printHelp(KeyGenerator.class.getSimpleName(), options, true); - } - - private Result generateKeypair(String keystore, String password, String keypairName, Boolean shouldShowPk) { - var keystoreFile = new File(keystore); - var newFile = !keystoreFile.canWrite(); - var isNew = newFile ? "new" : "existing"; - - if (shouldShowPk) { - return printPublicKey(keystoreFile, password, keypairName, newFile); - } - - var keyPair = ECKeyPair.generateNew(); - var publicKey = keyPair.getPublicKey().toHex(); - - System.out.printf("Writing keypair '%s' [public key: %s]%ninto %s keystore %s%n", keypairName, publicKey, isNew, keystore); - - return Result.wrap(UNABLE_TO_LOAD_KEYSTORE, () -> { - RadixKeyStore.fromFile(keystoreFile, password.toCharArray(), newFile) - .writeKeyPair(keypairName, keyPair); - return null; - }); - } - - private Result parseShowPk(CommandLine commandLine) { - return Result.ok(commandLine.hasOption("pk")); - } - - private Result printPublicKey(File keystoreFile, String password, String keypairName, boolean newFile) { - if (!keystoreFile.exists() || !keystoreFile.canRead()) { - return Result.fail(MISSING_KEYSTORE_FILE.with(keystoreFile)); - } - - return Result.wrap(GENERAL, () -> { - ECKeyPair keyPair = RadixKeyStore.fromFile(keystoreFile, password.toCharArray(), newFile) - .readKeyPair(keypairName, false); - System.out.printf("Public key of keypair '%s': %s%n", keypairName, keyPair.getPublicKey().toHex()); - return null; - }); - } - - private Result parseKeystore(CommandLine commandLine) { - return requiredString(commandLine, "k"); - } - - private Result parsePassword(CommandLine commandLine) { - return requiredString(commandLine, "p"); - } - - private Result parseKeypair(CommandLine commandLine) { - return requiredString(commandLine, "n").or(Result.ok(DEFAULT_KEYPAIR_NAME)); - } - - private Result requiredString(CommandLine commandLine, String opt) { - return fromOptional(() -> MISSING_PARAMETER.with(opt), ofNullable(commandLine.getOptionValue(opt))); - } - - private Result parseParameters(String[] args) { - return Result.wrap(UNABLE_TO_PARSE_COMMAND_LINE, () -> new DefaultParser().parse(options, args)); - } + private static final String DEFAULT_KEYPAIR_NAME = "node"; + + static { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + + private final Options options; + + private KeyGenerator() { + options = + new Options() + .addOption("h", "help", false, "Show usage information (this message)") + .addOption("k", "keystore", true, "Keystore name") + .addOption("p", "password", true, "Password for keystore") + .addOption( + "n", "keypair-name", true, "Key pair name (optional, default name is 'node')") + .addOption( + "pk", + "show-public-key", + false, + "Prints the public key of an existing " + "keypair and exits"); + } + + public static void main(String[] args) { + var rc = new KeyGenerator().run(args).fold(Failure::code, __ -> 0); + System.exit(rc); + } + + private Result run(String[] args) { + return parseParameters(args) + .filter(commandLine -> !commandLine.hasOption("h"), irrelevant()) + .filter(commandLine -> commandLine.getOptions().length != 0, irrelevant()) + .flatMap( + cli -> + allOf(parseKeystore(cli), parsePassword(cli), parseKeypair(cli), parseShowPk(cli)) + .flatMap(this::generateKeypair)) + .onFailure(failure -> usage(failure.message())) + .onSuccessDo(() -> System.out.println("Done")); + } + + private Failure irrelevant() { + return failure(0, ""); + } + + private void usage(String message) { + if (!message.isEmpty()) { + System.out.println("ERROR: " + message); + } + new HelpFormatter().printHelp(KeyGenerator.class.getSimpleName(), options, true); + } + + private Result generateKeypair( + String keystore, String password, String keypairName, Boolean shouldShowPk) { + var keystoreFile = new File(keystore); + var newFile = !keystoreFile.canWrite(); + var isNew = newFile ? "new" : "existing"; + + if (shouldShowPk) { + return printPublicKey(keystoreFile, password, keypairName, newFile); + } + + var keyPair = ECKeyPair.generateNew(); + var publicKey = keyPair.getPublicKey().toHex(); + + System.out.printf( + "Writing keypair '%s' [public key: %s]%ninto %s keystore %s%n", + keypairName, publicKey, isNew, keystore); + + return Result.wrap( + UNABLE_TO_LOAD_KEYSTORE, + () -> { + RadixKeyStore.fromFile(keystoreFile, password.toCharArray(), newFile) + .writeKeyPair(keypairName, keyPair); + return null; + }); + } + + private Result parseShowPk(CommandLine commandLine) { + return Result.ok(commandLine.hasOption("pk")); + } + + private Result printPublicKey( + File keystoreFile, String password, String keypairName, boolean newFile) { + if (!keystoreFile.exists() || !keystoreFile.canRead()) { + return Result.fail(MISSING_KEYSTORE_FILE.with(keystoreFile)); + } + + return Result.wrap( + GENERAL, + () -> { + ECKeyPair keyPair = + RadixKeyStore.fromFile(keystoreFile, password.toCharArray(), newFile) + .readKeyPair(keypairName, false); + System.out.printf( + "Public key of keypair '%s': %s%n", keypairName, keyPair.getPublicKey().toHex()); + return null; + }); + } + + private Result parseKeystore(CommandLine commandLine) { + return requiredString(commandLine, "k"); + } + + private Result parsePassword(CommandLine commandLine) { + return requiredString(commandLine, "p"); + } + + private Result parseKeypair(CommandLine commandLine) { + return requiredString(commandLine, "n").or(Result.ok(DEFAULT_KEYPAIR_NAME)); + } + + private Result requiredString(CommandLine commandLine, String opt) { + return fromOptional( + () -> MISSING_PARAMETER.with(opt), ofNullable(commandLine.getOptionValue(opt))); + } + + private Result parseParameters(String[] args) { + return Result.wrap( + UNABLE_TO_PARSE_COMMAND_LINE, () -> new DefaultParser().parse(options, args)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/InMemoryBFTKeyModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/InMemoryBFTKeyModule.java index 9ab9a4f1c0..18b01c0db7 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/InMemoryBFTKeyModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/InMemoryBFTKeyModule.java @@ -72,38 +72,35 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.qualifier.LocalSigner; - import java.util.function.Function; -/** - * In memory Hash signing and identity handling - */ +/** In memory Hash signing and identity handling */ public final class InMemoryBFTKeyModule extends AbstractModule { - @Override - public void configure() { - bind(HashSigner.class).annotatedWith(LocalSigner.class).to(HashSigner.class); - } + @Override + public void configure() { + bind(HashSigner.class).annotatedWith(LocalSigner.class).to(HashSigner.class); + } - @Provides - public HashSigner hashSigner(@Self ECKeyPair self) { - return self::sign; - } + @Provides + public HashSigner hashSigner(@Self ECKeyPair self) { + return self::sign; + } - @Provides - @Self - ECPublicKey node(@Self BFTNode bftNode) { - return bftNode.getKey(); - } + @Provides + @Self + ECPublicKey node(@Self BFTNode bftNode) { + return bftNode.getKey(); + } - @Provides - @Self - public BFTNode node(@Self ECKeyPair self) { - return BFTNode.create(self.getPublicKey()); - } + @Provides + @Self + public BFTNode node(@Self ECKeyPair self) { + return BFTNode.create(self.getPublicKey()); + } - @Provides - @Self - String name(Function nodeToString, @Self ECPublicKey key) { - return nodeToString.apply(key); - } + @Provides + @Self + String name(Function nodeToString, @Self ECPublicKey key) { + return nodeToString.apply(key); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/Keys.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/Keys.java index bdef2a748d..ac24e42823 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/Keys.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/Keys.java @@ -67,91 +67,102 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.RadixKeyStore; import com.radixdlt.crypto.exception.CryptoException; - import java.io.File; import java.io.IOException; import java.util.Arrays; -/** - * Helper methods for key handling. - */ +/** Helper methods for key handling. */ public final class Keys { - private Keys() { - throw new IllegalStateException("Can't construct"); - } + private Keys() { + throw new IllegalStateException("Can't construct"); + } - private static void reset(char[]... passwords) { - for (var password : passwords) { - if (password != null) { - Arrays.fill(password, ' '); - } - } - } + private static void reset(char[]... passwords) { + for (var password : passwords) { + if (password != null) { + Arrays.fill(password, ' '); + } + } + } - /** - * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using key pair name and environment variables - * specific to node. If keystore or key pair don't exists, throws an exception. - * - * @param keyStore Key store path. - */ - public static ECKeyPair readNodeKey(String keyStore) throws IOException, CryptoException { - return readKey(keyStore, "node", "RADIX_NODE_KEYSTORE_PASSWORD", "RADIX_NODE_KEY_PASSWORD", false); - } + /** + * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using key pair name and + * environment variables specific to node. If keystore or key pair don't exists, throws an + * exception. + * + * @param keyStore Key store path. + */ + public static ECKeyPair readNodeKey(String keyStore) throws IOException, CryptoException { + return readKey( + keyStore, "node", "RADIX_NODE_KEYSTORE_PASSWORD", "RADIX_NODE_KEY_PASSWORD", false); + } - /** - * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using key pair name and environment variables - * specific to staker. If keystore or key pair don't exists, they are created. - * - * @param keyStore Key store path. - */ - public static ECKeyPair readStakerKey(String keyStore) throws IOException, CryptoException { - return readKey(keyStore, "wallet", "RADIX_STAKER_KEYSTORE_PASSWORD", "RADIX_STAKER_KEY_PASSWORD", true); - } + /** + * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using key pair name and + * environment variables specific to staker. If keystore or key pair don't exists, they are + * created. + * + * @param keyStore Key store path. + */ + public static ECKeyPair readStakerKey(String keyStore) throws IOException, CryptoException { + return readKey( + keyStore, "wallet", "RADIX_STAKER_KEYSTORE_PASSWORD", "RADIX_STAKER_KEY_PASSWORD", true); + } - /** - * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using key pair name and environment variables - * specific to validator. If keystore or key pair don't exists, they are created. - * - * @param keyStore Key store path. - */ - public static ECKeyPair readValidatorKey(String keyStore) throws IOException, CryptoException { - return readKey(keyStore, "node", "RADIX_VALIDATOR_KEYSTORE_PASSWORD", "RADIX_VALIDATOR_KEY_PASSWORD", true); - } + /** + * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using key pair name and + * environment variables specific to validator. If keystore or key pair don't exists, they are + * created. + * + * @param keyStore Key store path. + */ + public static ECKeyPair readValidatorKey(String keyStore) throws IOException, CryptoException { + return readKey( + keyStore, + "node", + "RADIX_VALIDATOR_KEYSTORE_PASSWORD", + "RADIX_VALIDATOR_KEY_PASSWORD", + true); + } - /** - * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, - * using the specified key pair name and environment variables for passwords. - *

- * If the specified key store does not exist, then it will be created, - * if possible and {@code create} parameter is set to {@code true}. - * - * @param keyStorePath The path to the {@link RadixKeyStore} - * @param keyName The name of the key within the key store to read - * @param keyStorePasswordEnv The environment variable holding the keystore password. This environment variable is read and used - * as the password for accessing the key store overall. If the environment variable does not exist, no password is used. - * @param keyPasswordEnv The environment variable holding the key password. This environment variable is read and used as - * the password for accessing the key within the store. If the environment variable does not exist, no password is used. - * @param create If set to {@code true}, then keystore file and keypair will be created if not exists. - * - * @return The key read from the key store - */ - private static ECKeyPair readKey( - String keyStorePath, String keyName, - String keyStorePasswordEnv, String keyPasswordEnv, - boolean create - ) throws IOException, CryptoException { - var keyPassword = readPassword(keyPasswordEnv); - var keyStorePassword = readPassword(keyStorePasswordEnv); + /** + * Read an {@link ECKeyPair} from the specified {@link RadixKeyStore}, using the specified key + * pair name and environment variables for passwords. + * + *

If the specified key store does not exist, then it will be created, if possible and {@code + * create} parameter is set to {@code true}. + * + * @param keyStorePath The path to the {@link RadixKeyStore} + * @param keyName The name of the key within the key store to read + * @param keyStorePasswordEnv The environment variable holding the keystore password. This + * environment variable is read and used as the password for accessing the key store overall. + * If the environment variable does not exist, no password is used. + * @param keyPasswordEnv The environment variable holding the key password. This environment + * variable is read and used as the password for accessing the key within the store. If the + * environment variable does not exist, no password is used. + * @param create If set to {@code true}, then keystore file and keypair will be created if not + * exists. + * @return The key read from the key store + */ + private static ECKeyPair readKey( + String keyStorePath, + String keyName, + String keyStorePasswordEnv, + String keyPasswordEnv, + boolean create) + throws IOException, CryptoException { + var keyPassword = readPassword(keyPasswordEnv); + var keyStorePassword = readPassword(keyStorePasswordEnv); - try (var ks = RadixKeyStore.fromFile(new File(keyStorePath), keyStorePassword, create)) { - return ks.readKeyPair(keyName, create); - } finally { - reset(keyPassword, keyStorePassword); - } - } + try (var ks = RadixKeyStore.fromFile(new File(keyStorePath), keyStorePassword, create)) { + return ks.readKeyPair(keyName, create); + } finally { + reset(keyPassword, keyStorePassword); + } + } - private static char[] readPassword(String envVar) { - var envValue = System.getenv(envVar); - return envValue == null ? null : envValue.toCharArray(); - } + private static char[] readPassword(String envVar) { + var envValue = System.getenv(envVar); + return envValue == null ? null : envValue.toCharArray(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyManager.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyManager.java index f2f8ff8018..70e39b2e40 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyManager.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyManager.java @@ -69,54 +69,52 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.exception.CryptoException; import com.radixdlt.utils.Bytes; - import java.io.IOException; import java.util.Optional; -/** - * Manages a persisted key pair to be used for signing - */ +/** Manages a persisted key pair to be used for signing */ public final class PersistedBFTKeyManager { - private final ECKeyPair ecKeyPair; + private final ECKeyPair ecKeyPair; - public PersistedBFTKeyManager(String nodeKeyPath) { - this.ecKeyPair = loadNodeKey(nodeKeyPath); - } + public PersistedBFTKeyManager(String nodeKeyPath) { + this.ecKeyPair = loadNodeKey(nodeKeyPath); + } - private static ECKeyPair loadNodeKey(String nodeKeyPath) { - return readNodeKeyFromEnvironment().orElseGet(() -> loadNodeKeyFromFile(nodeKeyPath)); - } + private static ECKeyPair loadNodeKey(String nodeKeyPath) { + return readNodeKeyFromEnvironment().orElseGet(() -> loadNodeKeyFromFile(nodeKeyPath)); + } - private static ECKeyPair loadNodeKeyFromFile(String nodeKeyPath) { - try { - return Keys.readNodeKey(nodeKeyPath); - } catch (IOException | CryptoException ex) { - throw new IllegalStateException("While loading node key from " + nodeKeyPath, ex); - } - } + private static ECKeyPair loadNodeKeyFromFile(String nodeKeyPath) { + try { + return Keys.readNodeKey(nodeKeyPath); + } catch (IOException | CryptoException ex) { + throw new IllegalStateException("While loading node key from " + nodeKeyPath, ex); + } + } - private static Optional readNodeKeyFromEnvironment() { - return Optional.ofNullable(System.getenv("RADIXDLT_NODE_KEY")) - .map(Bytes::fromBase64String) - .map(keyBytes -> { - try { - // Note that the keypair takes ownership of keyBytes at this point - return ECKeyPair.fromPrivateKey(keyBytes); - } catch (CryptoException e) { - throw new IllegalStateException("While reading key from environment", e); - } - }); - } + private static Optional readNodeKeyFromEnvironment() { + return Optional.ofNullable(System.getenv("RADIXDLT_NODE_KEY")) + .map(Bytes::fromBase64String) + .map( + keyBytes -> { + try { + // Note that the keypair takes ownership of keyBytes at this point + return ECKeyPair.fromPrivateKey(keyBytes); + } catch (CryptoException e) { + throw new IllegalStateException("While reading key from environment", e); + } + }); + } - public ECDSASignature sign(byte[] hash) { - return ecKeyPair.sign(hash); - } + public ECDSASignature sign(byte[] hash) { + return ecKeyPair.sign(hash); + } - public BFTNode self() { - return BFTNode.create(ecKeyPair.getPublicKey()); - } + public BFTNode self() { + return BFTNode.create(ecKeyPair.getPublicKey()); + } - public ECKeyPair getKeyPair() { - return this.ecKeyPair; - } + public ECKeyPair getKeyPair() { + return this.ecKeyPair; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyModule.java index f91cce438a..0de5f35c82 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/keys/PersistedBFTKeyModule.java @@ -76,55 +76,52 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.properties.RuntimeProperties; import com.radixdlt.qualifier.LocalSigner; - import java.util.function.Function; -/** - * Configures the key to be used for signing things as a BFT validator. - */ +/** Configures the key to be used for signing things as a BFT validator. */ public final class PersistedBFTKeyModule extends AbstractModule { - @Override - public void configure() { - bind(HashSigner.class).annotatedWith(LocalSigner.class).to(HashSigner.class); - } + @Override + public void configure() { + bind(HashSigner.class).annotatedWith(LocalSigner.class).to(HashSigner.class); + } - @Provides - @Singleton - PersistedBFTKeyManager bftKeyManager(RuntimeProperties properties) { - var nodeKeyPath = properties.get("node.key.path", "node.ks"); + @Provides + @Singleton + PersistedBFTKeyManager bftKeyManager(RuntimeProperties properties) { + var nodeKeyPath = properties.get("node.key.path", "node.ks"); - return new PersistedBFTKeyManager(nodeKeyPath); - } + return new PersistedBFTKeyManager(nodeKeyPath); + } - @Provides - @Self - ECPublicKey key(@Self BFTNode bftNode) { - return bftNode.getKey(); - } + @Provides + @Self + ECPublicKey key(@Self BFTNode bftNode) { + return bftNode.getKey(); + } - @Provides - ECKeyOps ecKeyOps(PersistedBFTKeyManager keyManager) { - return ECKeyOps.fromKeyPair(keyManager.getKeyPair()); - } + @Provides + ECKeyOps ecKeyOps(PersistedBFTKeyManager keyManager) { + return ECKeyOps.fromKeyPair(keyManager.getKeyPair()); + } - @Provides - @Self - BFTNode bftNode(PersistedBFTKeyManager bftKeyManager) { - return bftKeyManager.self(); - } + @Provides + @Self + BFTNode bftNode(PersistedBFTKeyManager bftKeyManager) { + return bftKeyManager.self(); + } - @Provides - @Self - String name(Function nodeToString, @Self ECPublicKey key) { - return nodeToString.apply(key); - } + @Provides + @Self + String name(Function nodeToString, @Self ECPublicKey key) { + return nodeToString.apply(key); + } - @Provides - @Singleton - HashSigner hashSigner(PersistedBFTKeyManager bftKeyManager, SystemCounters counters) { - return hash -> { - counters.increment(CounterType.SIGNATURES_SIGNED); - return bftKeyManager.sign(hash); - }; - } + @Provides + @Singleton + HashSigner hashSigner(PersistedBFTKeyManager bftKeyManager, SystemCounters counters) { + return hash -> { + counters.increment(CounterType.SIGNATURES_SIGNED); + return bftKeyManager.sign(hash); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/AccumulatorState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/AccumulatorState.java index 28beae5385..8dd042b1fa 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/AccumulatorState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/AccumulatorState.java @@ -72,65 +72,63 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; - import java.util.Objects; +import javax.annotation.concurrent.Immutable; @Immutable @SerializerId2("ledger.accumulator_state") public final class AccumulatorState { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; - @JsonProperty("state_version") - @DsonOutput(Output.ALL) - private final long stateVersion; + @JsonProperty("state_version") + @DsonOutput(Output.ALL) + private final long stateVersion; - @JsonProperty("accumulator_hash") - @DsonOutput(Output.ALL) - private final HashCode accumulatorHash; + @JsonProperty("accumulator_hash") + @DsonOutput(Output.ALL) + private final HashCode accumulatorHash; - @JsonCreator - public AccumulatorState( - @JsonProperty("state_version") long stateVersion, - @JsonProperty(value = "accumulator_hash", required = true) HashCode accumulatorHash - ) { - if (stateVersion < 0) { - throw new IllegalArgumentException("State version must be >= 0"); - } + @JsonCreator + public AccumulatorState( + @JsonProperty("state_version") long stateVersion, + @JsonProperty(value = "accumulator_hash", required = true) HashCode accumulatorHash) { + if (stateVersion < 0) { + throw new IllegalArgumentException("State version must be >= 0"); + } - this.accumulatorHash = Objects.requireNonNull(accumulatorHash); - this.stateVersion = stateVersion; - } + this.accumulatorHash = Objects.requireNonNull(accumulatorHash); + this.stateVersion = stateVersion; + } - public long getStateVersion() { - return stateVersion; - } + public long getStateVersion() { + return stateVersion; + } - public HashCode getAccumulatorHash() { - return accumulatorHash; - } + public HashCode getAccumulatorHash() { + return accumulatorHash; + } - @Override - public int hashCode() { - return Objects.hash(stateVersion, accumulatorHash); - } + @Override + public int hashCode() { + return Objects.hash(stateVersion, accumulatorHash); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof AccumulatorState)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof AccumulatorState)) { + return false; + } - AccumulatorState other = (AccumulatorState) o; - return stateVersion == other.stateVersion - && Objects.equals(accumulatorHash, other.accumulatorHash); - } + AccumulatorState other = (AccumulatorState) o; + return stateVersion == other.stateVersion + && Objects.equals(accumulatorHash, other.accumulatorHash); + } - @Override - public String toString() { - return String.format("%s{version=%s hash=%s}", getClass().getSimpleName(), stateVersion, accumulatorHash); - } + @Override + public String toString() { + return String.format( + "%s{version=%s hash=%s}", getClass().getSimpleName(), stateVersion, accumulatorHash); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/ByzantineQuorumException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/ByzantineQuorumException.java index d9b72b58be..652d56def8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/ByzantineQuorumException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/ByzantineQuorumException.java @@ -65,18 +65,18 @@ package com.radixdlt.ledger; /** - * Exception which suggests that there exists a byzantine quorum which - * got us to this exception state. + * Exception which suggests that there exists a byzantine quorum which got us to this exception + * state. * - * TODO: Remove all instance of this class and replace with mechanism to - * log and revert to last known good state. + *

TODO: Remove all instance of this class and replace with mechanism to log and revert to last + * known good state. */ public class ByzantineQuorumException extends RuntimeException { - public ByzantineQuorumException(String message) { - super(message); - } + public ByzantineQuorumException(String message) { + super(message); + } - public ByzantineQuorumException(String message, Exception cause) { - super(message, cause); - } + public ByzantineQuorumException(String message, Exception cause) { + super(message, cause); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/CommittedBadTxnException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/CommittedBadTxnException.java index 16a16da2a1..144dbb8223 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/CommittedBadTxnException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/CommittedBadTxnException.java @@ -67,11 +67,19 @@ import com.radixdlt.engine.RadixEngineException; public class CommittedBadTxnException extends ByzantineQuorumException { - public CommittedBadTxnException(VerifiedTxnsAndProof txnsAndProof, RadixEngineException cause) { - super("epoch=" + txnsAndProof.getProof().getEpoch() + " version=" + versionWithIssue(txnsAndProof, cause), cause); - } + public CommittedBadTxnException(VerifiedTxnsAndProof txnsAndProof, RadixEngineException cause) { + super( + "epoch=" + + txnsAndProof.getProof().getEpoch() + + " version=" + + versionWithIssue(txnsAndProof, cause), + cause); + } - private static long versionWithIssue(VerifiedTxnsAndProof txnsAndProof, RadixEngineException cause) { - return txnsAndProof.getProof().getStateVersion() - txnsAndProof.getTxns().size() + cause.getTxnIndex(); - } + private static long versionWithIssue( + VerifiedTxnsAndProof txnsAndProof, RadixEngineException cause) { + return txnsAndProof.getProof().getStateVersion() + - txnsAndProof.getTxns().size() + + cause.getTxnIndex(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoLedgerProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoLedgerProof.java index 166307420e..60fe72795a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoLedgerProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoLedgerProof.java @@ -74,76 +74,72 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.Objects; +import javax.annotation.concurrent.Immutable; -/** - * A ledger header and proof which has not been verified - */ +/** A ledger header and proof which has not been verified */ @Immutable @SerializerId2("ledger.dto_proof") public final class DtoLedgerProof { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; - // proposed - @JsonProperty("opaque") - @DsonOutput(Output.ALL) - private final HashCode opaque; + // proposed + @JsonProperty("opaque") + @DsonOutput(Output.ALL) + private final HashCode opaque; - // committed ledgerState - @JsonProperty("ledgerState") - @DsonOutput(Output.ALL) - private final LedgerHeader ledgerHeader; + // committed ledgerState + @JsonProperty("ledgerState") + @DsonOutput(Output.ALL) + private final LedgerHeader ledgerHeader; - @JsonProperty("signatures") - @DsonOutput(Output.ALL) - private final TimestampedECDSASignatures signatures; + @JsonProperty("signatures") + @DsonOutput(Output.ALL) + private final TimestampedECDSASignatures signatures; - @JsonCreator - public DtoLedgerProof( - @JsonProperty(value = "opaque", required = true) HashCode opaque, - @JsonProperty(value = "ledgerState", required = true) LedgerHeader ledgerHeader, - @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures - ) { - this.opaque = Objects.requireNonNull(opaque); - this.ledgerHeader = Objects.requireNonNull(ledgerHeader); - this.signatures = Objects.requireNonNull(signatures); - } + @JsonCreator + public DtoLedgerProof( + @JsonProperty(value = "opaque", required = true) HashCode opaque, + @JsonProperty(value = "ledgerState", required = true) LedgerHeader ledgerHeader, + @JsonProperty(value = "signatures", required = true) TimestampedECDSASignatures signatures) { + this.opaque = Objects.requireNonNull(opaque); + this.ledgerHeader = Objects.requireNonNull(ledgerHeader); + this.signatures = Objects.requireNonNull(signatures); + } - public HashCode getOpaque() { - return opaque; - } + public HashCode getOpaque() { + return opaque; + } - public TimestampedECDSASignatures getSignatures() { - return signatures; - } + public TimestampedECDSASignatures getSignatures() { + return signatures; + } - public LedgerHeader getLedgerHeader() { - return ledgerHeader; - } + public LedgerHeader getLedgerHeader() { + return ledgerHeader; + } - @Override - public String toString() { - return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.ledgerHeader); - } + @Override + public String toString() { + return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.ledgerHeader); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof DtoLedgerProof that) - && Objects.equals(opaque, that.opaque) - && Objects.equals(ledgerHeader, that.ledgerHeader) - && Objects.equals(signatures, that.signatures); - } + return (o instanceof DtoLedgerProof that) + && Objects.equals(opaque, that.opaque) + && Objects.equals(ledgerHeader, that.ledgerHeader) + && Objects.equals(signatures, that.signatures); + } - @Override - public int hashCode() { - return Objects.hash(opaque, ledgerHeader, signatures); - } + @Override + public int hashCode() { + return Objects.hash(opaque, ledgerHeader, signatures); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoTxnsAndProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoTxnsAndProof.java index 81a6f4fb43..9c37e0e752 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoTxnsAndProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/DtoTxnsAndProof.java @@ -64,6 +64,8 @@ package com.radixdlt.ledger; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; @@ -73,79 +75,73 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - -import javax.annotation.concurrent.Immutable; import java.util.List; import java.util.Objects; +import javax.annotation.concurrent.Immutable; -import static java.util.Objects.requireNonNull; - -/** - * A commands and proof which has not been verified - */ +/** A commands and proof which has not been verified */ @Immutable @SerializerId2("ledger.commands_and_proof") public final class DtoTxnsAndProof { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("txns") - @DsonOutput(Output.ALL) - private final List txns; - - @JsonProperty("head") - @DsonOutput(Output.ALL) - private final DtoLedgerProof head; - - @JsonProperty("tail") - @DsonOutput(Output.ALL) - private final DtoLedgerProof tail; - - @JsonCreator - public DtoTxnsAndProof( - @JsonProperty("txns") List txns, - @JsonProperty(value = "head", required = true) DtoLedgerProof head, - @JsonProperty(value = "tail", required = true) DtoLedgerProof tail - ) { - this.txns = txns == null ? ImmutableList.of() : txns; - this.head = requireNonNull(head); - this.tail = requireNonNull(tail); - - this.txns.forEach(Objects::requireNonNull); - } - - public List getTxns() { - return txns; - } - - public DtoLedgerProof getHead() { - return head; - } - - public DtoLedgerProof getTail() { - return tail; - } - - @Override - public String toString() { - return String.format("%s{head=%s tail=%s}", this.getClass().getSimpleName(), head, tail); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - return (o instanceof DtoTxnsAndProof that) - && Objects.equals(txns, that.txns) - && Objects.equals(head, that.head) - && Objects.equals(tail, that.tail); - } - - @Override - public int hashCode() { - return Objects.hash(txns, head, tail); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("txns") + @DsonOutput(Output.ALL) + private final List txns; + + @JsonProperty("head") + @DsonOutput(Output.ALL) + private final DtoLedgerProof head; + + @JsonProperty("tail") + @DsonOutput(Output.ALL) + private final DtoLedgerProof tail; + + @JsonCreator + public DtoTxnsAndProof( + @JsonProperty("txns") List txns, + @JsonProperty(value = "head", required = true) DtoLedgerProof head, + @JsonProperty(value = "tail", required = true) DtoLedgerProof tail) { + this.txns = txns == null ? ImmutableList.of() : txns; + this.head = requireNonNull(head); + this.tail = requireNonNull(tail); + + this.txns.forEach(Objects::requireNonNull); + } + + public List getTxns() { + return txns; + } + + public DtoLedgerProof getHead() { + return head; + } + + public DtoLedgerProof getTail() { + return tail; + } + + @Override + public String toString() { + return String.format("%s{head=%s tail=%s}", this.getClass().getSimpleName(), head, tail); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + return (o instanceof DtoTxnsAndProof that) + && Objects.equals(txns, that.txns) + && Objects.equals(head, that.head) + && Objects.equals(tail, that.tail); + } + + @Override + public int hashCode() { + return Objects.hash(txns, head, tail); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulator.java index 6fa4013b91..39eb6ab17a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulator.java @@ -67,12 +67,12 @@ import com.google.common.hash.HashCode; /** - * Accumulates commands into a single version hash which represents - * all commands which have been committed in a certain order. + * Accumulates commands into a single version hash which represents all commands which have been + * committed in a certain order. * - * All implementations should be functional and stateless. + *

All implementations should be functional and stateless. */ @FunctionalInterface public interface LedgerAccumulator { - AccumulatorState accumulate(AccumulatorState parent, HashCode hash); + AccumulatorState accumulate(AccumulatorState parent, HashCode hash); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulatorVerifier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulatorVerifier.java index e778e2e2de..76a3203575 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulatorVerifier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerAccumulatorVerifier.java @@ -66,7 +66,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; - import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -74,16 +73,15 @@ /** * Verifies whether a given accumulator extends a given one. * - * All implementations should be stateless. + *

All implementations should be stateless. */ public interface LedgerAccumulatorVerifier { - // TODO: use stream instead of list - boolean verify(AccumulatorState head, ImmutableList commands, AccumulatorState tail); + // TODO: use stream instead of list + boolean verify(AccumulatorState head, ImmutableList commands, AccumulatorState tail); - Optional> verifyAndGetExtension( - AccumulatorState current, - List commands, - Function hashCodeMapper, - AccumulatorState tail - ); + Optional> verifyAndGetExtension( + AccumulatorState current, + List commands, + Function hashCodeMapper, + AccumulatorState tail); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerUpdate.java index c8c47af169..3004d8cf6a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/LedgerUpdate.java @@ -68,55 +68,55 @@ import com.radixdlt.atom.Txn; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.BFTValidatorSet; - import java.util.List; import java.util.Objects; import java.util.Optional; public final class LedgerUpdate { - private final VerifiedTxnsAndProof verifiedTxnsAndProof; - // FIXME: Easiest way to implement this part for now - private final ClassToInstanceMap output; + private final VerifiedTxnsAndProof verifiedTxnsAndProof; + // FIXME: Easiest way to implement this part for now + private final ClassToInstanceMap output; - public LedgerUpdate(VerifiedTxnsAndProof verifiedTxnsAndProof, ClassToInstanceMap output) { - this.verifiedTxnsAndProof = Objects.requireNonNull(verifiedTxnsAndProof); - this.output = Objects.requireNonNull(output); - } + public LedgerUpdate( + VerifiedTxnsAndProof verifiedTxnsAndProof, ClassToInstanceMap output) { + this.verifiedTxnsAndProof = Objects.requireNonNull(verifiedTxnsAndProof); + this.output = Objects.requireNonNull(output); + } - public ClassToInstanceMap getStateComputerOutput() { - return output; - } + public ClassToInstanceMap getStateComputerOutput() { + return output; + } - public List getNewTxns() { - return verifiedTxnsAndProof.getTxns(); - } + public List getNewTxns() { + return verifiedTxnsAndProof.getTxns(); + } - public LedgerProof getTail() { - return verifiedTxnsAndProof.getProof(); - } + public LedgerProof getTail() { + return verifiedTxnsAndProof.getProof(); + } - public Optional getNextValidatorSet() { - return verifiedTxnsAndProof.getProof().getNextValidatorSet(); - } + public Optional getNextValidatorSet() { + return verifiedTxnsAndProof.getProof().getNextValidatorSet(); + } - @Override - public String toString() { - return String.format("%s{commands=%s}", this.getClass().getSimpleName(), verifiedTxnsAndProof); - } + @Override + public String toString() { + return String.format("%s{commands=%s}", this.getClass().getSimpleName(), verifiedTxnsAndProof); + } - @Override - public int hashCode() { - return Objects.hash(verifiedTxnsAndProof, output); - } + @Override + public int hashCode() { + return Objects.hash(verifiedTxnsAndProof, output); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof LedgerUpdate)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof LedgerUpdate)) { + return false; + } - LedgerUpdate other = (LedgerUpdate) o; - return Objects.equals(other.verifiedTxnsAndProof, this.verifiedTxnsAndProof) - && Objects.equals(other.output, this.output); - } + LedgerUpdate other = (LedgerUpdate) o; + return Objects.equals(other.verifiedTxnsAndProof, this.verifiedTxnsAndProof) + && Objects.equals(other.output, this.output); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifier.java index e191feea68..1da42d4620 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifier.java @@ -68,75 +68,72 @@ import com.google.common.hash.HashCode; import com.google.inject.Inject; import com.radixdlt.crypto.Hasher; - import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Function; import javax.annotation.concurrent.ThreadSafe; -/** - * Hash chain accumulator and verifier - */ +/** Hash chain accumulator and verifier */ @ThreadSafe -public class SimpleLedgerAccumulatorAndVerifier implements LedgerAccumulator, LedgerAccumulatorVerifier { - private final Hasher hasher; +public class SimpleLedgerAccumulatorAndVerifier + implements LedgerAccumulator, LedgerAccumulatorVerifier { + private final Hasher hasher; - @Inject - public SimpleLedgerAccumulatorAndVerifier(Hasher hasher) { - this.hasher = hasher; - } + @Inject + public SimpleLedgerAccumulatorAndVerifier(Hasher hasher) { + this.hasher = hasher; + } - @Override - public AccumulatorState accumulate(AccumulatorState parent, HashCode hash) { - byte[] concat = new byte[32 * 2]; - System.arraycopy(parent.getAccumulatorHash().asBytes(), 0, concat, 0, 32); - System.arraycopy(hash.asBytes(), 0, concat, 32, 32); - HashCode nextAccumulatorHash = hasher.hashBytes(concat); - return new AccumulatorState( - parent.getStateVersion() + 1, - nextAccumulatorHash - ); - } + @Override + public AccumulatorState accumulate(AccumulatorState parent, HashCode hash) { + byte[] concat = new byte[32 * 2]; + System.arraycopy(parent.getAccumulatorHash().asBytes(), 0, concat, 0, 32); + System.arraycopy(hash.asBytes(), 0, concat, 32, 32); + HashCode nextAccumulatorHash = hasher.hashBytes(concat); + return new AccumulatorState(parent.getStateVersion() + 1, nextAccumulatorHash); + } - @Override - public boolean verify(AccumulatorState start, ImmutableList hashes, AccumulatorState end) { - AccumulatorState accumulatorState = start; - for (HashCode hash : hashes) { - accumulatorState = this.accumulate(accumulatorState, hash); - } - return Objects.equals(accumulatorState, end); - } + @Override + public boolean verify( + AccumulatorState start, ImmutableList hashes, AccumulatorState end) { + AccumulatorState accumulatorState = start; + for (HashCode hash : hashes) { + accumulatorState = this.accumulate(accumulatorState, hash); + } + return Objects.equals(accumulatorState, end); + } - @Override - public Optional> verifyAndGetExtension( - AccumulatorState current, - List commands, - Function hashCodeMapper, - AccumulatorState tail - ) { - if (tail.getStateVersion() < current.getStateVersion()) { - throw new IllegalArgumentException(String.format("Tail %s is has lower state version than current %s", tail, current)); - } + @Override + public Optional> verifyAndGetExtension( + AccumulatorState current, + List commands, + Function hashCodeMapper, + AccumulatorState tail) { + if (tail.getStateVersion() < current.getStateVersion()) { + throw new IllegalArgumentException( + String.format("Tail %s is has lower state version than current %s", tail, current)); + } - final long firstVersion = tail.getStateVersion() - commands.size() + 1; - if (current.getStateVersion() + 1 < firstVersion) { - // Missing versions - return Optional.empty(); - } + final long firstVersion = tail.getStateVersion() - commands.size() + 1; + if (current.getStateVersion() + 1 < firstVersion) { + // Missing versions + return Optional.empty(); + } - if (commands.isEmpty()) { - return Objects.equals(current, tail) ? Optional.of(ImmutableList.of()) : Optional.empty(); - } + if (commands.isEmpty()) { + return Objects.equals(current, tail) ? Optional.of(ImmutableList.of()) : Optional.empty(); + } - final int startIndex = (int) (current.getStateVersion() + 1 - firstVersion); - final List extension = commands.subList(startIndex, commands.size()); - final ImmutableList hashes = extension.stream().map(hashCodeMapper::apply).collect(ImmutableList.toImmutableList()); - if (!verify(current, hashes, tail)) { - // Does not extend - return Optional.empty(); - } + final int startIndex = (int) (current.getStateVersion() + 1 - firstVersion); + final List extension = commands.subList(startIndex, commands.size()); + final ImmutableList hashes = + extension.stream().map(hashCodeMapper::apply).collect(ImmutableList.toImmutableList()); + if (!verify(current, hashes, tail)) { + // Does not extend + return Optional.empty(); + } - return Optional.of(extension); - } -} \ No newline at end of file + return Optional.of(extension); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/StateComputerLedger.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/StateComputerLedger.java index cc58375699..43c9f2e20e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/StateComputerLedger.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/StateComputerLedger.java @@ -68,12 +68,12 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import com.radixdlt.atom.Txn; +import com.radixdlt.consensus.Ledger; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.BFTCommittedUpdate; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidatorSet; -import com.radixdlt.consensus.Ledger; import com.radixdlt.consensus.bft.PreparedVertex; import com.radixdlt.consensus.bft.VerifiedVertex; import com.radixdlt.consensus.bft.VerifiedVertexStoreState; @@ -84,8 +84,8 @@ import com.radixdlt.environment.EventProcessor; import com.radixdlt.environment.RemoteEventProcessor; import com.radixdlt.mempool.MempoolAdd; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.store.LastProof; +import com.radixdlt.utils.TimeSupplier; import java.util.Comparator; import java.util.LinkedList; import java.util.List; @@ -93,232 +93,241 @@ import java.util.Objects; import java.util.Optional; -/** - * Synchronizes execution - */ +/** Synchronizes execution */ public final class StateComputerLedger implements Ledger, NextTxnsGenerator { - public interface PreparedTxn { - Txn txn(); - } - - public static class StateComputerResult { - private final List preparedTxns; - private final Map failedCommands; - private final BFTValidatorSet nextValidatorSet; - - public StateComputerResult( - List preparedTxns, - Map failedCommands, - BFTValidatorSet nextValidatorSet - ) { - this.preparedTxns = Objects.requireNonNull(preparedTxns); - this.failedCommands = Objects.requireNonNull(failedCommands); - this.nextValidatorSet = nextValidatorSet; - } - - public StateComputerResult(List preparedTxns, Map failedCommands) { - this(preparedTxns, failedCommands, null); - } - - public Optional getNextValidatorSet() { - return Optional.ofNullable(nextValidatorSet); - } - - public List getSuccessfulCommands() { - return preparedTxns; - } - - public Map getFailedCommands() { - return failedCommands; - } - } - - public interface StateComputer { - void addToMempool(MempoolAdd mempoolAdd, BFTNode origin); - List getNextTxnsFromMempool(List prepared); - StateComputerResult prepare(List previous, VerifiedVertex vertex, long timestamp); - void commit(VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState); - } - - private final Comparator headerComparator; - private final StateComputer stateComputer; - private final SystemCounters counters; - private final LedgerAccumulator accumulator; - private final LedgerAccumulatorVerifier verifier; - private final Object lock = new Object(); - private final TimeSupplier timeSupplier; - - private LedgerProof currentLedgerHeader; - - @Inject - public StateComputerLedger( - TimeSupplier timeSupplier, - @LastProof LedgerProof initialLedgerState, - Comparator headerComparator, - StateComputer stateComputer, - LedgerAccumulator accumulator, - LedgerAccumulatorVerifier verifier, - SystemCounters counters - ) { - this.timeSupplier = Objects.requireNonNull(timeSupplier); - this.headerComparator = Objects.requireNonNull(headerComparator); - this.stateComputer = Objects.requireNonNull(stateComputer); - this.counters = Objects.requireNonNull(counters); - this.accumulator = Objects.requireNonNull(accumulator); - this.verifier = Objects.requireNonNull(verifier); - this.currentLedgerHeader = initialLedgerState; - } - - public RemoteEventProcessor mempoolAddRemoteEventProcessor() { - return (node, mempoolAdd) -> { - synchronized (lock) { - stateComputer.addToMempool(mempoolAdd, node); - } - }; - } - - public EventProcessor mempoolAddEventProcessor() { - return mempoolAdd -> { - synchronized (lock) { - stateComputer.addToMempool(mempoolAdd, null); - } - }; - } - - @Override - public List generateNextTxns(View view, List prepared) { - final ImmutableList preparedTxns = prepared.stream() - .flatMap(PreparedVertex::successfulCommands) - .collect(ImmutableList.toImmutableList()); - synchronized (lock) { - return stateComputer.getNextTxnsFromMempool(preparedTxns); - } - } - - @Override - public Optional prepare(LinkedList previous, VerifiedVertex vertex) { - final LedgerHeader parentHeader = vertex.getParentHeader().getLedgerHeader(); - final AccumulatorState parentAccumulatorState = parentHeader.getAccumulatorState(); - final ImmutableList prevCommands = previous.stream() - .flatMap(PreparedVertex::successfulCommands) - .collect(ImmutableList.toImmutableList()); - final long quorumTimestamp; - // if vertex has genesis parent then QC is mocked so just use previous timestamp - // this does have the edge case of never increasing timestamps if configuration is - // one view per epoch but good enough for now - if (vertex.getParentHeader().getView().isGenesis()) { - quorumTimestamp = vertex.getParentHeader().getLedgerHeader().timestamp(); - } else { - quorumTimestamp = vertex.getQC().getTimestampedSignatures().weightedTimestamp(); - } - - - synchronized (lock) { - if (this.currentLedgerHeader.getStateVersion() > parentAccumulatorState.getStateVersion()) { - return Optional.empty(); - } - - // Don't execute atom if in process of epoch change - if (parentHeader.isEndOfEpoch()) { - final long localTimestamp = timeSupplier.currentTime(); - final PreparedVertex preparedVertex = vertex - .withHeader(parentHeader.updateViewAndTimestamp(vertex.getView(), quorumTimestamp), localTimestamp) - .andTxns(ImmutableList.of(), ImmutableMap.of()); - return Optional.of(preparedVertex); - } - - final var maybeCommands = this.verifier.verifyAndGetExtension( - this.currentLedgerHeader.getAccumulatorState(), - prevCommands, - p -> p.txn().getId().asHashCode(), - parentAccumulatorState - ); - - // TODO: Write a test to get here - // Can possibly get here without maliciousness if parent vertex isn't locked by everyone else - if (maybeCommands.isEmpty()) { - return Optional.empty(); - } - - final var concatenatedCommands = maybeCommands.get(); - - final StateComputerResult result = stateComputer.prepare( - concatenatedCommands, vertex, quorumTimestamp - ); - - AccumulatorState accumulatorState = parentHeader.getAccumulatorState(); - for (PreparedTxn txn : result.getSuccessfulCommands()) { - accumulatorState = this.accumulator.accumulate(accumulatorState, txn.txn().getId().asHashCode()); - } - - final LedgerHeader ledgerHeader = LedgerHeader.create( - parentHeader.getEpoch(), - vertex.getView(), - accumulatorState, - quorumTimestamp, - result.getNextValidatorSet().orElse(null) - ); - - final long localTimestamp = timeSupplier.currentTime(); - return Optional.of(vertex - .withHeader(ledgerHeader, localTimestamp) - .andTxns(result.getSuccessfulCommands(), result.getFailedCommands()) - ); - } - } - - public EventProcessor bftCommittedUpdateEventProcessor() { - return committedUpdate -> { - final ImmutableList txns = committedUpdate.getCommitted().stream() - .flatMap(PreparedVertex::successfulCommands) - .map(PreparedTxn::txn) - .collect(ImmutableList.toImmutableList()); - var proof = committedUpdate.getVertexStoreState().getRootHeader(); - var verifiedTxnsAndProof = VerifiedTxnsAndProof.create(txns, proof); - - // TODO: Make these two atomic (RPNV1-827) - this.commit(verifiedTxnsAndProof, committedUpdate.getVertexStoreState()); - }; - } - - public EventProcessor syncEventProcessor() { - return p -> this.commit(p, null); - } - - private void commit(VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { - synchronized (lock) { - final LedgerProof nextHeader = verifiedTxnsAndProof.getProof(); - if (headerComparator.compare(nextHeader, this.currentLedgerHeader) <= 0) { - return; - } - - var verifiedExtension = verifier.verifyAndGetExtension( - this.currentLedgerHeader.getAccumulatorState(), - verifiedTxnsAndProof.getTxns(), - txn -> txn.getId().asHashCode(), - verifiedTxnsAndProof.getProof().getAccumulatorState() - ); - - if (verifiedExtension.isEmpty()) { - throw new ByzantineQuorumException("Accumulator failure " + currentLedgerHeader + " " + verifiedTxnsAndProof); - } - - var txns = verifiedExtension.get(); - if (vertexStoreState == null) { - this.counters.add(CounterType.LEDGER_SYNC_COMMANDS_PROCESSED, txns.size()); - } else { - this.counters.add(CounterType.LEDGER_BFT_COMMANDS_PROCESSED, txns.size()); - } - - var txnsAndProof = VerifiedTxnsAndProof.create(txns, verifiedTxnsAndProof.getProof()); - - // persist - this.stateComputer.commit(txnsAndProof, vertexStoreState); - - // TODO: move all of the following to post-persist event handling - this.currentLedgerHeader = nextHeader; - this.counters.set(CounterType.LEDGER_STATE_VERSION, this.currentLedgerHeader.getStateVersion()); - } - } + public interface PreparedTxn { + Txn txn(); + } + + public static class StateComputerResult { + private final List preparedTxns; + private final Map failedCommands; + private final BFTValidatorSet nextValidatorSet; + + public StateComputerResult( + List preparedTxns, + Map failedCommands, + BFTValidatorSet nextValidatorSet) { + this.preparedTxns = Objects.requireNonNull(preparedTxns); + this.failedCommands = Objects.requireNonNull(failedCommands); + this.nextValidatorSet = nextValidatorSet; + } + + public StateComputerResult(List preparedTxns, Map failedCommands) { + this(preparedTxns, failedCommands, null); + } + + public Optional getNextValidatorSet() { + return Optional.ofNullable(nextValidatorSet); + } + + public List getSuccessfulCommands() { + return preparedTxns; + } + + public Map getFailedCommands() { + return failedCommands; + } + } + + public interface StateComputer { + void addToMempool(MempoolAdd mempoolAdd, BFTNode origin); + + List getNextTxnsFromMempool(List prepared); + + StateComputerResult prepare(List previous, VerifiedVertex vertex, long timestamp); + + void commit( + VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState); + } + + private final Comparator headerComparator; + private final StateComputer stateComputer; + private final SystemCounters counters; + private final LedgerAccumulator accumulator; + private final LedgerAccumulatorVerifier verifier; + private final Object lock = new Object(); + private final TimeSupplier timeSupplier; + + private LedgerProof currentLedgerHeader; + + @Inject + public StateComputerLedger( + TimeSupplier timeSupplier, + @LastProof LedgerProof initialLedgerState, + Comparator headerComparator, + StateComputer stateComputer, + LedgerAccumulator accumulator, + LedgerAccumulatorVerifier verifier, + SystemCounters counters) { + this.timeSupplier = Objects.requireNonNull(timeSupplier); + this.headerComparator = Objects.requireNonNull(headerComparator); + this.stateComputer = Objects.requireNonNull(stateComputer); + this.counters = Objects.requireNonNull(counters); + this.accumulator = Objects.requireNonNull(accumulator); + this.verifier = Objects.requireNonNull(verifier); + this.currentLedgerHeader = initialLedgerState; + } + + public RemoteEventProcessor mempoolAddRemoteEventProcessor() { + return (node, mempoolAdd) -> { + synchronized (lock) { + stateComputer.addToMempool(mempoolAdd, node); + } + }; + } + + public EventProcessor mempoolAddEventProcessor() { + return mempoolAdd -> { + synchronized (lock) { + stateComputer.addToMempool(mempoolAdd, null); + } + }; + } + + @Override + public List generateNextTxns(View view, List prepared) { + final ImmutableList preparedTxns = + prepared.stream() + .flatMap(PreparedVertex::successfulCommands) + .collect(ImmutableList.toImmutableList()); + synchronized (lock) { + return stateComputer.getNextTxnsFromMempool(preparedTxns); + } + } + + @Override + public Optional prepare( + LinkedList previous, VerifiedVertex vertex) { + final LedgerHeader parentHeader = vertex.getParentHeader().getLedgerHeader(); + final AccumulatorState parentAccumulatorState = parentHeader.getAccumulatorState(); + final ImmutableList prevCommands = + previous.stream() + .flatMap(PreparedVertex::successfulCommands) + .collect(ImmutableList.toImmutableList()); + final long quorumTimestamp; + // if vertex has genesis parent then QC is mocked so just use previous timestamp + // this does have the edge case of never increasing timestamps if configuration is + // one view per epoch but good enough for now + if (vertex.getParentHeader().getView().isGenesis()) { + quorumTimestamp = vertex.getParentHeader().getLedgerHeader().timestamp(); + } else { + quorumTimestamp = vertex.getQC().getTimestampedSignatures().weightedTimestamp(); + } + + synchronized (lock) { + if (this.currentLedgerHeader.getStateVersion() > parentAccumulatorState.getStateVersion()) { + return Optional.empty(); + } + + // Don't execute atom if in process of epoch change + if (parentHeader.isEndOfEpoch()) { + final long localTimestamp = timeSupplier.currentTime(); + final PreparedVertex preparedVertex = + vertex + .withHeader( + parentHeader.updateViewAndTimestamp(vertex.getView(), quorumTimestamp), + localTimestamp) + .andTxns(ImmutableList.of(), ImmutableMap.of()); + return Optional.of(preparedVertex); + } + + final var maybeCommands = + this.verifier.verifyAndGetExtension( + this.currentLedgerHeader.getAccumulatorState(), + prevCommands, + p -> p.txn().getId().asHashCode(), + parentAccumulatorState); + + // TODO: Write a test to get here + // Can possibly get here without maliciousness if parent vertex isn't locked by everyone else + if (maybeCommands.isEmpty()) { + return Optional.empty(); + } + + final var concatenatedCommands = maybeCommands.get(); + + final StateComputerResult result = + stateComputer.prepare(concatenatedCommands, vertex, quorumTimestamp); + + AccumulatorState accumulatorState = parentHeader.getAccumulatorState(); + for (PreparedTxn txn : result.getSuccessfulCommands()) { + accumulatorState = + this.accumulator.accumulate(accumulatorState, txn.txn().getId().asHashCode()); + } + + final LedgerHeader ledgerHeader = + LedgerHeader.create( + parentHeader.getEpoch(), + vertex.getView(), + accumulatorState, + quorumTimestamp, + result.getNextValidatorSet().orElse(null)); + + final long localTimestamp = timeSupplier.currentTime(); + return Optional.of( + vertex + .withHeader(ledgerHeader, localTimestamp) + .andTxns(result.getSuccessfulCommands(), result.getFailedCommands())); + } + } + + public EventProcessor bftCommittedUpdateEventProcessor() { + return committedUpdate -> { + final ImmutableList txns = + committedUpdate.getCommitted().stream() + .flatMap(PreparedVertex::successfulCommands) + .map(PreparedTxn::txn) + .collect(ImmutableList.toImmutableList()); + var proof = committedUpdate.getVertexStoreState().getRootHeader(); + var verifiedTxnsAndProof = VerifiedTxnsAndProof.create(txns, proof); + + // TODO: Make these two atomic (RPNV1-827) + this.commit(verifiedTxnsAndProof, committedUpdate.getVertexStoreState()); + }; + } + + public EventProcessor syncEventProcessor() { + return p -> this.commit(p, null); + } + + private void commit( + VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { + synchronized (lock) { + final LedgerProof nextHeader = verifiedTxnsAndProof.getProof(); + if (headerComparator.compare(nextHeader, this.currentLedgerHeader) <= 0) { + return; + } + + var verifiedExtension = + verifier.verifyAndGetExtension( + this.currentLedgerHeader.getAccumulatorState(), + verifiedTxnsAndProof.getTxns(), + txn -> txn.getId().asHashCode(), + verifiedTxnsAndProof.getProof().getAccumulatorState()); + + if (verifiedExtension.isEmpty()) { + throw new ByzantineQuorumException( + "Accumulator failure " + currentLedgerHeader + " " + verifiedTxnsAndProof); + } + + var txns = verifiedExtension.get(); + if (vertexStoreState == null) { + this.counters.add(CounterType.LEDGER_SYNC_COMMANDS_PROCESSED, txns.size()); + } else { + this.counters.add(CounterType.LEDGER_BFT_COMMANDS_PROCESSED, txns.size()); + } + + var txnsAndProof = VerifiedTxnsAndProof.create(txns, verifiedTxnsAndProof.getProof()); + + // persist + this.stateComputer.commit(txnsAndProof, vertexStoreState); + + // TODO: move all of the following to post-persist event handling + this.currentLedgerHeader = nextHeader; + this.counters.set( + CounterType.LEDGER_STATE_VERSION, this.currentLedgerHeader.getStateVersion()); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/VerifiedTxnsAndProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/VerifiedTxnsAndProof.java index fcbe839417..fd08555789 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/VerifiedTxnsAndProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ledger/VerifiedTxnsAndProof.java @@ -66,59 +66,52 @@ import com.radixdlt.atom.Txn; import com.radixdlt.consensus.LedgerProof; - import java.util.List; import java.util.Objects; -/** - * Commands along with proof that they have been committed on ledger. - */ +/** Commands along with proof that they have been committed on ledger. */ public final class VerifiedTxnsAndProof { - private final List txns; - private final LedgerProof proof; + private final List txns; + private final LedgerProof proof; - private VerifiedTxnsAndProof( - List txns, - LedgerProof proof - ) { - this.txns = Objects.requireNonNull(txns); - this.proof = Objects.requireNonNull(proof); - } + private VerifiedTxnsAndProof(List txns, LedgerProof proof) { + this.txns = Objects.requireNonNull(txns); + this.proof = Objects.requireNonNull(proof); + } - public static VerifiedTxnsAndProof create(List txns, LedgerProof proof) { - return new VerifiedTxnsAndProof(txns, proof); - } + public static VerifiedTxnsAndProof create(List txns, LedgerProof proof) { + return new VerifiedTxnsAndProof(txns, proof); + } - public List getTxns() { - return txns; - } + public List getTxns() { + return txns; + } - public boolean contains(Txn txn) { - return txns.contains(txn); - } + public boolean contains(Txn txn) { + return txns.contains(txn); + } - public LedgerProof getProof() { - return proof; - } + public LedgerProof getProof() { + return proof; + } - @Override - public int hashCode() { - return Objects.hash(txns, proof); - } + @Override + public int hashCode() { + return Objects.hash(txns, proof); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof VerifiedTxnsAndProof)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof VerifiedTxnsAndProof)) { + return false; + } - VerifiedTxnsAndProof other = (VerifiedTxnsAndProof) o; - return Objects.equals(this.txns, other.txns) - && Objects.equals(this.proof, other.proof); - } + VerifiedTxnsAndProof other = (VerifiedTxnsAndProof) o; + return Objects.equals(this.txns, other.txns) && Objects.equals(this.proof, other.proof); + } - @Override - public String toString() { - return String.format("%s{txns=%s proof=%s}", this.getClass().getSimpleName(), txns, proof); - } + @Override + public String toString() { + return String.format("%s{txns=%s proof=%s}", this.getClass().getSimpleName(), txns, proof); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempool.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempool.java index 04fbb4fdb2..62dc045253 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempool.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempool.java @@ -61,42 +61,39 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.mempool; import com.radixdlt.atom.Txn; - import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; /** * Basic mempool functionality. - *

- * Note that conceptually, a mempoolcan be thought of as a list indexable - * by hash. + * + *

Note that conceptually, a mempoolcan be thought of as a list indexable by hash. */ public interface Mempool { - /** - * Add a transaction to the local mempool. - */ - T add(Txn txn) throws MempoolRejectedException; + /** Add a transaction to the local mempool. */ + T add(Txn txn) throws MempoolRejectedException; - /** - * Retrieve a list of atoms from the local mempool for processing by - * consensus. - *

- * Note that the supplied {@code seen} parameter is used to avoid inclusion - * of atoms that are "in-flight" but not yet committed to the ledger. - * - * @param count the number of atoms to retrieve - * @param seen hashes of commands seen by consensus, but not yet committed to the ledger - * @return A list of commands for processing by consensus - */ - List getTxns(int count, List seen); + /** + * Retrieve a list of atoms from the local mempool for processing by consensus. + * + *

Note that the supplied {@code seen} parameter is used to avoid inclusion of atoms that are + * "in-flight" but not yet committed to the ledger. + * + * @param count the number of atoms to retrieve + * @param seen hashes of commands seen by consensus, but not yet committed to the ledger + * @return A list of commands for processing by consensus + */ + List getTxns(int count, List seen); - List scanUpdateAndGet(Predicate predicate, Consumer operator); + List scanUpdateAndGet( + Predicate predicate, Consumer operator); - List committed(List committed); + List committed(List committed); - int getCount(); + int getCount(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAdd.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAdd.java index 083baafa05..3de002a109 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAdd.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAdd.java @@ -65,21 +65,18 @@ package com.radixdlt.mempool; import com.radixdlt.atom.Txn; - import java.util.List; import java.util.Objects; -/** - * Message to attempt to add commands to the mempool - */ +/** Message to attempt to add commands to the mempool */ public record MempoolAdd(List txns) { - public static MempoolAdd create(Txn txn) { - Objects.requireNonNull(txn); - return new MempoolAdd(List.of(txn)); - } + public static MempoolAdd create(Txn txn) { + Objects.requireNonNull(txn); + return new MempoolAdd(List.of(txn)); + } - public static MempoolAdd create(List txns) { - Objects.requireNonNull(txns); - return new MempoolAdd(txns); - } + public static MempoolAdd create(List txns) { + Objects.requireNonNull(txns); + return new MempoolAdd(txns); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAddSuccess.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAddSuccess.java index 564beb89f2..8c0fd708e5 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAddSuccess.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolAddSuccess.java @@ -66,60 +66,57 @@ import com.radixdlt.atom.Txn; import com.radixdlt.consensus.bft.BFTNode; - import java.util.Objects; import java.util.Optional; -/** - * Message indicating that a command was successfully added to the mempool - */ +/** Message indicating that a command was successfully added to the mempool */ public final class MempoolAddSuccess { - private final Txn txn; - private final Object processedTxn; - private final BFTNode origin; + private final Txn txn; + private final Object processedTxn; + private final BFTNode origin; - private MempoolAddSuccess(Txn txn, Object processedTxn, BFTNode origin) { - this.txn = txn; - this.processedTxn = processedTxn; - this.origin = origin; - } + private MempoolAddSuccess(Txn txn, Object processedTxn, BFTNode origin) { + this.txn = txn; + this.processedTxn = processedTxn; + this.origin = origin; + } - public Txn getTxn() { - return txn; - } + public Txn getTxn() { + return txn; + } - public T getProcessedTxn(Class processedTxnClass) { - return processedTxnClass.cast(processedTxn); - } + public T getProcessedTxn(Class processedTxnClass) { + return processedTxnClass.cast(processedTxn); + } - public Optional getOrigin() { - return Optional.ofNullable(origin); - } + public Optional getOrigin() { + return Optional.ofNullable(origin); + } - public static MempoolAddSuccess create(Txn txn, Object processedTxn, BFTNode origin) { - Objects.requireNonNull(txn); - return new MempoolAddSuccess(txn, processedTxn, origin); - } + public static MempoolAddSuccess create(Txn txn, Object processedTxn, BFTNode origin) { + Objects.requireNonNull(txn); + return new MempoolAddSuccess(txn, processedTxn, origin); + } - @Override - public int hashCode() { - return Objects.hash(txn, processedTxn, origin); - } + @Override + public int hashCode() { + return Objects.hash(txn, processedTxn, origin); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof MempoolAddSuccess)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof MempoolAddSuccess)) { + return false; + } - MempoolAddSuccess other = (MempoolAddSuccess) o; - return Objects.equals(this.txn, other.txn) - && Objects.equals(this.processedTxn, other.processedTxn) - && Objects.equals(this.origin, other.origin); - } + MempoolAddSuccess other = (MempoolAddSuccess) o; + return Objects.equals(this.txn, other.txn) + && Objects.equals(this.processedTxn, other.processedTxn) + && Objects.equals(this.origin, other.origin); + } - @Override - public String toString() { - return String.format("%s{txn=%s}", this.getClass().getSimpleName(), this.txn); - } + @Override + public String toString() { + return String.format("%s{txn=%s}", this.getClass().getSimpleName(), this.txn); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolConfig.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolConfig.java index 14ad204817..54ef158ba0 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolConfig.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolConfig.java @@ -66,34 +66,31 @@ import com.google.inject.AbstractModule; -/** - * Configuration parameters for mempool. - */ +/** Configuration parameters for mempool. */ public final class MempoolConfig { - private MempoolConfig() { - throw new IllegalStateException("Cannot instantiate."); - } + private MempoolConfig() { + throw new IllegalStateException("Cannot instantiate."); + } - public static AbstractModule asModule(int maxSize, long throttleMs) { - return asModule(maxSize, throttleMs, 60000, 60000, 100); - } + public static AbstractModule asModule(int maxSize, long throttleMs) { + return asModule(maxSize, throttleMs, 60000, 60000, 100); + } - public static AbstractModule asModule( - int maxSize, - long throttleMs, - long relayInitialDelay, - long relayRepeatDelay, - int relayMaxPeers - ) { - return new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(MempoolMaxSize.class).to(maxSize); - bindConstant().annotatedWith(MempoolThrottleMs.class).to(throttleMs); - bindConstant().annotatedWith(MempoolRelayInitialDelay.class).to(relayInitialDelay); - bindConstant().annotatedWith(MempoolRelayRepeatDelay.class).to(relayRepeatDelay); - bindConstant().annotatedWith(MempoolRelayMaxPeers.class).to(relayMaxPeers); - } - }; - } + public static AbstractModule asModule( + int maxSize, + long throttleMs, + long relayInitialDelay, + long relayRepeatDelay, + int relayMaxPeers) { + return new AbstractModule() { + @Override + protected void configure() { + bindConstant().annotatedWith(MempoolMaxSize.class).to(maxSize); + bindConstant().annotatedWith(MempoolThrottleMs.class).to(throttleMs); + bindConstant().annotatedWith(MempoolRelayInitialDelay.class).to(relayInitialDelay); + bindConstant().annotatedWith(MempoolRelayRepeatDelay.class).to(relayRepeatDelay); + bindConstant().annotatedWith(MempoolRelayMaxPeers.class).to(relayMaxPeers); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolDuplicateException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolDuplicateException.java index 2ffbc95402..8a6d758c8a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolDuplicateException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolDuplicateException.java @@ -64,12 +64,9 @@ package com.radixdlt.mempool; -/** - * Exception thrown when an attempt to add an item which - * already exists in the mempool - */ +/** Exception thrown when an attempt to add an item which already exists in the mempool */ public class MempoolDuplicateException extends MempoolRejectedException { - public MempoolDuplicateException(String message) { - super(message); - } + public MempoolDuplicateException(String message) { + super(message); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolFullException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolFullException.java index 3f8ec9c347..8a00cfc265 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolFullException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolFullException.java @@ -65,17 +65,17 @@ package com.radixdlt.mempool; /** - * Exception thrown when an attempt to add new items would - * exceed the mempool's maximum capacity. + * Exception thrown when an attempt to add new items would exceed the mempool's maximum capacity. */ public class MempoolFullException extends MempoolRejectedException { - private final int maxSize; - public MempoolFullException(int curSize, int maxSize) { - super(String.format("Mempool full: %s of %s items", curSize, maxSize)); - this.maxSize = maxSize; - } + private final int maxSize; - public int getMaxSize() { - return maxSize; - } + public MempoolFullException(int curSize, int maxSize) { + super(String.format("Mempool full: %s of %s items", curSize, maxSize)); + this.maxSize = maxSize; + } + + public int getMaxSize() { + return maxSize; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMaxSize.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMaxSize.java index bc4f30d40b..4bb1fa16c2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMaxSize.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMaxSize.java @@ -64,18 +64,15 @@ package com.radixdlt.mempool; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Maximum number of txns a mempool should store - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Maximum number of txns a mempool should store */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MempoolMaxSize { -} +public @interface MempoolMaxSize {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMetadata.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMetadata.java index 0b1adc4794..bf88017d80 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMetadata.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolMetadata.java @@ -67,56 +67,54 @@ import java.util.Objects; import java.util.OptionalLong; -/** - * An atom with additional information stored in a mempool. - */ +/** An atom with additional information stored in a mempool. */ public final class MempoolMetadata { - private final long inserted; - private long lastRelayed; + private final long inserted; + private long lastRelayed; - private MempoolMetadata(long inserted, long lastRelayed) { - this.inserted = inserted; - this.lastRelayed = lastRelayed; - } + private MempoolMetadata(long inserted, long lastRelayed) { + this.inserted = inserted; + this.lastRelayed = lastRelayed; + } - public static MempoolMetadata create(long inserted) { - return new MempoolMetadata(inserted, -1); - } + public static MempoolMetadata create(long inserted) { + return new MempoolMetadata(inserted, -1); + } - public long getInserted() { - return inserted; - } + public long getInserted() { + return inserted; + } - public OptionalLong getLastRelayed() { - return lastRelayed < 0 ? OptionalLong.empty() : OptionalLong.of(lastRelayed); - } + public OptionalLong getLastRelayed() { + return lastRelayed < 0 ? OptionalLong.empty() : OptionalLong.of(lastRelayed); + } - public void setLastRelayed(long lastRelayed) { - this.lastRelayed = lastRelayed; - } + public void setLastRelayed(long lastRelayed) { + this.lastRelayed = lastRelayed; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (MempoolMetadata) o; - return inserted == that.inserted - && lastRelayed == that.lastRelayed; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (MempoolMetadata) o; + return inserted == that.inserted && lastRelayed == that.lastRelayed; + } - @Override - public int hashCode() { - return Objects.hash(inserted, lastRelayed); - } + @Override + public int hashCode() { + return Objects.hash(inserted, lastRelayed); + } - @Override - public String toString() { - return String.format("%s{inserted=%s lastRelayed=%s}", - getClass().getSimpleName(), this.inserted, this.lastRelayed); - } + @Override + public String toString() { + return String.format( + "%s{inserted=%s lastRelayed=%s}", + getClass().getSimpleName(), this.inserted, this.lastRelayed); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolReceiverModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolReceiverModule.java index 8b46063e80..4b2fc1f14a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolReceiverModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolReceiverModule.java @@ -69,45 +69,39 @@ import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.consensus.liveness.NextTxnsGenerator; -import com.radixdlt.environment.LocalEvents; import com.radixdlt.environment.EventProcessorOnRunner; +import com.radixdlt.environment.LocalEvents; import com.radixdlt.environment.RemoteEventProcessorOnRunner; import com.radixdlt.environment.Runners; import com.radixdlt.ledger.StateComputerLedger; public class MempoolReceiverModule extends AbstractModule { - @Override - protected void configure() { - bind(NextTxnsGenerator.class).to(StateComputerLedger.class); - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(MempoolAdd.class); - } - - @ProvidesIntoSet - private EventProcessorOnRunner mempoolAddEventProcessor( - StateComputerLedger stateComputerLedger, - @MempoolThrottleMs long mempoolThrottleMs - ) { - return new EventProcessorOnRunner<>( - Runners.MEMPOOL, - MempoolAdd.class, - stateComputerLedger.mempoolAddEventProcessor(), - mempoolThrottleMs - ); - } + @Override + protected void configure() { + bind(NextTxnsGenerator.class).to(StateComputerLedger.class); + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(MempoolAdd.class); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner mempoolAddRemoteEventProcessor( - StateComputerLedger stateComputerLedger, - @MempoolThrottleMs long mempoolThrottleMs - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.MEMPOOL, - MempoolAdd.class, - stateComputerLedger.mempoolAddRemoteEventProcessor(), - mempoolThrottleMs - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner mempoolAddEventProcessor( + StateComputerLedger stateComputerLedger, @MempoolThrottleMs long mempoolThrottleMs) { + return new EventProcessorOnRunner<>( + Runners.MEMPOOL, + MempoolAdd.class, + stateComputerLedger.mempoolAddEventProcessor(), + mempoolThrottleMs); + } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner mempoolAddRemoteEventProcessor( + StateComputerLedger stateComputerLedger, @MempoolThrottleMs long mempoolThrottleMs) { + return new RemoteEventProcessorOnRunner<>( + Runners.MEMPOOL, + MempoolAdd.class, + stateComputerLedger.mempoolAddRemoteEventProcessor(), + mempoolThrottleMs); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRejectedException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRejectedException.java index 10251055e6..8b9fdb7d90 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRejectedException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRejectedException.java @@ -64,16 +64,14 @@ package com.radixdlt.mempool; -/** - * Exception thrown when mempool rejects an atom. - */ +/** Exception thrown when mempool rejects an atom. */ public class MempoolRejectedException extends Exception { - public MempoolRejectedException(String message) { - super(message); - } + public MempoolRejectedException(String message) { + super(message); + } - public MempoolRejectedException(Throwable cause) { - super(cause); - } + public MempoolRejectedException(Throwable cause) { + super(cause); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayInitialDelay.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayInitialDelay.java index b4c6ea372b..6f42beb365 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayInitialDelay.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayInitialDelay.java @@ -64,19 +64,15 @@ package com.radixdlt.mempool; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies how long a txn needs to stay in a mempool in - * order to be relayed to other peers. - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Specifies how long a txn needs to stay in a mempool in order to be relayed to other peers. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MempoolRelayInitialDelay { -} +public @interface MempoolRelayInitialDelay {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayMaxPeers.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayMaxPeers.java index 8b86ef3ea2..cb68ddf7b3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayMaxPeers.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayMaxPeers.java @@ -64,18 +64,15 @@ package com.radixdlt.mempool; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Maximum number of peers to relay txn to. - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Maximum number of peers to relay txn to. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MempoolRelayMaxPeers { -} +public @interface MempoolRelayMaxPeers {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayRepeatDelay.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayRepeatDelay.java index f49159a42b..086900317d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayRepeatDelay.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayRepeatDelay.java @@ -64,19 +64,15 @@ package com.radixdlt.mempool; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies how often a txn is re-relayed once its eligible - * for relay - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Specifies how often a txn is re-relayed once its eligible for relay */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MempoolRelayRepeatDelay { -} +public @interface MempoolRelayRepeatDelay {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayTrigger.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayTrigger.java index 56c3ab716a..e996223d68 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayTrigger.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayTrigger.java @@ -66,28 +66,27 @@ public final class MempoolRelayTrigger { - private MempoolRelayTrigger() { - } + private MempoolRelayTrigger() {} - public static MempoolRelayTrigger create() { - return new MempoolRelayTrigger(); - } + public static MempoolRelayTrigger create() { + return new MempoolRelayTrigger(); + } - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o != null && getClass() == o.getClass(); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o != null && getClass() == o.getClass(); + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayer.java index 6e32fd92ac..85a9aca83f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayer.java @@ -73,82 +73,77 @@ import com.radixdlt.environment.EventProcessor; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.network.p2p.PeersView; - -import javax.inject.Inject; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import javax.inject.Inject; -/** - * Relays commands from the local mempool to node neighbors. - */ +/** Relays commands from the local mempool to node neighbors. */ @Singleton public final class MempoolRelayer { - private final PeersView peersView; - private final RemoteEventDispatcher remoteEventDispatcher; - private final SystemCounters counters; - private final Mempool mempool; - private final long initialDelay; - private final long repeatDelay; - private final int maxPeers; + private final PeersView peersView; + private final RemoteEventDispatcher remoteEventDispatcher; + private final SystemCounters counters; + private final Mempool mempool; + private final long initialDelay; + private final long repeatDelay; + private final int maxPeers; - @Inject - public MempoolRelayer( - Mempool mempool, - RemoteEventDispatcher remoteEventDispatcher, - PeersView peersView, - @MempoolRelayInitialDelay long initialDelay, - @MempoolRelayRepeatDelay long repeatDelay, - @MempoolRelayMaxPeers int maxPeers, - SystemCounters counters - ) { - this.mempool = mempool; - this.remoteEventDispatcher = Objects.requireNonNull(remoteEventDispatcher); - this.peersView = Objects.requireNonNull(peersView); - this.initialDelay = initialDelay; - this.repeatDelay = repeatDelay; - this.maxPeers = maxPeers; - this.counters = Objects.requireNonNull(counters); - } + @Inject + public MempoolRelayer( + Mempool mempool, + RemoteEventDispatcher remoteEventDispatcher, + PeersView peersView, + @MempoolRelayInitialDelay long initialDelay, + @MempoolRelayRepeatDelay long repeatDelay, + @MempoolRelayMaxPeers int maxPeers, + SystemCounters counters) { + this.mempool = mempool; + this.remoteEventDispatcher = Objects.requireNonNull(remoteEventDispatcher); + this.peersView = Objects.requireNonNull(peersView); + this.initialDelay = initialDelay; + this.repeatDelay = repeatDelay; + this.maxPeers = maxPeers; + this.counters = Objects.requireNonNull(counters); + } - public EventProcessor mempoolAddSuccessEventProcessor() { - return mempoolAddSuccess -> { - final var ignorePeers = mempoolAddSuccess.getOrigin() - .map(ImmutableList::of) - .orElse(ImmutableList.of()); - relayCommands(ImmutableList.of(mempoolAddSuccess.getTxn()), ignorePeers); - }; - } + public EventProcessor mempoolAddSuccessEventProcessor() { + return mempoolAddSuccess -> { + final var ignorePeers = + mempoolAddSuccess.getOrigin().map(ImmutableList::of).orElse(ImmutableList.of()); + relayCommands(ImmutableList.of(mempoolAddSuccess.getTxn()), ignorePeers); + }; + } - public EventProcessor mempoolRelayTriggerEventProcessor() { - return ev -> { - final var now = System.currentTimeMillis(); - final var maxAddTime = now - initialDelay; - final var txns = mempool.scanUpdateAndGet( - m -> m.getInserted() <= maxAddTime - && now >= m.getLastRelayed().orElse(0L) - + repeatDelay, - m -> m.setLastRelayed(now) - ); - if (!txns.isEmpty()) { - relayCommands(txns, ImmutableList.of()); - } - }; - } + public EventProcessor mempoolRelayTriggerEventProcessor() { + return ev -> { + final var now = System.currentTimeMillis(); + final var maxAddTime = now - initialDelay; + final var txns = + mempool.scanUpdateAndGet( + m -> + m.getInserted() <= maxAddTime + && now >= m.getLastRelayed().orElse(0L) + repeatDelay, + m -> m.setLastRelayed(now)); + if (!txns.isEmpty()) { + relayCommands(txns, ImmutableList.of()); + } + }; + } - private void relayCommands(List txns, ImmutableList ignorePeers) { - final var mempoolAddMsg = MempoolAdd.create(txns); - final var peers = this.peersView.peers() - .map(PeersView.PeerInfo::bftNode) - .collect(Collectors.toList()); - peers.removeAll(ignorePeers); - Collections.shuffle(peers); - peers.stream() - .limit(maxPeers) - .forEach(peer -> { - counters.add(CounterType.MEMPOOL_RELAYS_SENT, txns.size()); - this.remoteEventDispatcher.dispatch(peer, mempoolAddMsg); - }); - } + private void relayCommands(List txns, ImmutableList ignorePeers) { + final var mempoolAddMsg = MempoolAdd.create(txns); + final var peers = + this.peersView.peers().map(PeersView.PeerInfo::bftNode).collect(Collectors.toList()); + peers.removeAll(ignorePeers); + Collections.shuffle(peers); + peers.stream() + .limit(maxPeers) + .forEach( + peer -> { + counters.add(CounterType.MEMPOOL_RELAYS_SENT, txns.size()); + this.remoteEventDispatcher.dispatch(peer, mempoolAddMsg); + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayerModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayerModule.java index 3ecf3dbc14..57f69ba38c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayerModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolRelayerModule.java @@ -70,53 +70,47 @@ import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.environment.EventDispatcher; -import com.radixdlt.environment.LocalEvents; import com.radixdlt.environment.EventProcessorOnRunner; +import com.radixdlt.environment.LocalEvents; import com.radixdlt.environment.Runners; import com.radixdlt.environment.ScheduledEventProducerOnRunner; - import java.time.Duration; -/** - * Module responsible for sending mempool messages to other nodes. - */ +/** Module responsible for sending mempool messages to other nodes. */ public final class MempoolRelayerModule extends AbstractModule { - @Override - public void configure() { - bind(MempoolRelayer.class).in(Scopes.SINGLETON); - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(MempoolAddSuccess.class); - eventBinder.addBinding().toInstance(MempoolRelayTrigger.class); - } - - @ProvidesIntoSet - private EventProcessorOnRunner mempoolAddEventProcessor(MempoolRelayer mempoolRelayer) { - return new EventProcessorOnRunner<>(Runners.MEMPOOL, MempoolAddSuccess.class, mempoolRelayer.mempoolAddSuccessEventProcessor()); - } + @Override + public void configure() { + bind(MempoolRelayer.class).in(Scopes.SINGLETON); + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(MempoolAddSuccess.class); + eventBinder.addBinding().toInstance(MempoolRelayTrigger.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner mempoolRelayCommandsEventProcessor( - MempoolRelayer mempoolRelayer - ) { - return new EventProcessorOnRunner<>( - Runners.MEMPOOL, - MempoolRelayTrigger.class, - mempoolRelayer.mempoolRelayTriggerEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner mempoolAddEventProcessor(MempoolRelayer mempoolRelayer) { + return new EventProcessorOnRunner<>( + Runners.MEMPOOL, MempoolAddSuccess.class, mempoolRelayer.mempoolAddSuccessEventProcessor()); + } - @ProvidesIntoSet - public ScheduledEventProducerOnRunner mempoolRelayTriggerEventProducer( - EventDispatcher mempoolRelayTriggerEventDispatcher - ) { - return new ScheduledEventProducerOnRunner<>( - Runners.MEMPOOL, - mempoolRelayTriggerEventDispatcher, - MempoolRelayTrigger::create, - Duration.ofSeconds(10), - Duration.ofSeconds(10) - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner mempoolRelayCommandsEventProcessor( + MempoolRelayer mempoolRelayer) { + return new EventProcessorOnRunner<>( + Runners.MEMPOOL, + MempoolRelayTrigger.class, + mempoolRelayer.mempoolRelayTriggerEventProcessor()); + } + @ProvidesIntoSet + public ScheduledEventProducerOnRunner mempoolRelayTriggerEventProducer( + EventDispatcher mempoolRelayTriggerEventDispatcher) { + return new ScheduledEventProducerOnRunner<>( + Runners.MEMPOOL, + mempoolRelayTriggerEventDispatcher, + MempoolRelayTrigger::create, + Duration.ofSeconds(10), + Duration.ofSeconds(10)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolThrottleMs.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolThrottleMs.java index 96b35c19fc..0703526215 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolThrottleMs.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/MempoolThrottleMs.java @@ -64,18 +64,15 @@ package com.radixdlt.mempool; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * The amount of time in milliseconds to throttle mempool additions - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** The amount of time in milliseconds to throttle mempool additions */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MempoolThrottleMs { -} +public @interface MempoolThrottleMs {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempools.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempools.java index 82b71fd9c5..78bdf07202 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempools.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempool/Mempools.java @@ -65,46 +65,44 @@ package com.radixdlt.mempool; import com.radixdlt.atom.Txn; - import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; -/** - * Mempool which is always empty - */ +/** Mempool which is always empty */ public class Mempools { - private Mempools() { - throw new IllegalStateException("Cannot instantiate."); - } + private Mempools() { + throw new IllegalStateException("Cannot instantiate."); + } - public static Mempool empty() { - return new Mempool<>() { - @Override - public T add(Txn txn) throws MempoolFullException, MempoolDuplicateException { - // No-op - return null; - } + public static Mempool empty() { + return new Mempool<>() { + @Override + public T add(Txn txn) throws MempoolFullException, MempoolDuplicateException { + // No-op + return null; + } - @Override - public List committed(List committed) { - return List.of(); - } + @Override + public List committed(List committed) { + return List.of(); + } - @Override - public int getCount() { - return 0; - } + @Override + public int getCount() { + return 0; + } - @Override - public List getTxns(int count, List seen) { - return List.of(); - } + @Override + public List getTxns(int count, List seen) { + return List.of(); + } - @Override - public List scanUpdateAndGet(Predicate predicate, Consumer operator) { - return List.of(); - } - }; - } + @Override + public List scanUpdateAndGet( + Predicate predicate, Consumer operator) { + return List.of(); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFiller.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFiller.java index 3cc3c51bf8..ec970f0785 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFiller.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFiller.java @@ -64,12 +64,9 @@ package com.radixdlt.mempoolfiller; -import com.radixdlt.application.tokens.TokenUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import com.google.common.collect.ImmutableList; import com.google.inject.Inject; +import com.radixdlt.application.tokens.TokenUtils; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.Txn; import com.radixdlt.atom.TxnConstructionRequest; @@ -88,130 +85,132 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.RadixEngineMempool; import com.radixdlt.utils.UInt256; - import java.util.ArrayList; import java.util.Random; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Periodically fills the mempool with valid transactions - */ +/** Periodically fills the mempool with valid transactions */ public final class MempoolFiller { - private static final Logger logger = LogManager.getLogger(); - private final RadixEngine radixEngine; - - private final RemoteEventDispatcher remoteMempoolAddEventDispatcher; - private final EventDispatcher mempoolAddEventDispatcher; - - private final RadixEngineMempool radixEngineMempool; - private final ScheduledEventDispatcher mempoolFillDispatcher; - private final SystemCounters systemCounters; - private final PeersView peersView; - private final Random random; - private final HashSigner hashSigner; - private final REAddr account; - - private boolean enabled = false; - private int numTransactions; - private boolean sendToSelf = false; - - @Inject - public MempoolFiller( - @Self REAddr account, - @LocalSigner HashSigner hashSigner, - RadixEngineMempool radixEngineMempool, - RadixEngine radixEngine, - EventDispatcher mempoolAddEventDispatcher, - RemoteEventDispatcher remoteMempoolAddEventDispatcher, - ScheduledEventDispatcher mempoolFillDispatcher, - PeersView peersView, - Random random, - SystemCounters systemCounters - ) { - this.account = account; - this.hashSigner = hashSigner; - this.radixEngine = radixEngine; - this.radixEngineMempool = radixEngineMempool; - this.mempoolAddEventDispatcher = mempoolAddEventDispatcher; - this.remoteMempoolAddEventDispatcher = remoteMempoolAddEventDispatcher; - this.mempoolFillDispatcher = mempoolFillDispatcher; - this.peersView = peersView; - this.random = random; - this.systemCounters = systemCounters; - } - - public EventProcessor mempoolFillerUpdateEventProcessor() { - return u -> { - u.numTransactions().ifPresent(numTx -> this.numTransactions = numTx); - u.sendToSelf().ifPresent(sendToSelf -> this.sendToSelf = sendToSelf); - - if (u.enabled() == enabled) { - u.onError("Already " + (enabled ? "enabled." : "disabled.")); - return; - } - - logger.info("Mempool Filler: Updating " + u.enabled()); - u.onSuccess(); - - if (u.enabled()) { - enabled = true; - mempoolFillDispatcher.dispatch(ScheduledMempoolFill.create(), 50); - } else { - enabled = false; - } - }; - } - - public EventProcessor scheduledMempoolFillEventProcessor() { - return p -> { - if (!enabled) { - return; - } - - var minSize = UInt256.TWO.multiply(UInt256.TEN.pow(TokenUtils.SUB_UNITS_POW_10 - 4)); - var shuttingDown = radixEngineMempool.getShuttingDownSubstates(); - var txnConstructionRequest = TxnConstructionRequest.create() - .feePayer(account) - .splitNative(REAddr.ofNativeToken(), account, minSize) - .avoidSubstates(shuttingDown); - - var txns = new ArrayList(); - for (int i = 0; i < numTransactions; i++) { - try { - var builder = radixEngine.construct(txnConstructionRequest); - shuttingDown.addAll(builder.toLowLevelBuilder().remoteDownSubstate()); - var txn = builder.signAndBuild(hashSigner::sign); - txns.add(txn); - } catch (TxBuilderException e) { - break; - } - } - - if (txns.size() == 1) { - logger.info("Mempool Filler mempool: {} Adding txn {} to mempool...", - systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE), - txns.get(0).getId() - ); - } else { - logger.info("Mempool Filler mempool: {} Adding {} txns to mempool...", - systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE), - txns.size() - ); - } - - final var peers = peersView.peers() - .map(PeersView.PeerInfo::bftNode) - .collect(ImmutableList.toImmutableList()); - txns.forEach(txn -> { - int index = random.nextInt(sendToSelf ? peers.size() + 1 : peers.size()); - var mempoolAdd = MempoolAdd.create(txn); - if (index == peers.size()) { - this.mempoolAddEventDispatcher.dispatch(mempoolAdd); - } else { - this.remoteMempoolAddEventDispatcher.dispatch(peers.get(index), mempoolAdd); - } - }); - - mempoolFillDispatcher.dispatch(ScheduledMempoolFill.create(), 500); - }; - } + private static final Logger logger = LogManager.getLogger(); + private final RadixEngine radixEngine; + + private final RemoteEventDispatcher remoteMempoolAddEventDispatcher; + private final EventDispatcher mempoolAddEventDispatcher; + + private final RadixEngineMempool radixEngineMempool; + private final ScheduledEventDispatcher mempoolFillDispatcher; + private final SystemCounters systemCounters; + private final PeersView peersView; + private final Random random; + private final HashSigner hashSigner; + private final REAddr account; + + private boolean enabled = false; + private int numTransactions; + private boolean sendToSelf = false; + + @Inject + public MempoolFiller( + @Self REAddr account, + @LocalSigner HashSigner hashSigner, + RadixEngineMempool radixEngineMempool, + RadixEngine radixEngine, + EventDispatcher mempoolAddEventDispatcher, + RemoteEventDispatcher remoteMempoolAddEventDispatcher, + ScheduledEventDispatcher mempoolFillDispatcher, + PeersView peersView, + Random random, + SystemCounters systemCounters) { + this.account = account; + this.hashSigner = hashSigner; + this.radixEngine = radixEngine; + this.radixEngineMempool = radixEngineMempool; + this.mempoolAddEventDispatcher = mempoolAddEventDispatcher; + this.remoteMempoolAddEventDispatcher = remoteMempoolAddEventDispatcher; + this.mempoolFillDispatcher = mempoolFillDispatcher; + this.peersView = peersView; + this.random = random; + this.systemCounters = systemCounters; + } + + public EventProcessor mempoolFillerUpdateEventProcessor() { + return u -> { + u.numTransactions().ifPresent(numTx -> this.numTransactions = numTx); + u.sendToSelf().ifPresent(sendToSelf -> this.sendToSelf = sendToSelf); + + if (u.enabled() == enabled) { + u.onError("Already " + (enabled ? "enabled." : "disabled.")); + return; + } + + logger.info("Mempool Filler: Updating " + u.enabled()); + u.onSuccess(); + + if (u.enabled()) { + enabled = true; + mempoolFillDispatcher.dispatch(ScheduledMempoolFill.create(), 50); + } else { + enabled = false; + } + }; + } + + public EventProcessor scheduledMempoolFillEventProcessor() { + return p -> { + if (!enabled) { + return; + } + + var minSize = UInt256.TWO.multiply(UInt256.TEN.pow(TokenUtils.SUB_UNITS_POW_10 - 4)); + var shuttingDown = radixEngineMempool.getShuttingDownSubstates(); + var txnConstructionRequest = + TxnConstructionRequest.create() + .feePayer(account) + .splitNative(REAddr.ofNativeToken(), account, minSize) + .avoidSubstates(shuttingDown); + + var txns = new ArrayList(); + for (int i = 0; i < numTransactions; i++) { + try { + var builder = radixEngine.construct(txnConstructionRequest); + shuttingDown.addAll(builder.toLowLevelBuilder().remoteDownSubstate()); + var txn = builder.signAndBuild(hashSigner::sign); + txns.add(txn); + } catch (TxBuilderException e) { + break; + } + } + + if (txns.size() == 1) { + logger.info( + "Mempool Filler mempool: {} Adding txn {} to mempool...", + systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE), + txns.get(0).getId()); + } else { + logger.info( + "Mempool Filler mempool: {} Adding {} txns to mempool...", + systemCounters.get(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE), + txns.size()); + } + + final var peers = + peersView + .peers() + .map(PeersView.PeerInfo::bftNode) + .collect(ImmutableList.toImmutableList()); + txns.forEach( + txn -> { + int index = random.nextInt(sendToSelf ? peers.size() + 1 : peers.size()); + var mempoolAdd = MempoolAdd.create(txn); + if (index == peers.size()) { + this.mempoolAddEventDispatcher.dispatch(mempoolAdd); + } else { + this.remoteMempoolAddEventDispatcher.dispatch(peers.get(index), mempoolAdd); + } + }); + + mempoolFillDispatcher.dispatch(ScheduledMempoolFill.create(), 500); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerKey.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerKey.java index c72f7cda15..a1d9c62a88 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerKey.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerKey.java @@ -64,20 +64,17 @@ package com.radixdlt.mempoolfiller; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Key for the mempool filler - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Key for the mempool filler */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MempoolFillerKey { -} +public @interface MempoolFillerKey {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerModule.java index e4291f1731..ff040c3b31 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerModule.java @@ -76,32 +76,34 @@ import com.radixdlt.environment.LocalEvents; import com.radixdlt.identifiers.REAddr; -/** - * Module responsible for the mempool filler chaos attack - */ +/** Module responsible for the mempool filler chaos attack */ public final class MempoolFillerModule extends AbstractModule { - @Override - public void configure() { - bind(MempoolFiller.class).in(Scopes.SINGLETON); - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(MempoolFillerUpdate.class); - eventBinder.addBinding().toInstance(ScheduledMempoolFill.class); - } + @Override + public void configure() { + bind(MempoolFiller.class).in(Scopes.SINGLETON); + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(MempoolFillerUpdate.class); + eventBinder.addBinding().toInstance(ScheduledMempoolFill.class); + } - @Provides - @Self - private REAddr addr(@Self ECPublicKey self) { - return REAddr.ofPubKeyAccount(self); - } + @Provides + @Self + private REAddr addr(@Self ECPublicKey self) { + return REAddr.ofPubKeyAccount(self); + } - @ProvidesIntoSet - public EventProcessorOnRunner mempoolFillerUpdateProcessor(MempoolFiller mempoolFiller) { - return new EventProcessorOnRunner<>("Mempool", MempoolFillerUpdate.class, mempoolFiller.mempoolFillerUpdateEventProcessor()); - } + @ProvidesIntoSet + public EventProcessorOnRunner mempoolFillerUpdateProcessor(MempoolFiller mempoolFiller) { + return new EventProcessorOnRunner<>( + "Mempool", MempoolFillerUpdate.class, mempoolFiller.mempoolFillerUpdateEventProcessor()); + } - @ProvidesIntoSet - public EventProcessorOnRunner scheduledMessageFloodEventProcessor(MempoolFiller mempoolFiller) { - return new EventProcessorOnRunner<>("Mempool", ScheduledMempoolFill.class, mempoolFiller.scheduledMempoolFillEventProcessor()); - } + @ProvidesIntoSet + public EventProcessorOnRunner scheduledMessageFloodEventProcessor( + MempoolFiller mempoolFiller) { + return new EventProcessorOnRunner<>( + "Mempool", ScheduledMempoolFill.class, mempoolFiller.scheduledMempoolFillEventProcessor()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerUpdate.java index 13fd74ba55..40835a0b6b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/MempoolFillerUpdate.java @@ -69,89 +69,78 @@ import java.util.OptionalInt; import java.util.concurrent.CompletableFuture; -/** - * An update event to the mempool filler - */ +/** An update event to the mempool filler */ public final class MempoolFillerUpdate { - private final int parallelTransactions; - private final boolean sendToSelf; - private final CompletableFuture completableFuture; - - private MempoolFillerUpdate( - int parallelTransactions, - boolean sendToSelf, - CompletableFuture completableFuture - ) { - this.parallelTransactions = parallelTransactions; - this.sendToSelf = sendToSelf; - this.completableFuture = completableFuture; + private final int parallelTransactions; + private final boolean sendToSelf; + private final CompletableFuture completableFuture; + + private MempoolFillerUpdate( + int parallelTransactions, boolean sendToSelf, CompletableFuture completableFuture) { + this.parallelTransactions = parallelTransactions; + this.sendToSelf = sendToSelf; + this.completableFuture = completableFuture; + } + + public static MempoolFillerUpdate enable(int parallelTransactions, boolean sendToSelf) { + return new MempoolFillerUpdate(parallelTransactions, sendToSelf, null); + } + + public static MempoolFillerUpdate enable( + int parallelTransactions, boolean sendToSelf, CompletableFuture completableFuture) { + if (parallelTransactions < 0) { + throw new IllegalArgumentException("parallelTransactions must be > 0."); } - - public static MempoolFillerUpdate enable( - int parallelTransactions, - boolean sendToSelf - ) { - return new MempoolFillerUpdate(parallelTransactions, sendToSelf, null); + Objects.requireNonNull(completableFuture); + return new MempoolFillerUpdate(parallelTransactions, sendToSelf, completableFuture); + } + + public static MempoolFillerUpdate disable() { + return new MempoolFillerUpdate(-1, false, null); + } + + public static MempoolFillerUpdate disable(CompletableFuture completableFuture) { + Objects.requireNonNull(completableFuture); + return new MempoolFillerUpdate(-1, false, completableFuture); + } + + public void onSuccess() { + if (completableFuture != null) { + completableFuture.complete(null); } + } - public static MempoolFillerUpdate enable( - int parallelTransactions, - boolean sendToSelf, - CompletableFuture completableFuture - ) { - if (parallelTransactions < 0) { - throw new IllegalArgumentException("parallelTransactions must be > 0."); - } - Objects.requireNonNull(completableFuture); - return new MempoolFillerUpdate(parallelTransactions, sendToSelf, completableFuture); + public void onError(String error) { + if (completableFuture != null) { + completableFuture.completeExceptionally(new RuntimeException(error)); } + } - public static MempoolFillerUpdate disable() { - return new MempoolFillerUpdate(-1, false, null); - } + public boolean enabled() { + return parallelTransactions > 0; + } - public static MempoolFillerUpdate disable(CompletableFuture completableFuture) { - Objects.requireNonNull(completableFuture); - return new MempoolFillerUpdate(-1, false, completableFuture); - } + public OptionalInt numTransactions() { + return parallelTransactions > 0 ? OptionalInt.of(parallelTransactions) : OptionalInt.empty(); + } - public void onSuccess() { - if (completableFuture != null) { - completableFuture.complete(null); - } - } + public Optional sendToSelf() { + return parallelTransactions > 0 ? Optional.of(sendToSelf) : Optional.empty(); + } - public void onError(String error) { - if (completableFuture != null) { - completableFuture.completeExceptionally(new RuntimeException(error)); - } - } - - public boolean enabled() { - return parallelTransactions > 0; - } + @Override + public int hashCode() { + return Objects.hash(parallelTransactions, sendToSelf); + } - public OptionalInt numTransactions() { - return parallelTransactions > 0 ? OptionalInt.of(parallelTransactions) : OptionalInt.empty(); + @Override + public boolean equals(Object o) { + if (!(o instanceof MempoolFillerUpdate)) { + return false; } - public Optional sendToSelf() { - return parallelTransactions > 0 ? Optional.of(sendToSelf) : Optional.empty(); - } - - @Override - public int hashCode() { - return Objects.hash(parallelTransactions, sendToSelf); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof MempoolFillerUpdate)) { - return false; - } - - MempoolFillerUpdate other = (MempoolFillerUpdate) o; - return this.parallelTransactions == other.parallelTransactions - && this.sendToSelf == other.sendToSelf; - } + MempoolFillerUpdate other = (MempoolFillerUpdate) o; + return this.parallelTransactions == other.parallelTransactions + && this.sendToSelf == other.sendToSelf; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/ScheduledMempoolFill.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/ScheduledMempoolFill.java index 1b79289820..63c39bfb54 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/ScheduledMempoolFill.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/mempoolfiller/ScheduledMempoolFill.java @@ -64,13 +64,11 @@ package com.radixdlt.mempoolfiller; -/** - * Scheduled event for filling the mempool - */ +/** Scheduled event for filling the mempool */ public enum ScheduledMempoolFill { - INSTANCE; + INSTANCE; - public static ScheduledMempoolFill create() { - return INSTANCE; - } + public static ScheduledMempoolFill create() { + return INSTANCE; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/ConsensusEventMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/ConsensusEventMessage.java index 478b450eed..a7d1dbc086 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/ConsensusEventMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/ConsensusEventMessage.java @@ -72,89 +72,88 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; /** - * The Data Transfer Object for Consensus messages. Each type of consensus message currently needs to be - * a parameter in this class due to lack of interface serialization. + * The Data Transfer Object for Consensus messages. Each type of consensus message currently needs + * to be a parameter in this class due to lack of interface serialization. */ @SerializerId2("message.consensus.event") -//TODO: requires rework +// TODO: requires rework public final class ConsensusEventMessage extends Message { - @JsonProperty("proposal") - @DsonOutput(Output.ALL) - private final Proposal proposal; - - @JsonProperty("vote") - @DsonOutput(Output.ALL) - private final Vote vote; - - @JsonCreator - public ConsensusEventMessage( - @JsonProperty("proposal") Proposal proposal, - @JsonProperty("vote") Vote vote - ) { - if (proposal == null && vote == null) { - throw new IllegalStateException("No vote nor proposal are provided for ConsensusEventMessage"); - } - - if (proposal != null && vote != null) { - throw new IllegalArgumentException("Both, vote and proposal are provided for ConsensusEventMessage"); - } - - this.proposal = proposal; - this.vote = vote; - } - - public ConsensusEventMessage(Proposal proposal) { - this(proposal, null); - } - - public ConsensusEventMessage(Vote vote) { - this(null, vote); - } - - public ConsensusEvent getConsensusMessage() { - var event = consensusMessageInternal(); - if (event == null) { - throw new IllegalStateException("No consensus message."); - } - return event; - } - - private ConsensusEvent consensusMessageInternal() { - if (this.proposal != null) { - return this.proposal; - } - - if (this.vote != null) { - return this.vote; - } - - return null; - } - - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), consensusMessageInternal()); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - return (o instanceof ConsensusEventMessage that) - && Objects.equals(proposal, that.proposal) - && Objects.equals(vote, that.vote) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } - - @Override - public int hashCode() { - return Objects.hash(proposal, vote, getTimestamp()); - } + @JsonProperty("proposal") + @DsonOutput(Output.ALL) + private final Proposal proposal; + + @JsonProperty("vote") + @DsonOutput(Output.ALL) + private final Vote vote; + + @JsonCreator + public ConsensusEventMessage( + @JsonProperty("proposal") Proposal proposal, @JsonProperty("vote") Vote vote) { + if (proposal == null && vote == null) { + throw new IllegalStateException( + "No vote nor proposal are provided for ConsensusEventMessage"); + } + + if (proposal != null && vote != null) { + throw new IllegalArgumentException( + "Both, vote and proposal are provided for ConsensusEventMessage"); + } + + this.proposal = proposal; + this.vote = vote; + } + + public ConsensusEventMessage(Proposal proposal) { + this(proposal, null); + } + + public ConsensusEventMessage(Vote vote) { + this(null, vote); + } + + public ConsensusEvent getConsensusMessage() { + var event = consensusMessageInternal(); + if (event == null) { + throw new IllegalStateException("No consensus message."); + } + return event; + } + + private ConsensusEvent consensusMessageInternal() { + if (this.proposal != null) { + return this.proposal; + } + + if (this.vote != null) { + return this.vote; + } + + return null; + } + + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), consensusMessageInternal()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + return (o instanceof ConsensusEventMessage that) + && Objects.equals(proposal, that.proposal) + && Objects.equals(vote, that.vote) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } + + @Override + public int hashCode() { + return Objects.hash(proposal, vote, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java index 1ed63926ac..80bed241e9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java @@ -70,56 +70,54 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; @SerializerId2("message.consensus.vertices_error_response") public final class GetVerticesErrorResponseMessage extends Message { - @JsonProperty("high_qc") - @DsonOutput(Output.ALL) - private final HighQC highQC; + @JsonProperty("high_qc") + @DsonOutput(Output.ALL) + private final HighQC highQC; - @JsonProperty("request") - @DsonOutput(Output.ALL) - private final GetVerticesRequestMessage request; + @JsonProperty("request") + @DsonOutput(Output.ALL) + private final GetVerticesRequestMessage request; - @JsonCreator - public GetVerticesErrorResponseMessage( - @JsonProperty(value = "high_qc", required = true) HighQC highQC, - @JsonProperty(value = "request", required = true) GetVerticesRequestMessage request - ) { - this.highQC = Objects.requireNonNull(highQC); - this.request = Objects.requireNonNull(request); - } + @JsonCreator + public GetVerticesErrorResponseMessage( + @JsonProperty(value = "high_qc", required = true) HighQC highQC, + @JsonProperty(value = "request", required = true) GetVerticesRequestMessage request) { + this.highQC = Objects.requireNonNull(highQC); + this.request = Objects.requireNonNull(request); + } - public HighQC highQC() { - return this.highQC; - } + public HighQC highQC() { + return this.highQC; + } - public GetVerticesRequestMessage request() { - return this.request; - } + public GetVerticesRequestMessage request() { + return this.request; + } - @Override - public String toString() { - return String.format("%s{%s}", getClass().getSimpleName(), this.highQC); - } + @Override + public String toString() { + return String.format("%s{%s}", getClass().getSimpleName(), this.highQC); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof GetVerticesErrorResponseMessage that) - && Objects.equals(this.highQC, that.highQC) - && Objects.equals(this.request, that.request) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof GetVerticesErrorResponseMessage that) + && Objects.equals(this.highQC, that.highQC) + && Objects.equals(this.request, that.request) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(this.highQC, this.request, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(this.highQC, this.request, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestMessage.java index 57ab1501b1..a6e0fd8a95 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestMessage.java @@ -70,63 +70,59 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * RPC Message to get request for a vertex - */ +/** RPC Message to get request for a vertex */ @SerializerId2("message.consensus.vertices_request") public final class GetVerticesRequestMessage extends Message { - @JsonProperty("vertexId") - @DsonOutput(Output.ALL) - private final HashCode vertexId; + @JsonProperty("vertexId") + @DsonOutput(Output.ALL) + private final HashCode vertexId; - @JsonProperty("count") - @DsonOutput(Output.ALL) - private final int count; + @JsonProperty("count") + @DsonOutput(Output.ALL) + private final int count; - @JsonCreator - public GetVerticesRequestMessage( - @JsonProperty(value = "vertexId", required = true) HashCode vertexId, - @JsonProperty("count") int count - ) { - if (count <= 0) { - throw new IllegalArgumentException("Request contains negative count of vertices: " + count); - } + @JsonCreator + public GetVerticesRequestMessage( + @JsonProperty(value = "vertexId", required = true) HashCode vertexId, + @JsonProperty("count") int count) { + if (count <= 0) { + throw new IllegalArgumentException("Request contains negative count of vertices: " + count); + } - this.vertexId = Objects.requireNonNull(vertexId); - this.count = count; - } + this.vertexId = Objects.requireNonNull(vertexId); + this.count = count; + } - public HashCode getVertexId() { - return vertexId; - } + public HashCode getVertexId() { + return vertexId; + } - public int getCount() { - return count; - } + public int getCount() { + return count; + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), vertexId); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), vertexId); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof GetVerticesRequestMessage that) - && count == that.count - && Objects.equals(vertexId, that.vertexId) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof GetVerticesRequestMessage that) + && count == that.count + && Objects.equals(vertexId, that.vertexId) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(vertexId, count, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(vertexId, count, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestRateLimit.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestRateLimit.java index 22e02941ec..b1cb1e4a1b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestRateLimit.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesRequestRateLimit.java @@ -1,83 +1,80 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.middleware2.network; - -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * Identifies that the target represents a rate limit for outgoing vertex sync messages. - */ -@Qualifier -@Target({ FIELD, PARAMETER, METHOD }) -@Retention(RUNTIME) -public @interface GetVerticesRequestRateLimit { -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2.network; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Identifies that the target represents a rate limit for outgoing vertex sync messages. */ +@Qualifier +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) +public @interface GetVerticesRequestRateLimit {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesResponseMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesResponseMessage.java index 00e2aa8116..524bfa603c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesResponseMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesResponseMessage.java @@ -70,50 +70,46 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.List; import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * RPC Response message for GetVertex call - */ +/** RPC Response message for GetVertex call */ @SerializerId2("message.consensus.vertices_response") public final class GetVerticesResponseMessage extends Message { - @JsonProperty("vertices") - @DsonOutput(Output.ALL) - private final List vertices; + @JsonProperty("vertices") + @DsonOutput(Output.ALL) + private final List vertices; - @JsonCreator - public GetVerticesResponseMessage( - @JsonProperty(value = "vertices", required = true) List vertices - ) { - this.vertices = Objects.requireNonNull(vertices); - vertices.forEach(Objects::requireNonNull); - } + @JsonCreator + public GetVerticesResponseMessage( + @JsonProperty(value = "vertices", required = true) List vertices) { + this.vertices = Objects.requireNonNull(vertices); + vertices.forEach(Objects::requireNonNull); + } - public List getVertices() { - return vertices; - } + public List getVertices() { + return vertices; + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), vertices); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), vertices); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof GetVerticesResponseMessage that) - && Objects.equals(vertices, that.vertices) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof GetVerticesResponseMessage that) + && Objects.equals(vertices, that.vertices) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(vertices, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(vertices, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessage.java index 0796d306b5..710b6518d4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessage.java @@ -70,48 +70,44 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * A push message with the latest ledger status update. - */ +/** A push message with the latest ledger status update. */ @SerializerId2("message.sync.ledger_status_update") public final class LedgerStatusUpdateMessage extends Message { - @JsonProperty("header") - @DsonOutput(Output.ALL) - private final LedgerProof header; + @JsonProperty("header") + @DsonOutput(Output.ALL) + private final LedgerProof header; - @JsonCreator - public LedgerStatusUpdateMessage( - @JsonProperty(value = "header", required = true) LedgerProof header - ) { - this.header = Objects.requireNonNull(header); - } + @JsonCreator + public LedgerStatusUpdateMessage( + @JsonProperty(value = "header", required = true) LedgerProof header) { + this.header = Objects.requireNonNull(header); + } - public LedgerProof getHeader() { - return header; - } + public LedgerProof getHeader() { + return header; + } - @Override - public String toString() { - return String.format("%s{header=%s}", getClass().getSimpleName(), header); - } + @Override + public String toString() { + return String.format("%s{header=%s}", getClass().getSimpleName(), header); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof LedgerStatusUpdateMessage that) - && Objects.equals(header, that.header) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof LedgerStatusUpdateMessage that) + && Objects.equals(header, that.header) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(header, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(header, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MempoolAddMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MempoolAddMessage.java index 5e8541f2df..dade0042c4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MempoolAddMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MempoolAddMessage.java @@ -70,51 +70,48 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.List; import java.util.Objects; +import org.radix.network.messaging.Message; @SerializerId2("message.mempool.add") public final class MempoolAddMessage extends Message { - @JsonProperty("txns") - @DsonOutput(Output.ALL) - private final List txns; + @JsonProperty("txns") + @DsonOutput(Output.ALL) + private final List txns; - @JsonCreator - public MempoolAddMessage( - @JsonProperty(value = "txns", required = true) List txns - ) { - this.txns = Objects.requireNonNull(txns); - this.txns.forEach(Objects::requireNonNull); - } + @JsonCreator + public MempoolAddMessage(@JsonProperty(value = "txns", required = true) List txns) { + this.txns = Objects.requireNonNull(txns); + this.txns.forEach(Objects::requireNonNull); + } - public static MempoolAddMessage from(List txns) { - return new MempoolAddMessage(txns.stream().map(Txn::getPayload).toList()); - } + public static MempoolAddMessage from(List txns) { + return new MempoolAddMessage(txns.stream().map(Txn::getPayload).toList()); + } - public List getTxns() { - return txns == null ? List.of() : txns.stream().map(Txn::create).toList(); - } + public List getTxns() { + return txns == null ? List.of() : txns.stream().map(Txn::create).toList(); + } - @Override - public String toString() { - return String.format("%s{txns=%s}", getClass().getSimpleName(), getTxns()); - } + @Override + public String toString() { + return String.format("%s{txns=%s}", getClass().getSimpleName(), getTxns()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof MempoolAddMessage that) - && Objects.equals(getTxns(), that.getTxns()) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof MempoolAddMessage that) + && Objects.equals(getTxns(), that.getTxns()) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(getTxns(), getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(getTxns(), getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralBFTNetwork.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralBFTNetwork.java index dd59982e1d..e78fa9ed0d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralBFTNetwork.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralBFTNetwork.java @@ -75,70 +75,68 @@ import com.radixdlt.network.p2p.NodeId; import io.reactivex.rxjava3.core.BackpressureStrategy; import io.reactivex.rxjava3.core.Flowable; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * BFT Network sending and receiving layer used on top of the MessageCentral - * layer. - */ +/** BFT Network sending and receiving layer used on top of the MessageCentral layer. */ public final class MessageCentralBFTNetwork { - private final MessageCentral messageCentral; + private final MessageCentral messageCentral; - @Inject - public MessageCentralBFTNetwork(MessageCentral messageCentral) { - this.messageCentral = Objects.requireNonNull(messageCentral); - } + @Inject + public MessageCentralBFTNetwork(MessageCentral messageCentral) { + this.messageCentral = Objects.requireNonNull(messageCentral); + } - //TODO: cleanup unnecessary code duplication and "fat" lambdas - public Flowable> remoteVotes() { - return remoteBftEvents() - .filter(m -> m.getMessage().getConsensusMessage() instanceof Vote) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - final var msg = m.getMessage(); - var vote = (Vote) msg.getConsensusMessage(); - return RemoteEvent.create(node, vote); - }); - } + // TODO: cleanup unnecessary code duplication and "fat" lambdas + public Flowable> remoteVotes() { + return remoteBftEvents() + .filter(m -> m.getMessage().getConsensusMessage() instanceof Vote) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + final var msg = m.getMessage(); + var vote = (Vote) msg.getConsensusMessage(); + return RemoteEvent.create(node, vote); + }); + } - public Flowable> remoteProposals() { - return remoteBftEvents() - .filter(m -> m.getMessage().getConsensusMessage() instanceof Proposal) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - final var msg = m.getMessage(); - var proposal = (Proposal) msg.getConsensusMessage(); - return RemoteEvent.create(node, proposal); - }); - } + public Flowable> remoteProposals() { + return remoteBftEvents() + .filter(m -> m.getMessage().getConsensusMessage() instanceof Proposal) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + final var msg = m.getMessage(); + var proposal = (Proposal) msg.getConsensusMessage(); + return RemoteEvent.create(node, proposal); + }); + } - private Flowable> remoteBftEvents() { - return this.messageCentral - .messagesOf(ConsensusEventMessage.class) - .toFlowable(BackpressureStrategy.BUFFER); - } + private Flowable> remoteBftEvents() { + return this.messageCentral + .messagesOf(ConsensusEventMessage.class) + .toFlowable(BackpressureStrategy.BUFFER); + } - public RemoteEventDispatcher proposalDispatcher() { - return this::sendProposal; - } + public RemoteEventDispatcher proposalDispatcher() { + return this::sendProposal; + } - private void sendProposal(BFTNode receiver, Proposal proposal) { - ConsensusEventMessage message = new ConsensusEventMessage(proposal); - send(message, receiver); - } + private void sendProposal(BFTNode receiver, Proposal proposal) { + ConsensusEventMessage message = new ConsensusEventMessage(proposal); + send(message, receiver); + } - public RemoteEventDispatcher voteDispatcher() { - return this::sendVote; - } + public RemoteEventDispatcher voteDispatcher() { + return this::sendVote; + } - private void sendVote(BFTNode receiver, Vote vote) { - ConsensusEventMessage message = new ConsensusEventMessage(vote); - send(message, receiver); - } + private void sendVote(BFTNode receiver, Vote vote) { + ConsensusEventMessage message = new ConsensusEventMessage(vote); + send(message, receiver); + } - private void send(Message message, BFTNode recipient) { - this.messageCentral.send(NodeId.fromPublicKey(recipient.getKey()), message); - } + private void send(Message message, BFTNode recipient) { + this.messageCentral.send(NodeId.fromPublicKey(recipient.getKey()), message); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralLedgerSync.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralLedgerSync.java index 7b23720088..83c3948072 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralLedgerSync.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralLedgerSync.java @@ -74,115 +74,121 @@ import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; - -import java.util.Objects; - import io.reactivex.rxjava3.core.BackpressureStrategy; import io.reactivex.rxjava3.core.Flowable; +import java.util.Objects; import javax.inject.Inject; -/** - * Network interface for syncing committed state using the MessageCentral - */ +/** Network interface for syncing committed state using the MessageCentral */ public final class MessageCentralLedgerSync { - private final MessageCentral messageCentral; - - @Inject - public MessageCentralLedgerSync(MessageCentral messageCentral) { - this.messageCentral = Objects.requireNonNull(messageCentral); - } - - public Flowable> statusRequests() { - return this.messageCentral.messagesOf(StatusRequestMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - return RemoteEvent.create(node, StatusRequest.create()); - }); - } - - public Flowable> statusResponses() { - return this.messageCentral.messagesOf(StatusResponseMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - final var msg = m.getMessage(); - return RemoteEvent.create(node, StatusResponse.create(msg.getHeader())); - }); - } - - public Flowable> syncRequests() { - return this.messageCentral.messagesOf(SyncRequestMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - final var msg = m.getMessage(); - return RemoteEvent.create(node, SyncRequest.create(msg.getCurrentHeader())); - }); - } - - public Flowable> syncResponses() { - return this.messageCentral.messagesOf(SyncResponseMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - final var msg = m.getMessage(); - return RemoteEvent.create(node, SyncResponse.create(msg.getCommands())); - }); - } - - public Flowable> ledgerStatusUpdates() { - return this.messageCentral.messagesOf(LedgerStatusUpdateMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - final var header = m.getMessage().getHeader(); - return RemoteEvent.create(node, LedgerStatusUpdate.create(header)); - }); - } - - public RemoteEventDispatcher syncRequestDispatcher() { - return this::sendSyncRequest; - } - - private void sendSyncRequest(BFTNode node, SyncRequest syncRequest) { - final var msg = new SyncRequestMessage(syncRequest.getHeader()); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } - - public RemoteEventDispatcher syncResponseDispatcher() { - return this::sendSyncResponse; - } - - private void sendSyncResponse(BFTNode node, SyncResponse syncResponse) { - final var msg = new SyncResponseMessage(syncResponse.getTxnsAndProof()); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } - - public RemoteEventDispatcher statusRequestDispatcher() { - return this::sendStatusRequest; - } - - private void sendStatusRequest(BFTNode node, StatusRequest statusRequest) { - final var msg = new StatusRequestMessage(); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } - - public RemoteEventDispatcher statusResponseDispatcher() { - return this::sendStatusResponse; - } - - private void sendStatusResponse(BFTNode node, StatusResponse statusResponse) { - final var msg = new StatusResponseMessage(statusResponse.getHeader()); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } - - public RemoteEventDispatcher ledgerStatusUpdateDispatcher() { - return this::sendLedgerStatusUpdate; - } - - private void sendLedgerStatusUpdate(BFTNode node, LedgerStatusUpdate ledgerStatusUpdate) { - final var msg = new LedgerStatusUpdateMessage(ledgerStatusUpdate.getHeader()); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } + private final MessageCentral messageCentral; + + @Inject + public MessageCentralLedgerSync(MessageCentral messageCentral) { + this.messageCentral = Objects.requireNonNull(messageCentral); + } + + public Flowable> statusRequests() { + return this.messageCentral + .messagesOf(StatusRequestMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + return RemoteEvent.create(node, StatusRequest.create()); + }); + } + + public Flowable> statusResponses() { + return this.messageCentral + .messagesOf(StatusResponseMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + final var msg = m.getMessage(); + return RemoteEvent.create(node, StatusResponse.create(msg.getHeader())); + }); + } + + public Flowable> syncRequests() { + return this.messageCentral + .messagesOf(SyncRequestMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + final var msg = m.getMessage(); + return RemoteEvent.create(node, SyncRequest.create(msg.getCurrentHeader())); + }); + } + + public Flowable> syncResponses() { + return this.messageCentral + .messagesOf(SyncResponseMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + final var msg = m.getMessage(); + return RemoteEvent.create(node, SyncResponse.create(msg.getCommands())); + }); + } + + public Flowable> ledgerStatusUpdates() { + return this.messageCentral + .messagesOf(LedgerStatusUpdateMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + final var header = m.getMessage().getHeader(); + return RemoteEvent.create(node, LedgerStatusUpdate.create(header)); + }); + } + + public RemoteEventDispatcher syncRequestDispatcher() { + return this::sendSyncRequest; + } + + private void sendSyncRequest(BFTNode node, SyncRequest syncRequest) { + final var msg = new SyncRequestMessage(syncRequest.getHeader()); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } + + public RemoteEventDispatcher syncResponseDispatcher() { + return this::sendSyncResponse; + } + + private void sendSyncResponse(BFTNode node, SyncResponse syncResponse) { + final var msg = new SyncResponseMessage(syncResponse.getTxnsAndProof()); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } + + public RemoteEventDispatcher statusRequestDispatcher() { + return this::sendStatusRequest; + } + + private void sendStatusRequest(BFTNode node, StatusRequest statusRequest) { + final var msg = new StatusRequestMessage(); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } + + public RemoteEventDispatcher statusResponseDispatcher() { + return this::sendStatusResponse; + } + + private void sendStatusResponse(BFTNode node, StatusResponse statusResponse) { + final var msg = new StatusResponseMessage(statusResponse.getHeader()); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } + + public RemoteEventDispatcher ledgerStatusUpdateDispatcher() { + return this::sendLedgerStatusUpdate; + } + + private void sendLedgerStatusUpdate(BFTNode node, LedgerStatusUpdate ledgerStatusUpdate) { + final var msg = new LedgerStatusUpdateMessage(ledgerStatusUpdate.getHeader()); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralMempool.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralMempool.java index ef956d0a15..cf25912e88 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralMempool.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralMempool.java @@ -72,43 +72,38 @@ import com.radixdlt.network.p2p.NodeId; import io.reactivex.rxjava3.core.BackpressureStrategy; import io.reactivex.rxjava3.core.Flowable; -import org.radix.network.messaging.Message; - -import javax.inject.Inject; import java.util.Objects; +import javax.inject.Inject; +import org.radix.network.messaging.Message; -/** - * Network layer for the mempool - */ +/** Network layer for the mempool */ public final class MessageCentralMempool { - private final MessageCentral messageCentral; + private final MessageCentral messageCentral; - @Inject - public MessageCentralMempool(MessageCentral messageCentral) { - this.messageCentral = Objects.requireNonNull(messageCentral); - } + @Inject + public MessageCentralMempool(MessageCentral messageCentral) { + this.messageCentral = Objects.requireNonNull(messageCentral); + } - public RemoteEventDispatcher mempoolAddRemoteEventDispatcher() { - return (receiver, msg) -> { - MempoolAddMessage message = MempoolAddMessage.from(msg.txns()); - this.send(message, receiver); - }; - } + public RemoteEventDispatcher mempoolAddRemoteEventDispatcher() { + return (receiver, msg) -> { + MempoolAddMessage message = MempoolAddMessage.from(msg.txns()); + this.send(message, receiver); + }; + } - private void send(Message message, BFTNode recipient) { - this.messageCentral.send(NodeId.fromPublicKey(recipient.getKey()), message); - } + private void send(Message message, BFTNode recipient) { + this.messageCentral.send(NodeId.fromPublicKey(recipient.getKey()), message); + } - public Flowable> mempoolComands() { - return messageCentral - .messagesOf(MempoolAddMessage.class) - .map(msg -> { - final BFTNode node = BFTNode.create(msg.getSource().getPublicKey()); - return RemoteEvent.create( - node, - MempoolAdd.create(msg.getMessage().getTxns()) - ); - }) - .toFlowable(BackpressureStrategy.BUFFER); - } + public Flowable> mempoolComands() { + return messageCentral + .messagesOf(MempoolAddMessage.class) + .map( + msg -> { + final BFTNode node = BFTNode.create(msg.getSource().getPublicKey()); + return RemoteEvent.create(node, MempoolAdd.create(msg.getMessage().getTxns())); + }) + .toFlowable(BackpressureStrategy.BUFFER); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerDiscovery.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerDiscovery.java index 20cb5a51f3..da4ef84efa 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerDiscovery.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerDiscovery.java @@ -64,9 +64,6 @@ package com.radixdlt.middleware2.network; -import org.radix.network.messages.GetPeersMessage; -import org.radix.network.messages.PeersResponseMessage; - import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.rx.RemoteEvent; @@ -74,59 +71,61 @@ import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.discovery.GetPeers; import com.radixdlt.network.p2p.discovery.PeersResponse; - -import java.util.Objects; - import io.reactivex.rxjava3.core.BackpressureStrategy; import io.reactivex.rxjava3.core.Flowable; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Singleton; +import org.radix.network.messages.GetPeersMessage; +import org.radix.network.messages.PeersResponseMessage; -/** - * Network interface for peer discovery messages the MessageCentral - */ +/** Network interface for peer discovery messages the MessageCentral */ @Singleton public final class MessageCentralPeerDiscovery { - private final MessageCentral messageCentral; + private final MessageCentral messageCentral; - @Inject - public MessageCentralPeerDiscovery(MessageCentral messageCentral) { - this.messageCentral = Objects.requireNonNull(messageCentral); - } + @Inject + public MessageCentralPeerDiscovery(MessageCentral messageCentral) { + this.messageCentral = Objects.requireNonNull(messageCentral); + } - public Flowable> getPeersEvents() { - return this.messageCentral.messagesOf(GetPeersMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - return RemoteEvent.create(node, GetPeers.create()); - }); - } + public Flowable> getPeersEvents() { + return this.messageCentral + .messagesOf(GetPeersMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + return RemoteEvent.create(node, GetPeers.create()); + }); + } - public Flowable> peersResponses() { - return this.messageCentral.messagesOf(PeersResponseMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - return RemoteEvent.create(node, PeersResponse.create(m.getMessage().getPeers())); - }); - } + public Flowable> peersResponses() { + return this.messageCentral + .messagesOf(PeersResponseMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + return RemoteEvent.create(node, PeersResponse.create(m.getMessage().getPeers())); + }); + } - public RemoteEventDispatcher getPeersDispatcher() { - return this::sendGetPeers; - } + public RemoteEventDispatcher getPeersDispatcher() { + return this::sendGetPeers; + } - private void sendGetPeers(BFTNode node, GetPeers getPeers) { - final var msg = new GetPeersMessage(); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } + private void sendGetPeers(BFTNode node, GetPeers getPeers) { + final var msg = new GetPeersMessage(); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } - public RemoteEventDispatcher peersResponseDispatcher() { - return this::sendPeersResponse; - } + public RemoteEventDispatcher peersResponseDispatcher() { + return this::sendPeersResponse; + } - private void sendPeersResponse(BFTNode node, PeersResponse peersResponse) { - final var msg = new PeersResponseMessage(peersResponse.getPeers()); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } + private void sendPeersResponse(BFTNode node, PeersResponse peersResponse) { + final var msg = new PeersResponseMessage(peersResponse.getPeers()); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerLiveness.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerLiveness.java index 7d6a68aa4b..dec1bd3157 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerLiveness.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralPeerLiveness.java @@ -64,9 +64,6 @@ package com.radixdlt.middleware2.network; -import org.radix.network.messages.PeerPingMessage; -import org.radix.network.messages.PeerPongMessage; - import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.rx.RemoteEvent; @@ -74,59 +71,61 @@ import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.liveness.Ping; import com.radixdlt.network.p2p.liveness.Pong; - -import java.util.Objects; - import io.reactivex.rxjava3.core.BackpressureStrategy; import io.reactivex.rxjava3.core.Flowable; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Singleton; +import org.radix.network.messages.PeerPingMessage; +import org.radix.network.messages.PeerPongMessage; -/** - * Network interface for peer liveness messages using the MessageCentral - */ +/** Network interface for peer liveness messages using the MessageCentral */ @Singleton public final class MessageCentralPeerLiveness { - private final MessageCentral messageCentral; + private final MessageCentral messageCentral; - @Inject - public MessageCentralPeerLiveness(MessageCentral messageCentral) { - this.messageCentral = Objects.requireNonNull(messageCentral); - } + @Inject + public MessageCentralPeerLiveness(MessageCentral messageCentral) { + this.messageCentral = Objects.requireNonNull(messageCentral); + } - public Flowable> pings() { - return this.messageCentral.messagesOf(PeerPingMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - return RemoteEvent.create(node, Ping.create()); - }); - } + public Flowable> pings() { + return this.messageCentral + .messagesOf(PeerPingMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + return RemoteEvent.create(node, Ping.create()); + }); + } - public Flowable> pongs() { - return this.messageCentral.messagesOf(PeerPongMessage.class) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> { - final var node = BFTNode.create(m.getSource().getPublicKey()); - return RemoteEvent.create(node, Pong.create()); - }); - } + public Flowable> pongs() { + return this.messageCentral + .messagesOf(PeerPongMessage.class) + .toFlowable(BackpressureStrategy.BUFFER) + .map( + m -> { + final var node = BFTNode.create(m.getSource().getPublicKey()); + return RemoteEvent.create(node, Pong.create()); + }); + } - public RemoteEventDispatcher pingDispatcher() { - return this::sendPing; - } + public RemoteEventDispatcher pingDispatcher() { + return this::sendPing; + } - private void sendPing(BFTNode node, Ping ping) { - final var msg = new PeerPingMessage(); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } + private void sendPing(BFTNode node, Ping ping) { + final var msg = new PeerPingMessage(); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } - public RemoteEventDispatcher pongDispatcher() { - return this::sendPong; - } + public RemoteEventDispatcher pongDispatcher() { + return this::sendPong; + } - private void sendPong(BFTNode node, Pong pong) { - final var msg = new PeerPongMessage(); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } + private void sendPong(BFTNode node, Pong pong) { + final var msg = new PeerPongMessage(); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralValidatorSync.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralValidatorSync.java index 99ec8d896b..74597dce5b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralValidatorSync.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralValidatorSync.java @@ -64,8 +64,6 @@ package com.radixdlt.middleware2.network; -import org.radix.network.messaging.Message; - import com.google.common.collect.ImmutableList; import com.google.inject.Inject; import com.radixdlt.consensus.bft.BFTNode; @@ -78,102 +76,99 @@ import com.radixdlt.environment.rx.RemoteEvent; import com.radixdlt.network.messaging.MessageCentral; import com.radixdlt.network.p2p.NodeId; - +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; import java.util.Objects; import java.util.function.BiFunction; import java.util.stream.Collectors; +import org.radix.network.messaging.Message; -import io.reactivex.rxjava3.core.BackpressureStrategy; -import io.reactivex.rxjava3.core.Flowable; - -/** - * Network interface for syncing vertices using the MessageCentral - */ +/** Network interface for syncing vertices using the MessageCentral */ public class MessageCentralValidatorSync { - private final MessageCentral messageCentral; - private final Hasher hasher; - - @Inject - public MessageCentralValidatorSync(MessageCentral messageCentral, Hasher hasher) { - this.messageCentral = Objects.requireNonNull(messageCentral); - this.hasher = Objects.requireNonNull(hasher); - } - - public RemoteEventDispatcher verticesRequestDispatcher() { - return this::sendGetVerticesRequest; - } - - public RemoteEventDispatcher verticesResponseDispatcher() { - return this::sendGetVerticesResponse; - } - - public RemoteEventDispatcher verticesErrorResponseDispatcher() { - return this::sendGetVerticesErrorResponse; - } - - private void sendGetVerticesRequest(BFTNode node, GetVerticesRequest request) { - final GetVerticesRequestMessage vertexRequest = - new GetVerticesRequestMessage(request.getVertexId(), request.getCount()); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), vertexRequest); - } - - private void sendGetVerticesResponse(BFTNode node, GetVerticesResponse response) { - var rawVertices = response.getVertices().stream() - .map(VerifiedVertex::toSerializable) - .collect(Collectors.toList()); - var msg = new GetVerticesResponseMessage(rawVertices); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } - - public void sendGetVerticesErrorResponse(BFTNode node, GetVerticesErrorResponse response) { - var request = response.request(); - var requestMsg = new GetVerticesRequestMessage(request.getVertexId(), request.getCount()); - var msg = new GetVerticesErrorResponseMessage(response.highQC(), requestMsg); - this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); - } - - public Flowable> requests() { - return this.createFlowable( - GetVerticesRequestMessage.class, - (peer, msg) -> { - final BFTNode node = BFTNode.create(peer.getPublicKey()); - return RemoteEvent.create(node, new GetVerticesRequest(msg.getVertexId(), msg.getCount())); - } - ); - } - - public Flowable> responses() { - return this.createFlowable( - GetVerticesResponseMessage.class, - (src, msg) -> { - BFTNode node = BFTNode.create(src.getPublicKey()); - // TODO: Move hasher to a more appropriate place - ImmutableList hashedVertices = msg.getVertices().stream() - .map(v -> new VerifiedVertex(v, hasher.hash(v))) - .collect(ImmutableList.toImmutableList()); - - return RemoteEvent.create(node, new GetVerticesResponse(hashedVertices)); - } - ); - } - - public Flowable> errorResponses() { - return this.createFlowable( - GetVerticesErrorResponseMessage.class, - (src, msg) -> { - final var node = BFTNode.create(src.getPublicKey()); - final var request = new GetVerticesRequest(msg.request().getVertexId(), msg.request().getCount()); - return RemoteEvent.create(node, new GetVerticesErrorResponse(msg.highQC(), request)); - } - ); - } - - private Flowable createFlowable( - Class c, - BiFunction mapper - ) { - return this.messageCentral.messagesOf(c) - .toFlowable(BackpressureStrategy.BUFFER) - .map(m -> mapper.apply(m.getSource(), m.getMessage())); - } + private final MessageCentral messageCentral; + private final Hasher hasher; + + @Inject + public MessageCentralValidatorSync(MessageCentral messageCentral, Hasher hasher) { + this.messageCentral = Objects.requireNonNull(messageCentral); + this.hasher = Objects.requireNonNull(hasher); + } + + public RemoteEventDispatcher verticesRequestDispatcher() { + return this::sendGetVerticesRequest; + } + + public RemoteEventDispatcher verticesResponseDispatcher() { + return this::sendGetVerticesResponse; + } + + public RemoteEventDispatcher verticesErrorResponseDispatcher() { + return this::sendGetVerticesErrorResponse; + } + + private void sendGetVerticesRequest(BFTNode node, GetVerticesRequest request) { + final GetVerticesRequestMessage vertexRequest = + new GetVerticesRequestMessage(request.getVertexId(), request.getCount()); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), vertexRequest); + } + + private void sendGetVerticesResponse(BFTNode node, GetVerticesResponse response) { + var rawVertices = + response.getVertices().stream() + .map(VerifiedVertex::toSerializable) + .collect(Collectors.toList()); + var msg = new GetVerticesResponseMessage(rawVertices); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } + + public void sendGetVerticesErrorResponse(BFTNode node, GetVerticesErrorResponse response) { + var request = response.request(); + var requestMsg = new GetVerticesRequestMessage(request.getVertexId(), request.getCount()); + var msg = new GetVerticesErrorResponseMessage(response.highQC(), requestMsg); + this.messageCentral.send(NodeId.fromPublicKey(node.getKey()), msg); + } + + public Flowable> requests() { + return this.createFlowable( + GetVerticesRequestMessage.class, + (peer, msg) -> { + final BFTNode node = BFTNode.create(peer.getPublicKey()); + return RemoteEvent.create( + node, new GetVerticesRequest(msg.getVertexId(), msg.getCount())); + }); + } + + public Flowable> responses() { + return this.createFlowable( + GetVerticesResponseMessage.class, + (src, msg) -> { + BFTNode node = BFTNode.create(src.getPublicKey()); + // TODO: Move hasher to a more appropriate place + ImmutableList hashedVertices = + msg.getVertices().stream() + .map(v -> new VerifiedVertex(v, hasher.hash(v))) + .collect(ImmutableList.toImmutableList()); + + return RemoteEvent.create(node, new GetVerticesResponse(hashedVertices)); + }); + } + + public Flowable> errorResponses() { + return this.createFlowable( + GetVerticesErrorResponseMessage.class, + (src, msg) -> { + final var node = BFTNode.create(src.getPublicKey()); + final var request = + new GetVerticesRequest(msg.request().getVertexId(), msg.request().getCount()); + return RemoteEvent.create(node, new GetVerticesErrorResponse(msg.highQC(), request)); + }); + } + + private Flowable createFlowable( + Class c, BiFunction mapper) { + return this.messageCentral + .messagesOf(c) + .toFlowable(BackpressureStrategy.BUFFER) + .map(m -> mapper.apply(m.getSource(), m.getMessage())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusRequestMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusRequestMessage.java index db1a86f6b1..9af0f9d94c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusRequestMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusRequestMessage.java @@ -66,36 +66,32 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * A status request message. - */ +/** A status request message. */ @SerializerId2("message.sync.status_request") public final class StatusRequestMessage extends Message { - @JsonCreator - public StatusRequestMessage() { - } + @JsonCreator + public StatusRequestMessage() {} - @Override - public String toString() { - return String.format("%s", getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s", getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof StatusRequestMessage that) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof StatusRequestMessage that) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusResponseMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusResponseMessage.java index 3b3ec90f01..6df7b73643 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusResponseMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/StatusResponseMessage.java @@ -70,48 +70,44 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * A status response message - */ +/** A status response message */ @SerializerId2("message.sync.status_response") public final class StatusResponseMessage extends Message { - @JsonProperty("header") - @DsonOutput(Output.ALL) - private final LedgerProof header; + @JsonProperty("header") + @DsonOutput(Output.ALL) + private final LedgerProof header; - @JsonCreator - public StatusResponseMessage( - @JsonProperty(value = "header", required = true) LedgerProof header - ) { - this.header = Objects.requireNonNull(header); - } + @JsonCreator + public StatusResponseMessage( + @JsonProperty(value = "header", required = true) LedgerProof header) { + this.header = Objects.requireNonNull(header); + } - public LedgerProof getHeader() { - return header; - } + public LedgerProof getHeader() { + return header; + } - @Override - public String toString() { - return String.format("%s{header=%s}", getClass().getSimpleName(), header); - } + @Override + public String toString() { + return String.format("%s{header=%s}", getClass().getSimpleName(), header); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof StatusResponseMessage that) - && Objects.equals(header, that.header) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof StatusResponseMessage that) + && Objects.equals(header, that.header) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(header, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(header, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncRequestMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncRequestMessage.java index 800a325966..468add5441 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncRequestMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncRequestMessage.java @@ -70,48 +70,44 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * Message to request for sync atoms - */ +/** Message to request for sync atoms */ @SerializerId2("message.sync.sync_request") public final class SyncRequestMessage extends Message { - @JsonProperty("currentHeader") - @DsonOutput(Output.ALL) - private final DtoLedgerProof currentHeader; + @JsonProperty("currentHeader") + @DsonOutput(Output.ALL) + private final DtoLedgerProof currentHeader; - @JsonCreator - public SyncRequestMessage( - @JsonProperty(value = "currentHeader", required = true) DtoLedgerProof currentHeader - ) { - this.currentHeader = Objects.requireNonNull(currentHeader); - } + @JsonCreator + public SyncRequestMessage( + @JsonProperty(value = "currentHeader", required = true) DtoLedgerProof currentHeader) { + this.currentHeader = Objects.requireNonNull(currentHeader); + } - public DtoLedgerProof getCurrentHeader() { - return currentHeader; - } + public DtoLedgerProof getCurrentHeader() { + return currentHeader; + } - @Override - public String toString() { - return String.format("%s{current=%s}", getClass().getSimpleName(), currentHeader); - } + @Override + public String toString() { + return String.format("%s{current=%s}", getClass().getSimpleName(), currentHeader); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof SyncRequestMessage that) - && Objects.equals(currentHeader, that.currentHeader) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof SyncRequestMessage that) + && Objects.equals(currentHeader, that.currentHeader) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(currentHeader, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(currentHeader, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncResponseMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncResponseMessage.java index 25deae4a8e..ff6a099660 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncResponseMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/middleware2/network/SyncResponseMessage.java @@ -70,48 +70,44 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; -/** - * Message with sync atoms as a response to sync request - */ +/** Message with sync atoms as a response to sync request */ @SerializerId2("message.sync.sync_response") public final class SyncResponseMessage extends Message { - @JsonProperty("commands") - @DsonOutput(Output.ALL) - private final DtoTxnsAndProof commands; + @JsonProperty("commands") + @DsonOutput(Output.ALL) + private final DtoTxnsAndProof commands; - @JsonCreator - public SyncResponseMessage( - @JsonProperty(value = "commands", required = true) DtoTxnsAndProof commands - ) { - this.commands = Objects.requireNonNull(commands); - } + @JsonCreator + public SyncResponseMessage( + @JsonProperty(value = "commands", required = true) DtoTxnsAndProof commands) { + this.commands = Objects.requireNonNull(commands); + } - public DtoTxnsAndProof getCommands() { - return commands; - } + public DtoTxnsAndProof getCommands() { + return commands; + } - @Override - public String toString() { - return String.format("%s{commands=%s}", getClass().getSimpleName(), commands); - } + @Override + public String toString() { + return String.format("%s{commands=%s}", getClass().getSimpleName(), commands); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof SyncResponseMessage that) - && Objects.equals(commands, that.commands) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof SyncResponseMessage that) + && Objects.equals(commands, that.commands) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(commands, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(commands, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/EnvironmentHostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/EnvironmentHostIp.java index 7cfee22bc8..ac44166dd3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/EnvironmentHostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/EnvironmentHostIp.java @@ -64,52 +64,48 @@ package com.radixdlt.network.hostip; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Suppliers; +import com.google.common.net.HostAndPort; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Optional; import java.util.function.Supplier; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Suppliers; -import com.google.common.net.HostAndPort; - -/** - * Query for a public IP address from environment variable. - */ +/** Query for a public IP address from environment variable. */ final class EnvironmentHostIp implements HostIp { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - @VisibleForTesting - static final String ENV_VAR = "RADIXDLT_HOST_IP_ADDRESS"; + @VisibleForTesting static final String ENV_VAR = "RADIXDLT_HOST_IP_ADDRESS"; - private final Supplier> result = Suppliers.memoize(() -> hostIp(System.getenv(ENV_VAR))); + private final Supplier> result = + Suppliers.memoize(() -> hostIp(System.getenv(ENV_VAR))); - static HostIp create() { - return new EnvironmentHostIp(); - } + static HostIp create() { + return new EnvironmentHostIp(); + } - @Override - public Optional hostIp() { - return result.get(); - } + @Override + public Optional hostIp() { + return result.get(); + } - // Broken out for testing as environment is immutable from Java runtime - @VisibleForTesting - Optional hostIp(String value) { - if (value != null && !value.trim().isEmpty()) { - try { - InetAddress address = InetAddress.getByName(value); - HostAndPort hap = HostAndPort.fromHost(address.getHostAddress()); - log.info("Found address {}", hap); - return Optional.of(hap.getHost()); - } catch (UnknownHostException | IllegalArgumentException e) { - log.warn("Environment variable {} is invalid: '{}'", ENV_VAR, value); - } - } - log.info("No suitable address found"); - return Optional.empty(); - } -} \ No newline at end of file + // Broken out for testing as environment is immutable from Java runtime + @VisibleForTesting + Optional hostIp(String value) { + if (value != null && !value.trim().isEmpty()) { + try { + InetAddress address = InetAddress.getByName(value); + HostAndPort hap = HostAndPort.fromHost(address.getHostAddress()); + log.info("Found address {}", hap); + return Optional.of(hap.getHost()); + } catch (UnknownHostException | IllegalArgumentException e) { + log.warn("Environment variable {} is invalid: '{}'", ENV_VAR, value); + } + } + log.info("No suitable address found"); + return Optional.empty(); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIp.java index 0b0a407961..c516f051b8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIp.java @@ -66,33 +66,30 @@ import java.util.Optional; -/** - * Interface for obtaining a hosts IP. - */ +/** Interface for obtaining a hosts IP. */ public interface HostIp { - /** - * Return a host IP, if possible. - * The underlying query mechanism is used to determine a IP address for - * the host. - * @return An optional host IP address, if one can be determined - */ - Optional hostIp(); + /** + * Return a host IP, if possible. The underlying query mechanism is used to determine a IP address + * for the host. + * + * @return An optional host IP address, if one can be determined + */ + Optional hostIp(); - /** - * Chain two {@link HostIp} methods together. - * If this object returns a non-empty host IP, then supply that, - * otherwise supply the result of the specified host IP. - * - * @param other The other {@link HostIp} to use if this returns empty. - * @return Either this if non-empty, otherwise other. - */ - default HostIp or(HostIp other) { - return () -> { - Optional hap = hostIp(); - if (hap.isPresent()) { - return hap; - } - return other.hostIp(); - }; - } -} \ No newline at end of file + /** + * Chain two {@link HostIp} methods together. If this object returns a non-empty host IP, then + * supply that, otherwise supply the result of the specified host IP. + * + * @param other The other {@link HostIp} to use if this returns empty. + * @return Either this if non-empty, otherwise other. + */ + default HostIp or(HostIp other) { + return () -> { + Optional hap = hostIp(); + if (hap.isPresent()) { + return hap; + } + return other.hostIp(); + }; + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIpModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIpModule.java index 39896099f0..4e23c0b1bb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIpModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/HostIpModule.java @@ -64,25 +64,22 @@ package com.radixdlt.network.hostip; -import java.util.Objects; - import com.google.inject.AbstractModule; import com.radixdlt.properties.RuntimeProperties; +import java.util.Objects; -/** - * Guice configuration for {@link HostIp}. - */ +/** Guice configuration for {@link HostIp}. */ public final class HostIpModule extends AbstractModule { - private final RuntimeProperties properties; + private final RuntimeProperties properties; - public HostIpModule(RuntimeProperties properties) { - this.properties = Objects.requireNonNull(properties); - } + public HostIpModule(RuntimeProperties properties) { + this.properties = Objects.requireNonNull(properties); + } - @Override - protected void configure() { - // The main target - bind(HostIp.class).toInstance(StandardHostIp.defaultHostIp(properties)); - } + @Override + protected void configure() { + // The main target + bind(HostIp.class).toInstance(StandardHostIp.defaultHostIp(properties)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NetworkQueryHostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NetworkQueryHostIp.java index b676d1e76f..f62e91125a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NetworkQueryHostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NetworkQueryHostIp.java @@ -64,6 +64,13 @@ package com.radixdlt.network.hostip; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.common.io.CharStreams; +import com.google.common.net.HostAndPort; +import com.radixdlt.properties.RuntimeProperties; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -80,140 +87,127 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import com.google.common.io.CharStreams; -import com.google.common.net.HostAndPort; -import com.radixdlt.properties.RuntimeProperties; - /** - * Query for a public IP address using an oracle. - * This class can be used to query a single oracle, or if a number - * of oracles are provided, a simple majority vote is used. + * Query for a public IP address using an oracle. This class can be used to query a single oracle, + * or if a number of oracles are provided, a simple majority vote is used. */ final class NetworkQueryHostIp implements HostIp { - private static final Logger log = LogManager.getLogger(); - - @VisibleForTesting - static final String QUERY_URLS_PROPERTY = "network.host_ip_query_urls"; - - @VisibleForTesting - static final ImmutableList DEFAULT_QUERY_URLS = ImmutableList.of( - makeurl("https://checkip.amazonaws.com/"), - makeurl("https://ipv4.icanhazip.com/"), - makeurl("https://myexternalip.com/raw"), - makeurl("https://ipecho.net/plain"), - makeurl("https://bot.whatismyipaddress.com/"), - makeurl("https://www.trackip.net/ip"), - makeurl("https://ifconfig.co/ip") - ); - - static HostIp create(Collection urls) { - return new NetworkQueryHostIp(urls); - } - - static HostIp create(RuntimeProperties properties) { - String urlsProperty = properties.get(QUERY_URLS_PROPERTY, ""); - if (urlsProperty == null || urlsProperty.trim().isEmpty()) { - return create(DEFAULT_QUERY_URLS); - } - ImmutableList urls = Arrays.asList(urlsProperty.split(",")) - .stream() - .map(NetworkQueryHostIp::makeurl) - .collect(ImmutableList.toImmutableList()); - return create(urls); - } - - private final List hosts; - private final Supplier> result = Suppliers.memoize(this::get); - - NetworkQueryHostIp(Collection urls) { - if (urls.isEmpty()) { - throw new IllegalArgumentException("At least one URL must be specified"); - } - this.hosts = new ArrayList<>(urls); - } - - int count() { - return this.hosts.size(); - } - - @Override - public Optional hostIp() { - return result.get(); - } - - Optional get() { - return publicIp((count() + 1) / 2); // Round up - } - - Optional publicIp(int threshold) { - // Make sure we don't DoS the first one on the list - Collections.shuffle(this.hosts); - log.debug("Using hosts {}", this.hosts); - final Map ips = Maps.newHashMap(); - for (URL url : this.hosts) { - HostAndPort q = query(url); - if (q != null) { - int newValue = ips.computeIfAbsent(q, k -> new AtomicInteger()).incrementAndGet(); - if (newValue >= threshold) { - log.info("Found address {}", q); - return Optional.of(q.getHost()); - } - } - } - log.info("No suitable address found"); - return Optional.empty(); - } - - @VisibleForTesting - static HostAndPort query(URL url) { - try { - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - // Pretty much required by shared hosting - con.setRequestProperty("Host", url.getHost()); - - // A user agent is required by some hosts - con.setRequestProperty("User-Agent", "curl/7.58.0"); - - // Some don't like it unless an accept is set - con.setRequestProperty("Accept", "*/*"); - - int status = con.getResponseCode(); - if (status > 299) { - log.debug("Host {} failed with status {}", url, status); - return null; - } - - try ( - InputStream is = con.getInputStream(); - InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8) - ) { - String result = CharStreams.toString(isr).trim(); - log.debug("Host {} returned {}", url, result); - return HostAndPort.fromHost(result); - } - } catch (IOException | IllegalArgumentException ex) { - // Ignored - if (log.isDebugEnabled()) { - log.debug(String.format("Host %s failed with exception", url), ex); - } - return null; - } - } - - private static URL makeurl(String s) { - try { - return new URL(s); - } catch (MalformedURLException ex) { - throw new IllegalStateException("While constructing URL for " + s, ex); - } - } -} \ No newline at end of file + private static final Logger log = LogManager.getLogger(); + + @VisibleForTesting static final String QUERY_URLS_PROPERTY = "network.host_ip_query_urls"; + + @VisibleForTesting + static final ImmutableList DEFAULT_QUERY_URLS = + ImmutableList.of( + makeurl("https://checkip.amazonaws.com/"), + makeurl("https://ipv4.icanhazip.com/"), + makeurl("https://myexternalip.com/raw"), + makeurl("https://ipecho.net/plain"), + makeurl("https://bot.whatismyipaddress.com/"), + makeurl("https://www.trackip.net/ip"), + makeurl("https://ifconfig.co/ip")); + + static HostIp create(Collection urls) { + return new NetworkQueryHostIp(urls); + } + + static HostIp create(RuntimeProperties properties) { + String urlsProperty = properties.get(QUERY_URLS_PROPERTY, ""); + if (urlsProperty == null || urlsProperty.trim().isEmpty()) { + return create(DEFAULT_QUERY_URLS); + } + ImmutableList urls = + Arrays.asList(urlsProperty.split(",")).stream() + .map(NetworkQueryHostIp::makeurl) + .collect(ImmutableList.toImmutableList()); + return create(urls); + } + + private final List hosts; + private final Supplier> result = Suppliers.memoize(this::get); + + NetworkQueryHostIp(Collection urls) { + if (urls.isEmpty()) { + throw new IllegalArgumentException("At least one URL must be specified"); + } + this.hosts = new ArrayList<>(urls); + } + + int count() { + return this.hosts.size(); + } + + @Override + public Optional hostIp() { + return result.get(); + } + + Optional get() { + return publicIp((count() + 1) / 2); // Round up + } + + Optional publicIp(int threshold) { + // Make sure we don't DoS the first one on the list + Collections.shuffle(this.hosts); + log.debug("Using hosts {}", this.hosts); + final Map ips = Maps.newHashMap(); + for (URL url : this.hosts) { + HostAndPort q = query(url); + if (q != null) { + int newValue = ips.computeIfAbsent(q, k -> new AtomicInteger()).incrementAndGet(); + if (newValue >= threshold) { + log.info("Found address {}", q); + return Optional.of(q.getHost()); + } + } + } + log.info("No suitable address found"); + return Optional.empty(); + } + + @VisibleForTesting + static HostAndPort query(URL url) { + try { + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + // Pretty much required by shared hosting + con.setRequestProperty("Host", url.getHost()); + + // A user agent is required by some hosts + con.setRequestProperty("User-Agent", "curl/7.58.0"); + + // Some don't like it unless an accept is set + con.setRequestProperty("Accept", "*/*"); + + int status = con.getResponseCode(); + if (status > 299) { + log.debug("Host {} failed with status {}", url, status); + return null; + } + + try (InputStream is = con.getInputStream(); + InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) { + String result = CharStreams.toString(isr).trim(); + log.debug("Host {} returned {}", url, result); + return HostAndPort.fromHost(result); + } + } catch (IOException | IllegalArgumentException ex) { + // Ignored + if (log.isDebugEnabled()) { + log.debug(String.format("Host %s failed with exception", url), ex); + } + return null; + } + } + + private static URL makeurl(String s) { + try { + return new URL(s); + } catch (MalformedURLException ex) { + throw new IllegalStateException("While constructing URL for " + s, ex); + } + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIp.java index 4e1b980f81..621b93dce6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIp.java @@ -64,6 +64,12 @@ package com.radixdlt.network.hostip; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Streams; +import com.google.common.net.HostAndPort; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; @@ -74,87 +80,77 @@ import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterators; -import com.google.common.collect.Streams; -import com.google.common.net.HostAndPort; -/** - * Query for a IP address from local interfaces. - * Non-routable IP addresses are included. - */ +/** Query for a IP address from local interfaces. Non-routable IP addresses are included. */ final class NonroutableInterfaceHostIp implements HostIp { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - static HostIp create() { - return new NonroutableInterfaceHostIp(); - } + static HostIp create() { + return new NonroutableInterfaceHostIp(); + } - private final Supplier> result = Suppliers.memoize(this::get); + private final Supplier> result = Suppliers.memoize(this::get); - @Override - public Optional hostIp() { - return result.get(); - } + @Override + public Optional hostIp() { + return result.get(); + } - private Optional get() { - try { - return hostIp(Iterators.forEnumeration(NetworkInterface.getNetworkInterfaces())); - } catch (SocketException e) { - log.warn("Exception while retrieving network interfaces", e); - } - return Optional.empty(); - } + private Optional get() { + try { + return hostIp(Iterators.forEnumeration(NetworkInterface.getNetworkInterfaces())); + } catch (SocketException e) { + log.warn("Exception while retrieving network interfaces", e); + } + return Optional.empty(); + } - @VisibleForTesting - Optional hostIp(Iterator interfaces) { - try { - ImmutableList addresses = addresses(interfaces) - .map(addr -> HostAndPort.fromHost(addr.getHostAddress())) - .collect(ImmutableList.toImmutableList()); - if (addresses.isEmpty()) { - log.debug("No addresses found"); - } else if (addresses.size() > 1) { - log.warn("Too many addresses {}", addresses); - } else { - HostAndPort hap = addresses.get(0); - log.debug("Found address {}", hap); - return Optional.of(hap.getHost()); - } - } catch (IllegalArgumentException e) { - log.warn("Exception while retrieving interface address: {}", e.getMessage()); - } - return Optional.empty(); - } + @VisibleForTesting + Optional hostIp(Iterator interfaces) { + try { + ImmutableList addresses = + addresses(interfaces) + .map(addr -> HostAndPort.fromHost(addr.getHostAddress())) + .collect(ImmutableList.toImmutableList()); + if (addresses.isEmpty()) { + log.debug("No addresses found"); + } else if (addresses.size() > 1) { + log.warn("Too many addresses {}", addresses); + } else { + HostAndPort hap = addresses.get(0); + log.debug("Found address {}", hap); + return Optional.of(hap.getHost()); + } + } catch (IllegalArgumentException e) { + log.warn("Exception while retrieving interface address: {}", e.getMessage()); + } + return Optional.empty(); + } - private Stream addresses(Iterator networkInterfaces) { - return Streams.stream(networkInterfaces) - .flatMap(this::interfaceAddresses) - .filter(NonroutableInterfaceHostIp::filter); - } + private Stream addresses(Iterator networkInterfaces) { + return Streams.stream(networkInterfaces) + .flatMap(this::interfaceAddresses) + .filter(NonroutableInterfaceHostIp::filter); + } - private Stream interfaceAddresses(NetworkInterface ni) { - Enumeration inetAddresses = ni.getInetAddresses(); - List addrList = Collections.list(inetAddresses); - if (log.isDebugEnabled()) { - for (InetAddress addr : addrList) { - log.debug("Interface {}/{} IP {}", ni.getName(), ni.getDisplayName(), addr.getHostAddress()); - } - } - return addrList.stream(); - } + private Stream interfaceAddresses(NetworkInterface ni) { + Enumeration inetAddresses = ni.getInetAddresses(); + List addrList = Collections.list(inetAddresses); + if (log.isDebugEnabled()) { + for (InetAddress addr : addrList) { + log.debug( + "Interface {}/{} IP {}", ni.getName(), ni.getDisplayName(), addr.getHostAddress()); + } + } + return addrList.stream(); + } - @VisibleForTesting - static boolean filter(InetAddress address) { - return !( - address.isLinkLocalAddress() - || address.isLoopbackAddress() - || address.isMulticastAddress() - ); - } -} \ No newline at end of file + @VisibleForTesting + static boolean filter(InetAddress address) { + return !(address.isLinkLocalAddress() + || address.isLoopbackAddress() + || address.isMulticastAddress()); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RoutableInterfaceHostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RoutableInterfaceHostIp.java index ca4fe920ff..79b1e02d41 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RoutableInterfaceHostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RoutableInterfaceHostIp.java @@ -64,6 +64,12 @@ package com.radixdlt.network.hostip; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Streams; +import com.google.common.net.HostAndPort; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; @@ -74,89 +80,78 @@ import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterators; -import com.google.common.collect.Streams; -import com.google.common.net.HostAndPort; - -/** - * Query for a public IP address from local interfaces. - * Non-routable IP addresses are ignored. - */ +/** Query for a public IP address from local interfaces. Non-routable IP addresses are ignored. */ public class RoutableInterfaceHostIp implements HostIp { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - static HostIp create() { - return new RoutableInterfaceHostIp(); - } + static HostIp create() { + return new RoutableInterfaceHostIp(); + } - private final Supplier> result = Suppliers.memoize(this::get); + private final Supplier> result = Suppliers.memoize(this::get); - @Override - public Optional hostIp() { - return result.get(); - } + @Override + public Optional hostIp() { + return result.get(); + } - private Optional get() { - try { - return hostIp(Iterators.forEnumeration(NetworkInterface.getNetworkInterfaces())); - } catch (SocketException e) { - log.warn("Exception while retrieving network interfaces", e); - } - return Optional.empty(); - } + private Optional get() { + try { + return hostIp(Iterators.forEnumeration(NetworkInterface.getNetworkInterfaces())); + } catch (SocketException e) { + log.warn("Exception while retrieving network interfaces", e); + } + return Optional.empty(); + } - @VisibleForTesting - Optional hostIp(Iterator interfaces) { - try { - ImmutableList addresses = addresses(interfaces) - .map(addr -> HostAndPort.fromHost(addr.getHostAddress())) - .collect(ImmutableList.toImmutableList()); - if (addresses.isEmpty()) { - log.debug("No addresses found"); - } else if (addresses.size() > 1) { - log.warn("Too many addresses {}", addresses); - } else { - HostAndPort hap = addresses.get(0); - log.debug("Found address {}", hap); - return Optional.of(hap.getHost()); - } - } catch (IllegalArgumentException e) { - log.warn("Exception while retrieving interface address: {}", e.getMessage()); - } - return Optional.empty(); - } + @VisibleForTesting + Optional hostIp(Iterator interfaces) { + try { + ImmutableList addresses = + addresses(interfaces) + .map(addr -> HostAndPort.fromHost(addr.getHostAddress())) + .collect(ImmutableList.toImmutableList()); + if (addresses.isEmpty()) { + log.debug("No addresses found"); + } else if (addresses.size() > 1) { + log.warn("Too many addresses {}", addresses); + } else { + HostAndPort hap = addresses.get(0); + log.debug("Found address {}", hap); + return Optional.of(hap.getHost()); + } + } catch (IllegalArgumentException e) { + log.warn("Exception while retrieving interface address: {}", e.getMessage()); + } + return Optional.empty(); + } - private Stream addresses(Iterator networkInterfaces) { - return Streams.stream(networkInterfaces) - .flatMap(this::interfaceAddresses) - .filter(RoutableInterfaceHostIp::filter); - } + private Stream addresses(Iterator networkInterfaces) { + return Streams.stream(networkInterfaces) + .flatMap(this::interfaceAddresses) + .filter(RoutableInterfaceHostIp::filter); + } - private Stream interfaceAddresses(NetworkInterface ni) { - Enumeration inetAddresses = ni.getInetAddresses(); - List addrList = Collections.list(inetAddresses); - if (log.isDebugEnabled()) { - for (InetAddress addr : addrList) { - log.debug("Interface {}/{} IP {}", ni.getName(), ni.getDisplayName(), addr.getHostAddress()); - } - } - return addrList.stream(); - } + private Stream interfaceAddresses(NetworkInterface ni) { + Enumeration inetAddresses = ni.getInetAddresses(); + List addrList = Collections.list(inetAddresses); + if (log.isDebugEnabled()) { + for (InetAddress addr : addrList) { + log.debug( + "Interface {}/{} IP {}", ni.getName(), ni.getDisplayName(), addr.getHostAddress()); + } + } + return addrList.stream(); + } - @VisibleForTesting - static boolean filter(InetAddress address) { - return !( - address.isSiteLocalAddress() - || address.isLinkLocalAddress() - || address.isLoopbackAddress() - || address.isMulticastAddress() - ); - } -} \ No newline at end of file + @VisibleForTesting + static boolean filter(InetAddress address) { + return !(address.isSiteLocalAddress() + || address.isLinkLocalAddress() + || address.isLoopbackAddress() + || address.isMulticastAddress()); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RuntimePropertiesHostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RuntimePropertiesHostIp.java index b65909937a..d4e4e9b31e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RuntimePropertiesHostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/RuntimePropertiesHostIp.java @@ -64,49 +64,45 @@ package com.radixdlt.network.hostip; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.net.HostAndPort; +import com.radixdlt.properties.RuntimeProperties; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.net.HostAndPort; -import com.radixdlt.properties.RuntimeProperties; - -/** - * Query for a public IP address from {@link RuntimeProperties}. - */ +/** Query for a public IP address from {@link RuntimeProperties}. */ final class RuntimePropertiesHostIp implements HostIp { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - @VisibleForTesting - static final String HOST_IP_PROPERTY = "network.host_ip"; + @VisibleForTesting static final String HOST_IP_PROPERTY = "network.host_ip"; - private final String value; + private final String value; - RuntimePropertiesHostIp(RuntimeProperties properties) { - String hostProperty = properties.get(HOST_IP_PROPERTY, ""); - this.value = hostProperty == null ? "" : hostProperty.trim(); - } + RuntimePropertiesHostIp(RuntimeProperties properties) { + String hostProperty = properties.get(HOST_IP_PROPERTY, ""); + this.value = hostProperty == null ? "" : hostProperty.trim(); + } - static HostIp create(RuntimeProperties properties) { - return new RuntimePropertiesHostIp(properties); - } + static HostIp create(RuntimeProperties properties) { + return new RuntimePropertiesHostIp(properties); + } - @Override - public Optional hostIp() { - if (!this.value.isEmpty()) { - try { - InetAddress address = InetAddress.getByName(this.value); - HostAndPort hap = HostAndPort.fromHost(address.getHostAddress()); - log.info("Found address {}", hap); - return Optional.of(hap.getHost()); - } catch (UnknownHostException | IllegalArgumentException e) { - log.warn("Property {} is invalid: '{}'", HOST_IP_PROPERTY, this.value); - } - } - log.info("No suitable address found"); - return Optional.empty(); - } -} \ No newline at end of file + @Override + public Optional hostIp() { + if (!this.value.isEmpty()) { + try { + InetAddress address = InetAddress.getByName(this.value); + HostAndPort hap = HostAndPort.fromHost(address.getHostAddress()); + log.info("Found address {}", hap); + return Optional.of(hap.getHost()); + } catch (UnknownHostException | IllegalArgumentException e) { + log.warn("Property {} is invalid: '{}'", HOST_IP_PROPERTY, this.value); + } + } + log.info("No suitable address found"); + return Optional.empty(); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/StandardHostIp.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/StandardHostIp.java index 35fe3456dd..11df017d0b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/StandardHostIp.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/hostip/StandardHostIp.java @@ -66,26 +66,22 @@ import com.radixdlt.properties.RuntimeProperties; -/** - * Provides a standard {@link HostIp} retriever. - */ +/** Provides a standard {@link HostIp} retriever. */ public final class StandardHostIp { - private StandardHostIp() { - throw new IllegalStateException("Can't construct"); - } + private StandardHostIp() { + throw new IllegalStateException("Can't construct"); + } - /** - * Queries the {@value EnvironmentHostIp#ENV_VAR} environment variable - * for a host name or IP, and if that fails, uses well-known web services - * to determine a public IP address. - * - * @return A {@link HostIp} object from which a host address can be queried - */ - public static HostIp defaultHostIp(RuntimeProperties properties) { - return - RuntimePropertiesHostIp.create(properties) - .or(EnvironmentHostIp.create()) - .or(NetworkQueryHostIp.create(properties)); - } + /** + * Queries the {@value EnvironmentHostIp#ENV_VAR} environment variable for a host name or IP, and + * if that fails, uses well-known web services to determine a public IP address. + * + * @return A {@link HostIp} object from which a host address can be queried + */ + public static HostIp defaultHostIp(RuntimeProperties properties) { + return RuntimePropertiesHostIp.create(properties) + .or(EnvironmentHostIp.create()) + .or(NetworkQueryHostIp.create(properties)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/EventQueueFactory.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/EventQueueFactory.java index dcd7191255..8674132dfd 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/EventQueueFactory.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/EventQueueFactory.java @@ -68,5 +68,5 @@ @FunctionalInterface public interface EventQueueFactory { - SimpleBlockingQueue createEventQueue(int queueSize, Comparator comparator); + SimpleBlockingQueue createEventQueue(int queueSize, Comparator comparator); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/InboundMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/InboundMessage.java index 809a6f582d..f0ec300b0d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/InboundMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/InboundMessage.java @@ -66,4 +66,4 @@ import com.radixdlt.network.p2p.NodeId; -public record InboundMessage(long receiveTime, NodeId source, byte[] message) { } +public record InboundMessage(long receiveTime, NodeId source, byte[] message) {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentral.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentral.java index 76d520c758..dcdf22aa3e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentral.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentral.java @@ -64,39 +64,36 @@ package com.radixdlt.network.messaging; -import java.io.IOException; - import com.radixdlt.network.p2p.NodeId; import io.reactivex.rxjava3.core.Observable; +import java.io.IOException; import org.radix.network.messaging.Message; -/** - * Central processing facility for inbound and outbound messages. - */ +/** Central processing facility for inbound and outbound messages. */ public interface MessageCentral { - /** - * Sends a single message to a node. - * If required, the messaging system will establish a connection to the peer, or - * re-use an existing connection. - * - * @param receiver The node to send the message to - * @param message The message to send - */ - void send(NodeId receiver, Message message); + /** + * Sends a single message to a node. If required, the messaging system will establish a connection + * to the peer, or re-use an existing connection. + * + * @param receiver The node to send the message to + * @param message The message to send + */ + void send(NodeId receiver, Message message); - /** - * Returns a Flowable of inbound peer messages of specified type. - * @param messageType the message type - * @return a Flowable of inbound peer messages - */ - Observable> messagesOf(Class messageType); + /** + * Returns a Flowable of inbound peer messages of specified type. + * + * @param messageType the message type + * @return a Flowable of inbound peer messages + */ + Observable> messagesOf(Class messageType); - /** - * Closes this {@code MessageCentral} and releases any system resources associated - * with it. If it is already closed then invoking this method has no effect. - * - * @throws IOException if an I/O error occurs - */ - void close() throws IOException; + /** + * Closes this {@code MessageCentral} and releases any system resources associated with it. If it + * is already closed then invoking this method has no effect. + * + * @throws IOException if an I/O error occurs + */ + void close() throws IOException; } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralConfiguration.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralConfiguration.java index b04619b9c7..ba084e2048 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralConfiguration.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralConfiguration.java @@ -66,61 +66,58 @@ import com.radixdlt.properties.RuntimeProperties; -/** - * Static configuration data for {@link MessageCentral}. - */ +/** Static configuration data for {@link MessageCentral}. */ public interface MessageCentralConfiguration { - /** - * Retrieves the maximum queue depth for inbound messages before - * incoming messages will be dropped. - * - * @param defaultValue a default value if no special configuration value is set - * @return The maximum queue depth - */ - int messagingInboundQueueMax(int defaultValue); + /** + * Retrieves the maximum queue depth for inbound messages before incoming messages will be + * dropped. + * + * @param defaultValue a default value if no special configuration value is set + * @return The maximum queue depth + */ + int messagingInboundQueueMax(int defaultValue); - /** - * Retrieves the maximum queue depth for outbound messages before - * further outgoing messages will be dropped. - * - * @param defaultValue a default value if no special configuration value is set - * @return The maximum queue depth - */ - int messagingOutboundQueueMax(int defaultValue); + /** + * Retrieves the maximum queue depth for outbound messages before further outgoing messages will + * be dropped. + * + * @param defaultValue a default value if no special configuration value is set + * @return The maximum queue depth + */ + int messagingOutboundQueueMax(int defaultValue); - /** - * Retrieves the maximum time-to-live for inbound and outbound messages in milliseconds. - * If messages are not processed and dispatched within this time, they will be - * dropped. - * - * @param defaultValue a default value if no special configuration value is set - * @return Message time-to-live in milliseconds - */ - long messagingTimeToLive(long defaultValue); + /** + * Retrieves the maximum time-to-live for inbound and outbound messages in milliseconds. If + * messages are not processed and dispatched within this time, they will be dropped. + * + * @param defaultValue a default value if no special configuration value is set + * @return Message time-to-live in milliseconds + */ + long messagingTimeToLive(long defaultValue); - /** - * Create a configuration from specified {@link RuntimeProperties}. - * - * @param properties the properties to read the configuration from - * @return The configuration - */ - static MessageCentralConfiguration fromRuntimeProperties(RuntimeProperties properties) { - return new MessageCentralConfiguration() { - @Override - public int messagingInboundQueueMax(int defaultValue) { - return properties.get("messaging.inbound.queue_max", defaultValue); - } + /** + * Create a configuration from specified {@link RuntimeProperties}. + * + * @param properties the properties to read the configuration from + * @return The configuration + */ + static MessageCentralConfiguration fromRuntimeProperties(RuntimeProperties properties) { + return new MessageCentralConfiguration() { + @Override + public int messagingInboundQueueMax(int defaultValue) { + return properties.get("messaging.inbound.queue_max", defaultValue); + } - @Override - public int messagingOutboundQueueMax(int defaultValue) { - return properties.get("messaging.outbound.queue_max", defaultValue); - } + @Override + public int messagingOutboundQueueMax(int defaultValue) { + return properties.get("messaging.outbound.queue_max", defaultValue); + } - @Override - public long messagingTimeToLive(long defaultValue) { - return properties.get("messaging.time_to_live", defaultValue); - } - }; - } + @Override + public long messagingTimeToLive(long defaultValue) { + return properties.get("messaging.time_to_live", defaultValue); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralImpl.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralImpl.java index ec5cbfecc6..a9e41eb5c2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralImpl.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralImpl.java @@ -64,18 +64,23 @@ package com.radixdlt.network.messaging; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - import com.google.common.base.Stopwatch; +import com.google.common.util.concurrent.RateLimiter; +import com.google.inject.Inject; import com.google.inject.Provider; import com.radixdlt.api.system.health.MovingAverage; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.PeerControl; import com.radixdlt.network.p2p.PeerManager; +import com.radixdlt.serialization.Serialization; +import com.radixdlt.utils.TimeSupplier; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.schedulers.Schedulers; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -83,160 +88,152 @@ import org.radix.time.Time; import org.radix.utils.SimpleThreadPool; -import com.google.common.util.concurrent.RateLimiter; -import com.google.inject.Inject; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.utils.TimeSupplier; -import com.radixdlt.serialization.Serialization; - public final class MessageCentralImpl implements MessageCentral { - private static final Logger log = LogManager.getLogger(); - - // Dependencies - private final SystemCounters counters; - - // Message dispatching - private final MessageDispatcher messageDispatcher; - private final MessagePreprocessor messagePreprocessor; - - // Our time base for System.nanoTime() differences. Per documentation can only compare deltas - private final long timeBase = System.nanoTime(); - - private final RateLimiter outboundLogRateLimiter = RateLimiter.create(1.0); - private final RateLimiter discardedInboundMessagesLogRateLimiter = RateLimiter.create(1.0); - - private final MovingAverage avgMessageQueuedTime = MovingAverage.create(5L); - private final MovingAverage avgMessageProcessingTime = MovingAverage.create(5L); - private long totalMessageQueuedTime = 0L; - private long totalMessageProcessingTime = 0L; - - private final Observable> peerMessages; - - // Outbound message handling - private final SimpleBlockingQueue outboundQueue; - private final SimpleThreadPool outboundThreadPool; - - @Inject - public MessageCentralImpl( - MessageCentralConfiguration config, - Serialization serialization, - PeerManager peerManager, - TimeSupplier timeSource, - EventQueueFactory outboundEventQueueFactory, - SystemCounters counters, - Provider peerControl - ) { - this.counters = Objects.requireNonNull(counters); - this.outboundQueue = outboundEventQueueFactory.createEventQueue( - config.messagingOutboundQueueMax(16384), - OutboundMessageEvent.comparator() - ); - - Objects.requireNonNull(timeSource); - Objects.requireNonNull(serialization); - - this.messageDispatcher = new MessageDispatcher( - counters, - config, - serialization, - timeSource, - peerManager - ); - - this.messagePreprocessor = new MessagePreprocessor( - counters, - config, - timeSource, - serialization, - peerControl - ); - - // Start outbound processing thread - this.outboundThreadPool = new SimpleThreadPool<>( - "Outbound message processing", - 1, // Ensure messages sent in-order - outboundQueue::take, - this::outboundMessageProcessor, - log - ); - this.outboundThreadPool.start(); - - this.peerMessages = peerManager.messages() - .observeOn(Schedulers.computation()) - .map(this::processInboundMessage) - .filter(Optional::isPresent) - .map(Optional::get) - .publish() - .autoConnect(); - } - - private Optional> processInboundMessage(InboundMessage inboundMessage) { - final var messageQueuedTime = Time.currentTimestamp() - inboundMessage.receiveTime(); - avgMessageQueuedTime.update(messageQueuedTime); - totalMessageQueuedTime = Math.max(totalMessageQueuedTime + messageQueuedTime, 0L); - updateCounters(); - final var processingStopwatch = Stopwatch.createStarted(); - try { - return this.messagePreprocessor.process(inboundMessage).fold( - error -> { - final var logLevel = - discardedInboundMessagesLogRateLimiter.tryAcquire() ? Level.INFO : Level.TRACE; - log.log(logLevel, "Dropping inbound message from {} because of {}", inboundMessage.source(), error); - return Optional.empty(); - }, - messageFromPeer -> { - logPreprocessedMessageAndUpdateCounters(messageFromPeer, processingStopwatch); - return Optional.of(messageFromPeer); - } - ); - } catch (Exception ex) { - final var msg = String.format("Message preprocessing from %s failed", inboundMessage.source()); - log.error(msg, ex); - return Optional.empty(); - } - } - - private void logPreprocessedMessageAndUpdateCounters(MessageFromPeer message, Stopwatch processingStopwatch) { - final var messageProcessingTime = processingStopwatch.elapsed(TimeUnit.MILLISECONDS); - avgMessageProcessingTime.update(messageProcessingTime); - totalMessageProcessingTime = Math.max(totalMessageProcessingTime + messageProcessingTime, 0L); - updateCounters(); - if (log.isTraceEnabled()) { - log.trace("Received from {}: {}", message.getSource(), message.getMessage()); - } - } - - private void updateCounters() { - this.counters.set(CounterType.MESSAGES_INBOUND_AVG_QUEUED_TIME, avgMessageQueuedTime.asLong()); - this.counters.set(CounterType.MESSAGES_INBOUND_TOTAL_QUEUED_TIME, totalMessageQueuedTime); - this.counters.set(CounterType.MESSAGES_INBOUND_AVG_PROCESSING_TIME, avgMessageProcessingTime.asLong()); - this.counters.set(CounterType.MESSAGES_INBOUND_TOTAL_PROCESSING_TIME, totalMessageProcessingTime); - } - - @Override - @SuppressWarnings("unchecked") - public Observable> messagesOf(Class messageType) { - return this.peerMessages - .filter(p -> messageType.isInstance(p.getMessage())) - .map(p -> (MessageFromPeer) p); - } - - @Override - public void close() { - this.outboundThreadPool.stop(); - } - - @Override - public void send(NodeId receiver, Message message) { - final var event = new OutboundMessageEvent(receiver, message, System.nanoTime() - timeBase); - if (!outboundQueue.offer(event) && outboundLogRateLimiter.tryAcquire()) { - log.error("Outbound message to {} dropped", receiver); - } - } - - private void outboundMessageProcessor(OutboundMessageEvent outbound) { - this.counters.set(CounterType.MESSAGES_OUTBOUND_PENDING, outboundQueue.size()); - messageDispatcher.send(outbound); - } + private static final Logger log = LogManager.getLogger(); + + // Dependencies + private final SystemCounters counters; + + // Message dispatching + private final MessageDispatcher messageDispatcher; + private final MessagePreprocessor messagePreprocessor; + + // Our time base for System.nanoTime() differences. Per documentation can only compare deltas + private final long timeBase = System.nanoTime(); + + private final RateLimiter outboundLogRateLimiter = RateLimiter.create(1.0); + private final RateLimiter discardedInboundMessagesLogRateLimiter = RateLimiter.create(1.0); + + private final MovingAverage avgMessageQueuedTime = MovingAverage.create(5L); + private final MovingAverage avgMessageProcessingTime = MovingAverage.create(5L); + private long totalMessageQueuedTime = 0L; + private long totalMessageProcessingTime = 0L; + + private final Observable> peerMessages; + + // Outbound message handling + private final SimpleBlockingQueue outboundQueue; + private final SimpleThreadPool outboundThreadPool; + + @Inject + public MessageCentralImpl( + MessageCentralConfiguration config, + Serialization serialization, + PeerManager peerManager, + TimeSupplier timeSource, + EventQueueFactory outboundEventQueueFactory, + SystemCounters counters, + Provider peerControl) { + this.counters = Objects.requireNonNull(counters); + this.outboundQueue = + outboundEventQueueFactory.createEventQueue( + config.messagingOutboundQueueMax(16384), OutboundMessageEvent.comparator()); + + Objects.requireNonNull(timeSource); + Objects.requireNonNull(serialization); + + this.messageDispatcher = + new MessageDispatcher(counters, config, serialization, timeSource, peerManager); + + this.messagePreprocessor = + new MessagePreprocessor(counters, config, timeSource, serialization, peerControl); + + // Start outbound processing thread + this.outboundThreadPool = + new SimpleThreadPool<>( + "Outbound message processing", + 1, // Ensure messages sent in-order + outboundQueue::take, + this::outboundMessageProcessor, + log); + this.outboundThreadPool.start(); + + this.peerMessages = + peerManager + .messages() + .observeOn(Schedulers.computation()) + .map(this::processInboundMessage) + .filter(Optional::isPresent) + .map(Optional::get) + .publish() + .autoConnect(); + } + + private Optional> processInboundMessage(InboundMessage inboundMessage) { + final var messageQueuedTime = Time.currentTimestamp() - inboundMessage.receiveTime(); + avgMessageQueuedTime.update(messageQueuedTime); + totalMessageQueuedTime = Math.max(totalMessageQueuedTime + messageQueuedTime, 0L); + updateCounters(); + final var processingStopwatch = Stopwatch.createStarted(); + try { + return this.messagePreprocessor + .process(inboundMessage) + .fold( + error -> { + final var logLevel = + discardedInboundMessagesLogRateLimiter.tryAcquire() ? Level.INFO : Level.TRACE; + log.log( + logLevel, + "Dropping inbound message from {} because of {}", + inboundMessage.source(), + error); + return Optional.empty(); + }, + messageFromPeer -> { + logPreprocessedMessageAndUpdateCounters(messageFromPeer, processingStopwatch); + return Optional.of(messageFromPeer); + }); + } catch (Exception ex) { + final var msg = + String.format("Message preprocessing from %s failed", inboundMessage.source()); + log.error(msg, ex); + return Optional.empty(); + } + } + + private void logPreprocessedMessageAndUpdateCounters( + MessageFromPeer message, Stopwatch processingStopwatch) { + final var messageProcessingTime = processingStopwatch.elapsed(TimeUnit.MILLISECONDS); + avgMessageProcessingTime.update(messageProcessingTime); + totalMessageProcessingTime = Math.max(totalMessageProcessingTime + messageProcessingTime, 0L); + updateCounters(); + if (log.isTraceEnabled()) { + log.trace("Received from {}: {}", message.getSource(), message.getMessage()); + } + } + + private void updateCounters() { + this.counters.set(CounterType.MESSAGES_INBOUND_AVG_QUEUED_TIME, avgMessageQueuedTime.asLong()); + this.counters.set(CounterType.MESSAGES_INBOUND_TOTAL_QUEUED_TIME, totalMessageQueuedTime); + this.counters.set( + CounterType.MESSAGES_INBOUND_AVG_PROCESSING_TIME, avgMessageProcessingTime.asLong()); + this.counters.set( + CounterType.MESSAGES_INBOUND_TOTAL_PROCESSING_TIME, totalMessageProcessingTime); + } + + @Override + @SuppressWarnings("unchecked") + public Observable> messagesOf(Class messageType) { + return this.peerMessages + .filter(p -> messageType.isInstance(p.getMessage())) + .map(p -> (MessageFromPeer) p); + } + + @Override + public void close() { + this.outboundThreadPool.stop(); + } + + @Override + public void send(NodeId receiver, Message message) { + final var event = new OutboundMessageEvent(receiver, message, System.nanoTime() - timeBase); + if (!outboundQueue.offer(event) && outboundLogRateLimiter.tryAcquire()) { + log.error("Outbound message to {} dropped", receiver); + } + } + + private void outboundMessageProcessor(OutboundMessageEvent outbound) { + this.counters.set(CounterType.MESSAGES_OUTBOUND_PENDING, outboundQueue.size()); + messageDispatcher.send(outbound); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralModule.java index a0bb84f44b..a687033577 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageCentralModule.java @@ -64,38 +64,34 @@ package com.radixdlt.network.messaging; -import java.util.Objects; - -import com.google.inject.Singleton; - import com.google.inject.AbstractModule; +import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.radixdlt.properties.RuntimeProperties; +import java.util.Objects; -/** - * Guice configuration for {@link MessageCentral} that includes a UDP - * transport. - */ +/** Guice configuration for {@link MessageCentral} that includes a UDP transport. */ public final class MessageCentralModule extends AbstractModule { - private final MessageCentralConfiguration config; + private final MessageCentralConfiguration config; - public MessageCentralModule(RuntimeProperties properties) { - this(MessageCentralConfiguration.fromRuntimeProperties(properties)); - } + public MessageCentralModule(RuntimeProperties properties) { + this(MessageCentralConfiguration.fromRuntimeProperties(properties)); + } - MessageCentralModule(MessageCentralConfiguration config) { - this.config = Objects.requireNonNull(config); - } + MessageCentralModule(MessageCentralConfiguration config) { + this.config = Objects.requireNonNull(config); + } - @Override - protected void configure() { - // The main target - bind(new TypeLiteral>() { }).toInstance(SimplePriorityBlockingQueue::new); + @Override + protected void configure() { + // The main target + bind(new TypeLiteral>() {}) + .toInstance(SimplePriorityBlockingQueue::new); - bind(MessageCentral.class).to(MessageCentralImpl.class).in(Singleton.class); + bind(MessageCentral.class).to(MessageCentralImpl.class).in(Singleton.class); - // MessageCentral dependencies - bind(MessageCentralConfiguration.class).toInstance(this.config); - } + // MessageCentral dependencies + bind(MessageCentralConfiguration.class).toInstance(this.config); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageDispatcher.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageDispatcher.java index e11e480953..9b8cf0a56b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageDispatcher.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageDispatcher.java @@ -64,101 +64,102 @@ package com.radixdlt.network.messaging; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; +import static com.radixdlt.network.messaging.MessagingErrors.IO_ERROR; +import static com.radixdlt.network.messaging.MessagingErrors.MESSAGE_EXPIRED; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.network.p2p.NodeId; -import com.radixdlt.network.p2p.transport.PeerChannel; import com.radixdlt.network.p2p.PeerManager; -import com.radixdlt.serialization.Serialization; +import com.radixdlt.network.p2p.transport.PeerChannel; import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.Serialization; import com.radixdlt.utils.Compress; - +import com.radixdlt.utils.TimeSupplier; import com.radixdlt.utils.functional.Result; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.radix.network.messaging.Message; -import static com.radixdlt.network.messaging.MessagingErrors.IO_ERROR; -import static com.radixdlt.network.messaging.MessagingErrors.MESSAGE_EXPIRED; - /* * This could be moved into MessageCentralImpl at some stage, but has been * separated out so that we can check if all the functionality here is * required, and remove the stuff we don't want to keep. */ class MessageDispatcher { - private static final Logger log = LogManager.getLogger(); - - private final long messageTtlMs; - private final SystemCounters counters; - private final Serialization serialization; - private final TimeSupplier timeSource; - private final PeerManager peerManager; - - MessageDispatcher( - SystemCounters counters, - MessageCentralConfiguration config, - Serialization serialization, - TimeSupplier timeSource, - PeerManager peerManager - ) { - this.messageTtlMs = Objects.requireNonNull(config).messagingTimeToLive(30_000L); - this.counters = Objects.requireNonNull(counters); - this.serialization = Objects.requireNonNull(serialization); - this.timeSource = Objects.requireNonNull(timeSource); - this.peerManager = Objects.requireNonNull(peerManager); - } - - CompletableFuture> send(final OutboundMessageEvent outboundMessage) { - final var message = outboundMessage.message(); - final var receiver = outboundMessage.receiver(); - - if (timeSource.currentTime() - message.getTimestamp() > messageTtlMs) { - String msg = String.format("TTL for %s message to %s has expired", message.getClass().getSimpleName(), receiver); - log.warn(msg); - this.counters.increment(CounterType.MESSAGES_OUTBOUND_ABORTED); - return CompletableFuture.completedFuture(MESSAGE_EXPIRED.result()); - } - - final var bytes = serialize(message); - - return peerManager.findOrCreateChannel(outboundMessage.receiver()) - .thenApply(channel -> send(channel, bytes)) - .thenApply(this::updateStatistics) - .exceptionally(t -> completionException(t, receiver, message)); - } - - private Result send(PeerChannel channel, byte[] bytes) { - this.counters.add(CounterType.NETWORKING_BYTES_SENT, bytes.length); - return channel.send(bytes); - } - - private Result completionException(Throwable cause, NodeId receiver, Message message) { - final var msg = String.format("Send %s to %s failed", message.getClass().getSimpleName(), receiver); - log.warn("{}: {}", msg, cause.getMessage()); - return IO_ERROR.result(); - } - - private Result updateStatistics(Result result) { - this.counters.increment(CounterType.MESSAGES_OUTBOUND_PROCESSED); - if (result.isSuccess()) { - this.counters.increment(CounterType.MESSAGES_OUTBOUND_SENT); - } - return result; - } - - private byte[] serialize(Message out) { - try { - byte[] uncompressed = serialization.toDson(out, Output.WIRE); - return Compress.compress(uncompressed); - } catch (IOException e) { - throw new UncheckedIOException("While serializing message", e); - } - } + private static final Logger log = LogManager.getLogger(); + + private final long messageTtlMs; + private final SystemCounters counters; + private final Serialization serialization; + private final TimeSupplier timeSource; + private final PeerManager peerManager; + + MessageDispatcher( + SystemCounters counters, + MessageCentralConfiguration config, + Serialization serialization, + TimeSupplier timeSource, + PeerManager peerManager) { + this.messageTtlMs = Objects.requireNonNull(config).messagingTimeToLive(30_000L); + this.counters = Objects.requireNonNull(counters); + this.serialization = Objects.requireNonNull(serialization); + this.timeSource = Objects.requireNonNull(timeSource); + this.peerManager = Objects.requireNonNull(peerManager); + } + + CompletableFuture> send(final OutboundMessageEvent outboundMessage) { + final var message = outboundMessage.message(); + final var receiver = outboundMessage.receiver(); + + if (timeSource.currentTime() - message.getTimestamp() > messageTtlMs) { + String msg = + String.format( + "TTL for %s message to %s has expired", message.getClass().getSimpleName(), receiver); + log.warn(msg); + this.counters.increment(CounterType.MESSAGES_OUTBOUND_ABORTED); + return CompletableFuture.completedFuture(MESSAGE_EXPIRED.result()); + } + + final var bytes = serialize(message); + + return peerManager + .findOrCreateChannel(outboundMessage.receiver()) + .thenApply(channel -> send(channel, bytes)) + .thenApply(this::updateStatistics) + .exceptionally(t -> completionException(t, receiver, message)); + } + + private Result send(PeerChannel channel, byte[] bytes) { + this.counters.add(CounterType.NETWORKING_BYTES_SENT, bytes.length); + return channel.send(bytes); + } + + private Result completionException(Throwable cause, NodeId receiver, Message message) { + final var msg = + String.format("Send %s to %s failed", message.getClass().getSimpleName(), receiver); + log.warn("{}: {}", msg, cause.getMessage()); + return IO_ERROR.result(); + } + + private Result updateStatistics(Result result) { + this.counters.increment(CounterType.MESSAGES_OUTBOUND_PROCESSED); + if (result.isSuccess()) { + this.counters.increment(CounterType.MESSAGES_OUTBOUND_SENT); + } + return result; + } + + private byte[] serialize(Message out) { + try { + byte[] uncompressed = serialization.toDson(out, Output.WIRE); + return Compress.compress(uncompressed); + } catch (IOException e) { + throw new UncheckedIOException("While serializing message", e); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageFromPeer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageFromPeer.java index bb588ec58e..27bc9ed0be 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageFromPeer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessageFromPeer.java @@ -65,41 +65,39 @@ package com.radixdlt.network.messaging; import com.radixdlt.network.p2p.NodeId; - import java.util.Objects; public final class MessageFromPeer { - private final NodeId source; - private final T message; + private final NodeId source; + private final T message; - public MessageFromPeer(NodeId source, T message) { - this.source = source; - this.message = message; - } + public MessageFromPeer(NodeId source, T message) { + this.source = source; + this.message = message; + } - public NodeId getSource() { - return source; - } + public NodeId getSource() { + return source; + } - public T getMessage() { - return message; - } + public T getMessage() { + return message; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MessageFromPeer that = (MessageFromPeer) o; - return Objects.equals(source, that.source) - && Objects.equals(message, that.message); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MessageFromPeer that = (MessageFromPeer) o; + return Objects.equals(source, that.source) && Objects.equals(message, that.message); + } - @Override - public int hashCode() { - return Objects.hash(source, message); - } + @Override + public int hashCode() { + return Objects.hash(source, message); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagePreprocessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagePreprocessor.java index 9bb49aa379..e56b5e1f0f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagePreprocessor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagePreprocessor.java @@ -64,9 +64,9 @@ package com.radixdlt.network.messaging; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.radix.network.messaging.Message; +import static com.radixdlt.network.messaging.MessagingErrors.IO_ERROR; +import static com.radixdlt.network.messaging.MessagingErrors.MESSAGE_EXPIRED; +import static java.util.Optional.ofNullable; import com.google.inject.Provider; import com.radixdlt.counters.SystemCounters; @@ -77,74 +77,75 @@ import com.radixdlt.utils.Compress; import com.radixdlt.utils.TimeSupplier; import com.radixdlt.utils.functional.Result; - import java.io.IOException; import java.time.Duration; import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.radix.network.messaging.Message; -import static com.radixdlt.network.messaging.MessagingErrors.IO_ERROR; -import static com.radixdlt.network.messaging.MessagingErrors.MESSAGE_EXPIRED; - -import static java.util.Optional.ofNullable; - -/** - * Handles incoming messages. Deserializes raw messages and validates them. - */ +/** Handles incoming messages. Deserializes raw messages and validates them. */ final class MessagePreprocessor { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - private final long messageTtlMs; - private final SystemCounters counters; - private final TimeSupplier timeSource; - private final Serialization serialization; - private final Provider peerControl; + private final long messageTtlMs; + private final SystemCounters counters; + private final TimeSupplier timeSource; + private final Serialization serialization; + private final Provider peerControl; - MessagePreprocessor( - SystemCounters counters, - MessageCentralConfiguration config, - TimeSupplier timeSource, - Serialization serialization, - Provider peerControl - ) { - this.messageTtlMs = Objects.requireNonNull(config).messagingTimeToLive(30_000L); - this.counters = Objects.requireNonNull(counters); - this.timeSource = Objects.requireNonNull(timeSource); - this.serialization = Objects.requireNonNull(serialization); - this.peerControl = Objects.requireNonNull(peerControl); - } + MessagePreprocessor( + SystemCounters counters, + MessageCentralConfiguration config, + TimeSupplier timeSource, + Serialization serialization, + Provider peerControl) { + this.messageTtlMs = Objects.requireNonNull(config).messagingTimeToLive(30_000L); + this.counters = Objects.requireNonNull(counters); + this.timeSource = Objects.requireNonNull(timeSource); + this.serialization = Objects.requireNonNull(serialization); + this.peerControl = Objects.requireNonNull(peerControl); + } - Result> process(InboundMessage inboundMessage) { - final byte[] messageBytes = inboundMessage.message(); - this.counters.add(CounterType.NETWORKING_BYTES_RECEIVED, messageBytes.length); - final var result = deserialize(inboundMessage, messageBytes) - .flatMap(message -> processMessage(inboundMessage.source(), message)); - this.counters.increment(CounterType.MESSAGES_INBOUND_RECEIVED); - result.fold( - unused -> this.counters.increment(CounterType.MESSAGES_INBOUND_DISCARDED), - unused -> this.counters.increment(CounterType.MESSAGES_INBOUND_PROCESSED) - ); - return result; - } + Result> process(InboundMessage inboundMessage) { + final byte[] messageBytes = inboundMessage.message(); + this.counters.add(CounterType.NETWORKING_BYTES_RECEIVED, messageBytes.length); + final var result = + deserialize(inboundMessage, messageBytes) + .flatMap(message -> processMessage(inboundMessage.source(), message)); + this.counters.increment(CounterType.MESSAGES_INBOUND_RECEIVED); + result.fold( + unused -> this.counters.increment(CounterType.MESSAGES_INBOUND_DISCARDED), + unused -> this.counters.increment(CounterType.MESSAGES_INBOUND_PROCESSED)); + return result; + } - Result> processMessage(NodeId source, Message message) { - final var currentTime = timeSource.currentTime(); + Result> processMessage(NodeId source, Message message) { + final var currentTime = timeSource.currentTime(); - if (currentTime - message.getTimestamp() > messageTtlMs) { - return MESSAGE_EXPIRED.result(); - } else { - return Result.ok(new MessageFromPeer<>(source, message)); - } - } + if (currentTime - message.getTimestamp() > messageTtlMs) { + return MESSAGE_EXPIRED.result(); + } else { + return Result.ok(new MessageFromPeer<>(source, message)); + } + } - private Result deserialize(InboundMessage inboundMessage, byte[] in) { - try { - byte[] uncompressed = Compress.uncompress(in); + private Result deserialize(InboundMessage inboundMessage, byte[] in) { + try { + byte[] uncompressed = Compress.uncompress(in); - return Result.fromOptional(IO_ERROR, ofNullable(serialization.fromDson(uncompressed, Message.class))); - } catch (IOException e) { - log.error(String.format("Failed to deserialize message from peer %s", inboundMessage.source()), e); - peerControl.get().banPeer(inboundMessage.source(), Duration.ofMinutes(5), "Failed to deserialize inbound message"); - return IO_ERROR.result(); - } - } + return Result.fromOptional( + IO_ERROR, ofNullable(serialization.fromDson(uncompressed, Message.class))); + } catch (IOException e) { + log.error( + String.format("Failed to deserialize message from peer %s", inboundMessage.source()), e); + peerControl + .get() + .banPeer( + inboundMessage.source(), + Duration.ofMinutes(5), + "Failed to deserialize inbound message"); + return IO_ERROR.result(); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingErrors.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingErrors.java index 9c0d829db1..0850b92ddc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingErrors.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingErrors.java @@ -67,26 +67,26 @@ import com.radixdlt.utils.functional.Failure; public enum MessagingErrors implements Failure { - MESSAGE_EXPIRED(1, "Message expired"), - IO_ERROR(2, "IO Error"), - SELF_CONNECTION_ATTEMPT(3, "Attempt to connect to self"), - PEER_BANNED(4, "Peer is banned"); + MESSAGE_EXPIRED(1, "Message expired"), + IO_ERROR(2, "IO Error"), + SELF_CONNECTION_ATTEMPT(3, "Attempt to connect to self"), + PEER_BANNED(4, "Peer is banned"); - private final int code; - private final String message; + private final int code; + private final String message; - MessagingErrors(int code, String message) { - this.code = code; - this.message = message; - } + MessagingErrors(int code, String message) { + this.code = code; + this.message = message; + } - @Override - public String message() { - return message; - } + @Override + public String message() { + return message; + } - @Override - public int code() { - return code; - } + @Override + public int code() { + return code; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingModule.java index 6a903b172d..f275a3ae37 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/MessagingModule.java @@ -80,12 +80,12 @@ import com.radixdlt.environment.rx.RxRemoteEnvironment; import com.radixdlt.mempool.MempoolAdd; import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; +import com.radixdlt.middleware2.network.MessageCentralBFTNetwork; +import com.radixdlt.middleware2.network.MessageCentralLedgerSync; import com.radixdlt.middleware2.network.MessageCentralMempool; import com.radixdlt.middleware2.network.MessageCentralPeerDiscovery; import com.radixdlt.middleware2.network.MessageCentralPeerLiveness; import com.radixdlt.middleware2.network.MessageCentralValidatorSync; -import com.radixdlt.middleware2.network.MessageCentralBFTNetwork; -import com.radixdlt.middleware2.network.MessageCentralLedgerSync; import com.radixdlt.network.p2p.discovery.GetPeers; import com.radixdlt.network.p2p.discovery.PeersResponse; import com.radixdlt.network.p2p.liveness.Ping; @@ -97,152 +97,172 @@ import com.radixdlt.sync.messages.remote.SyncResponse; import io.reactivex.rxjava3.core.Flowable; -/** - * Network related module - */ +/** Network related module */ public final class MessagingModule extends AbstractModule { - @Override - protected void configure() { - // provides (for SharedMempool) - bind(MessageCentralMempool.class).in(Scopes.SINGLETON); - - // Network BFT/Epoch Sync messages - //TODO: make rate limits configurable - bind(RateLimiter.class).annotatedWith(GetVerticesRequestRateLimit.class).toInstance(RateLimiter.create(50.0)); - bind(MessageCentralValidatorSync.class).in(Scopes.SINGLETON); - - // Network BFT messages - bind(MessageCentralBFTNetwork.class).in(Scopes.SINGLETON); - } - - @ProvidesIntoSet - private RxRemoteDispatcher mempoolAddDispatcher(MessageCentralMempool messageCentralMempool) { - return RxRemoteDispatcher.create(MempoolAdd.class, messageCentralMempool.mempoolAddRemoteEventDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher proposalDispatcher(MessageCentralBFTNetwork bftNetwork) { - return RxRemoteDispatcher.create(Proposal.class, bftNetwork.proposalDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher voteDispatcher(MessageCentralBFTNetwork bftNetwork) { - return RxRemoteDispatcher.create(Vote.class, bftNetwork.voteDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher vertexRequestDispatcher(MessageCentralValidatorSync messageCentralValidatorSync) { - return RxRemoteDispatcher.create(GetVerticesRequest.class, messageCentralValidatorSync.verticesRequestDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher vertexResponseDispatcher(MessageCentralValidatorSync messageCentralValidatorSync) { - return RxRemoteDispatcher.create(GetVerticesResponse.class, messageCentralValidatorSync.verticesResponseDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher vertexErrorResponseDispatcher(MessageCentralValidatorSync messageCentralValidatorSync) { - return RxRemoteDispatcher.create( - GetVerticesErrorResponse.class, - messageCentralValidatorSync.verticesErrorResponseDispatcher() - ); - } - - @ProvidesIntoSet - private RxRemoteDispatcher syncRequestDispatcher(MessageCentralLedgerSync messageCentralLedgerSync) { - return RxRemoteDispatcher.create(SyncRequest.class, messageCentralLedgerSync.syncRequestDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher syncResponseDispatcher(MessageCentralLedgerSync messageCentralLedgerSync) { - return RxRemoteDispatcher.create(SyncResponse.class, messageCentralLedgerSync.syncResponseDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher statusRequestDispatcher(MessageCentralLedgerSync messageCentralLedgerSync) { - return RxRemoteDispatcher.create(StatusRequest.class, messageCentralLedgerSync.statusRequestDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher statusResponseDispatcher(MessageCentralLedgerSync messageCentralLedgerSync) { - return RxRemoteDispatcher.create(StatusResponse.class, messageCentralLedgerSync.statusResponseDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher pingDispatcher(MessageCentralPeerLiveness messageCentralPeerLiveness) { - return RxRemoteDispatcher.create(Ping.class, messageCentralPeerLiveness.pingDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher pongDispatcher(MessageCentralPeerLiveness messageCentralPeerLiveness) { - return RxRemoteDispatcher.create(Pong.class, messageCentralPeerLiveness.pongDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher getPeersDispatcher(MessageCentralPeerDiscovery messageCentralPeerDiscovery) { - return RxRemoteDispatcher.create(GetPeers.class, messageCentralPeerDiscovery.getPeersDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher peersResponseDispatcher(MessageCentralPeerDiscovery messageCentralPeerDiscovery) { - return RxRemoteDispatcher.create(PeersResponse.class, messageCentralPeerDiscovery.peersResponseDispatcher()); - } - - @ProvidesIntoSet - private RxRemoteDispatcher ledgerStatusUpdateDispatcher(MessageCentralLedgerSync messageCentralLedgerSync) { - return RxRemoteDispatcher.create(LedgerStatusUpdate.class, messageCentralLedgerSync.ledgerStatusUpdateDispatcher()); - } - - // TODO: Clean this up - @Provides - @Singleton - @SuppressWarnings("unchecked") - RxRemoteEnvironment rxRemoteEnvironment( - MessageCentralMempool messageCentralMempool, - MessageCentralLedgerSync messageCentralLedgerSync, - MessageCentralBFTNetwork messageCentralBFT, - MessageCentralValidatorSync messageCentralBFTSync, - MessageCentralPeerLiveness messageCentralPeerLiveness, - MessageCentralPeerDiscovery messageCentralPeerDiscovery - ) { - return new RxRemoteEnvironment() { - @Override - public Flowable> remoteEvents(Class remoteEventClass) { - if (remoteEventClass == Vote.class) { - return messageCentralBFT.remoteVotes().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == Proposal.class) { - return messageCentralBFT.remoteProposals().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == GetVerticesRequest.class) { - return messageCentralBFTSync.requests().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == GetVerticesResponse.class) { - return messageCentralBFTSync.responses().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == GetVerticesErrorResponse.class) { - return messageCentralBFTSync.errorResponses().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == MempoolAdd.class) { - return messageCentralMempool.mempoolComands().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == SyncRequest.class) { - return messageCentralLedgerSync.syncRequests().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == SyncResponse.class) { - return messageCentralLedgerSync.syncResponses().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == StatusRequest.class) { - return messageCentralLedgerSync.statusRequests().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == StatusResponse.class) { - return messageCentralLedgerSync.statusResponses().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == LedgerStatusUpdate.class) { - return messageCentralLedgerSync.ledgerStatusUpdates().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == Ping.class) { - return messageCentralPeerLiveness.pings().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == Pong.class) { - return messageCentralPeerLiveness.pongs().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == GetPeers.class) { - return messageCentralPeerDiscovery.getPeersEvents().map(m -> (RemoteEvent) m); - } else if (remoteEventClass == PeersResponse.class) { - return messageCentralPeerDiscovery.peersResponses().map(m -> (RemoteEvent) m); - } else { - throw new IllegalStateException(); - } - } - }; - } + @Override + protected void configure() { + // provides (for SharedMempool) + bind(MessageCentralMempool.class).in(Scopes.SINGLETON); + + // Network BFT/Epoch Sync messages + // TODO: make rate limits configurable + bind(RateLimiter.class) + .annotatedWith(GetVerticesRequestRateLimit.class) + .toInstance(RateLimiter.create(50.0)); + bind(MessageCentralValidatorSync.class).in(Scopes.SINGLETON); + + // Network BFT messages + bind(MessageCentralBFTNetwork.class).in(Scopes.SINGLETON); + } + + @ProvidesIntoSet + private RxRemoteDispatcher mempoolAddDispatcher(MessageCentralMempool messageCentralMempool) { + return RxRemoteDispatcher.create( + MempoolAdd.class, messageCentralMempool.mempoolAddRemoteEventDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher proposalDispatcher(MessageCentralBFTNetwork bftNetwork) { + return RxRemoteDispatcher.create(Proposal.class, bftNetwork.proposalDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher voteDispatcher(MessageCentralBFTNetwork bftNetwork) { + return RxRemoteDispatcher.create(Vote.class, bftNetwork.voteDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher vertexRequestDispatcher( + MessageCentralValidatorSync messageCentralValidatorSync) { + return RxRemoteDispatcher.create( + GetVerticesRequest.class, messageCentralValidatorSync.verticesRequestDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher vertexResponseDispatcher( + MessageCentralValidatorSync messageCentralValidatorSync) { + return RxRemoteDispatcher.create( + GetVerticesResponse.class, messageCentralValidatorSync.verticesResponseDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher vertexErrorResponseDispatcher( + MessageCentralValidatorSync messageCentralValidatorSync) { + return RxRemoteDispatcher.create( + GetVerticesErrorResponse.class, + messageCentralValidatorSync.verticesErrorResponseDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher syncRequestDispatcher( + MessageCentralLedgerSync messageCentralLedgerSync) { + return RxRemoteDispatcher.create( + SyncRequest.class, messageCentralLedgerSync.syncRequestDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher syncResponseDispatcher( + MessageCentralLedgerSync messageCentralLedgerSync) { + return RxRemoteDispatcher.create( + SyncResponse.class, messageCentralLedgerSync.syncResponseDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher statusRequestDispatcher( + MessageCentralLedgerSync messageCentralLedgerSync) { + return RxRemoteDispatcher.create( + StatusRequest.class, messageCentralLedgerSync.statusRequestDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher statusResponseDispatcher( + MessageCentralLedgerSync messageCentralLedgerSync) { + return RxRemoteDispatcher.create( + StatusResponse.class, messageCentralLedgerSync.statusResponseDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher pingDispatcher( + MessageCentralPeerLiveness messageCentralPeerLiveness) { + return RxRemoteDispatcher.create(Ping.class, messageCentralPeerLiveness.pingDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher pongDispatcher( + MessageCentralPeerLiveness messageCentralPeerLiveness) { + return RxRemoteDispatcher.create(Pong.class, messageCentralPeerLiveness.pongDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher getPeersDispatcher( + MessageCentralPeerDiscovery messageCentralPeerDiscovery) { + return RxRemoteDispatcher.create( + GetPeers.class, messageCentralPeerDiscovery.getPeersDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher peersResponseDispatcher( + MessageCentralPeerDiscovery messageCentralPeerDiscovery) { + return RxRemoteDispatcher.create( + PeersResponse.class, messageCentralPeerDiscovery.peersResponseDispatcher()); + } + + @ProvidesIntoSet + private RxRemoteDispatcher ledgerStatusUpdateDispatcher( + MessageCentralLedgerSync messageCentralLedgerSync) { + return RxRemoteDispatcher.create( + LedgerStatusUpdate.class, messageCentralLedgerSync.ledgerStatusUpdateDispatcher()); + } + + // TODO: Clean this up + @Provides + @Singleton + @SuppressWarnings("unchecked") + RxRemoteEnvironment rxRemoteEnvironment( + MessageCentralMempool messageCentralMempool, + MessageCentralLedgerSync messageCentralLedgerSync, + MessageCentralBFTNetwork messageCentralBFT, + MessageCentralValidatorSync messageCentralBFTSync, + MessageCentralPeerLiveness messageCentralPeerLiveness, + MessageCentralPeerDiscovery messageCentralPeerDiscovery) { + return new RxRemoteEnvironment() { + @Override + public Flowable> remoteEvents(Class remoteEventClass) { + if (remoteEventClass == Vote.class) { + return messageCentralBFT.remoteVotes().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == Proposal.class) { + return messageCentralBFT.remoteProposals().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == GetVerticesRequest.class) { + return messageCentralBFTSync.requests().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == GetVerticesResponse.class) { + return messageCentralBFTSync.responses().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == GetVerticesErrorResponse.class) { + return messageCentralBFTSync.errorResponses().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == MempoolAdd.class) { + return messageCentralMempool.mempoolComands().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == SyncRequest.class) { + return messageCentralLedgerSync.syncRequests().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == SyncResponse.class) { + return messageCentralLedgerSync.syncResponses().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == StatusRequest.class) { + return messageCentralLedgerSync.statusRequests().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == StatusResponse.class) { + return messageCentralLedgerSync.statusResponses().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == LedgerStatusUpdate.class) { + return messageCentralLedgerSync.ledgerStatusUpdates().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == Ping.class) { + return messageCentralPeerLiveness.pings().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == Pong.class) { + return messageCentralPeerLiveness.pongs().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == GetPeers.class) { + return messageCentralPeerDiscovery.getPeersEvents().map(m -> (RemoteEvent) m); + } else if (remoteEventClass == PeersResponse.class) { + return messageCentralPeerDiscovery.peersResponses().map(m -> (RemoteEvent) m); + } else { + throw new IllegalStateException(); + } + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/OutboundMessageEvent.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/OutboundMessageEvent.java index 3f4d7cc284..d69586bf21 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/OutboundMessageEvent.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/OutboundMessageEvent.java @@ -64,110 +64,110 @@ package com.radixdlt.network.messaging; +import com.google.common.collect.ImmutableMap; +import com.radixdlt.network.p2p.NodeId; import java.util.Comparator; import java.util.Map; import java.util.Objects; - -import com.radixdlt.network.p2p.NodeId; import org.radix.network.messages.PeerPingMessage; import org.radix.network.messages.PeerPongMessage; import org.radix.network.messaging.Message; -import com.google.common.collect.ImmutableMap; - /** * Outbound message wrapper with priority, time and destination. - *

- * Note that priority is calculated from a fixed table of priorities for - * specific message types, and cannot be specified by the user. - *

- * Time is number of nanoseconds since some arbitrary baseline. + * + *

Note that priority is calculated from a fixed table of priorities for specific message types, + * and cannot be specified by the user. + * + *

Time is number of nanoseconds since some arbitrary baseline. */ public final class OutboundMessageEvent { - private static final int DEFAULT_PRIORITY = 0; - // Lower (inc -ve) numbers are higher priority than larger numbers - private static final Map, Integer> MESSAGE_PRIORITIES = ImmutableMap.of( - PeerPingMessage.class, Integer.MIN_VALUE, - PeerPongMessage.class, Integer.MIN_VALUE - ); - - public static Comparator comparator() { - return Comparator.comparingInt(OutboundMessageEvent::priority).thenComparingLong(OutboundMessageEvent::nanoTimeDiff); - } - - private final int priority; - private final long nanoTimeDiff; - private final NodeId receiver; - private final Message message; - - OutboundMessageEvent(NodeId receiver, Message message, long nanoTimeDiff) { - this.priority = MESSAGE_PRIORITIES.getOrDefault(message.getClass(), DEFAULT_PRIORITY); - this.nanoTimeDiff = nanoTimeDiff; - this.receiver = receiver; - this.message = message; - } - - /** - * Returns the messages priority. - * - * @return the messages priority. - */ - public int priority() { - return priority; - } - - /** - * Returns the time this event was created as a number of nanoseconds - * since some arbitrary baseline. - * - * @return the time this event was created - */ - public long nanoTimeDiff() { - return nanoTimeDiff; - } - - /** - * Returns the destination of the message. - * - * @return the source or destination of the message. - */ - public NodeId receiver() { - return receiver; - } - - /** - * Returns the message. - * - * @return the message. - */ - public Message message() { - return message; - } - - @Override - public int hashCode() { - return Objects.hash(this.priority, this.nanoTimeDiff, this.receiver, this.message); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof OutboundMessageEvent) { - OutboundMessageEvent that = (OutboundMessageEvent) obj; - return this.priority == that.priority - && this.nanoTimeDiff == that.nanoTimeDiff - && Objects.equals(this.receiver, that.receiver) - && Objects.equals(this.message, that.message); - } - return false; - } - - @Override - public String toString() { - return String.format("%s[priority=%s, nanoTime=%s, receiver=%s, message=%s]", - getClass().getSimpleName(), priority, nanoTimeDiff, receiver, message); - } -} \ No newline at end of file + private static final int DEFAULT_PRIORITY = 0; + // Lower (inc -ve) numbers are higher priority than larger numbers + private static final Map, Integer> MESSAGE_PRIORITIES = + ImmutableMap.of( + PeerPingMessage.class, Integer.MIN_VALUE, + PeerPongMessage.class, Integer.MIN_VALUE); + + public static Comparator comparator() { + return Comparator.comparingInt(OutboundMessageEvent::priority) + .thenComparingLong(OutboundMessageEvent::nanoTimeDiff); + } + + private final int priority; + private final long nanoTimeDiff; + private final NodeId receiver; + private final Message message; + + OutboundMessageEvent(NodeId receiver, Message message, long nanoTimeDiff) { + this.priority = MESSAGE_PRIORITIES.getOrDefault(message.getClass(), DEFAULT_PRIORITY); + this.nanoTimeDiff = nanoTimeDiff; + this.receiver = receiver; + this.message = message; + } + + /** + * Returns the messages priority. + * + * @return the messages priority. + */ + public int priority() { + return priority; + } + + /** + * Returns the time this event was created as a number of nanoseconds since some arbitrary + * baseline. + * + * @return the time this event was created + */ + public long nanoTimeDiff() { + return nanoTimeDiff; + } + + /** + * Returns the destination of the message. + * + * @return the source or destination of the message. + */ + public NodeId receiver() { + return receiver; + } + + /** + * Returns the message. + * + * @return the message. + */ + public Message message() { + return message; + } + + @Override + public int hashCode() { + return Objects.hash(this.priority, this.nanoTimeDiff, this.receiver, this.message); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OutboundMessageEvent) { + OutboundMessageEvent that = (OutboundMessageEvent) obj; + return this.priority == that.priority + && this.nanoTimeDiff == that.nanoTimeDiff + && Objects.equals(this.receiver, that.receiver) + && Objects.equals(this.message, that.message); + } + return false; + } + + @Override + public String toString() { + return String.format( + "%s[priority=%s, nanoTime=%s, receiver=%s, message=%s]", + getClass().getSimpleName(), priority, nanoTimeDiff, receiver, message); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimpleBlockingQueue.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimpleBlockingQueue.java index abb134bf4d..9659ad80ef 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimpleBlockingQueue.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimpleBlockingQueue.java @@ -70,36 +70,33 @@ * @param elements of the queue */ interface SimpleBlockingQueue { - /** - * Retrieves and removes the head of this queue, waiting if necessary - * until an element becomes available. - * - * @return the head of this queue - * @throws InterruptedException if interrupted while waiting - */ - T take() throws InterruptedException; + /** + * Retrieves and removes the head of this queue, waiting if necessary until an element becomes + * available. + * + * @return the head of this queue + * @throws InterruptedException if interrupted while waiting + */ + T take() throws InterruptedException; - /** - * Inserts the specified element into this queue if it is possible to do - * so immediately without violating capacity restrictions, returning - * {@code true} upon success and {@code false} if no space is currently - * available. - * - * @param e the element to add - * @return {@code true} if the element was added to this queue, else - * {@code false} - * @throws ClassCastException if the class of the specified element - * prevents it from being added to this queue - * @throws NullPointerException if the specified element is null - */ - boolean offer(T item); + /** + * Inserts the specified element into this queue if it is possible to do so immediately without + * violating capacity restrictions, returning {@code true} upon success and {@code false} if no + * space is currently available. + * + * @param e the element to add + * @return {@code true} if the element was added to this queue, else {@code false} + * @throws ClassCastException if the class of the specified element prevents it from being added + * to this queue + * @throws NullPointerException if the specified element is null + */ + boolean offer(T item); - /** - * Returns the number of elements in this collection. If this collection - * contains more than {@code Integer.MAX_VALUE} elements, returns - * {@code Integer.MAX_VALUE}. - * - * @return the number of elements in this collection - */ - int size(); + /** + * Returns the number of elements in this collection. If this collection contains more than {@code + * Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. + * + * @return the number of elements in this collection + */ + int size(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueue.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueue.java index 4828440d04..5531f00a1a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueue.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueue.java @@ -70,67 +70,67 @@ import java.util.concurrent.atomic.AtomicLong; /** - * Implementation of a {@link SimpleBlockingQueue} that provides - * priority ordering, while respecting FIFO properties - * within a priority class. + * Implementation of a {@link SimpleBlockingQueue} that provides priority ordering, while respecting + * FIFO properties within a priority class. * * @param The element type */ public class SimplePriorityBlockingQueue implements SimpleBlockingQueue { - private final PriorityBlockingQueue> queue; - - static final class SimpleEntry { - private static final AtomicLong sequence = new AtomicLong(0); - - final long seq; - final U entry; - - SimpleEntry(U entry) { - this.seq = sequence.getAndIncrement(); - this.entry = entry; - } - - long getSeq() { - return this.seq; - } - - U getEntry() { - return this.entry; - } - - @Override - public String toString() { - return this.entry.toString(); - } - } - - public SimplePriorityBlockingQueue(int size, Comparator comparator) { - this.queue = new PriorityBlockingQueue<>(size, comparator(comparator)); - } - - private Comparator> comparator(Comparator comparator) { - return Comparator.comparing(SimpleEntry::getEntry, comparator).thenComparingLong(SimpleEntry::getSeq); - } - - @Override - public T take() throws InterruptedException { - return this.queue.take().getEntry(); - } - - @Override - public boolean offer(T item) { - return this.queue.offer(new SimpleEntry<>(Objects.requireNonNull(item))); - } - - @Override - public int size() { - return this.queue.size(); - } - - // No straightforward way to implement equals, so not doing that here - - @Override - public String toString() { - return this.queue.toString(); - } + private final PriorityBlockingQueue> queue; + + static final class SimpleEntry { + private static final AtomicLong sequence = new AtomicLong(0); + + final long seq; + final U entry; + + SimpleEntry(U entry) { + this.seq = sequence.getAndIncrement(); + this.entry = entry; + } + + long getSeq() { + return this.seq; + } + + U getEntry() { + return this.entry; + } + + @Override + public String toString() { + return this.entry.toString(); + } + } + + public SimplePriorityBlockingQueue(int size, Comparator comparator) { + this.queue = new PriorityBlockingQueue<>(size, comparator(comparator)); + } + + private Comparator> comparator(Comparator comparator) { + return Comparator.comparing(SimpleEntry::getEntry, comparator) + .thenComparingLong(SimpleEntry::getSeq); + } + + @Override + public T take() throws InterruptedException { + return this.queue.take().getEntry(); + } + + @Override + public boolean offer(T item) { + return this.queue.offer(new SimpleEntry<>(Objects.requireNonNull(item))); + } + + @Override + public int size() { + return this.queue.size(); + } + + // No straightforward way to implement equals, so not doing that here + + @Override + public String toString() { + return this.queue.toString(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/NodeId.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/NodeId.java index d7eaa9be5e..64c6c38d0f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/NodeId.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/NodeId.java @@ -68,53 +68,52 @@ import com.fasterxml.jackson.annotation.JsonValue; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.exception.PublicKeyException; - import java.util.Objects; public final class NodeId { - private final ECPublicKey publicKey; + private final ECPublicKey publicKey; - @JsonCreator - public static NodeId deserialize(byte[] pubKey) throws PublicKeyException { - return fromPublicKey(ECPublicKey.fromBytes(pubKey)); - } + @JsonCreator + public static NodeId deserialize(byte[] pubKey) throws PublicKeyException { + return fromPublicKey(ECPublicKey.fromBytes(pubKey)); + } - public static NodeId fromPublicKey(ECPublicKey publicKey) { - return new NodeId(publicKey); - } + public static NodeId fromPublicKey(ECPublicKey publicKey) { + return new NodeId(publicKey); + } - private NodeId(ECPublicKey publicKey) { - this.publicKey = Objects.requireNonNull(publicKey); - } + private NodeId(ECPublicKey publicKey) { + this.publicKey = Objects.requireNonNull(publicKey); + } - public ECPublicKey getPublicKey() { - return publicKey; - } + public ECPublicKey getPublicKey() { + return publicKey; + } - @JsonValue - public byte[] getPubKey() { - return publicKey.getCompressedBytes(); - } + @JsonValue + public byte[] getPubKey() { + return publicKey.getCompressedBytes(); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var other = (NodeId) o; - return Objects.equals(publicKey, other.publicKey); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var other = (NodeId) o; + return Objects.equals(publicKey, other.publicKey); + } - @Override - public int hashCode() { - return Objects.hash(publicKey); - } + @Override + public int hashCode() { + return Objects.hash(publicKey); + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), publicKey.toHex()); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), publicKey.toHex()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PConfig.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PConfig.java index d008bfe5e1..18302af471 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PConfig.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PConfig.java @@ -66,161 +66,139 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.properties.RuntimeProperties; - import java.util.Arrays; -/** - * Static configuration data for P2P layer. - */ +/** Static configuration data for P2P layer. */ public interface P2PConfig { - /** - * List of seed nodes for discovery. - */ - ImmutableList seedNodes(); - - /** - * Default port to use for discovery seed nodes. - */ - int defaultPort(); - - /** - * An interval at which peer discovery rounds trigger. - */ - long discoveryInterval(); - - /** - * Get the host to bind the p2p server to. - */ - String listenAddress(); - - /** - * Get the port number to bind the p2p server to. - */ - int listenPort(); - - /** - * Specifies whether the server should process the PROXY header for inbound connections. - */ - boolean useProxyProtocol(); - - /** - * Get node's port number to broadcast to other peers. - */ - int broadcastPort(); - - /** - * The timeout for initiating outbound peer connection. - */ - int peerConnectionTimeout(); - - /** - * Get the maximum number of inbound open channels allowed. - * Note that each channel consumes some resources on the host - * machine, and there may be other global operating-system defined limits - * that come into play. - */ - int maxInboundChannels(); - - /** - * Get the maximum number of outbound open channels allowed. - * Note that each channel consumes some resources on the host - * machine, and there may be other global operating-system defined limits - * that come into play. - */ - int maxOutboundChannels(); - - /** - * Get the buffer size of incoming messages for each TCP connection. - * - * @return the size of a message buffer - */ - int channelBufferSize(); - - /** - * An interval at which peer liveness check is triggered (ping message). - */ - long peerLivenessCheckInterval(); - - /** - * The timeout for receiving a Pong message. PeerLivenessLost event is triggered if pong is not received on time. - */ - long pingTimeout(); - - /** - * Create a configuration from specified {@link RuntimeProperties}. - * - * @param properties the properties to read the configuration from - * @return The configuration - */ - static P2PConfig fromRuntimeProperties(RuntimeProperties properties) { - return new P2PConfig() { - @Override - public ImmutableList seedNodes() { - return Arrays.stream(properties.get("network.p2p.seed_nodes", "").split(",")) - .map(String::trim) - .filter(hn -> !hn.isEmpty()) - .collect(ImmutableList.toImmutableList()); - } - - @Override - public int defaultPort() { - return properties.get("network.p2p.default_port", 30000); - } - - @Override - public long discoveryInterval() { - return properties.get("network.p2p.discovery_interval", 30_000); - } - - @Override - public String listenAddress() { - return properties.get("network.p2p.listen_address", "0.0.0.0"); - } - - @Override - public int listenPort() { - return properties.get("network.p2p.listen_port", 30000); - } - - @Override - public boolean useProxyProtocol() { - return properties.get("network.p2p.use_proxy_protocol", false); - } - - @Override - public int broadcastPort() { - return properties.get("network.p2p.broadcast_port", listenPort()); - } - - @Override - public int peerConnectionTimeout() { - return properties.get("network.p2p.peer_connection_timeout", 5000); - } - - @Override - public int maxInboundChannels() { - return properties.get("network.p2p.max_inbound_channels", 1024); - } - - @Override - public int maxOutboundChannels() { - return properties.get("network.p2p.max_outbound_channels", 1024); - } - - @Override - public int channelBufferSize() { - return properties.get("network.p2p.channel_buffer_size", 255); - } - - @Override - public long peerLivenessCheckInterval() { - return properties.get("network.p2p.peer_liveness_check_interval", 10000); - } - - @Override - public long pingTimeout() { - return properties.get("network.p2p.ping_timeout", 5000); - } - }; - } + /** List of seed nodes for discovery. */ + ImmutableList seedNodes(); + + /** Default port to use for discovery seed nodes. */ + int defaultPort(); + + /** An interval at which peer discovery rounds trigger. */ + long discoveryInterval(); + + /** Get the host to bind the p2p server to. */ + String listenAddress(); + + /** Get the port number to bind the p2p server to. */ + int listenPort(); + + /** Specifies whether the server should process the PROXY header for inbound connections. */ + boolean useProxyProtocol(); + + /** Get node's port number to broadcast to other peers. */ + int broadcastPort(); + + /** The timeout for initiating outbound peer connection. */ + int peerConnectionTimeout(); + + /** + * Get the maximum number of inbound open channels allowed. Note that each channel consumes some + * resources on the host machine, and there may be other global operating-system defined limits + * that come into play. + */ + int maxInboundChannels(); + + /** + * Get the maximum number of outbound open channels allowed. Note that each channel consumes some + * resources on the host machine, and there may be other global operating-system defined limits + * that come into play. + */ + int maxOutboundChannels(); + + /** + * Get the buffer size of incoming messages for each TCP connection. + * + * @return the size of a message buffer + */ + int channelBufferSize(); + + /** An interval at which peer liveness check is triggered (ping message). */ + long peerLivenessCheckInterval(); + + /** + * The timeout for receiving a Pong message. PeerLivenessLost event is triggered if pong is not + * received on time. + */ + long pingTimeout(); + + /** + * Create a configuration from specified {@link RuntimeProperties}. + * + * @param properties the properties to read the configuration from + * @return The configuration + */ + static P2PConfig fromRuntimeProperties(RuntimeProperties properties) { + return new P2PConfig() { + @Override + public ImmutableList seedNodes() { + return Arrays.stream(properties.get("network.p2p.seed_nodes", "").split(",")) + .map(String::trim) + .filter(hn -> !hn.isEmpty()) + .collect(ImmutableList.toImmutableList()); + } + + @Override + public int defaultPort() { + return properties.get("network.p2p.default_port", 30000); + } + + @Override + public long discoveryInterval() { + return properties.get("network.p2p.discovery_interval", 30_000); + } + + @Override + public String listenAddress() { + return properties.get("network.p2p.listen_address", "0.0.0.0"); + } + + @Override + public int listenPort() { + return properties.get("network.p2p.listen_port", 30000); + } + + @Override + public boolean useProxyProtocol() { + return properties.get("network.p2p.use_proxy_protocol", false); + } + + @Override + public int broadcastPort() { + return properties.get("network.p2p.broadcast_port", listenPort()); + } + + @Override + public int peerConnectionTimeout() { + return properties.get("network.p2p.peer_connection_timeout", 5000); + } + + @Override + public int maxInboundChannels() { + return properties.get("network.p2p.max_inbound_channels", 1024); + } + + @Override + public int maxOutboundChannels() { + return properties.get("network.p2p.max_outbound_channels", 1024); + } + + @Override + public int channelBufferSize() { + return properties.get("network.p2p.channel_buffer_size", 255); + } + + @Override + public long peerLivenessCheckInterval() { + return properties.get("network.p2p.peer_liveness_check_interval", 10000); + } + + @Override + public long pingTimeout() { + return properties.get("network.p2p.ping_timeout", 5000); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PModule.java index fe0c228644..58092df4f4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/P2PModule.java @@ -89,70 +89,66 @@ public final class P2PModule extends AbstractModule { - private final RuntimeProperties properties; + private final RuntimeProperties properties; - public P2PModule(RuntimeProperties properties) { - this.properties = properties; - } + public P2PModule(RuntimeProperties properties) { + this.properties = properties; + } - @Override - protected void configure() { - final var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(PeerEvent.class); - eventBinder.addBinding().toInstance(PeerOutboundConnectionTimeout.class); + @Override + protected void configure() { + final var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(PeerEvent.class); + eventBinder.addBinding().toInstance(PeerOutboundConnectionTimeout.class); - bind(AddressBook.class).in(Scopes.SINGLETON); - bind(PeersView.class).to(PeerManagerPeersView.class).in(Scopes.SINGLETON); - bind(PeerControl.class).to(AddressBookPeerControl.class).in(Scopes.SINGLETON); - bind(PeerOutboundBootstrap.class).to(PeerOutboundBootstrapImpl.class).in(Scopes.SINGLETON); - bind(AddressBookPersistence.class).to(BerkeleyAddressBookPersistence.class).in(Scopes.SINGLETON); - bind(PeerServerBootstrap.class).in(Scopes.SINGLETON); - bind(PendingOutboundChannelsManager.class).in(Scopes.SINGLETON); - bind(PeerManager.class).in(Scopes.SINGLETON); - } + bind(AddressBook.class).in(Scopes.SINGLETON); + bind(PeersView.class).to(PeerManagerPeersView.class).in(Scopes.SINGLETON); + bind(PeerControl.class).to(AddressBookPeerControl.class).in(Scopes.SINGLETON); + bind(PeerOutboundBootstrap.class).to(PeerOutboundBootstrapImpl.class).in(Scopes.SINGLETON); + bind(AddressBookPersistence.class) + .to(BerkeleyAddressBookPersistence.class) + .in(Scopes.SINGLETON); + bind(PeerServerBootstrap.class).in(Scopes.SINGLETON); + bind(PendingOutboundChannelsManager.class).in(Scopes.SINGLETON); + bind(PeerManager.class).in(Scopes.SINGLETON); + } - @ProvidesIntoSet - private EventProcessorOnRunner peerManagerPeerEventProcessor(PeerManager peerManager) { - return new EventProcessorOnRunner<>( - Runners.P2P_NETWORK, - PeerEvent.class, - peerManager.peerEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner peerManagerPeerEventProcessor(PeerManager peerManager) { + return new EventProcessorOnRunner<>( + Runners.P2P_NETWORK, PeerEvent.class, peerManager.peerEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner pendingOutboundChannelsManagerPeerEventProcessor( - PendingOutboundChannelsManager pendingOutboundChannelsManager - ) { - return new EventProcessorOnRunner<>( - Runners.P2P_NETWORK, - PeerEvent.class, - pendingOutboundChannelsManager.peerEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner pendingOutboundChannelsManagerPeerEventProcessor( + PendingOutboundChannelsManager pendingOutboundChannelsManager) { + return new EventProcessorOnRunner<>( + Runners.P2P_NETWORK, PeerEvent.class, pendingOutboundChannelsManager.peerEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner peerOutboundConnectionTimeoutEventProcessor( - PendingOutboundChannelsManager pendingOutboundChannelsManager - ) { - return new EventProcessorOnRunner<>( - Runners.P2P_NETWORK, - PeerOutboundConnectionTimeout.class, - pendingOutboundChannelsManager.peerOutboundConnectionTimeoutEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner peerOutboundConnectionTimeoutEventProcessor( + PendingOutboundChannelsManager pendingOutboundChannelsManager) { + return new EventProcessorOnRunner<>( + Runners.P2P_NETWORK, + PeerOutboundConnectionTimeout.class, + pendingOutboundChannelsManager.peerOutboundConnectionTimeoutEventProcessor()); + } - @Provides - public P2PConfig p2pConfig() { - return P2PConfig.fromRuntimeProperties(this.properties); - } + @Provides + public P2PConfig p2pConfig() { + return P2PConfig.fromRuntimeProperties(this.properties); + } - @Provides - @Self - public RadixNodeUri selfUri(@NetworkId int networkId, @Self ECPublicKey selfKey, HostIp hostIp, P2PConfig p2pConfig) { - final var host = hostIp.hostIp().orElseThrow(() -> new IllegalStateException("Unable to determine host IP")); - final var port = p2pConfig.broadcastPort(); - return RadixNodeUri.fromPubKeyAndAddress(networkId, selfKey, host, port); - } + @Provides + @Self + public RadixNodeUri selfUri( + @NetworkId int networkId, @Self ECPublicKey selfKey, HostIp hostIp, P2PConfig p2pConfig) { + final var host = + hostIp.hostIp().orElseThrow(() -> new IllegalStateException("Unable to determine host IP")); + final var port = p2pConfig.broadcastPort(); + return RadixNodeUri.fromPubKeyAndAddress(networkId, selfKey, host, port); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerControl.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerControl.java index 382e2f6f4c..704bec3f61 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerControl.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerControl.java @@ -68,13 +68,13 @@ public interface PeerControl { - /** - * Bans a peer for a specified period of time. - * Any open connections to a banned peer are immediately closed. - */ - void banPeer(NodeId nodeId, Duration banDuration, String reason); + /** + * Bans a peer for a specified period of time. Any open connections to a banned peer are + * immediately closed. + */ + void banPeer(NodeId nodeId, Duration banDuration, String reason); - default void banPeerForever(NodeId nodeId, String reason) { - banPeer(nodeId, Duration.ofMillis(Long.MAX_VALUE), reason); - } + default void banPeerForever(NodeId nodeId, String reason) { + banPeer(nodeId, Duration.ofMillis(Long.MAX_VALUE), reason); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerDiscoveryModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerDiscoveryModule.java index 899cd2bfd7..4925762046 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerDiscoveryModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerDiscoveryModule.java @@ -79,64 +79,50 @@ import com.radixdlt.network.p2p.discovery.GetPeers; import com.radixdlt.network.p2p.discovery.PeerDiscovery; import com.radixdlt.network.p2p.discovery.PeersResponse; - import java.time.Duration; public final class PeerDiscoveryModule extends AbstractModule { - @Override - protected void configure() { - final var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(DiscoverPeers.class); + @Override + protected void configure() { + final var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(DiscoverPeers.class); - bind(PeerDiscovery.class).in(Scopes.SINGLETON); - } + bind(PeerDiscovery.class).in(Scopes.SINGLETON); + } - @ProvidesIntoSet - public ScheduledEventProducerOnRunner discoverPeersEventProducer( - EventDispatcher discoverPeersEventDispatcher, - P2PConfig config - ) { - return new ScheduledEventProducerOnRunner<>( - Runners.P2P_NETWORK, - discoverPeersEventDispatcher, - DiscoverPeers::create, - Duration.ofMillis(500L), - Duration.ofMillis(config.discoveryInterval()) - ); - } + @ProvidesIntoSet + public ScheduledEventProducerOnRunner discoverPeersEventProducer( + EventDispatcher discoverPeersEventDispatcher, P2PConfig config) { + return new ScheduledEventProducerOnRunner<>( + Runners.P2P_NETWORK, + discoverPeersEventDispatcher, + DiscoverPeers::create, + Duration.ofMillis(500L), + Duration.ofMillis(config.discoveryInterval())); + } - @ProvidesIntoSet - private EventProcessorOnRunner discoverPeersEventProcessor( - PeerDiscovery peerDiscovery - ) { - return new EventProcessorOnRunner<>( - Runners.P2P_NETWORK, - DiscoverPeers.class, - peerDiscovery.discoverPeersEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner discoverPeersEventProcessor(PeerDiscovery peerDiscovery) { + return new EventProcessorOnRunner<>( + Runners.P2P_NETWORK, DiscoverPeers.class, peerDiscovery.discoverPeersEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner getPeersRemoteEventProcessor( - PeerDiscovery peerDiscovery - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.P2P_NETWORK, - GetPeers.class, - peerDiscovery.getPeersRemoteEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner getPeersRemoteEventProcessor( + PeerDiscovery peerDiscovery) { + return new RemoteEventProcessorOnRunner<>( + Runners.P2P_NETWORK, GetPeers.class, peerDiscovery.getPeersRemoteEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner peersResponseRemoteEventProcessor( - PeerDiscovery peerDiscovery - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.P2P_NETWORK, - PeersResponse.class, - peerDiscovery.peersResponseRemoteEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner peersResponseRemoteEventProcessor( + PeerDiscovery peerDiscovery) { + return new RemoteEventProcessorOnRunner<>( + Runners.P2P_NETWORK, + PeersResponse.class, + peerDiscovery.peersResponseRemoteEventProcessor()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerEvent.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerEvent.java index 712b60271c..a899842850 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerEvent.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerEvent.java @@ -68,99 +68,99 @@ public sealed interface PeerEvent { - final class PeerConnected implements PeerEvent { - private final PeerChannel channel; + final class PeerConnected implements PeerEvent { + private final PeerChannel channel; - public static PeerConnected create(PeerChannel channel) { - return new PeerConnected(channel); - } + public static PeerConnected create(PeerChannel channel) { + return new PeerConnected(channel); + } - private PeerConnected(PeerChannel channel) { - this.channel = channel; - } + private PeerConnected(PeerChannel channel) { + this.channel = channel; + } - public PeerChannel getChannel() { - return this.channel; - } - } + public PeerChannel getChannel() { + return this.channel; + } + } - final class PeerDisconnected implements PeerEvent { - private final PeerChannel channel; + final class PeerDisconnected implements PeerEvent { + private final PeerChannel channel; - public static PeerDisconnected create(PeerChannel channel) { - return new PeerDisconnected(channel); - } + public static PeerDisconnected create(PeerChannel channel) { + return new PeerDisconnected(channel); + } - private PeerDisconnected(PeerChannel channel) { - this.channel = channel; - } + private PeerDisconnected(PeerChannel channel) { + this.channel = channel; + } - public PeerChannel getChannel() { - return this.channel; - } - } + public PeerChannel getChannel() { + return this.channel; + } + } - final class PeerLostLiveness implements PeerEvent { - private final NodeId nodeId; + final class PeerLostLiveness implements PeerEvent { + private final NodeId nodeId; - public static PeerLostLiveness create(NodeId nodeId) { - return new PeerLostLiveness(nodeId); - } + public static PeerLostLiveness create(NodeId nodeId) { + return new PeerLostLiveness(nodeId); + } - private PeerLostLiveness(NodeId nodeId) { - this.nodeId = nodeId; - } + private PeerLostLiveness(NodeId nodeId) { + this.nodeId = nodeId; + } - public NodeId getNodeId() { - return this.nodeId; - } - } + public NodeId getNodeId() { + return this.nodeId; + } + } - final class PeerBanned implements PeerEvent { - private final NodeId nodeId; + final class PeerBanned implements PeerEvent { + private final NodeId nodeId; - public static PeerBanned create(NodeId nodeId) { - return new PeerBanned(nodeId); - } + public static PeerBanned create(NodeId nodeId) { + return new PeerBanned(nodeId); + } - private PeerBanned(NodeId nodeId) { - this.nodeId = nodeId; - } + private PeerBanned(NodeId nodeId) { + this.nodeId = nodeId; + } - public NodeId getNodeId() { - return this.nodeId; - } - } + public NodeId getNodeId() { + return this.nodeId; + } + } - final class PeerConnectionTimeout implements PeerEvent { - private final RadixNodeUri uri; + final class PeerConnectionTimeout implements PeerEvent { + private final RadixNodeUri uri; - public static PeerConnectionTimeout create(RadixNodeUri uri) { - return new PeerConnectionTimeout(uri); - } + public static PeerConnectionTimeout create(RadixNodeUri uri) { + return new PeerConnectionTimeout(uri); + } - private PeerConnectionTimeout(RadixNodeUri uri) { - this.uri = uri; - } + private PeerConnectionTimeout(RadixNodeUri uri) { + this.uri = uri; + } - public RadixNodeUri getUri() { - return this.uri; - } - } + public RadixNodeUri getUri() { + return this.uri; + } + } - final class PeerHandshakeFailed implements PeerEvent { - private final PeerChannel channel; + final class PeerHandshakeFailed implements PeerEvent { + private final PeerChannel channel; - public static PeerHandshakeFailed create(PeerChannel channel) { - return new PeerHandshakeFailed(channel); - } + public static PeerHandshakeFailed create(PeerChannel channel) { + return new PeerHandshakeFailed(channel); + } - private PeerHandshakeFailed(PeerChannel channel) { - this.channel = channel; - } + private PeerHandshakeFailed(PeerChannel channel) { + this.channel = channel; + } - public PeerChannel getChannel() { - return this.channel; - } - } + public PeerChannel getChannel() { + return this.channel; + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerLivenessMonitorModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerLivenessMonitorModule.java index d460e08bf6..1222026391 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerLivenessMonitorModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerLivenessMonitorModule.java @@ -80,76 +80,62 @@ import com.radixdlt.network.p2p.liveness.PeersLivenessCheckTrigger; import com.radixdlt.network.p2p.liveness.Ping; import com.radixdlt.network.p2p.liveness.Pong; - import java.time.Duration; public final class PeerLivenessMonitorModule extends AbstractModule { - @Override - protected void configure() { - final var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(PeersLivenessCheckTrigger.class); - eventBinder.addBinding().toInstance(PeerPingTimeout.class); + @Override + protected void configure() { + final var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(PeersLivenessCheckTrigger.class); + eventBinder.addBinding().toInstance(PeerPingTimeout.class); - bind(PeerLivenessMonitor.class).in(Scopes.SINGLETON); - } + bind(PeerLivenessMonitor.class).in(Scopes.SINGLETON); + } - @ProvidesIntoSet - private EventProcessorOnRunner peersLivenessCheckTriggerEventProcessor( - PeerLivenessMonitor peerLivenessMonitor - ) { - return new EventProcessorOnRunner<>( - Runners.P2P_NETWORK, - PeersLivenessCheckTrigger.class, - peerLivenessMonitor.peersLivenessCheckTriggerEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner peersLivenessCheckTriggerEventProcessor( + PeerLivenessMonitor peerLivenessMonitor) { + return new EventProcessorOnRunner<>( + Runners.P2P_NETWORK, + PeersLivenessCheckTrigger.class, + peerLivenessMonitor.peersLivenessCheckTriggerEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner peerPingTimeoutEventProcessor( - PeerLivenessMonitor peerLivenessMonitor - ) { - return new EventProcessorOnRunner<>( - Runners.P2P_NETWORK, - PeerPingTimeout.class, - peerLivenessMonitor.pingTimeoutEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner peerPingTimeoutEventProcessor( + PeerLivenessMonitor peerLivenessMonitor) { + return new EventProcessorOnRunner<>( + Runners.P2P_NETWORK, + PeerPingTimeout.class, + peerLivenessMonitor.pingTimeoutEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner peerPingRemoteEventProcessor( - PeerLivenessMonitor peerLivenessMonitor - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.P2P_NETWORK, - Ping.class, - peerLivenessMonitor.pingRemoteEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner peerPingRemoteEventProcessor( + PeerLivenessMonitor peerLivenessMonitor) { + return new RemoteEventProcessorOnRunner<>( + Runners.P2P_NETWORK, Ping.class, peerLivenessMonitor.pingRemoteEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner pongPingRemoteEventProcessor( - PeerLivenessMonitor peerLivenessMonitor - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.P2P_NETWORK, - Pong.class, - peerLivenessMonitor.pongRemoteEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner pongPingRemoteEventProcessor( + PeerLivenessMonitor peerLivenessMonitor) { + return new RemoteEventProcessorOnRunner<>( + Runners.P2P_NETWORK, Pong.class, peerLivenessMonitor.pongRemoteEventProcessor()); + } - @ProvidesIntoSet - public ScheduledEventProducerOnRunner peersLivenessCheckTriggerEventProducer( - EventDispatcher peersLivenessCheckTriggerEventDispatcher, - P2PConfig config - ) { - return new ScheduledEventProducerOnRunner<>( - Runners.P2P_NETWORK, - peersLivenessCheckTriggerEventDispatcher, - PeersLivenessCheckTrigger::create, - Duration.ofMillis(config.peerLivenessCheckInterval()), - Duration.ofMillis(config.peerLivenessCheckInterval()) - ); - } + @ProvidesIntoSet + public ScheduledEventProducerOnRunner peersLivenessCheckTriggerEventProducer( + EventDispatcher peersLivenessCheckTriggerEventDispatcher, + P2PConfig config) { + return new ScheduledEventProducerOnRunner<>( + Runners.P2P_NETWORK, + peersLivenessCheckTriggerEventDispatcher, + PeersLivenessCheckTrigger::create, + Duration.ofMillis(config.peerLivenessCheckInterval()), + Duration.ofMillis(config.peerLivenessCheckInterval())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManager.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManager.java index 3dc8622c09..ad35624587 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManager.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManager.java @@ -64,6 +64,10 @@ package com.radixdlt.network.p2p; +import static com.radixdlt.network.messaging.MessagingErrors.PEER_BANNED; +import static com.radixdlt.network.messaging.MessagingErrors.SELF_CONNECTION_ATTEMPT; +import static java.util.function.Predicate.not; + import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.inject.Inject; @@ -73,22 +77,19 @@ import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.environment.EventProcessor; import com.radixdlt.network.messaging.InboundMessage; -import com.radixdlt.network.p2p.addressbook.AddressBook; -import com.radixdlt.network.p2p.addressbook.AddressBookEntry; +import com.radixdlt.network.p2p.PeerEvent.PeerBanned; import com.radixdlt.network.p2p.PeerEvent.PeerConnected; -import com.radixdlt.network.p2p.PeerEvent.PeerDisconnected; -import com.radixdlt.network.p2p.PeerEvent.PeerLostLiveness; import com.radixdlt.network.p2p.PeerEvent.PeerConnectionTimeout; +import com.radixdlt.network.p2p.PeerEvent.PeerDisconnected; import com.radixdlt.network.p2p.PeerEvent.PeerHandshakeFailed; -import com.radixdlt.network.p2p.PeerEvent.PeerBanned; +import com.radixdlt.network.p2p.PeerEvent.PeerLostLiveness; +import com.radixdlt.network.p2p.addressbook.AddressBook; +import com.radixdlt.network.p2p.addressbook.AddressBookEntry; import com.radixdlt.network.p2p.transport.PeerChannel; import com.radixdlt.networks.Addressing; import com.radixdlt.utils.functional.Result; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.subjects.PublishSubject; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.Collection; import java.util.Comparator; import java.util.Map; @@ -97,273 +98,271 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import static com.radixdlt.network.messaging.MessagingErrors.PEER_BANNED; -import static com.radixdlt.network.messaging.MessagingErrors.SELF_CONNECTION_ATTEMPT; -import static java.util.function.Predicate.not; - -/** - * Manages active connections to other peers. - */ +/** Manages active connections to other peers. */ public final class PeerManager { - private static final Logger log = LogManager.getLogger(); - - private final NodeId self; - private final P2PConfig config; - private final Addressing addressing; - private final Provider addressBook; - private final Provider pendingOutboundChannelsManager; - private final SystemCounters counters; - - private final Object lock = new Object(); - private final Map> activeChannels = new ConcurrentHashMap<>(); - private final PublishSubject> inboundMessagesFromChannels = PublishSubject.create(); - - @Inject - public PeerManager( - @Self RadixNodeUri self, - P2PConfig config, - Addressing addressing, - Provider addressBook, - Provider pendingOutboundChannelsManager, - SystemCounters counters - ) { - this.self = Objects.requireNonNull(self.getNodeId()); - this.config = Objects.requireNonNull(config); - this.addressing = addressing; - this.addressBook = Objects.requireNonNull(addressBook); - this.pendingOutboundChannelsManager = Objects.requireNonNull(pendingOutboundChannelsManager); - this.counters = Objects.requireNonNull(counters); - - log.info("Node URI: {}", self); - } - - public Observable messages() { - return Observable.merge(inboundMessagesFromChannels); - } - - public CompletableFuture findOrCreateChannel(NodeId nodeId) { - synchronized (lock) { - final var checkResult = this.canConnectTo(nodeId); - return checkResult.fold( - error -> CompletableFuture.failedFuture(new RuntimeException(error.message())), - unused -> this.findOrCreateChannelInternal(nodeId) - ); - } - } - - private CompletableFuture findOrCreateChannelInternal(NodeId nodeId) { - final var maybeActiveChannel = channelFor(nodeId); - if (maybeActiveChannel.isPresent()) { - return CompletableFuture.completedFuture(maybeActiveChannel.get()); - } else { - final var maybeAddress = this.addressBook.get().findBestKnownAddressById(nodeId); - if (maybeAddress.isPresent()) { - return connect(maybeAddress.get()); - } else { - return CompletableFuture.failedFuture(new RuntimeException("Unknown peer " - + addressing.forNodes().of(nodeId.getPublicKey()))); - } - } - } - - /** - * Try connecting to a specific URI - */ - public void tryConnect(RadixNodeUri uri) { - synchronized (lock) { - if (!canConnectTo(uri.getNodeId()).isSuccess()) { - return; - } - - if (this.getRemainingOutboundSlots() <= 0) { - return; - } - - if (channelFor(uri.getNodeId()).isEmpty()) { - this.connect(uri); - } - } - } - - private Result canConnectTo(NodeId nodeId) { - if (nodeId.equals(self)) { - log.info("Ignoring self connection attempt"); - return SELF_CONNECTION_ATTEMPT.result(); - } - - if (this.addressBook.get().findById(nodeId).filter(AddressBookEntry::isBanned).isPresent()) { - return PEER_BANNED.result(); - } - - return Result.ok(new Object()); - } - - private Optional channelFor(NodeId nodeId) { - return Optional.ofNullable(this.activeChannels.get(nodeId)) - .stream().map(s -> s.iterator().next()) - .findAny(); - } - - private CompletableFuture connect(RadixNodeUri uri) { - synchronized (lock) { - return channelFor(uri.getNodeId()) - .map(CompletableFuture::completedFuture) // either return an existing channel - .orElseGet(() -> this.pendingOutboundChannelsManager.get().connectTo(uri)); // or try to create a new one - } - } - - public EventProcessor peerEventProcessor() { - return peerEvent -> { - if (peerEvent instanceof PeerConnected) { - this.handlePeerConnected((PeerConnected) peerEvent); - } else if (peerEvent instanceof PeerDisconnected) { - this.handlePeerDisconnected((PeerDisconnected) peerEvent); - } else if (peerEvent instanceof PeerLostLiveness) { - this.handlePeerLostLiveness((PeerLostLiveness) peerEvent); - } else if (peerEvent instanceof PeerBanned) { - this.handlePeerBanned((PeerBanned) peerEvent); - } else if (peerEvent instanceof PeerConnectionTimeout) { - this.handlePeerConnectionTimeout((PeerConnectionTimeout) peerEvent); - } else if (peerEvent instanceof PeerHandshakeFailed) { - this.handlePeerHandshakeFailed((PeerHandshakeFailed) peerEvent); - } - }; - } - - private void handlePeerConnected(PeerConnected peerConnected) { - synchronized (lock) { - final var channel = peerConnected.getChannel(); - final var channels = this.activeChannels.computeIfAbsent( - channel.getRemoteNodeId(), - unused -> Sets.newConcurrentHashSet() - ); - channels.add(channel); - channel.getUri().ifPresent(this.addressBook.get()::addOrUpdatePeerWithSuccessfulConnection); - inboundMessagesFromChannels.onNext(channel.inboundMessages().toObservable()); - - if (channel.isInbound() && !this.shouldAcceptInboundPeer(channel.getRemoteNodeId())) { - channel.disconnect(); - } - - if (channel.isOutbound() && this.getRemainingOutboundSlots() < 0) { - // we're over the limit, need to disconnect one of the peers - this.disconnectOutboundPeersOverLimit(peerConnected.getChannel().getRemoteNodeId()); - } - - updateChannelsCounters(); - } - } - - private void disconnectOutboundPeersOverLimit(NodeId justConnectedPeer) { - // TODO(luk): first try to disconnect duplicated channels (inbound) - - if (this.getRemainingOutboundSlots() >= 0) { - return; // we're good - } - - final var peersOverLimit = -this.getRemainingOutboundSlots(); - - final Comparator comparator = - (p1, p2) -> (int) (p1.sentMessagesRate() - p2.sentMessagesRate()); - - this.activeChannels().stream() - // not disconnecting peer that has just connected - .filter(not(p -> p.getRemoteNodeId().equals(justConnectedPeer))) - .sorted(comparator) - .limit(peersOverLimit) - .forEach(PeerChannel::disconnect); - } - - private boolean shouldAcceptInboundPeer(NodeId nodeId) { - final var isBanned = this.addressBook.get().findById(nodeId) - .map(AddressBookEntry::isBanned) - .orElse(false); - - if (isBanned) { - log.info( - "Dropping inbound connection from peer {}: peer is banned", - addressing.forNodes().of(nodeId.getPublicKey()) - ); - } - - final var limitReached = this.activeChannels.size() > config.maxInboundChannels(); - if (limitReached) { - log.info( - "Dropping inbound connection from peer {}: no more inbound channels allowed", - addressing.forNodes().of(nodeId.getPublicKey()) - ); - } - - return !isBanned && !limitReached; - } - - private void handlePeerDisconnected(PeerDisconnected peerDisconnected) { - synchronized (lock) { - final var channel = peerDisconnected.getChannel(); - final var channelsForPubKey = this.activeChannels.get(channel.getRemoteNodeId()); - if (channelsForPubKey != null) { - channelsForPubKey.remove(channel); - if (channelsForPubKey.isEmpty()) { - this.activeChannels.remove(channel.getRemoteNodeId()); - updateChannelsCounters(); - } - } - } - } - - private void handlePeerLostLiveness(PeerLostLiveness peerLostLiveness) { - synchronized (lock) { - log.info( - "Peer {} lost liveness (ping timeout)", - addressing.forNodes().of(peerLostLiveness.getNodeId().getPublicKey()) - ); - channelFor(peerLostLiveness.getNodeId()).ifPresent(PeerChannel::disconnect); - } - } - - public ImmutableSet activeChannels() { - return this.activeChannels.values().stream() - .flatMap(Collection::stream) - .collect(ImmutableSet.toImmutableSet()); - } - - public boolean isPeerConnected(NodeId nodeId) { - return this.activeChannels.containsKey(nodeId); - } - - public int getRemainingOutboundSlots() { - final var numChannels = this.activeChannels.values().stream() - .flatMap(Set::stream) - .filter(not(PeerChannel::isInbound)) - .count(); - - return (int) (config.maxOutboundChannels() - numChannels); - } - - private void handlePeerBanned(PeerBanned event) { - this.activeChannels().stream() - .filter(p -> p.getRemoteNodeId().equals(event.getNodeId())) - .forEach(pc -> { - log.info( - "Closing channel to peer {} because peer has been banned", - addressing.forNodes().of(pc.getRemoteNodeId().getPublicKey()) - ); - pc.disconnect(); - }); - } - - private void handlePeerConnectionTimeout(PeerConnectionTimeout peerConnectionTimeout) { - this.addressBook.get().addOrUpdatePeerWithFailedConnection(peerConnectionTimeout.getUri()); - } - - private void handlePeerHandshakeFailed(PeerHandshakeFailed peerHandshakeFailed) { - peerHandshakeFailed.getChannel().getUri().ifPresent(this.addressBook.get()::blacklist); - } - - private void updateChannelsCounters() { - counters.set(CounterType.NETWORKING_P2P_ACTIVE_CHANNELS, activeChannels().size()); - counters.set(CounterType.NETWORKING_P2P_ACTIVE_INBOUND_CHANNELS, activeChannels().stream().filter(PeerChannel::isInbound).count()); - counters.set(CounterType.NETWORKING_P2P_ACTIVE_OUTBOUND_CHANNELS, activeChannels().stream().filter(PeerChannel::isOutbound).count()); - } + private static final Logger log = LogManager.getLogger(); + + private final NodeId self; + private final P2PConfig config; + private final Addressing addressing; + private final Provider addressBook; + private final Provider pendingOutboundChannelsManager; + private final SystemCounters counters; + + private final Object lock = new Object(); + private final Map> activeChannels = new ConcurrentHashMap<>(); + private final PublishSubject> inboundMessagesFromChannels = + PublishSubject.create(); + + @Inject + public PeerManager( + @Self RadixNodeUri self, + P2PConfig config, + Addressing addressing, + Provider addressBook, + Provider pendingOutboundChannelsManager, + SystemCounters counters) { + this.self = Objects.requireNonNull(self.getNodeId()); + this.config = Objects.requireNonNull(config); + this.addressing = addressing; + this.addressBook = Objects.requireNonNull(addressBook); + this.pendingOutboundChannelsManager = Objects.requireNonNull(pendingOutboundChannelsManager); + this.counters = Objects.requireNonNull(counters); + + log.info("Node URI: {}", self); + } + + public Observable messages() { + return Observable.merge(inboundMessagesFromChannels); + } + + public CompletableFuture findOrCreateChannel(NodeId nodeId) { + synchronized (lock) { + final var checkResult = this.canConnectTo(nodeId); + return checkResult.fold( + error -> CompletableFuture.failedFuture(new RuntimeException(error.message())), + unused -> this.findOrCreateChannelInternal(nodeId)); + } + } + + private CompletableFuture findOrCreateChannelInternal(NodeId nodeId) { + final var maybeActiveChannel = channelFor(nodeId); + if (maybeActiveChannel.isPresent()) { + return CompletableFuture.completedFuture(maybeActiveChannel.get()); + } else { + final var maybeAddress = this.addressBook.get().findBestKnownAddressById(nodeId); + if (maybeAddress.isPresent()) { + return connect(maybeAddress.get()); + } else { + return CompletableFuture.failedFuture( + new RuntimeException( + "Unknown peer " + addressing.forNodes().of(nodeId.getPublicKey()))); + } + } + } + + /** Try connecting to a specific URI */ + public void tryConnect(RadixNodeUri uri) { + synchronized (lock) { + if (!canConnectTo(uri.getNodeId()).isSuccess()) { + return; + } + + if (this.getRemainingOutboundSlots() <= 0) { + return; + } + + if (channelFor(uri.getNodeId()).isEmpty()) { + this.connect(uri); + } + } + } + + private Result canConnectTo(NodeId nodeId) { + if (nodeId.equals(self)) { + log.info("Ignoring self connection attempt"); + return SELF_CONNECTION_ATTEMPT.result(); + } + + if (this.addressBook.get().findById(nodeId).filter(AddressBookEntry::isBanned).isPresent()) { + return PEER_BANNED.result(); + } + + return Result.ok(new Object()); + } + + private Optional channelFor(NodeId nodeId) { + return Optional.ofNullable(this.activeChannels.get(nodeId)).stream() + .map(s -> s.iterator().next()) + .findAny(); + } + + private CompletableFuture connect(RadixNodeUri uri) { + synchronized (lock) { + return channelFor(uri.getNodeId()) + .map(CompletableFuture::completedFuture) // either return an existing channel + .orElseGet( + () -> + this.pendingOutboundChannelsManager + .get() + .connectTo(uri)); // or try to create a new one + } + } + + public EventProcessor peerEventProcessor() { + return peerEvent -> { + if (peerEvent instanceof PeerConnected) { + this.handlePeerConnected((PeerConnected) peerEvent); + } else if (peerEvent instanceof PeerDisconnected) { + this.handlePeerDisconnected((PeerDisconnected) peerEvent); + } else if (peerEvent instanceof PeerLostLiveness) { + this.handlePeerLostLiveness((PeerLostLiveness) peerEvent); + } else if (peerEvent instanceof PeerBanned) { + this.handlePeerBanned((PeerBanned) peerEvent); + } else if (peerEvent instanceof PeerConnectionTimeout) { + this.handlePeerConnectionTimeout((PeerConnectionTimeout) peerEvent); + } else if (peerEvent instanceof PeerHandshakeFailed) { + this.handlePeerHandshakeFailed((PeerHandshakeFailed) peerEvent); + } + }; + } + + private void handlePeerConnected(PeerConnected peerConnected) { + synchronized (lock) { + final var channel = peerConnected.getChannel(); + final var channels = + this.activeChannels.computeIfAbsent( + channel.getRemoteNodeId(), unused -> Sets.newConcurrentHashSet()); + channels.add(channel); + channel.getUri().ifPresent(this.addressBook.get()::addOrUpdatePeerWithSuccessfulConnection); + inboundMessagesFromChannels.onNext(channel.inboundMessages().toObservable()); + + if (channel.isInbound() && !this.shouldAcceptInboundPeer(channel.getRemoteNodeId())) { + channel.disconnect(); + } + + if (channel.isOutbound() && this.getRemainingOutboundSlots() < 0) { + // we're over the limit, need to disconnect one of the peers + this.disconnectOutboundPeersOverLimit(peerConnected.getChannel().getRemoteNodeId()); + } + + updateChannelsCounters(); + } + } + + private void disconnectOutboundPeersOverLimit(NodeId justConnectedPeer) { + // TODO(luk): first try to disconnect duplicated channels (inbound) + + if (this.getRemainingOutboundSlots() >= 0) { + return; // we're good + } + + final var peersOverLimit = -this.getRemainingOutboundSlots(); + + final Comparator comparator = + (p1, p2) -> (int) (p1.sentMessagesRate() - p2.sentMessagesRate()); + + this.activeChannels().stream() + // not disconnecting peer that has just connected + .filter(not(p -> p.getRemoteNodeId().equals(justConnectedPeer))) + .sorted(comparator) + .limit(peersOverLimit) + .forEach(PeerChannel::disconnect); + } + + private boolean shouldAcceptInboundPeer(NodeId nodeId) { + final var isBanned = + this.addressBook.get().findById(nodeId).map(AddressBookEntry::isBanned).orElse(false); + + if (isBanned) { + log.info( + "Dropping inbound connection from peer {}: peer is banned", + addressing.forNodes().of(nodeId.getPublicKey())); + } + + final var limitReached = this.activeChannels.size() > config.maxInboundChannels(); + if (limitReached) { + log.info( + "Dropping inbound connection from peer {}: no more inbound channels allowed", + addressing.forNodes().of(nodeId.getPublicKey())); + } + + return !isBanned && !limitReached; + } + + private void handlePeerDisconnected(PeerDisconnected peerDisconnected) { + synchronized (lock) { + final var channel = peerDisconnected.getChannel(); + final var channelsForPubKey = this.activeChannels.get(channel.getRemoteNodeId()); + if (channelsForPubKey != null) { + channelsForPubKey.remove(channel); + if (channelsForPubKey.isEmpty()) { + this.activeChannels.remove(channel.getRemoteNodeId()); + updateChannelsCounters(); + } + } + } + } + + private void handlePeerLostLiveness(PeerLostLiveness peerLostLiveness) { + synchronized (lock) { + log.info( + "Peer {} lost liveness (ping timeout)", + addressing.forNodes().of(peerLostLiveness.getNodeId().getPublicKey())); + channelFor(peerLostLiveness.getNodeId()).ifPresent(PeerChannel::disconnect); + } + } + + public ImmutableSet activeChannels() { + return this.activeChannels.values().stream() + .flatMap(Collection::stream) + .collect(ImmutableSet.toImmutableSet()); + } + + public boolean isPeerConnected(NodeId nodeId) { + return this.activeChannels.containsKey(nodeId); + } + + public int getRemainingOutboundSlots() { + final var numChannels = + this.activeChannels.values().stream() + .flatMap(Set::stream) + .filter(not(PeerChannel::isInbound)) + .count(); + + return (int) (config.maxOutboundChannels() - numChannels); + } + + private void handlePeerBanned(PeerBanned event) { + this.activeChannels().stream() + .filter(p -> p.getRemoteNodeId().equals(event.getNodeId())) + .forEach( + pc -> { + log.info( + "Closing channel to peer {} because peer has been banned", + addressing.forNodes().of(pc.getRemoteNodeId().getPublicKey())); + pc.disconnect(); + }); + } + + private void handlePeerConnectionTimeout(PeerConnectionTimeout peerConnectionTimeout) { + this.addressBook.get().addOrUpdatePeerWithFailedConnection(peerConnectionTimeout.getUri()); + } + + private void handlePeerHandshakeFailed(PeerHandshakeFailed peerHandshakeFailed) { + peerHandshakeFailed.getChannel().getUri().ifPresent(this.addressBook.get()::blacklist); + } + + private void updateChannelsCounters() { + counters.set(CounterType.NETWORKING_P2P_ACTIVE_CHANNELS, activeChannels().size()); + counters.set( + CounterType.NETWORKING_P2P_ACTIVE_INBOUND_CHANNELS, + activeChannels().stream().filter(PeerChannel::isInbound).count()); + counters.set( + CounterType.NETWORKING_P2P_ACTIVE_OUTBOUND_CHANNELS, + activeChannels().stream().filter(PeerChannel::isOutbound).count()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManagerPeersView.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManagerPeersView.java index d0a1e17b51..1581365ce2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManagerPeersView.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeerManagerPeersView.java @@ -64,37 +64,39 @@ package com.radixdlt.network.p2p; +import static java.util.stream.Collectors.groupingBy; + import com.google.common.collect.ImmutableList; import com.radixdlt.network.p2p.transport.PeerChannel; - -import javax.inject.Inject; import java.util.stream.Stream; +import javax.inject.Inject; -import static java.util.stream.Collectors.groupingBy; - -/** - * A Peers view using PeersManager - */ +/** A Peers view using PeersManager */ public final class PeerManagerPeersView implements PeersView { - private final PeerManager peerManager; + private final PeerManager peerManager; - @Inject - public PeerManagerPeersView(PeerManager peerManager) { - this.peerManager = peerManager; - } + @Inject + public PeerManagerPeersView(PeerManager peerManager) { + this.peerManager = peerManager; + } - @Override - public Stream peers() { - final var grouppedByNodeId = this.peerManager.activeChannels() - .stream() - .collect(groupingBy(PeerChannel::getRemoteNodeId)); + @Override + public Stream peers() { + final var grouppedByNodeId = + this.peerManager.activeChannels().stream() + .collect(groupingBy(PeerChannel::getRemoteNodeId)); - return grouppedByNodeId.entrySet().stream() - .map(e -> { - final var channelsInfo = e.getValue().stream() - .map(c -> PeerChannelInfo.create(c.getUri(), c.getHost(), c.getPort(), c.isOutbound())) - .collect(ImmutableList.toImmutableList()); - return PeerInfo.create(e.getKey(), channelsInfo); - }); - } + return grouppedByNodeId.entrySet().stream() + .map( + e -> { + final var channelsInfo = + e.getValue().stream() + .map( + c -> + PeerChannelInfo.create( + c.getUri(), c.getHost(), c.getPort(), c.isOutbound())) + .collect(ImmutableList.toImmutableList()); + return PeerInfo.create(e.getKey(), channelsInfo); + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeersView.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeersView.java index 10982cf501..e157c08f53 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeersView.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PeersView.java @@ -66,127 +66,123 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.bft.BFTNode; - import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -/** - * Retrieve the node's current peers - */ +/** Retrieve the node's current peers */ public interface PeersView { - final class PeerChannelInfo { - private Optional uri; - private String host; - private int port; - private boolean isOutbound; - - public static PeerChannelInfo create(Optional uri, String host, int port, boolean isOutbound) { - return new PeerChannelInfo(uri, host, port, isOutbound); - } - - private PeerChannelInfo(Optional uri, String host, int port, boolean isOutbound) { - this.uri = uri; - this.host = host; - this.port = port; - this.isOutbound = isOutbound; - } - - public Optional getUri() { - return uri; - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - public boolean isOutbound() { - return isOutbound; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var other = (PeerChannelInfo) o; - return Objects.equals(uri, other.uri) - && Objects.equals(host, other.host) - && port == other.port - && Objects.equals(isOutbound, other.isOutbound); - } - - @Override - public int hashCode() { - return Objects.hash(uri, host, port, isOutbound); - } - } - - final class PeerInfo { - private NodeId nodeId; - private ImmutableList channels; - - public static PeerInfo fromBftNode(BFTNode bftNode) { - return new PeerInfo(NodeId.fromPublicKey(bftNode.getKey()), ImmutableList.of()); - } - - public static PeerInfo create(NodeId nodeId, ImmutableList channels) { - return new PeerInfo(nodeId, channels); - } - - private PeerInfo(NodeId nodeId, ImmutableList channels) { - this.nodeId = nodeId; - this.channels = channels; - } - - public NodeId getNodeId() { - return nodeId; - } - - public ImmutableList getChannels() { - return channels; - } - - public BFTNode bftNode() { - return BFTNode.create(nodeId.getPublicKey()); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var other = (PeerInfo) o; - return Objects.equals(nodeId, other.nodeId) - && Objects.equals(channels, other.channels); - } - - @Override - public int hashCode() { - return Objects.hash(nodeId, channels); - } - - @Override - public String toString() { - return String.format("%s{%s}", this.getClass().getSimpleName(), this.nodeId); - } - } - - Stream peers(); - - default boolean hasPeer(BFTNode bftNode) { - return peers() - .anyMatch(peer -> peer.nodeId.getPublicKey().equals(bftNode.getKey())); - } + final class PeerChannelInfo { + private Optional uri; + private String host; + private int port; + private boolean isOutbound; + + public static PeerChannelInfo create( + Optional uri, String host, int port, boolean isOutbound) { + return new PeerChannelInfo(uri, host, port, isOutbound); + } + + private PeerChannelInfo(Optional uri, String host, int port, boolean isOutbound) { + this.uri = uri; + this.host = host; + this.port = port; + this.isOutbound = isOutbound; + } + + public Optional getUri() { + return uri; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public boolean isOutbound() { + return isOutbound; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var other = (PeerChannelInfo) o; + return Objects.equals(uri, other.uri) + && Objects.equals(host, other.host) + && port == other.port + && Objects.equals(isOutbound, other.isOutbound); + } + + @Override + public int hashCode() { + return Objects.hash(uri, host, port, isOutbound); + } + } + + final class PeerInfo { + private NodeId nodeId; + private ImmutableList channels; + + public static PeerInfo fromBftNode(BFTNode bftNode) { + return new PeerInfo(NodeId.fromPublicKey(bftNode.getKey()), ImmutableList.of()); + } + + public static PeerInfo create(NodeId nodeId, ImmutableList channels) { + return new PeerInfo(nodeId, channels); + } + + private PeerInfo(NodeId nodeId, ImmutableList channels) { + this.nodeId = nodeId; + this.channels = channels; + } + + public NodeId getNodeId() { + return nodeId; + } + + public ImmutableList getChannels() { + return channels; + } + + public BFTNode bftNode() { + return BFTNode.create(nodeId.getPublicKey()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var other = (PeerInfo) o; + return Objects.equals(nodeId, other.nodeId) && Objects.equals(channels, other.channels); + } + + @Override + public int hashCode() { + return Objects.hash(nodeId, channels); + } + + @Override + public String toString() { + return String.format("%s{%s}", this.getClass().getSimpleName(), this.nodeId); + } + } + + Stream peers(); + + default boolean hasPeer(BFTNode bftNode) { + return peers().anyMatch(peer -> peer.nodeId.getPublicKey().equals(bftNode.getKey())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PendingOutboundChannelsManager.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PendingOutboundChannelsManager.java index 2f60f9c4e6..71510a5b10 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PendingOutboundChannelsManager.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/PendingOutboundChannelsManager.java @@ -69,121 +69,125 @@ import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.network.p2p.transport.PeerChannel; import com.radixdlt.network.p2p.transport.PeerOutboundBootstrap; - -import javax.inject.Inject; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import javax.inject.Inject; public final class PendingOutboundChannelsManager { - private final P2PConfig config; - private final PeerOutboundBootstrap peerOutboundBootstrap; - private final ScheduledEventDispatcher timeoutEventDispatcher; - private final EventDispatcher peerEventDispatcher; - - private final Object lock = new Object(); - private Map> pendingChannels = new HashMap<>(); - - @Inject - public PendingOutboundChannelsManager( - P2PConfig config, - PeerOutboundBootstrap peerOutboundBootstrap, - ScheduledEventDispatcher timeoutEventDispatcher, - EventDispatcher peerEventDispatcher - ) { - this.config = Objects.requireNonNull(config); - this.peerOutboundBootstrap = Objects.requireNonNull(peerOutboundBootstrap); - this.timeoutEventDispatcher = Objects.requireNonNull(timeoutEventDispatcher); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - } - - public CompletableFuture connectTo(RadixNodeUri uri) { - synchronized (lock) { - final var remoteNodeId = uri.getNodeId(); - - if (this.pendingChannels.containsKey(remoteNodeId)) { - return this.pendingChannels.get(remoteNodeId); - } else { - final var channelFuture = new CompletableFuture(); - this.pendingChannels.put(remoteNodeId, channelFuture); - this.peerOutboundBootstrap.initOutboundConnection(uri); - this.timeoutEventDispatcher.dispatch(new PeerOutboundConnectionTimeout(uri), config.peerConnectionTimeout()); - return channelFuture; - } - } - } - - public EventProcessor peerEventProcessor() { - return peerEvent -> { - if (peerEvent instanceof PeerEvent.PeerConnected) { - this.handlePeerConnected((PeerEvent.PeerConnected) peerEvent); - } else if (peerEvent instanceof PeerEvent.PeerHandshakeFailed) { - this.handlePeerHandshakeFailed((PeerEvent.PeerHandshakeFailed) peerEvent); - } - }; - } - - private void handlePeerConnected(PeerEvent.PeerConnected peerConnected) { - synchronized (lock) { - final var channel = peerConnected.getChannel(); - final var maybeFuture = this.pendingChannels.remove(channel.getRemoteNodeId()); - if (maybeFuture != null) { - maybeFuture.complete(channel); - } - } - } - - private void handlePeerHandshakeFailed(PeerEvent.PeerHandshakeFailed peerHandshakeFailed) { - synchronized (lock) { - peerHandshakeFailed.getChannel().getUri().ifPresent(uri -> { - final var maybeFuture = this.pendingChannels.remove(uri.getNodeId()); - if (maybeFuture != null) { - maybeFuture.completeExceptionally(new IOException("Peer connection failed")); - } - }); - } - } - - public EventProcessor peerOutboundConnectionTimeoutEventProcessor() { - return timeout -> { - synchronized (lock) { - final var maybeFuture = this.pendingChannels.remove(timeout.getUri().getNodeId()); - if (maybeFuture != null) { - maybeFuture.completeExceptionally(new IOException("Peer connection timeout")); - peerEventDispatcher.dispatch(PeerEvent.PeerConnectionTimeout.create(timeout.getUri())); - } - } - }; - } - - public static final class PeerOutboundConnectionTimeout { - private final RadixNodeUri uri; - - public PeerOutboundConnectionTimeout(RadixNodeUri uri) { - this.uri = uri; - } - - public RadixNodeUri getUri() { - return uri; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (PeerOutboundConnectionTimeout) o; - return Objects.equals(uri, that.uri); - } - - @Override - public int hashCode() { - return Objects.hash(uri); - } - } + private final P2PConfig config; + private final PeerOutboundBootstrap peerOutboundBootstrap; + private final ScheduledEventDispatcher timeoutEventDispatcher; + private final EventDispatcher peerEventDispatcher; + + private final Object lock = new Object(); + private Map> pendingChannels = new HashMap<>(); + + @Inject + public PendingOutboundChannelsManager( + P2PConfig config, + PeerOutboundBootstrap peerOutboundBootstrap, + ScheduledEventDispatcher timeoutEventDispatcher, + EventDispatcher peerEventDispatcher) { + this.config = Objects.requireNonNull(config); + this.peerOutboundBootstrap = Objects.requireNonNull(peerOutboundBootstrap); + this.timeoutEventDispatcher = Objects.requireNonNull(timeoutEventDispatcher); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + } + + public CompletableFuture connectTo(RadixNodeUri uri) { + synchronized (lock) { + final var remoteNodeId = uri.getNodeId(); + + if (this.pendingChannels.containsKey(remoteNodeId)) { + return this.pendingChannels.get(remoteNodeId); + } else { + final var channelFuture = new CompletableFuture(); + this.pendingChannels.put(remoteNodeId, channelFuture); + this.peerOutboundBootstrap.initOutboundConnection(uri); + this.timeoutEventDispatcher.dispatch( + new PeerOutboundConnectionTimeout(uri), config.peerConnectionTimeout()); + return channelFuture; + } + } + } + + public EventProcessor peerEventProcessor() { + return peerEvent -> { + if (peerEvent instanceof PeerEvent.PeerConnected) { + this.handlePeerConnected((PeerEvent.PeerConnected) peerEvent); + } else if (peerEvent instanceof PeerEvent.PeerHandshakeFailed) { + this.handlePeerHandshakeFailed((PeerEvent.PeerHandshakeFailed) peerEvent); + } + }; + } + + private void handlePeerConnected(PeerEvent.PeerConnected peerConnected) { + synchronized (lock) { + final var channel = peerConnected.getChannel(); + final var maybeFuture = this.pendingChannels.remove(channel.getRemoteNodeId()); + if (maybeFuture != null) { + maybeFuture.complete(channel); + } + } + } + + private void handlePeerHandshakeFailed(PeerEvent.PeerHandshakeFailed peerHandshakeFailed) { + synchronized (lock) { + peerHandshakeFailed + .getChannel() + .getUri() + .ifPresent( + uri -> { + final var maybeFuture = this.pendingChannels.remove(uri.getNodeId()); + if (maybeFuture != null) { + maybeFuture.completeExceptionally(new IOException("Peer connection failed")); + } + }); + } + } + + public EventProcessor + peerOutboundConnectionTimeoutEventProcessor() { + return timeout -> { + synchronized (lock) { + final var maybeFuture = this.pendingChannels.remove(timeout.getUri().getNodeId()); + if (maybeFuture != null) { + maybeFuture.completeExceptionally(new IOException("Peer connection timeout")); + peerEventDispatcher.dispatch(PeerEvent.PeerConnectionTimeout.create(timeout.getUri())); + } + } + }; + } + + public static final class PeerOutboundConnectionTimeout { + private final RadixNodeUri uri; + + public PeerOutboundConnectionTimeout(RadixNodeUri uri) { + this.uri = uri; + } + + public RadixNodeUri getUri() { + return uri; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (PeerOutboundConnectionTimeout) o; + return Objects.equals(uri, that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(uri); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/RadixNodeUri.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/RadixNodeUri.java index 488296d710..59df899380 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/RadixNodeUri.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/RadixNodeUri.java @@ -64,102 +64,107 @@ package com.radixdlt.network.p2p; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.NodeAddressing; import com.radixdlt.networks.Addressing; import com.radixdlt.serialization.DeserializeException; - import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class RadixNodeUri { - private final String host; - private final int port; - private final String networkNodeHrp; - private final NodeId nodeId; - - @JsonCreator - public static RadixNodeUri deserialize(byte[] uri) throws URISyntaxException, DeserializeException { - return fromUri(new URI(new String(requireNonNull(uri)))); - } - - public static RadixNodeUri fromPubKeyAndAddress(int networkId, ECPublicKey publicKey, String host, int port) { - var hrp = Addressing.ofNetworkId(networkId).forNodes().getHrp(); - return new RadixNodeUri(host, port, hrp, NodeId.fromPublicKey(publicKey)); - } - - public static RadixNodeUri fromUri(URI uri) throws DeserializeException { - var hrpAndKey = NodeAddressing.parseUnknownHrp(uri.getUserInfo()); - return new RadixNodeUri(uri.getHost(), uri.getPort(), hrpAndKey.getFirst(), NodeId.fromPublicKey(hrpAndKey.getSecond())); - } - - private RadixNodeUri(String host, int port, String networkNodeHrp, NodeId nodeId) { - if (port <= 0) { - throw new RuntimeException("Port must be a positive integer"); - } - this.host = requireNonNull(host); - this.port = port; - this.networkNodeHrp = networkNodeHrp; - this.nodeId = requireNonNull(nodeId); - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - public NodeId getNodeId() { - return nodeId; - } - - @JsonValue - private byte[] getSerializedValue() { - return getUriString().getBytes(StandardCharsets.UTF_8); - } - - private String getUriString() { - return String.format("radix://%s@%s:%s", nodeAddress(), host, port); - } - - public String nodeAddress() { - return NodeAddressing.of(networkNodeHrp, nodeId.getPublicKey()); - } - - public String getNetworkNodeHrp() { - return this.networkNodeHrp; - } - - @Override - public String toString() { - return getUriString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (RadixNodeUri) o; - return port == that.port - && Objects.equals(host, that.host) - && Objects.equals(nodeId, that.nodeId) - && Objects.equals(networkNodeHrp, that.networkNodeHrp); - } - - @Override - public int hashCode() { - return Objects.hash(host, port, nodeId, networkNodeHrp); - } + private final String host; + private final int port; + private final String networkNodeHrp; + private final NodeId nodeId; + + @JsonCreator + public static RadixNodeUri deserialize(byte[] uri) + throws URISyntaxException, DeserializeException { + return fromUri(new URI(new String(requireNonNull(uri)))); + } + + public static RadixNodeUri fromPubKeyAndAddress( + int networkId, ECPublicKey publicKey, String host, int port) { + var hrp = Addressing.ofNetworkId(networkId).forNodes().getHrp(); + return new RadixNodeUri(host, port, hrp, NodeId.fromPublicKey(publicKey)); + } + + public static RadixNodeUri fromUri(URI uri) throws DeserializeException { + var hrpAndKey = NodeAddressing.parseUnknownHrp(uri.getUserInfo()); + return new RadixNodeUri( + uri.getHost(), + uri.getPort(), + hrpAndKey.getFirst(), + NodeId.fromPublicKey(hrpAndKey.getSecond())); + } + + private RadixNodeUri(String host, int port, String networkNodeHrp, NodeId nodeId) { + if (port <= 0) { + throw new RuntimeException("Port must be a positive integer"); + } + this.host = requireNonNull(host); + this.port = port; + this.networkNodeHrp = networkNodeHrp; + this.nodeId = requireNonNull(nodeId); + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public NodeId getNodeId() { + return nodeId; + } + + @JsonValue + private byte[] getSerializedValue() { + return getUriString().getBytes(StandardCharsets.UTF_8); + } + + private String getUriString() { + return String.format("radix://%s@%s:%s", nodeAddress(), host, port); + } + + public String nodeAddress() { + return NodeAddressing.of(networkNodeHrp, nodeId.getPublicKey()); + } + + public String getNetworkNodeHrp() { + return this.networkNodeHrp; + } + + @Override + public String toString() { + return getUriString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (RadixNodeUri) o; + return port == that.port + && Objects.equals(host, that.host) + && Objects.equals(nodeId, that.nodeId) + && Objects.equals(networkNodeHrp, that.networkNodeHrp); + } + + @Override + public int hashCode() { + return Objects.hash(host, port, nodeId, networkNodeHrp); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBook.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBook.java index ebec12b7cb..907abfb1e9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBook.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBook.java @@ -64,6 +64,8 @@ package com.radixdlt.network.p2p.addressbook; +import static java.util.function.Predicate.not; + import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; @@ -75,7 +77,6 @@ import com.radixdlt.network.p2p.RadixNodeUri; import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry; import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry.LatestConnectionStatus; - import java.time.Duration; import java.time.Instant; import java.util.Comparator; @@ -87,240 +88,243 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import static java.util.function.Predicate.not; - -/** - * Manages known peers network addresses and their metadata. - */ +/** Manages known peers network addresses and their metadata. */ public final class AddressBook { - /** - * A stateful comparator for known peer addresses that uses both their latest connection status (persistent) - * and a number of failed connection attempts (volatile). Failure counts are used to cycle the addresses for retries. - * The entries are sorted in the following manner: - * successful -> unknown (no connection attempted yet) -> failed (by num of failures) - */ - private static final class PeerAddressEntryComparator implements Comparator { - private final Map failureCounts = new HashMap<>(); - - private int toIntValue(PeerAddressEntry peerAddressEntry) { - return peerAddressEntry.getLatestConnectionStatus().map(latestConnectionStatus -> { - if (latestConnectionStatus == LatestConnectionStatus.SUCCESS) { - return 1; - } else { - return -(1 + failureCounts.getOrDefault(peerAddressEntry.getUri(), 0)); - } - }).orElse(0); - } + /** + * A stateful comparator for known peer addresses that uses both their latest connection status + * (persistent) and a number of failed connection attempts (volatile). Failure counts are used to + * cycle the addresses for retries. The entries are sorted in the following manner: successful -> + * unknown (no connection attempted yet) -> failed (by num of failures) + */ + private static final class PeerAddressEntryComparator implements Comparator { + private final Map failureCounts = new HashMap<>(); - @Override - public int compare(PeerAddressEntry a, PeerAddressEntry b) { - return Integer.compare(toIntValue(b), toIntValue(a)); - } + private int toIntValue(PeerAddressEntry peerAddressEntry) { + return peerAddressEntry + .getLatestConnectionStatus() + .map( + latestConnectionStatus -> { + if (latestConnectionStatus == LatestConnectionStatus.SUCCESS) { + return 1; + } else { + return -(1 + failureCounts.getOrDefault(peerAddressEntry.getUri(), 0)); + } + }) + .orElse(0); + } - void incFailures(RadixNodeUri uri) { - synchronized (this.failureCounts) { - final var curr = this.failureCounts.getOrDefault(uri, 0); - this.failureCounts.put(uri, curr + 1); - } - } + @Override + public int compare(PeerAddressEntry a, PeerAddressEntry b) { + return Integer.compare(toIntValue(b), toIntValue(a)); + } - void resetFailures(RadixNodeUri uri) { - synchronized (this.failureCounts) { - this.failureCounts.remove(uri); - } - } - } + void incFailures(RadixNodeUri uri) { + synchronized (this.failureCounts) { + final var curr = this.failureCounts.getOrDefault(uri, 0); + this.failureCounts.put(uri, curr + 1); + } + } - private final RadixNodeUri self; - private final EventDispatcher peerEventDispatcher; - private final AddressBookPersistence persistence; - private final Object lock = new Object(); - private final Map knownPeers = new ConcurrentHashMap<>(); - private final PeerAddressEntryComparator addressEntryComparator = new PeerAddressEntryComparator(); + void resetFailures(RadixNodeUri uri) { + synchronized (this.failureCounts) { + this.failureCounts.remove(uri); + } + } + } - @Inject - public AddressBook( - @Self RadixNodeUri self, - EventDispatcher peerEventDispatcher, - AddressBookPersistence persistence - ) { - this.self = Objects.requireNonNull(self); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - this.persistence = Objects.requireNonNull(persistence); - persistence.getAllEntries().forEach(e -> knownPeers.put(e.getNodeId(), e)); - cleanup(); - } + private final RadixNodeUri self; + private final EventDispatcher peerEventDispatcher; + private final AddressBookPersistence persistence; + private final Object lock = new Object(); + private final Map knownPeers = new ConcurrentHashMap<>(); + private final PeerAddressEntryComparator addressEntryComparator = + new PeerAddressEntryComparator(); - // filters out the addresses with a different network ID that have been persisted before the filtering was added - private void cleanup() { - final var cleanedUpEntries = new ImmutableMap.Builder(); - this.knownPeers.values().forEach(entry -> { - final var filteredKnownAddresses = entry.getKnownAddresses().stream() - .filter(addr -> sameNetworkHrp(addr.getUri())) - .collect(ImmutableSet.toImmutableSet()); + @Inject + public AddressBook( + @Self RadixNodeUri self, + EventDispatcher peerEventDispatcher, + AddressBookPersistence persistence) { + this.self = Objects.requireNonNull(self); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + this.persistence = Objects.requireNonNull(persistence); + persistence.getAllEntries().forEach(e -> knownPeers.put(e.getNodeId(), e)); + cleanup(); + } - if (filteredKnownAddresses.isEmpty() && !entry.isBanned()) { - // there are no known addresses and no ban info for peer so just remove it - this.persistence.removeEntry(entry.getNodeId()); - } else if (filteredKnownAddresses.size() != entry.getKnownAddresses().size()) { - // some addresses got filtered out, need to persist a new entry - final var updatedEntry = new AddressBookEntry(entry.getNodeId(), entry.bannedUntil(), filteredKnownAddresses); - cleanedUpEntries.put(entry.getNodeId(), updatedEntry); - persistEntry(updatedEntry); - } else { - cleanedUpEntries.put(entry.getNodeId(), entry); - } - }); - this.knownPeers.clear(); - this.knownPeers.putAll(cleanedUpEntries.build()); - } + // filters out the addresses with a different network ID that have been persisted before the + // filtering was added + private void cleanup() { + final var cleanedUpEntries = new ImmutableMap.Builder(); + this.knownPeers + .values() + .forEach( + entry -> { + final var filteredKnownAddresses = + entry.getKnownAddresses().stream() + .filter(addr -> sameNetworkHrp(addr.getUri())) + .collect(ImmutableSet.toImmutableSet()); - public void addUncheckedPeers(Set peers) { - synchronized (lock) { - peers - .stream() - .filter(not(uri -> uri.getNodeId().equals(this.self.getNodeId()))) - .filter(this::sameNetworkHrp) - .forEach(uri -> { - final var maybeExistingEntry = this.knownPeers.get(uri.getNodeId()); - if (maybeExistingEntry == null) { - final var newEntry = AddressBookEntry.create(uri); - this.knownPeers.put(uri.getNodeId(), newEntry); - persistEntry(newEntry); - } else { - final var updatedEntry = maybeExistingEntry - .cleanupExpiredBlacklsitedUris() - .addUriIfNotExists(uri); - if (!updatedEntry.equals(maybeExistingEntry)) { - this.knownPeers.put(uri.getNodeId(), updatedEntry); - persistEntry(updatedEntry); - } - } - }); - } - } + if (filteredKnownAddresses.isEmpty() && !entry.isBanned()) { + // there are no known addresses and no ban info for peer so just remove it + this.persistence.removeEntry(entry.getNodeId()); + } else if (filteredKnownAddresses.size() != entry.getKnownAddresses().size()) { + // some addresses got filtered out, need to persist a new entry + final var updatedEntry = + new AddressBookEntry( + entry.getNodeId(), entry.bannedUntil(), filteredKnownAddresses); + cleanedUpEntries.put(entry.getNodeId(), updatedEntry); + persistEntry(updatedEntry); + } else { + cleanedUpEntries.put(entry.getNodeId(), entry); + } + }); + this.knownPeers.clear(); + this.knownPeers.putAll(cleanedUpEntries.build()); + } - private boolean sameNetworkHrp(RadixNodeUri uri) { - return uri.getNetworkNodeHrp().equals(this.self.getNetworkNodeHrp()); - } + public void addUncheckedPeers(Set peers) { + synchronized (lock) { + peers.stream() + .filter(not(uri -> uri.getNodeId().equals(this.self.getNodeId()))) + .filter(this::sameNetworkHrp) + .forEach( + uri -> { + final var maybeExistingEntry = this.knownPeers.get(uri.getNodeId()); + if (maybeExistingEntry == null) { + final var newEntry = AddressBookEntry.create(uri); + this.knownPeers.put(uri.getNodeId(), newEntry); + persistEntry(newEntry); + } else { + final var updatedEntry = + maybeExistingEntry.cleanupExpiredBlacklsitedUris().addUriIfNotExists(uri); + if (!updatedEntry.equals(maybeExistingEntry)) { + this.knownPeers.put(uri.getNodeId(), updatedEntry); + persistEntry(updatedEntry); + } + } + }); + } + } - public Optional findById(NodeId nodeId) { - return Optional.ofNullable(this.knownPeers.get(nodeId)); - } + private boolean sameNetworkHrp(RadixNodeUri uri) { + return uri.getNetworkNodeHrp().equals(this.self.getNetworkNodeHrp()); + } - public Optional findBestKnownAddressById(NodeId nodeId) { - synchronized (lock) { - return Optional.ofNullable(this.knownPeers.get(nodeId)) - .stream() - .filter(not(AddressBookEntry::isBanned)) - .flatMap(e -> e.getKnownAddresses().stream()) - .filter(not(PeerAddressEntry::blacklisted)) - .sorted(addressEntryComparator) - .map(AddressBookEntry.PeerAddressEntry::getUri) - .findFirst(); - } - } + public Optional findById(NodeId nodeId) { + return Optional.ofNullable(this.knownPeers.get(nodeId)); + } - public void addOrUpdatePeerWithSuccessfulConnection(RadixNodeUri radixNodeUri) { - this.addOrUpdatePeerWithLatestConnectionStatus(radixNodeUri, LatestConnectionStatus.SUCCESS); - this.addressEntryComparator.resetFailures(radixNodeUri); - } + public Optional findBestKnownAddressById(NodeId nodeId) { + synchronized (lock) { + return Optional.ofNullable(this.knownPeers.get(nodeId)).stream() + .filter(not(AddressBookEntry::isBanned)) + .flatMap(e -> e.getKnownAddresses().stream()) + .filter(not(PeerAddressEntry::blacklisted)) + .sorted(addressEntryComparator) + .map(AddressBookEntry.PeerAddressEntry::getUri) + .findFirst(); + } + } - public void addOrUpdatePeerWithFailedConnection(RadixNodeUri radixNodeUri) { - this.addOrUpdatePeerWithLatestConnectionStatus(radixNodeUri, LatestConnectionStatus.FAILURE); - this.addressEntryComparator.incFailures(radixNodeUri); - } + public void addOrUpdatePeerWithSuccessfulConnection(RadixNodeUri radixNodeUri) { + this.addOrUpdatePeerWithLatestConnectionStatus(radixNodeUri, LatestConnectionStatus.SUCCESS); + this.addressEntryComparator.resetFailures(radixNodeUri); + } - private void addOrUpdatePeerWithLatestConnectionStatus( - RadixNodeUri radixNodeUri, - LatestConnectionStatus latestConnectionStatus - ) { - synchronized (lock) { - final var maybeExistingEntry = this.knownPeers.get(radixNodeUri.getNodeId()); - if (maybeExistingEntry == null) { - final var newEntry = AddressBookEntry.createWithLatestConnectionStatus(radixNodeUri, latestConnectionStatus); - this.knownPeers.put(radixNodeUri.getNodeId(), newEntry); - persistEntry(newEntry); - } else { - final var updatedEntry = maybeExistingEntry - .cleanupExpiredBlacklsitedUris() - .withLatestConnectionStatusForUri(radixNodeUri, latestConnectionStatus); - this.knownPeers.put(radixNodeUri.getNodeId(), updatedEntry); - persistEntry(updatedEntry); - } - } - } + public void addOrUpdatePeerWithFailedConnection(RadixNodeUri radixNodeUri) { + this.addOrUpdatePeerWithLatestConnectionStatus(radixNodeUri, LatestConnectionStatus.FAILURE); + this.addressEntryComparator.incFailures(radixNodeUri); + } - public Stream bestCandidatesToConnect() { - return this.knownPeers.values() - .stream() - .filter(not(AddressBookEntry::isBanned)) - .flatMap(e -> e.getKnownAddresses().stream()) - .filter(not(PeerAddressEntry::blacklisted)) - .sorted(addressEntryComparator) - .map(AddressBookEntry.PeerAddressEntry::getUri); - } + private void addOrUpdatePeerWithLatestConnectionStatus( + RadixNodeUri radixNodeUri, LatestConnectionStatus latestConnectionStatus) { + synchronized (lock) { + final var maybeExistingEntry = this.knownPeers.get(radixNodeUri.getNodeId()); + if (maybeExistingEntry == null) { + final var newEntry = + AddressBookEntry.createWithLatestConnectionStatus(radixNodeUri, latestConnectionStatus); + this.knownPeers.put(radixNodeUri.getNodeId(), newEntry); + persistEntry(newEntry); + } else { + final var updatedEntry = + maybeExistingEntry + .cleanupExpiredBlacklsitedUris() + .withLatestConnectionStatusForUri(radixNodeUri, latestConnectionStatus); + this.knownPeers.put(radixNodeUri.getNodeId(), updatedEntry); + persistEntry(updatedEntry); + } + } + } - private void persistEntry(AddressBookEntry entry) { - this.persistence.removeEntry(entry.getNodeId()); - this.persistence.saveEntry(entry); - } + public Stream bestCandidatesToConnect() { + return this.knownPeers.values().stream() + .filter(not(AddressBookEntry::isBanned)) + .flatMap(e -> e.getKnownAddresses().stream()) + .filter(not(PeerAddressEntry::blacklisted)) + .sorted(addressEntryComparator) + .map(AddressBookEntry.PeerAddressEntry::getUri); + } - void banPeer(NodeId nodeId, Duration banDuration) { - synchronized (lock) { - final var banUntil = Instant.now().plus(banDuration); - final var maybeExistingEntry = findById(nodeId); - if (maybeExistingEntry.isPresent()) { - final var existingEntry = maybeExistingEntry.get(); - final var alreadyBanned = existingEntry.bannedUntil() - .filter(bu -> bu.isAfter(banUntil)).isPresent(); - if (!alreadyBanned) { - final var updatedEntry = existingEntry - .cleanupExpiredBlacklsitedUris() - .withBanUntil(banUntil); - this.knownPeers.put(nodeId, updatedEntry); - this.persistEntry(updatedEntry); - this.peerEventDispatcher.dispatch(PeerBanned.create(nodeId)); - } - } else { - final var newEntry = AddressBookEntry.createBanned(nodeId, banUntil); - this.knownPeers.put(nodeId, newEntry); - this.persistEntry(newEntry); - this.peerEventDispatcher.dispatch(PeerBanned.create(nodeId)); - } + private void persistEntry(AddressBookEntry entry) { + this.persistence.removeEntry(entry.getNodeId()); + this.persistence.saveEntry(entry); + } - } - } + void banPeer(NodeId nodeId, Duration banDuration) { + synchronized (lock) { + final var banUntil = Instant.now().plus(banDuration); + final var maybeExistingEntry = findById(nodeId); + if (maybeExistingEntry.isPresent()) { + final var existingEntry = maybeExistingEntry.get(); + final var alreadyBanned = + existingEntry.bannedUntil().filter(bu -> bu.isAfter(banUntil)).isPresent(); + if (!alreadyBanned) { + final var updatedEntry = + existingEntry.cleanupExpiredBlacklsitedUris().withBanUntil(banUntil); + this.knownPeers.put(nodeId, updatedEntry); + this.persistEntry(updatedEntry); + this.peerEventDispatcher.dispatch(PeerBanned.create(nodeId)); + } + } else { + final var newEntry = AddressBookEntry.createBanned(nodeId, banUntil); + this.knownPeers.put(nodeId, newEntry); + this.persistEntry(newEntry); + this.peerEventDispatcher.dispatch(PeerBanned.create(nodeId)); + } + } + } - public ImmutableMap knownPeers() { - return ImmutableMap.copyOf(knownPeers); - } + public ImmutableMap knownPeers() { + return ImmutableMap.copyOf(knownPeers); + } - public void blacklist(RadixNodeUri uri) { - synchronized (lock) { - final var blacklistUntil = Instant.now().plus(Duration.ofMinutes(30)); - final var maybeExistingEntry = this.knownPeers.get(uri.getNodeId()); - if (maybeExistingEntry == null) { - final var newEntry = AddressBookEntry.createBlacklisted(uri, blacklistUntil); - this.knownPeers.put(uri.getNodeId(), newEntry); - persistEntry(newEntry); - } else { - final var updatedEntry = maybeExistingEntry - .cleanupExpiredBlacklsitedUris() - .withBlacklistedUri(uri, blacklistUntil); - this.knownPeers.put(uri.getNodeId(), updatedEntry); - persistEntry(updatedEntry); - } - } - } + public void blacklist(RadixNodeUri uri) { + synchronized (lock) { + final var blacklistUntil = Instant.now().plus(Duration.ofMinutes(30)); + final var maybeExistingEntry = this.knownPeers.get(uri.getNodeId()); + if (maybeExistingEntry == null) { + final var newEntry = AddressBookEntry.createBlacklisted(uri, blacklistUntil); + this.knownPeers.put(uri.getNodeId(), newEntry); + persistEntry(newEntry); + } else { + final var updatedEntry = + maybeExistingEntry + .cleanupExpiredBlacklsitedUris() + .withBlacklistedUri(uri, blacklistUntil); + this.knownPeers.put(uri.getNodeId(), updatedEntry); + persistEntry(updatedEntry); + } + } + } - public void clear() { - synchronized (lock) { - this.persistence.close(); - this.persistence.reset(); - this.persistence.open(); - this.knownPeers.clear(); - } - } + public void clear() { + synchronized (lock) { + this.persistence.close(); + this.persistence.reset(); + this.persistence.open(); + this.knownPeers.clear(); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookEntry.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookEntry.java index c992852f31..ef3b90906f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookEntry.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookEntry.java @@ -64,6 +64,8 @@ package com.radixdlt.network.p2p.addressbook; +import static java.util.function.Predicate.not; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableSet; @@ -74,309 +76,315 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.time.Instant; import java.util.Objects; import java.util.Optional; -import static java.util.function.Predicate.not; - @SerializerId2("network.p2p.addressbook.address_book_entry") public final class AddressBookEntry { - // Placeholder for the serializer ID - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(DsonOutput.Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("nodeId") - @DsonOutput(DsonOutput.Output.ALL) - private final NodeId nodeId; - - private final Optional bannedUntil; - - @JsonProperty("knownAddresses") - @DsonOutput(DsonOutput.Output.ALL) - private final ImmutableSet knownAddresses; - - @JsonCreator - private static AddressBookEntry deserialize( - @JsonProperty(value = "nodeId", required = true) NodeId nodeId, - @JsonProperty("bannedUntil") long rawBannedUntil, - @JsonProperty("knownAddresses") ImmutableSet knownAddresses - ) { - final var bannedUntil = rawBannedUntil > 0 - ? Optional.of(Instant.ofEpochMilli(rawBannedUntil)) - : Optional.empty(); - return new AddressBookEntry( - nodeId, - bannedUntil, - knownAddresses != null ? knownAddresses : ImmutableSet.of() - ); - } - - public static AddressBookEntry create(RadixNodeUri uri) { - return create(uri, Optional.empty()); - } - - public static AddressBookEntry createBanned(NodeId nodeId, Instant bannedUntil) { - return new AddressBookEntry(nodeId, Optional.of(bannedUntil), ImmutableSet.of()); - } - - public static AddressBookEntry createWithLatestConnectionStatus(RadixNodeUri uri, LatestConnectionStatus latestConnectionStatus) { - return create(uri, Optional.of(latestConnectionStatus)); - } - - public static AddressBookEntry createBlacklisted(RadixNodeUri uri, Instant blacklistedUntil) { - return new AddressBookEntry( - uri.getNodeId(), - Optional.empty(), - ImmutableSet.of(new PeerAddressEntry(uri, Optional.empty(), Optional.of(blacklistedUntil))) - ); - } - - public static AddressBookEntry create(RadixNodeUri uri, Optional latestConnectionStatus) { - return new AddressBookEntry( - uri.getNodeId(), - Optional.empty(), - ImmutableSet.of(new AddressBookEntry.PeerAddressEntry(uri, latestConnectionStatus, Optional.empty())) - ); - } - - AddressBookEntry(NodeId nodeId, Optional bannedUntil, ImmutableSet knownAddresses) { - this.nodeId = Objects.requireNonNull(nodeId); - this.bannedUntil = bannedUntil; - this.knownAddresses = Objects.requireNonNull(knownAddresses); - } - - @JsonProperty("bannedUntil") - @DsonOutput(DsonOutput.Output.ALL) - public long rawBannedUntilForSerializer() { - return this.bannedUntil.map(Instant::toEpochMilli).orElse(0L); - } - - public NodeId getNodeId() { - return nodeId; - } - - public boolean isBanned() { - return bannedUntil.filter(v -> v.isAfter(Instant.now())).isPresent(); - } - - public Optional bannedUntil() { - return this.bannedUntil; - } - - public ImmutableSet getKnownAddresses() { - return knownAddresses; - } - - public AddressBookEntry withBanUntil(Instant banUntil) { - return new AddressBookEntry(nodeId, Optional.of(banUntil), knownAddresses); - } - - public AddressBookEntry addUriIfNotExists(RadixNodeUri uri) { - if (entryFor(uri).isPresent()) { - return this; - } else { - final var newAddressEntry = new PeerAddressEntry(uri, Optional.empty(), Optional.empty()); - final var newKnownAddresses = ImmutableSet.builder() - .addAll(this.knownAddresses) - .add(newAddressEntry) - .build(); - return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); - } - } - - public Optional entryFor(RadixNodeUri uri) { - return knownAddresses.stream() - .filter(e -> e.getUri().equals(uri)) - .findAny(); - } - - public AddressBookEntry withLatestConnectionStatusForUri(RadixNodeUri uri, LatestConnectionStatus latestConnectionStatus) { - Objects.requireNonNull(latestConnectionStatus); - - final var maybeExistingAddress = this.knownAddresses.stream() - .filter(e -> e.getUri().equals(uri)) - .findAny(); - - if (maybeExistingAddress.isPresent()) { - final var updatedAddressEntry = maybeExistingAddress.get() - .withLatestConnectionStatus(latestConnectionStatus); - final var knownAddressesWithoutTheOldOne = - this.knownAddresses.stream() - .filter(not(e -> e.getUri().equals(uri))) - .collect(ImmutableSet.toImmutableSet()); - final var newKnownAddresses = ImmutableSet.builder() - .addAll(knownAddressesWithoutTheOldOne) - .add(updatedAddressEntry) - .build(); - return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); - } else { - final var newAddressEntry = new PeerAddressEntry(uri, Optional.of(latestConnectionStatus), Optional.empty()); - final var newKnownAddresses = ImmutableSet.builder() - .addAll(this.knownAddresses) - .add(newAddressEntry) - .build(); - return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); - } - } - - public AddressBookEntry withBlacklistedUri(RadixNodeUri uri, Instant blacklistedUntil) { - final var maybeExistingAddress = this.knownAddresses.stream() - .filter(e -> e.getUri().equals(uri)) - .findAny(); - - if (maybeExistingAddress.isPresent()) { - final var updatedAddressEntry = maybeExistingAddress.get().blacklistUntil(blacklistedUntil); - final var knownAddressesWithoutTheOldOne = - this.knownAddresses.stream() - .filter(not(e -> e.getUri().equals(uri))) - .collect(ImmutableSet.toImmutableSet()); - final var newKnownAddresses = ImmutableSet.builder() - .addAll(knownAddressesWithoutTheOldOne) - .add(updatedAddressEntry) - .build(); - return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); - } else { - final var newAddressEntry = new PeerAddressEntry(uri, Optional.empty(), Optional.of(blacklistedUntil)); - final var newKnownAddresses = ImmutableSet.builder() - .addAll(this.knownAddresses) - .add(newAddressEntry) - .build(); - return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); - } - } - - public AddressBookEntry cleanupExpiredBlacklsitedUris() { - final var newKnownAddresses = knownAddresses.stream() - .filter(not(PeerAddressEntry::blacklistExpired)) - .collect(ImmutableSet.toImmutableSet()); - return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); - } - - @Override - public String toString() { - return String.format( - "%s[nodeId=%s, bannedUntil=%s, knownAddresses=%s]", getClass().getSimpleName(), - nodeId, bannedUntil, knownAddresses - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - return (o instanceof AddressBookEntry that) - && Objects.equals(bannedUntil, that.bannedUntil) - && Objects.equals(nodeId, that.nodeId) - && Objects.equals(knownAddresses, that.knownAddresses); - } - - @Override - public int hashCode() { - return Objects.hash(nodeId, bannedUntil, knownAddresses); - } - - @SerializerId2("network.p2p.addressbook.peer_address_entry") - public static final class PeerAddressEntry { - public enum LatestConnectionStatus { - SUCCESS, FAILURE - } - - // Placeholder for the serializer ID - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(DsonOutput.Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("uri") - @DsonOutput(DsonOutput.Output.ALL) - private final RadixNodeUri uri; - - private final Optional latestConnectionStatus; - - private final Optional blacklistedUntil; - - @JsonCreator - private static PeerAddressEntry deserialize( - @JsonProperty("uri") RadixNodeUri uri, - @JsonProperty("latestConnectionStatus") String rawLatestConnectionStatus, - @JsonProperty("blacklistedUntil") Long rawBlacklistedUntil - ) { - final var latestConnectionStatus = Optional.ofNullable(rawLatestConnectionStatus).map(LatestConnectionStatus::valueOf); - final var blacklistedUntil = Optional.ofNullable(rawBlacklistedUntil).map(Instant::ofEpochMilli); - return new PeerAddressEntry(uri, latestConnectionStatus, blacklistedUntil); - } - - PeerAddressEntry(RadixNodeUri uri, Optional latestConnectionStatus, Optional blacklistedUntil) { - this.uri = Objects.requireNonNull(uri); - this.latestConnectionStatus = Objects.requireNonNull(latestConnectionStatus); - this.blacklistedUntil = Objects.requireNonNull(blacklistedUntil); - } - - public RadixNodeUri getUri() { - return uri; - } - - public Optional getLatestConnectionStatus() { - return latestConnectionStatus; - } - - public boolean blacklisted() { - return blacklistedUntil.filter(v -> v.isAfter(Instant.now())).isPresent(); - } - - public boolean blacklistExpired() { - return blacklistedUntil.isPresent() && !blacklisted(); - } - - @JsonProperty("latestConnectionStatus") - @DsonOutput(DsonOutput.Output.ALL) - private String getLatestConnectionStatusForSerializer() { - return latestConnectionStatus.map(LatestConnectionStatus::toString).orElse(null); - } - - @JsonProperty("blacklistedUntil") - @DsonOutput(DsonOutput.Output.ALL) - public Long rawBlacklistedUntilForSerializer() { - return this.blacklistedUntil.map(Instant::toEpochMilli).orElse(null); - } - - public PeerAddressEntry withLatestConnectionStatus(LatestConnectionStatus latestConnectionStatus) { - return new PeerAddressEntry(uri, Optional.of(latestConnectionStatus), blacklistedUntil); - } - - public PeerAddressEntry blacklistUntil(Instant blacklistedUntil) { - return new PeerAddressEntry(uri, latestConnectionStatus, Optional.of(blacklistedUntil)); - } - - @Override - public String toString() { - return String.format( - "%s[uri=%s, latestConnectionStatus=%s, blacklistedUntil=%s]", getClass().getSimpleName(), - uri, latestConnectionStatus, blacklistedUntil - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PeerAddressEntry that = (PeerAddressEntry) o; - return Objects.equals(uri, that.uri) - && Objects.equals(latestConnectionStatus, that.latestConnectionStatus) - && Objects.equals(blacklistedUntil, that.blacklistedUntil); - } - - @Override - public int hashCode() { - return Objects.hash(uri, latestConnectionStatus, blacklistedUntil); - } - } + // Placeholder for the serializer ID + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(DsonOutput.Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("nodeId") + @DsonOutput(DsonOutput.Output.ALL) + private final NodeId nodeId; + + private final Optional bannedUntil; + + @JsonProperty("knownAddresses") + @DsonOutput(DsonOutput.Output.ALL) + private final ImmutableSet knownAddresses; + + @JsonCreator + private static AddressBookEntry deserialize( + @JsonProperty(value = "nodeId", required = true) NodeId nodeId, + @JsonProperty("bannedUntil") long rawBannedUntil, + @JsonProperty("knownAddresses") ImmutableSet knownAddresses) { + final var bannedUntil = + rawBannedUntil > 0 + ? Optional.of(Instant.ofEpochMilli(rawBannedUntil)) + : Optional.empty(); + return new AddressBookEntry( + nodeId, bannedUntil, knownAddresses != null ? knownAddresses : ImmutableSet.of()); + } + + public static AddressBookEntry create(RadixNodeUri uri) { + return create(uri, Optional.empty()); + } + + public static AddressBookEntry createBanned(NodeId nodeId, Instant bannedUntil) { + return new AddressBookEntry(nodeId, Optional.of(bannedUntil), ImmutableSet.of()); + } + + public static AddressBookEntry createWithLatestConnectionStatus( + RadixNodeUri uri, LatestConnectionStatus latestConnectionStatus) { + return create(uri, Optional.of(latestConnectionStatus)); + } + + public static AddressBookEntry createBlacklisted(RadixNodeUri uri, Instant blacklistedUntil) { + return new AddressBookEntry( + uri.getNodeId(), + Optional.empty(), + ImmutableSet.of( + new PeerAddressEntry(uri, Optional.empty(), Optional.of(blacklistedUntil)))); + } + + public static AddressBookEntry create( + RadixNodeUri uri, Optional latestConnectionStatus) { + return new AddressBookEntry( + uri.getNodeId(), + Optional.empty(), + ImmutableSet.of( + new AddressBookEntry.PeerAddressEntry(uri, latestConnectionStatus, Optional.empty()))); + } + + AddressBookEntry( + NodeId nodeId, Optional bannedUntil, ImmutableSet knownAddresses) { + this.nodeId = Objects.requireNonNull(nodeId); + this.bannedUntil = bannedUntil; + this.knownAddresses = Objects.requireNonNull(knownAddresses); + } + + @JsonProperty("bannedUntil") + @DsonOutput(DsonOutput.Output.ALL) + public long rawBannedUntilForSerializer() { + return this.bannedUntil.map(Instant::toEpochMilli).orElse(0L); + } + + public NodeId getNodeId() { + return nodeId; + } + + public boolean isBanned() { + return bannedUntil.filter(v -> v.isAfter(Instant.now())).isPresent(); + } + + public Optional bannedUntil() { + return this.bannedUntil; + } + + public ImmutableSet getKnownAddresses() { + return knownAddresses; + } + + public AddressBookEntry withBanUntil(Instant banUntil) { + return new AddressBookEntry(nodeId, Optional.of(banUntil), knownAddresses); + } + + public AddressBookEntry addUriIfNotExists(RadixNodeUri uri) { + if (entryFor(uri).isPresent()) { + return this; + } else { + final var newAddressEntry = new PeerAddressEntry(uri, Optional.empty(), Optional.empty()); + final var newKnownAddresses = + ImmutableSet.builder() + .addAll(this.knownAddresses) + .add(newAddressEntry) + .build(); + return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); + } + } + + public Optional entryFor(RadixNodeUri uri) { + return knownAddresses.stream().filter(e -> e.getUri().equals(uri)).findAny(); + } + + public AddressBookEntry withLatestConnectionStatusForUri( + RadixNodeUri uri, LatestConnectionStatus latestConnectionStatus) { + Objects.requireNonNull(latestConnectionStatus); + + final var maybeExistingAddress = + this.knownAddresses.stream().filter(e -> e.getUri().equals(uri)).findAny(); + + if (maybeExistingAddress.isPresent()) { + final var updatedAddressEntry = + maybeExistingAddress.get().withLatestConnectionStatus(latestConnectionStatus); + final var knownAddressesWithoutTheOldOne = + this.knownAddresses.stream() + .filter(not(e -> e.getUri().equals(uri))) + .collect(ImmutableSet.toImmutableSet()); + final var newKnownAddresses = + ImmutableSet.builder() + .addAll(knownAddressesWithoutTheOldOne) + .add(updatedAddressEntry) + .build(); + return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); + } else { + final var newAddressEntry = + new PeerAddressEntry(uri, Optional.of(latestConnectionStatus), Optional.empty()); + final var newKnownAddresses = + ImmutableSet.builder() + .addAll(this.knownAddresses) + .add(newAddressEntry) + .build(); + return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); + } + } + + public AddressBookEntry withBlacklistedUri(RadixNodeUri uri, Instant blacklistedUntil) { + final var maybeExistingAddress = + this.knownAddresses.stream().filter(e -> e.getUri().equals(uri)).findAny(); + + if (maybeExistingAddress.isPresent()) { + final var updatedAddressEntry = maybeExistingAddress.get().blacklistUntil(blacklistedUntil); + final var knownAddressesWithoutTheOldOne = + this.knownAddresses.stream() + .filter(not(e -> e.getUri().equals(uri))) + .collect(ImmutableSet.toImmutableSet()); + final var newKnownAddresses = + ImmutableSet.builder() + .addAll(knownAddressesWithoutTheOldOne) + .add(updatedAddressEntry) + .build(); + return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); + } else { + final var newAddressEntry = + new PeerAddressEntry(uri, Optional.empty(), Optional.of(blacklistedUntil)); + final var newKnownAddresses = + ImmutableSet.builder() + .addAll(this.knownAddresses) + .add(newAddressEntry) + .build(); + return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); + } + } + + public AddressBookEntry cleanupExpiredBlacklsitedUris() { + final var newKnownAddresses = + knownAddresses.stream() + .filter(not(PeerAddressEntry::blacklistExpired)) + .collect(ImmutableSet.toImmutableSet()); + return new AddressBookEntry(nodeId, bannedUntil, newKnownAddresses); + } + + @Override + public String toString() { + return String.format( + "%s[nodeId=%s, bannedUntil=%s, knownAddresses=%s]", + getClass().getSimpleName(), nodeId, bannedUntil, knownAddresses); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + return (o instanceof AddressBookEntry that) + && Objects.equals(bannedUntil, that.bannedUntil) + && Objects.equals(nodeId, that.nodeId) + && Objects.equals(knownAddresses, that.knownAddresses); + } + + @Override + public int hashCode() { + return Objects.hash(nodeId, bannedUntil, knownAddresses); + } + + @SerializerId2("network.p2p.addressbook.peer_address_entry") + public static final class PeerAddressEntry { + public enum LatestConnectionStatus { + SUCCESS, + FAILURE + } + + // Placeholder for the serializer ID + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(DsonOutput.Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("uri") + @DsonOutput(DsonOutput.Output.ALL) + private final RadixNodeUri uri; + + private final Optional latestConnectionStatus; + + private final Optional blacklistedUntil; + + @JsonCreator + private static PeerAddressEntry deserialize( + @JsonProperty("uri") RadixNodeUri uri, + @JsonProperty("latestConnectionStatus") String rawLatestConnectionStatus, + @JsonProperty("blacklistedUntil") Long rawBlacklistedUntil) { + final var latestConnectionStatus = + Optional.ofNullable(rawLatestConnectionStatus).map(LatestConnectionStatus::valueOf); + final var blacklistedUntil = + Optional.ofNullable(rawBlacklistedUntil).map(Instant::ofEpochMilli); + return new PeerAddressEntry(uri, latestConnectionStatus, blacklistedUntil); + } + + PeerAddressEntry( + RadixNodeUri uri, + Optional latestConnectionStatus, + Optional blacklistedUntil) { + this.uri = Objects.requireNonNull(uri); + this.latestConnectionStatus = Objects.requireNonNull(latestConnectionStatus); + this.blacklistedUntil = Objects.requireNonNull(blacklistedUntil); + } + + public RadixNodeUri getUri() { + return uri; + } + + public Optional getLatestConnectionStatus() { + return latestConnectionStatus; + } + + public boolean blacklisted() { + return blacklistedUntil.filter(v -> v.isAfter(Instant.now())).isPresent(); + } + + public boolean blacklistExpired() { + return blacklistedUntil.isPresent() && !blacklisted(); + } + + @JsonProperty("latestConnectionStatus") + @DsonOutput(DsonOutput.Output.ALL) + private String getLatestConnectionStatusForSerializer() { + return latestConnectionStatus.map(LatestConnectionStatus::toString).orElse(null); + } + + @JsonProperty("blacklistedUntil") + @DsonOutput(DsonOutput.Output.ALL) + public Long rawBlacklistedUntilForSerializer() { + return this.blacklistedUntil.map(Instant::toEpochMilli).orElse(null); + } + + public PeerAddressEntry withLatestConnectionStatus( + LatestConnectionStatus latestConnectionStatus) { + return new PeerAddressEntry(uri, Optional.of(latestConnectionStatus), blacklistedUntil); + } + + public PeerAddressEntry blacklistUntil(Instant blacklistedUntil) { + return new PeerAddressEntry(uri, latestConnectionStatus, Optional.of(blacklistedUntil)); + } + + @Override + public String toString() { + return String.format( + "%s[uri=%s, latestConnectionStatus=%s, blacklistedUntil=%s]", + getClass().getSimpleName(), uri, latestConnectionStatus, blacklistedUntil); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PeerAddressEntry that = (PeerAddressEntry) o; + return Objects.equals(uri, that.uri) + && Objects.equals(latestConnectionStatus, that.latestConnectionStatus) + && Objects.equals(blacklistedUntil, that.blacklistedUntil); + } + + @Override + public int hashCode() { + return Objects.hash(uri, latestConnectionStatus, blacklistedUntil); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPeerControl.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPeerControl.java index 771250ccbe..10118e2990 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPeerControl.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPeerControl.java @@ -67,24 +67,23 @@ import com.google.inject.Inject; import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.PeerControl; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.time.Duration; import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public final class AddressBookPeerControl implements PeerControl { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - private final AddressBook addressBook; + private final AddressBook addressBook; - @Inject - public AddressBookPeerControl(AddressBook addressBook) { - this.addressBook = Objects.requireNonNull(addressBook); - } + @Inject + public AddressBookPeerControl(AddressBook addressBook) { + this.addressBook = Objects.requireNonNull(addressBook); + } - public void banPeer(NodeId nodeId, Duration banDuration, String reason) { - log.info("Banning peer {} for {} because of {}", nodeId, banDuration, reason); - this.addressBook.banPeer(nodeId, banDuration); - } + public void banPeer(NodeId nodeId, Duration banDuration, String reason) { + log.info("Banning peer {} for {} because of {}", nodeId, banDuration, reason); + this.addressBook.banPeer(nodeId, banDuration); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPersistence.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPersistence.java index dca3322770..47f963ed51 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPersistence.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/addressbook/AddressBookPersistence.java @@ -68,10 +68,15 @@ import com.radixdlt.network.p2p.NodeId; public interface AddressBookPersistence { - void open(); - void reset(); - void close(); - boolean saveEntry(AddressBookEntry entry); - boolean removeEntry(NodeId nodeId); - ImmutableList getAllEntries(); + void open(); + + void reset(); + + void close(); + + boolean saveEntry(AddressBookEntry entry); + + boolean removeEntry(NodeId nodeId); + + ImmutableList getAllEntries(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/DiscoverPeers.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/DiscoverPeers.java index 1dbe102859..206cbef8d1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/DiscoverPeers.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/DiscoverPeers.java @@ -64,33 +64,30 @@ package com.radixdlt.network.p2p.discovery; -/** - * A message acting as as trigger for PeerDiscovery to start a discovery round. - */ +/** A message acting as as trigger for PeerDiscovery to start a discovery round. */ public final class DiscoverPeers { - public static DiscoverPeers create() { - return new DiscoverPeers(); - } + public static DiscoverPeers create() { + return new DiscoverPeers(); + } - private DiscoverPeers() { - } + private DiscoverPeers() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o instanceof DiscoverPeers; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o instanceof DiscoverPeers; + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/GetPeers.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/GetPeers.java index 50b88036fd..c5321f678f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/GetPeers.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/GetPeers.java @@ -66,28 +66,27 @@ public final class GetPeers { - public static GetPeers create() { - return new GetPeers(); - } + public static GetPeers create() { + return new GetPeers(); + } - private GetPeers() { - } + private GetPeers() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o instanceof GetPeers; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o instanceof GetPeers; + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeerDiscovery.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeerDiscovery.java index b042219669..fbc4e5a21b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeerDiscovery.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeerDiscovery.java @@ -64,6 +64,8 @@ package com.radixdlt.network.p2p.discovery; +import static java.util.function.Predicate.not; + import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; @@ -75,11 +77,6 @@ import com.radixdlt.network.p2p.PeerManager; import com.radixdlt.network.p2p.RadixNodeUri; import com.radixdlt.network.p2p.addressbook.AddressBook; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.radix.network.discovery.SeedNodesConfigParser; - -import javax.inject.Inject; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; @@ -87,106 +84,110 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Stream; - -import static java.util.function.Predicate.not; +import javax.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.radix.network.discovery.SeedNodesConfigParser; /** - * Discovers peers network addresses and adds them to the address book. - * Initial (seed) peers are "discovered" from the config (bootstrapDiscovery) - * and more peers are requested from the peers we're already connected to. + * Discovers peers network addresses and adds them to the address book. Initial (seed) peers are + * "discovered" from the config (bootstrapDiscovery) and more peers are requested from the peers + * we're already connected to. */ public final class PeerDiscovery { - private static final Logger log = LogManager.getLogger(); - - private static final int MAX_PEERS_IN_RESPONSE = 50; - private static final int MAX_REQUESTS_SENT_AT_ONCE = 5; - - private final RadixNodeUri selfUri; - private final PeerManager peerManager; - private final AddressBook addressBook; - private final PeerControl peerControl; - private final SeedNodesConfigParser seedNodesConfigParser; - private final RemoteEventDispatcher getPeersRemoteEventDispatcher; - private final RemoteEventDispatcher peersResponseRemoteEventDispatcher; - - private final Set peersAsked = new HashSet<>(); - - @Inject - public PeerDiscovery( - @Self RadixNodeUri selfUri, - PeerManager peerManager, - AddressBook addressBook, - PeerControl peerControl, - SeedNodesConfigParser seedNodesConfigParser, - RemoteEventDispatcher getPeersRemoteEventDispatcher, - RemoteEventDispatcher peersResponseRemoteEventDispatcher - ) { - this.selfUri = Objects.requireNonNull(selfUri); - this.peerManager = Objects.requireNonNull(peerManager); - this.addressBook = Objects.requireNonNull(addressBook); - this.peerControl = Objects.requireNonNull(peerControl); - this.seedNodesConfigParser = Objects.requireNonNull(seedNodesConfigParser); - this.getPeersRemoteEventDispatcher = Objects.requireNonNull(getPeersRemoteEventDispatcher); - this.peersResponseRemoteEventDispatcher = Objects.requireNonNull(peersResponseRemoteEventDispatcher); - } - - public EventProcessor discoverPeersEventProcessor() { - return unused -> { - final var seedNodes = seedNodesConfigParser.getResolvedSeedNodes(); - this.addressBook.addUncheckedPeers(seedNodes); - - final var channels = new ArrayList<>(this.peerManager.activeChannels()); - Collections.shuffle(channels); - channels.stream() - .filter(not(c -> peersAsked.contains(c.getRemoteNodeId()))) - .limit(MAX_REQUESTS_SENT_AT_ONCE) - .forEach(peer -> { - peersAsked.add(peer.getRemoteNodeId()); - getPeersRemoteEventDispatcher.dispatch( - BFTNode.create(peer.getRemoteNodeId().getPublicKey()), GetPeers.create()); - }); - - this.tryConnectToSomeKnownPeers(); - }; - } - - private void tryConnectToSomeKnownPeers() { - final var remainingSlots = this.peerManager.getRemainingOutboundSlots(); - final var maxSlotsToUse = Math.max(0, (remainingSlots / 2) - 2); // let's always leave some free slots - this.addressBook.bestCandidatesToConnect() - .filter(not(e -> peerManager.isPeerConnected(e.getNodeId()))) - .limit(maxSlotsToUse) - .forEach(this.peerManager::tryConnect); - } - - public RemoteEventProcessor peersResponseRemoteEventProcessor() { - return (sender, peersResponse) -> { - final var senderNodeId = NodeId.fromPublicKey(sender.getKey()); - if (!peersAsked.contains(senderNodeId)) { - log.warn("Received unexpected peers response from {}", senderNodeId); - this.peerControl.banPeer(senderNodeId, Duration.ofMinutes(15), "Unexpected peers response"); - return; - } - - this.peersAsked.remove(senderNodeId); - final var peersUpToLimit = peersResponse.getPeers() - .stream() - .limit(MAX_PEERS_IN_RESPONSE) - .collect(ImmutableSet.toImmutableSet()); - this.addressBook.addUncheckedPeers(peersUpToLimit); - }; - } - - public RemoteEventProcessor getPeersRemoteEventProcessor() { - return (sender, unused) -> { - final var peers = - Stream.concat( - Stream.of(selfUri), - this.addressBook.bestCandidatesToConnect() - .limit(MAX_PEERS_IN_RESPONSE - 1) - ).collect(ImmutableSet.toImmutableSet()); - - peersResponseRemoteEventDispatcher.dispatch(sender, PeersResponse.create(peers)); - }; - } + private static final Logger log = LogManager.getLogger(); + + private static final int MAX_PEERS_IN_RESPONSE = 50; + private static final int MAX_REQUESTS_SENT_AT_ONCE = 5; + + private final RadixNodeUri selfUri; + private final PeerManager peerManager; + private final AddressBook addressBook; + private final PeerControl peerControl; + private final SeedNodesConfigParser seedNodesConfigParser; + private final RemoteEventDispatcher getPeersRemoteEventDispatcher; + private final RemoteEventDispatcher peersResponseRemoteEventDispatcher; + + private final Set peersAsked = new HashSet<>(); + + @Inject + public PeerDiscovery( + @Self RadixNodeUri selfUri, + PeerManager peerManager, + AddressBook addressBook, + PeerControl peerControl, + SeedNodesConfigParser seedNodesConfigParser, + RemoteEventDispatcher getPeersRemoteEventDispatcher, + RemoteEventDispatcher peersResponseRemoteEventDispatcher) { + this.selfUri = Objects.requireNonNull(selfUri); + this.peerManager = Objects.requireNonNull(peerManager); + this.addressBook = Objects.requireNonNull(addressBook); + this.peerControl = Objects.requireNonNull(peerControl); + this.seedNodesConfigParser = Objects.requireNonNull(seedNodesConfigParser); + this.getPeersRemoteEventDispatcher = Objects.requireNonNull(getPeersRemoteEventDispatcher); + this.peersResponseRemoteEventDispatcher = + Objects.requireNonNull(peersResponseRemoteEventDispatcher); + } + + public EventProcessor discoverPeersEventProcessor() { + return unused -> { + final var seedNodes = seedNodesConfigParser.getResolvedSeedNodes(); + this.addressBook.addUncheckedPeers(seedNodes); + + final var channels = new ArrayList<>(this.peerManager.activeChannels()); + Collections.shuffle(channels); + channels.stream() + .filter(not(c -> peersAsked.contains(c.getRemoteNodeId()))) + .limit(MAX_REQUESTS_SENT_AT_ONCE) + .forEach( + peer -> { + peersAsked.add(peer.getRemoteNodeId()); + getPeersRemoteEventDispatcher.dispatch( + BFTNode.create(peer.getRemoteNodeId().getPublicKey()), GetPeers.create()); + }); + + this.tryConnectToSomeKnownPeers(); + }; + } + + private void tryConnectToSomeKnownPeers() { + final var remainingSlots = this.peerManager.getRemainingOutboundSlots(); + final var maxSlotsToUse = + Math.max(0, (remainingSlots / 2) - 2); // let's always leave some free slots + this.addressBook + .bestCandidatesToConnect() + .filter(not(e -> peerManager.isPeerConnected(e.getNodeId()))) + .limit(maxSlotsToUse) + .forEach(this.peerManager::tryConnect); + } + + public RemoteEventProcessor peersResponseRemoteEventProcessor() { + return (sender, peersResponse) -> { + final var senderNodeId = NodeId.fromPublicKey(sender.getKey()); + if (!peersAsked.contains(senderNodeId)) { + log.warn("Received unexpected peers response from {}", senderNodeId); + this.peerControl.banPeer(senderNodeId, Duration.ofMinutes(15), "Unexpected peers response"); + return; + } + + this.peersAsked.remove(senderNodeId); + final var peersUpToLimit = + peersResponse.getPeers().stream() + .limit(MAX_PEERS_IN_RESPONSE) + .collect(ImmutableSet.toImmutableSet()); + this.addressBook.addUncheckedPeers(peersUpToLimit); + }; + } + + public RemoteEventProcessor getPeersRemoteEventProcessor() { + return (sender, unused) -> { + final var peers = + Stream.concat( + Stream.of(selfUri), + this.addressBook.bestCandidatesToConnect().limit(MAX_PEERS_IN_RESPONSE - 1)) + .collect(ImmutableSet.toImmutableSet()); + + peersResponseRemoteEventDispatcher.dispatch(sender, PeersResponse.create(peers)); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeersResponse.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeersResponse.java index 11b542ed0a..159fddffa1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeersResponse.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/discovery/PeersResponse.java @@ -66,44 +66,43 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.network.p2p.RadixNodeUri; - import java.util.Objects; public final class PeersResponse { - private final ImmutableSet peers; + private final ImmutableSet peers; - public static PeersResponse create(ImmutableSet peers) { - return new PeersResponse(peers); - } + public static PeersResponse create(ImmutableSet peers) { + return new PeersResponse(peers); + } - private PeersResponse(ImmutableSet peers) { - this.peers = peers; - } + private PeersResponse(ImmutableSet peers) { + this.peers = peers; + } - public ImmutableSet getPeers() { - return peers; - } + public ImmutableSet getPeers() { + return peers; + } - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (PeersResponse) o; - return Objects.equals(peers, that.peers); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (PeersResponse) o; + return Objects.equals(peers, that.peers); + } - @Override - public int hashCode() { - return Objects.hash(peers); - } + @Override + public int hashCode() { + return Objects.hash(peers); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitor.java index 7eb6d740a1..9ca0986249 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitor.java @@ -71,85 +71,83 @@ import com.radixdlt.environment.RemoteEventProcessor; import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.network.p2p.NodeId; +import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.network.p2p.PeerEvent; import com.radixdlt.network.p2p.PeerEvent.PeerLostLiveness; -import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.network.p2p.PeersView; - -import javax.inject.Inject; import java.util.HashSet; import java.util.Objects; import java.util.Set; +import javax.inject.Inject; /** - * Periodically pings peers and awaits for pong response - * if pong is not received on time then it fires a PeerLostLiveness event. + * Periodically pings peers and awaits for pong response if pong is not received on time then it + * fires a PeerLostLiveness event. */ public final class PeerLivenessMonitor { - private final P2PConfig config; - private final PeersView peersView; - private final EventDispatcher peerEventDispatcher; - private final RemoteEventDispatcher pingEventDispatcher; - private final RemoteEventDispatcher pongEventDispatcher; - private final ScheduledEventDispatcher pingTimeoutEventDispatcher; + private final P2PConfig config; + private final PeersView peersView; + private final EventDispatcher peerEventDispatcher; + private final RemoteEventDispatcher pingEventDispatcher; + private final RemoteEventDispatcher pongEventDispatcher; + private final ScheduledEventDispatcher pingTimeoutEventDispatcher; - private final Set waitingForPong = new HashSet<>(); + private final Set waitingForPong = new HashSet<>(); - @Inject - public PeerLivenessMonitor( - P2PConfig config, - PeersView peersView, - EventDispatcher peerEventDispatcher, - RemoteEventDispatcher pingEventDispatcher, - RemoteEventDispatcher pongEventDispatcher, - ScheduledEventDispatcher pingTimeoutEventDispatcher - ) { - if (config.peerLivenessCheckInterval() <= config.pingTimeout()) { - throw new IllegalArgumentException("pingTimeout must be smaller than livenessCheckInterval"); - } - this.config = Objects.requireNonNull(config); - this.peersView = Objects.requireNonNull(peersView); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - this.pingEventDispatcher = Objects.requireNonNull(pingEventDispatcher); - this.pongEventDispatcher = Objects.requireNonNull(pongEventDispatcher); - this.pingTimeoutEventDispatcher = Objects.requireNonNull(pingTimeoutEventDispatcher); - } + @Inject + public PeerLivenessMonitor( + P2PConfig config, + PeersView peersView, + EventDispatcher peerEventDispatcher, + RemoteEventDispatcher pingEventDispatcher, + RemoteEventDispatcher pongEventDispatcher, + ScheduledEventDispatcher pingTimeoutEventDispatcher) { + if (config.peerLivenessCheckInterval() <= config.pingTimeout()) { + throw new IllegalArgumentException("pingTimeout must be smaller than livenessCheckInterval"); + } + this.config = Objects.requireNonNull(config); + this.peersView = Objects.requireNonNull(peersView); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + this.pingEventDispatcher = Objects.requireNonNull(pingEventDispatcher); + this.pongEventDispatcher = Objects.requireNonNull(pongEventDispatcher); + this.pingTimeoutEventDispatcher = Objects.requireNonNull(pingTimeoutEventDispatcher); + } - public EventProcessor peersLivenessCheckTriggerEventProcessor() { - return unused -> peersView.peers().forEach(this::pingPeer); - } + public EventProcessor peersLivenessCheckTriggerEventProcessor() { + return unused -> peersView.peers().forEach(this::pingPeer); + } - private void pingPeer(PeersView.PeerInfo peerInfo) { - final var nodeId = peerInfo.getNodeId(); + private void pingPeer(PeersView.PeerInfo peerInfo) { + final var nodeId = peerInfo.getNodeId(); - if (this.waitingForPong.contains(nodeId)) { - return; // already pinged - } + if (this.waitingForPong.contains(nodeId)) { + return; // already pinged + } - this.waitingForPong.add(nodeId); - this.pingEventDispatcher.dispatch(BFTNode.create(nodeId.getPublicKey()), Ping.create()); - this.pingTimeoutEventDispatcher.dispatch(PeerPingTimeout.create(nodeId), config.pingTimeout()); - } + this.waitingForPong.add(nodeId); + this.pingEventDispatcher.dispatch(BFTNode.create(nodeId.getPublicKey()), Ping.create()); + this.pingTimeoutEventDispatcher.dispatch(PeerPingTimeout.create(nodeId), config.pingTimeout()); + } - public EventProcessor pingTimeoutEventProcessor() { - return timeout -> { - final var waitingForPeer = this.waitingForPong.remove(timeout.getNodeId()); - if (waitingForPeer) { - this.peerEventDispatcher.dispatch(PeerLostLiveness.create(timeout.getNodeId())); - } - }; - } + public EventProcessor pingTimeoutEventProcessor() { + return timeout -> { + final var waitingForPeer = this.waitingForPong.remove(timeout.getNodeId()); + if (waitingForPeer) { + this.peerEventDispatcher.dispatch(PeerLostLiveness.create(timeout.getNodeId())); + } + }; + } - public RemoteEventProcessor pingRemoteEventProcessor() { - return (sender, ping) -> { - this.pongEventDispatcher.dispatch(sender, Pong.create()); - }; - } + public RemoteEventProcessor pingRemoteEventProcessor() { + return (sender, ping) -> { + this.pongEventDispatcher.dispatch(sender, Pong.create()); + }; + } - public RemoteEventProcessor pongRemoteEventProcessor() { - return (sender, pong) -> { - final var nodeId = NodeId.fromPublicKey(sender.getKey()); - this.waitingForPong.remove(nodeId); - }; - } + public RemoteEventProcessor pongRemoteEventProcessor() { + return (sender, pong) -> { + final var nodeId = NodeId.fromPublicKey(sender.getKey()); + this.waitingForPong.remove(nodeId); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerPingTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerPingTimeout.java index 3b18542b48..5cace46768 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerPingTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeerPingTimeout.java @@ -65,39 +65,38 @@ package com.radixdlt.network.p2p.liveness; import com.radixdlt.network.p2p.NodeId; - import java.util.Objects; public final class PeerPingTimeout { - private final NodeId nodeId; + private final NodeId nodeId; - public static PeerPingTimeout create(NodeId nodeId) { - return new PeerPingTimeout(nodeId); - } + public static PeerPingTimeout create(NodeId nodeId) { + return new PeerPingTimeout(nodeId); + } - private PeerPingTimeout(NodeId nodeId) { - this.nodeId = nodeId; - } + private PeerPingTimeout(NodeId nodeId) { + this.nodeId = nodeId; + } - public NodeId getNodeId() { - return nodeId; - } + public NodeId getNodeId() { + return nodeId; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (PeerPingTimeout) o; - return Objects.equals(nodeId, that.nodeId); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (PeerPingTimeout) o; + return Objects.equals(nodeId, that.nodeId); + } - @Override - public int hashCode() { - return Objects.hash(nodeId); - } + @Override + public int hashCode() { + return Objects.hash(nodeId); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTrigger.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTrigger.java index 34b14f841e..2df412fb39 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTrigger.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTrigger.java @@ -64,33 +64,30 @@ package com.radixdlt.network.p2p.liveness; -/** - * A message acting as a trigger for PeerLivenessMonitor to send ping messages. - */ +/** A message acting as a trigger for PeerLivenessMonitor to send ping messages. */ public final class PeersLivenessCheckTrigger { - public static PeersLivenessCheckTrigger create() { - return new PeersLivenessCheckTrigger(); - } + public static PeersLivenessCheckTrigger create() { + return new PeersLivenessCheckTrigger(); + } - private PeersLivenessCheckTrigger() { - } + private PeersLivenessCheckTrigger() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o instanceof PeersLivenessCheckTrigger; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o instanceof PeersLivenessCheckTrigger; + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Ping.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Ping.java index cc49ffd9e9..e352a8a531 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Ping.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Ping.java @@ -66,28 +66,27 @@ public final class Ping { - public static Ping create() { - return new Ping(); - } + public static Ping create() { + return new Ping(); + } - private Ping() { - } + private Ping() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o instanceof Ping; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o instanceof Ping; + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Pong.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Pong.java index fd4881ee41..570d83e71a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Pong.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/liveness/Pong.java @@ -66,28 +66,27 @@ public final class Pong { - public static Pong create() { - return new Pong(); - } + public static Pong create() { + return new Pong(); + } - private Pong() { - } + private Pong() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o instanceof Pong; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o instanceof Pong; + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/FrameCodec.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/FrameCodec.java index 28a7aac816..6e6872b9a4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/FrameCodec.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/FrameCodec.java @@ -66,6 +66,10 @@ import com.radixdlt.network.p2p.transport.handshake.Secrets; import io.netty.buffer.ByteBuf; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Optional; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.digests.KeccakDigest; import org.bouncycastle.crypto.engines.AESEngine; @@ -73,152 +77,152 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Optional; - -/** - * Low-level codec for encrypted communication. - */ +/** Low-level codec for encrypted communication. */ public final class FrameCodec { - private static final int HEADER_SIZE = 32; - private static final int MAC_SIZE = 16; - - private final StreamCipher enc; - private final StreamCipher dec; - private final KeccakDigest egressMac; - private final KeccakDigest ingressMac; - private final byte[] mac; - - public FrameCodec(Secrets secrets) { - this.mac = secrets.getMac(); - - final var encCipher = new AESEngine(); - enc = new SICBlockCipher(encCipher); - enc.init(true, new ParametersWithIV(new KeyParameter(secrets.getAes()), new byte[encCipher.getBlockSize()])); - - final var decCipher = new AESEngine(); - dec = new SICBlockCipher(decCipher); - dec.init(false, new ParametersWithIV(new KeyParameter(secrets.getAes()), new byte[decCipher.getBlockSize()])); - - egressMac = secrets.getEgressMac(); - ingressMac = secrets.getIngressMac(); - } - - public void writeFrame(byte[] frame, OutputStream out) throws IOException { - final var headBuffer = new byte[32]; - headBuffer[0] = (byte) (frame.length >> 16); - headBuffer[1] = (byte) (frame.length >> 8); - headBuffer[2] = (byte) (frame.length); - - enc.processBytes(headBuffer, 0, 16, headBuffer, 0); - final var headBufferMacResult = updateEgressMac(egressMac, headBuffer); - out.write(headBuffer, 0, 16); - out.write(headBufferMacResult, 0, 16); - - final var buff = new byte[256]; - final var payloadStream = new ByteArrayInputStream(frame); - while (true) { - final var n = payloadStream.read(buff); - if (n <= 0) { - break; - } - enc.processBytes(buff, 0, n, buff, 0); - egressMac.update(buff, 0, n); - out.write(buff, 0, n); - } - final var paddingSize = 16 - (frame.length % 16); - final var padding = new byte[16]; - if (paddingSize < 16) { - enc.processBytes(padding, 0, paddingSize, buff, 0); - egressMac.update(buff, 0, paddingSize); - out.write(buff, 0, paddingSize); - } - - final var macBuffer = new byte[egressMac.getDigestSize()]; - doSum(egressMac, macBuffer); - final var egressMacResult = updateEgressMac(egressMac, macBuffer); - out.write(egressMacResult, 0, 16); - } - - public Optional tryReadSingleFrame(ByteBuf input) throws IOException { - if (input.readableBytes() < HEADER_SIZE) { - return Optional.empty(); - } - - final var totalBodySize = readHeader(input); - - if (input.readableBytes() < HEADER_SIZE + totalBodySize) { - return Optional.empty(); - } - - final var paddingSize = totalBodySize % 16 == 0 ? 0 : 16 - (totalBodySize % 16); - final var payloadBuffer = new byte[totalBodySize + paddingSize + MAC_SIZE]; - input.getBytes(HEADER_SIZE, payloadBuffer, 0, payloadBuffer.length); - - final var frameSize = payloadBuffer.length - MAC_SIZE; - ingressMac.update(payloadBuffer, 0, frameSize); - dec.processBytes(payloadBuffer, 0, frameSize, payloadBuffer, 0); - - final var macBuffer = new byte[ingressMac.getDigestSize()]; - doSum(ingressMac, macBuffer); - updateIngressMac(ingressMac, macBuffer, payloadBuffer, frameSize); - - final var bodyBuffer = new byte[totalBodySize]; - System.arraycopy(payloadBuffer, 0, bodyBuffer, 0, totalBodySize); - - return Optional.of(bodyBuffer); - } - - private int readHeader(ByteBuf input) throws IOException { - final var headBuffer = new byte[32]; - input.getBytes(0, headBuffer, 0, headBuffer.length); - - updateIngressMac(ingressMac, headBuffer, headBuffer, 16); - dec.processBytes(headBuffer, 0, 16, headBuffer, 0); - - int totalBodySize = headBuffer[0] & 0xFF; - totalBodySize = (totalBodySize << 8) + (headBuffer[1] & 0xFF); - totalBodySize = (totalBodySize << 8) + (headBuffer[2] & 0xFF); - - return totalBodySize; - } - - private void updateIngressMac(KeccakDigest mac, byte[] seed, byte[] out, int outOffset) throws IOException { - final var result = updateMac(mac, seed); - - for (int i = 0; i < MAC_SIZE; i++) { - if (out[i + outOffset] != result[i]) { - throw new IOException("MAC mismatch"); - } - } - } - - private byte[] updateEgressMac(KeccakDigest mac, byte[] seed) { - return updateMac(mac, seed); - } - - private byte[] updateMac(KeccakDigest mac, byte[] seed) { - final var aesBlock = new byte[mac.getDigestSize()]; - doSum(mac, aesBlock); - makeMacCipher().processBlock(aesBlock, 0, aesBlock, 0); - for (int i = 0; i < MAC_SIZE; i++) { - aesBlock[i] ^= seed[i]; - } - mac.update(aesBlock, 0, MAC_SIZE); - final var result = new byte[mac.getDigestSize()]; - doSum(mac, result); - return result; - } - - private AESEngine makeMacCipher() { - final var aesEngine = new AESEngine(); - aesEngine.init(true, new KeyParameter(mac)); - return aesEngine; - } - - private void doSum(KeccakDigest mac, byte[] out) { - new KeccakDigest(mac).doFinal(out, 0); - } + private static final int HEADER_SIZE = 32; + private static final int MAC_SIZE = 16; + + private final StreamCipher enc; + private final StreamCipher dec; + private final KeccakDigest egressMac; + private final KeccakDigest ingressMac; + private final byte[] mac; + + public FrameCodec(Secrets secrets) { + this.mac = secrets.getMac(); + + final var encCipher = new AESEngine(); + enc = new SICBlockCipher(encCipher); + enc.init( + true, + new ParametersWithIV( + new KeyParameter(secrets.getAes()), new byte[encCipher.getBlockSize()])); + + final var decCipher = new AESEngine(); + dec = new SICBlockCipher(decCipher); + dec.init( + false, + new ParametersWithIV( + new KeyParameter(secrets.getAes()), new byte[decCipher.getBlockSize()])); + + egressMac = secrets.getEgressMac(); + ingressMac = secrets.getIngressMac(); + } + + public void writeFrame(byte[] frame, OutputStream out) throws IOException { + final var headBuffer = new byte[32]; + headBuffer[0] = (byte) (frame.length >> 16); + headBuffer[1] = (byte) (frame.length >> 8); + headBuffer[2] = (byte) (frame.length); + + enc.processBytes(headBuffer, 0, 16, headBuffer, 0); + final var headBufferMacResult = updateEgressMac(egressMac, headBuffer); + out.write(headBuffer, 0, 16); + out.write(headBufferMacResult, 0, 16); + + final var buff = new byte[256]; + final var payloadStream = new ByteArrayInputStream(frame); + while (true) { + final var n = payloadStream.read(buff); + if (n <= 0) { + break; + } + enc.processBytes(buff, 0, n, buff, 0); + egressMac.update(buff, 0, n); + out.write(buff, 0, n); + } + final var paddingSize = 16 - (frame.length % 16); + final var padding = new byte[16]; + if (paddingSize < 16) { + enc.processBytes(padding, 0, paddingSize, buff, 0); + egressMac.update(buff, 0, paddingSize); + out.write(buff, 0, paddingSize); + } + + final var macBuffer = new byte[egressMac.getDigestSize()]; + doSum(egressMac, macBuffer); + final var egressMacResult = updateEgressMac(egressMac, macBuffer); + out.write(egressMacResult, 0, 16); + } + + public Optional tryReadSingleFrame(ByteBuf input) throws IOException { + if (input.readableBytes() < HEADER_SIZE) { + return Optional.empty(); + } + + final var totalBodySize = readHeader(input); + + if (input.readableBytes() < HEADER_SIZE + totalBodySize) { + return Optional.empty(); + } + + final var paddingSize = totalBodySize % 16 == 0 ? 0 : 16 - (totalBodySize % 16); + final var payloadBuffer = new byte[totalBodySize + paddingSize + MAC_SIZE]; + input.getBytes(HEADER_SIZE, payloadBuffer, 0, payloadBuffer.length); + + final var frameSize = payloadBuffer.length - MAC_SIZE; + ingressMac.update(payloadBuffer, 0, frameSize); + dec.processBytes(payloadBuffer, 0, frameSize, payloadBuffer, 0); + + final var macBuffer = new byte[ingressMac.getDigestSize()]; + doSum(ingressMac, macBuffer); + updateIngressMac(ingressMac, macBuffer, payloadBuffer, frameSize); + + final var bodyBuffer = new byte[totalBodySize]; + System.arraycopy(payloadBuffer, 0, bodyBuffer, 0, totalBodySize); + + return Optional.of(bodyBuffer); + } + + private int readHeader(ByteBuf input) throws IOException { + final var headBuffer = new byte[32]; + input.getBytes(0, headBuffer, 0, headBuffer.length); + + updateIngressMac(ingressMac, headBuffer, headBuffer, 16); + dec.processBytes(headBuffer, 0, 16, headBuffer, 0); + + int totalBodySize = headBuffer[0] & 0xFF; + totalBodySize = (totalBodySize << 8) + (headBuffer[1] & 0xFF); + totalBodySize = (totalBodySize << 8) + (headBuffer[2] & 0xFF); + + return totalBodySize; + } + + private void updateIngressMac(KeccakDigest mac, byte[] seed, byte[] out, int outOffset) + throws IOException { + final var result = updateMac(mac, seed); + + for (int i = 0; i < MAC_SIZE; i++) { + if (out[i + outOffset] != result[i]) { + throw new IOException("MAC mismatch"); + } + } + } + + private byte[] updateEgressMac(KeccakDigest mac, byte[] seed) { + return updateMac(mac, seed); + } + + private byte[] updateMac(KeccakDigest mac, byte[] seed) { + final var aesBlock = new byte[mac.getDigestSize()]; + doSum(mac, aesBlock); + makeMacCipher().processBlock(aesBlock, 0, aesBlock, 0); + for (int i = 0; i < MAC_SIZE; i++) { + aesBlock[i] ^= seed[i]; + } + mac.update(aesBlock, 0, MAC_SIZE); + final var result = new byte[mac.getDigestSize()]; + doSum(mac, result); + return result; + } + + private AESEngine makeMacCipher() { + final var aesEngine = new AESEngine(); + aesEngine.init(true, new KeyParameter(mac)); + return aesEngine; + } + + private void doSum(KeccakDigest mac, byte[] out) { + new KeccakDigest(mac).doFinal(out, 0); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannel.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannel.java index cc6f4d2029..980760f1c2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannel.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannel.java @@ -64,22 +64,24 @@ package com.radixdlt.network.p2p.transport; +import static com.radixdlt.network.messaging.MessagingErrors.IO_ERROR; + import com.google.common.util.concurrent.RateLimiter; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECKeyOps; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.network.messaging.InboundMessage; import com.radixdlt.network.p2p.NodeId; -import com.radixdlt.network.p2p.RadixNodeUri; +import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.network.p2p.PeerEvent; +import com.radixdlt.network.p2p.PeerEvent.PeerConnected; +import com.radixdlt.network.p2p.PeerEvent.PeerDisconnected; +import com.radixdlt.network.p2p.PeerEvent.PeerHandshakeFailed; +import com.radixdlt.network.p2p.RadixNodeUri; import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult; import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeError; import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeSuccess; import com.radixdlt.network.p2p.transport.handshake.AuthHandshaker; -import com.radixdlt.network.p2p.PeerEvent.PeerConnected; -import com.radixdlt.network.p2p.PeerEvent.PeerDisconnected; -import com.radixdlt.network.p2p.PeerEvent.PeerHandshakeFailed; -import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.networks.Addressing; import com.radixdlt.serialization.Serialization; import com.radixdlt.utils.RateCalculator; @@ -94,265 +96,261 @@ import io.reactivex.rxjava3.core.BackpressureOverflowStrategy; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.processors.PublishProcessor; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.radix.time.Time; - import java.io.IOException; import java.net.InetSocketAddress; import java.security.SecureRandom; import java.time.Duration; import java.util.Objects; import java.util.Optional; - -import static com.radixdlt.network.messaging.MessagingErrors.IO_ERROR; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.radix.time.Time; /** - * Class that manages TCP connection channel. - * It takes care of the initial handshake, - * creating the frame and message codec - * and forwarding the messages to MessageCentral. + * Class that manages TCP connection channel. It takes care of the initial handshake, creating the + * frame and message codec and forwarding the messages to MessageCentral. */ public final class PeerChannel extends SimpleChannelInboundHandler { - private static final Logger log = LogManager.getLogger(); - - enum ChannelState { - INACTIVE, AUTH_HANDSHAKE, ACTIVE - } - - private final Object lock = new Object(); - private final RateLimiter droppedMessagesRateLimiter = RateLimiter.create(1.0); - private final PublishProcessor inboundMessageSink = PublishProcessor.create(); - private final Flowable inboundMessages; - - private final SystemCounters counters; - private final Addressing addressing; - private final EventDispatcher peerEventDispatcher; - private final Optional uri; - private final AuthHandshaker authHandshaker; - private final boolean isInitiator; - private final SocketChannel nettyChannel; - private Optional remoteAddress; - - private ChannelState state = ChannelState.INACTIVE; - private NodeId remoteNodeId; - private FrameCodec frameCodec; - - private final RateCalculator outMessagesStats = new RateCalculator(Duration.ofSeconds(10), 128); - - public PeerChannel( - P2PConfig config, - Addressing addressing, - int networkId, - SystemCounters counters, - Serialization serialization, - SecureRandom secureRandom, - ECKeyOps ecKeyOps, - EventDispatcher peerEventDispatcher, - Optional uri, - SocketChannel nettyChannel, - Optional remoteAddress - ) { - this.counters = Objects.requireNonNull(counters); - this.addressing = Objects.requireNonNull(addressing); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - this.uri = Objects.requireNonNull(uri); - uri.ifPresent(u -> this.remoteNodeId = u.getNodeId()); - this.authHandshaker = new AuthHandshaker(serialization, secureRandom, ecKeyOps, networkId); - this.nettyChannel = Objects.requireNonNull(nettyChannel); - this.remoteAddress = Objects.requireNonNull(remoteAddress); - - this.isInitiator = uri.isPresent(); - - this.inboundMessages = inboundMessageSink - .onBackpressureBuffer( - config.channelBufferSize(), - () -> { - this.counters.increment(SystemCounters.CounterType.NETWORKING_TCP_DROPPED_MESSAGES); - final var logLevel = droppedMessagesRateLimiter.tryAcquire() ? Level.WARN : Level.TRACE; - log.log(logLevel, "TCP msg buffer overflow, dropping msg on {}", this.toString()); - }, - BackpressureOverflowStrategy.DROP_LATEST); - - if (this.nettyChannel.isActive()) { - this.init(); - } - } - - private void initHandshake(NodeId remoteNodeId) { - final var initiatePacket = authHandshaker.initiate(remoteNodeId.getPublicKey()); - log.trace("Sending auth initiate to {}", this.toString()); - this.write(Unpooled.wrappedBuffer(initiatePacket)); - } - - public Flowable inboundMessages() { - return inboundMessages; - } - - private void handleHandshakeData(ByteBuf data) throws IOException { - if (this.isInitiator) { - log.trace("Auth response from {}", this.toString()); - final var handshakeResult = this.authHandshaker.handleResponseMessage(data); - this.finalizeHandshake(handshakeResult); - } else { - log.trace("Auth initiate from {}", this.toString()); - final var result = this.authHandshaker.handleInitialMessage(data); - this.write(Unpooled.wrappedBuffer(result.getFirst())); - this.finalizeHandshake(result.getSecond()); - } - } - - private void finalizeHandshake(AuthHandshakeResult handshakeResult) { - if (handshakeResult instanceof AuthHandshakeSuccess) { - final var successResult = (AuthHandshakeSuccess) handshakeResult; - this.remoteNodeId = successResult.getRemoteNodeId(); - this.frameCodec = new FrameCodec(successResult.getSecrets()); - this.state = ChannelState.ACTIVE; - log.trace("Successful auth handshake: {}", this.toString()); - peerEventDispatcher.dispatch(PeerConnected.create(this)); - } else { - final var errorResult = (AuthHandshakeError) handshakeResult; - log.warn("Auth handshake failed on {}: {}", this.toString(), errorResult.getMsg()); - peerEventDispatcher.dispatch(PeerHandshakeFailed.create(this)); - this.disconnect(); - } - } - - private void handleMessage(ByteBuf buf) throws IOException { - final var receiveTime = Time.currentTimestamp(); - - synchronized (this.lock) { - final var maybeFrame = this.frameCodec.tryReadSingleFrame(buf); - maybeFrame.ifPresentOrElse( - frame -> inboundMessageSink.onNext(new InboundMessage(receiveTime, remoteNodeId, frame)), - () -> log.error("Failed to read a complete frame: {}", this.toString()) - ); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - // if we weren't able to determine peer's address earlier, it should be available now - if (this.remoteAddress.isEmpty()) { - this.remoteAddress = Optional.ofNullable(this.nettyChannel.remoteAddress()); - } - - if (this.state == ChannelState.INACTIVE) { - this.init(); - } - } - - private void init() { - log.trace("Init: {}", this.toString()); - this.state = ChannelState.AUTH_HANDSHAKE; - if (this.isInitiator) { - this.initHandshake(this.remoteNodeId); - } - } - - @Override - public void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { - switch (this.state) { - case INACTIVE: - throw new RuntimeException("Unexpected read on inactive channel"); - case AUTH_HANDSHAKE: - this.handleHandshakeData(buf); - break; - case ACTIVE: - this.handleMessage(buf); - break; - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - log.info("Closed: {}", this.toString()); - - final var prevState = this.state; - this.state = ChannelState.INACTIVE; - this.inboundMessageSink.onComplete(); - - if (prevState == ChannelState.ACTIVE) { - // only send out event if peer was previously active - this.peerEventDispatcher.dispatch(PeerDisconnected.create(this)); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - log.warn("Exception on {}: {}", this.toString(), cause.getMessage()); - ctx.close(); - } - - private void write(ByteBuf data) { - this.nettyChannel.writeAndFlush(data); - } - - public Result send(byte[] data) { - synchronized (this.lock) { - if (this.state != ChannelState.ACTIVE) { - return IO_ERROR.result(); - } else { - try { - // we don't need to release the buffer manually as this is done by Netty (in writeAndFlush) - final var buf = PooledByteBufAllocator.DEFAULT.buffer(data.length); - try (var out = new ByteBufOutputStream(buf)) { - this.frameCodec.writeFrame(data, out); - } - this.write(buf); - this.outMessagesStats.tick(); - return Result.ok(new Object()); - } catch (IOException e) { - return IO_ERROR.result(); - } - } - } - } - - public long sentMessagesRate() { - return this.outMessagesStats.currentRate(); - } - - public void disconnect() { - synchronized (this.lock) { - this.nettyChannel.close(); - } - } - - public NodeId getRemoteNodeId() { - return this.remoteNodeId; - } - - public boolean isInbound() { - return !this.isInitiator; - } - - public boolean isOutbound() { - return this.isInitiator; - } - - public Optional getUri() { - return this.uri; - } - - public String getHost() { - return remoteAddress.map(InetSocketAddress::getHostString).orElse("?"); - } - - public int getPort() { - return remoteAddress.map(InetSocketAddress::getPort).orElse(0); - } - - @Override - public String toString() { - return String.format( - "{%s %s@%s:%s | %s}", - isInitiator ? "<-" : "->", - remoteNodeId != null ? addressing.forNodes().of(this.remoteNodeId.getPublicKey()) : "?", - getHost(), - getPort(), - state - ); - } + private static final Logger log = LogManager.getLogger(); + + enum ChannelState { + INACTIVE, + AUTH_HANDSHAKE, + ACTIVE + } + + private final Object lock = new Object(); + private final RateLimiter droppedMessagesRateLimiter = RateLimiter.create(1.0); + private final PublishProcessor inboundMessageSink = PublishProcessor.create(); + private final Flowable inboundMessages; + + private final SystemCounters counters; + private final Addressing addressing; + private final EventDispatcher peerEventDispatcher; + private final Optional uri; + private final AuthHandshaker authHandshaker; + private final boolean isInitiator; + private final SocketChannel nettyChannel; + private Optional remoteAddress; + + private ChannelState state = ChannelState.INACTIVE; + private NodeId remoteNodeId; + private FrameCodec frameCodec; + + private final RateCalculator outMessagesStats = new RateCalculator(Duration.ofSeconds(10), 128); + + public PeerChannel( + P2PConfig config, + Addressing addressing, + int networkId, + SystemCounters counters, + Serialization serialization, + SecureRandom secureRandom, + ECKeyOps ecKeyOps, + EventDispatcher peerEventDispatcher, + Optional uri, + SocketChannel nettyChannel, + Optional remoteAddress) { + this.counters = Objects.requireNonNull(counters); + this.addressing = Objects.requireNonNull(addressing); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + this.uri = Objects.requireNonNull(uri); + uri.ifPresent(u -> this.remoteNodeId = u.getNodeId()); + this.authHandshaker = new AuthHandshaker(serialization, secureRandom, ecKeyOps, networkId); + this.nettyChannel = Objects.requireNonNull(nettyChannel); + this.remoteAddress = Objects.requireNonNull(remoteAddress); + + this.isInitiator = uri.isPresent(); + + this.inboundMessages = + inboundMessageSink.onBackpressureBuffer( + config.channelBufferSize(), + () -> { + this.counters.increment(SystemCounters.CounterType.NETWORKING_TCP_DROPPED_MESSAGES); + final var logLevel = + droppedMessagesRateLimiter.tryAcquire() ? Level.WARN : Level.TRACE; + log.log(logLevel, "TCP msg buffer overflow, dropping msg on {}", this.toString()); + }, + BackpressureOverflowStrategy.DROP_LATEST); + + if (this.nettyChannel.isActive()) { + this.init(); + } + } + + private void initHandshake(NodeId remoteNodeId) { + final var initiatePacket = authHandshaker.initiate(remoteNodeId.getPublicKey()); + log.trace("Sending auth initiate to {}", this.toString()); + this.write(Unpooled.wrappedBuffer(initiatePacket)); + } + + public Flowable inboundMessages() { + return inboundMessages; + } + + private void handleHandshakeData(ByteBuf data) throws IOException { + if (this.isInitiator) { + log.trace("Auth response from {}", this.toString()); + final var handshakeResult = this.authHandshaker.handleResponseMessage(data); + this.finalizeHandshake(handshakeResult); + } else { + log.trace("Auth initiate from {}", this.toString()); + final var result = this.authHandshaker.handleInitialMessage(data); + this.write(Unpooled.wrappedBuffer(result.getFirst())); + this.finalizeHandshake(result.getSecond()); + } + } + + private void finalizeHandshake(AuthHandshakeResult handshakeResult) { + if (handshakeResult instanceof AuthHandshakeSuccess) { + final var successResult = (AuthHandshakeSuccess) handshakeResult; + this.remoteNodeId = successResult.getRemoteNodeId(); + this.frameCodec = new FrameCodec(successResult.getSecrets()); + this.state = ChannelState.ACTIVE; + log.trace("Successful auth handshake: {}", this.toString()); + peerEventDispatcher.dispatch(PeerConnected.create(this)); + } else { + final var errorResult = (AuthHandshakeError) handshakeResult; + log.warn("Auth handshake failed on {}: {}", this.toString(), errorResult.getMsg()); + peerEventDispatcher.dispatch(PeerHandshakeFailed.create(this)); + this.disconnect(); + } + } + + private void handleMessage(ByteBuf buf) throws IOException { + final var receiveTime = Time.currentTimestamp(); + + synchronized (this.lock) { + final var maybeFrame = this.frameCodec.tryReadSingleFrame(buf); + maybeFrame.ifPresentOrElse( + frame -> inboundMessageSink.onNext(new InboundMessage(receiveTime, remoteNodeId, frame)), + () -> log.error("Failed to read a complete frame: {}", this.toString())); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + // if we weren't able to determine peer's address earlier, it should be available now + if (this.remoteAddress.isEmpty()) { + this.remoteAddress = Optional.ofNullable(this.nettyChannel.remoteAddress()); + } + + if (this.state == ChannelState.INACTIVE) { + this.init(); + } + } + + private void init() { + log.trace("Init: {}", this.toString()); + this.state = ChannelState.AUTH_HANDSHAKE; + if (this.isInitiator) { + this.initHandshake(this.remoteNodeId); + } + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { + switch (this.state) { + case INACTIVE: + throw new RuntimeException("Unexpected read on inactive channel"); + case AUTH_HANDSHAKE: + this.handleHandshakeData(buf); + break; + case ACTIVE: + this.handleMessage(buf); + break; + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + log.info("Closed: {}", this.toString()); + + final var prevState = this.state; + this.state = ChannelState.INACTIVE; + this.inboundMessageSink.onComplete(); + + if (prevState == ChannelState.ACTIVE) { + // only send out event if peer was previously active + this.peerEventDispatcher.dispatch(PeerDisconnected.create(this)); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + log.warn("Exception on {}: {}", this.toString(), cause.getMessage()); + ctx.close(); + } + + private void write(ByteBuf data) { + this.nettyChannel.writeAndFlush(data); + } + + public Result send(byte[] data) { + synchronized (this.lock) { + if (this.state != ChannelState.ACTIVE) { + return IO_ERROR.result(); + } else { + try { + // we don't need to release the buffer manually as this is done by Netty (in + // writeAndFlush) + final var buf = PooledByteBufAllocator.DEFAULT.buffer(data.length); + try (var out = new ByteBufOutputStream(buf)) { + this.frameCodec.writeFrame(data, out); + } + this.write(buf); + this.outMessagesStats.tick(); + return Result.ok(new Object()); + } catch (IOException e) { + return IO_ERROR.result(); + } + } + } + } + + public long sentMessagesRate() { + return this.outMessagesStats.currentRate(); + } + + public void disconnect() { + synchronized (this.lock) { + this.nettyChannel.close(); + } + } + + public NodeId getRemoteNodeId() { + return this.remoteNodeId; + } + + public boolean isInbound() { + return !this.isInitiator; + } + + public boolean isOutbound() { + return this.isInitiator; + } + + public Optional getUri() { + return this.uri; + } + + public String getHost() { + return remoteAddress.map(InetSocketAddress::getHostString).orElse("?"); + } + + public int getPort() { + return remoteAddress.map(InetSocketAddress::getPort).orElse(0); + } + + @Override + public String toString() { + return String.format( + "{%s %s@%s:%s | %s}", + isInitiator ? "<-" : "->", + remoteNodeId != null ? addressing.forNodes().of(this.remoteNodeId.getPublicKey()) : "?", + getHost(), + getPort(), + state); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannelInitializer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannelInitializer.java index 2c90355eef..f3c3ffaf5f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannelInitializer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerChannelInitializer.java @@ -68,9 +68,9 @@ import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.ECKeyOps; import com.radixdlt.environment.EventDispatcher; -import com.radixdlt.network.p2p.RadixNodeUri; -import com.radixdlt.network.p2p.PeerEvent; import com.radixdlt.network.p2p.P2PConfig; +import com.radixdlt.network.p2p.PeerEvent; +import com.radixdlt.network.p2p.RadixNodeUri; import com.radixdlt.network.p2p.transport.logging.LogSink; import com.radixdlt.network.p2p.transport.logging.LoggingHandler; import com.radixdlt.networks.Addressing; @@ -84,145 +84,148 @@ import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.bytes.ByteArrayDecoder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.io.IOException; import java.net.InetSocketAddress; import java.security.SecureRandom; import java.util.Objects; import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public final class PeerChannelInitializer extends ChannelInitializer { - private static final Logger log = LogManager.getLogger(); - - private static final int MAX_PACKET_LENGTH = 1024 * 1024; - private static final int FRAME_HEADER_LENGTH = Integer.BYTES; - private static final int RECEIVE_BUFFER_SIZE = 1024 * 1024; - private static final int SOCKET_BACKLOG_SIZE = 1024; - - private final P2PConfig config; - private final Addressing addressing; - private final int networkId; - private final SystemCounters counters; - private final Serialization serialization; - private final SecureRandom secureRandom; - private final ECKeyOps ecKeyOps; - private final EventDispatcher peerEventDispatcher; - private final Optional uri; - - public PeerChannelInitializer( - P2PConfig config, - Addressing addressing, - int networkId, - SystemCounters counters, - Serialization serialization, - SecureRandom secureRandom, - ECKeyOps ecKeyOps, - EventDispatcher peerEventDispatcher, - Optional uri - ) { - this.config = Objects.requireNonNull(config); - this.addressing = Objects.requireNonNull(addressing); - this.networkId = networkId; - this.counters = Objects.requireNonNull(counters); - this.serialization = Objects.requireNonNull(serialization); - this.secureRandom = Objects.requireNonNull(secureRandom); - this.ecKeyOps = Objects.requireNonNull(ecKeyOps); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - this.uri = Objects.requireNonNull(uri); - } - - @Override - protected void initChannel(SocketChannel socketChannel) { - counters.increment(CounterType.NETWORKING_P2P_CHANNELS_INITIALIZED); - - final var socketChannelConfig = socketChannel.config(); - socketChannelConfig.setReceiveBufferSize(MAX_PACKET_LENGTH); - socketChannelConfig.setSendBufferSize(MAX_PACKET_LENGTH); - socketChannelConfig.setOption(ChannelOption.SO_RCVBUF, RECEIVE_BUFFER_SIZE); - socketChannelConfig.setOption(ChannelOption.SO_BACKLOG, SOCKET_BACKLOG_SIZE); - - if (log.isDebugEnabled()) { - socketChannel.pipeline().addLast(new LoggingHandler(LogSink.using(log), false)); - } - - uri.ifPresent(u -> log.trace("Initializing peer channel to {}", u)); - - if (uri.isEmpty() && this.config.useProxyProtocol()) { - /* If the node is configured to support the PROXY protocol, we add a dedicated - pipeline that decodes a single header packet (with host's real ip address and port), and then - replaces this one-time pipeline with the main one (which forwards to PeerChannel). */ - createProxyProtocolPipeline(socketChannel); - } else { - createPeerChannelPipeline(socketChannel, socketChannel.remoteAddress()); - } - } - - private void createProxyProtocolPipeline(SocketChannel socketChannel) { - socketChannel.pipeline() - .addLast("decode_proxy_header_line", new LineBasedFrameDecoder(255, true, true)) - .addLast("decode_proxy_header_bytes", new ByteArrayDecoder()) - .addLast("handle_proxy_header", new ProxyHeaderHandler(socketChannel)); - } - - private final class ProxyHeaderHandler extends SimpleChannelInboundHandler { - private final SocketChannel socketChannel; - - ProxyHeaderHandler(SocketChannel socketChannel) { - this.socketChannel = socketChannel; - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws IOException { - final var clientAddress = parseProxyHeader(new String(msg)); - - // remove the proxy pipeline - ctx.pipeline().remove(LineBasedFrameDecoder.class); - ctx.pipeline().remove(ByteArrayDecoder.class); - ctx.pipeline().remove(ProxyHeaderHandler.class); - - // and create a regular peer channel pipeline - createPeerChannelPipeline(socketChannel, clientAddress); - } - - private InetSocketAddress parseProxyHeader(String line) throws IOException { - /* The proxy protocol line is a single line that ends with a carriage return - and line feed ("\r\n"), and has the following form: - PROXY_STRING + single space + INET_PROTOCOL + single space + CLIENT_IP + single space - + PROXY_IP + single space + CLIENT_PORT + single space + PROXY_PORT + "\r\n" */ - final var components = line.split(" "); - - if (!components[0].equals("PROXY") || !components[1].startsWith("TCP")) { - log.warn("Received invalid PROXY protocol header line: {}", line); - socketChannel.close(); - throw new IOException("Invalid PROXY header"); - } - - return InetSocketAddress.createUnresolved(components[2], Integer.parseInt(components[4])); - } - } - - private void createPeerChannelPipeline(SocketChannel socketChannel, InetSocketAddress remoteAddress) { - final var peerChannel = new PeerChannel( - config, - addressing, - networkId, - counters, - serialization, - secureRandom, - ecKeyOps, - peerEventDispatcher, - uri, - socketChannel, - Optional.ofNullable(remoteAddress) - ); - - final int packetLength = MAX_PACKET_LENGTH + FRAME_HEADER_LENGTH; - final int headerLength = FRAME_HEADER_LENGTH; - socketChannel.pipeline() - .addLast("unpack", new LengthFieldBasedFrameDecoder(packetLength, 0, headerLength, 0, headerLength)) - .addLast("handler", peerChannel) - .addLast("pack", new LengthFieldPrepender(headerLength)); - } + private static final Logger log = LogManager.getLogger(); + + private static final int MAX_PACKET_LENGTH = 1024 * 1024; + private static final int FRAME_HEADER_LENGTH = Integer.BYTES; + private static final int RECEIVE_BUFFER_SIZE = 1024 * 1024; + private static final int SOCKET_BACKLOG_SIZE = 1024; + + private final P2PConfig config; + private final Addressing addressing; + private final int networkId; + private final SystemCounters counters; + private final Serialization serialization; + private final SecureRandom secureRandom; + private final ECKeyOps ecKeyOps; + private final EventDispatcher peerEventDispatcher; + private final Optional uri; + + public PeerChannelInitializer( + P2PConfig config, + Addressing addressing, + int networkId, + SystemCounters counters, + Serialization serialization, + SecureRandom secureRandom, + ECKeyOps ecKeyOps, + EventDispatcher peerEventDispatcher, + Optional uri) { + this.config = Objects.requireNonNull(config); + this.addressing = Objects.requireNonNull(addressing); + this.networkId = networkId; + this.counters = Objects.requireNonNull(counters); + this.serialization = Objects.requireNonNull(serialization); + this.secureRandom = Objects.requireNonNull(secureRandom); + this.ecKeyOps = Objects.requireNonNull(ecKeyOps); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + this.uri = Objects.requireNonNull(uri); + } + + @Override + protected void initChannel(SocketChannel socketChannel) { + counters.increment(CounterType.NETWORKING_P2P_CHANNELS_INITIALIZED); + + final var socketChannelConfig = socketChannel.config(); + socketChannelConfig.setReceiveBufferSize(MAX_PACKET_LENGTH); + socketChannelConfig.setSendBufferSize(MAX_PACKET_LENGTH); + socketChannelConfig.setOption(ChannelOption.SO_RCVBUF, RECEIVE_BUFFER_SIZE); + socketChannelConfig.setOption(ChannelOption.SO_BACKLOG, SOCKET_BACKLOG_SIZE); + + if (log.isDebugEnabled()) { + socketChannel.pipeline().addLast(new LoggingHandler(LogSink.using(log), false)); + } + + uri.ifPresent(u -> log.trace("Initializing peer channel to {}", u)); + + if (uri.isEmpty() && this.config.useProxyProtocol()) { + /* If the node is configured to support the PROXY protocol, we add a dedicated + pipeline that decodes a single header packet (with host's real ip address and port), and then + replaces this one-time pipeline with the main one (which forwards to PeerChannel). */ + createProxyProtocolPipeline(socketChannel); + } else { + createPeerChannelPipeline(socketChannel, socketChannel.remoteAddress()); + } + } + + private void createProxyProtocolPipeline(SocketChannel socketChannel) { + socketChannel + .pipeline() + .addLast("decode_proxy_header_line", new LineBasedFrameDecoder(255, true, true)) + .addLast("decode_proxy_header_bytes", new ByteArrayDecoder()) + .addLast("handle_proxy_header", new ProxyHeaderHandler(socketChannel)); + } + + private final class ProxyHeaderHandler extends SimpleChannelInboundHandler { + private final SocketChannel socketChannel; + + ProxyHeaderHandler(SocketChannel socketChannel) { + this.socketChannel = socketChannel; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws IOException { + final var clientAddress = parseProxyHeader(new String(msg)); + + // remove the proxy pipeline + ctx.pipeline().remove(LineBasedFrameDecoder.class); + ctx.pipeline().remove(ByteArrayDecoder.class); + ctx.pipeline().remove(ProxyHeaderHandler.class); + + // and create a regular peer channel pipeline + createPeerChannelPipeline(socketChannel, clientAddress); + } + + private InetSocketAddress parseProxyHeader(String line) throws IOException { + /* The proxy protocol line is a single line that ends with a carriage return + and line feed ("\r\n"), and has the following form: + PROXY_STRING + single space + INET_PROTOCOL + single space + CLIENT_IP + single space + + PROXY_IP + single space + CLIENT_PORT + single space + PROXY_PORT + "\r\n" */ + final var components = line.split(" "); + + if (!components[0].equals("PROXY") || !components[1].startsWith("TCP")) { + log.warn("Received invalid PROXY protocol header line: {}", line); + socketChannel.close(); + throw new IOException("Invalid PROXY header"); + } + + return InetSocketAddress.createUnresolved(components[2], Integer.parseInt(components[4])); + } + } + + private void createPeerChannelPipeline( + SocketChannel socketChannel, InetSocketAddress remoteAddress) { + final var peerChannel = + new PeerChannel( + config, + addressing, + networkId, + counters, + serialization, + secureRandom, + ecKeyOps, + peerEventDispatcher, + uri, + socketChannel, + Optional.ofNullable(remoteAddress)); + + final int packetLength = MAX_PACKET_LENGTH + FRAME_HEADER_LENGTH; + final int headerLength = FRAME_HEADER_LENGTH; + socketChannel + .pipeline() + .addLast( + "unpack", + new LengthFieldBasedFrameDecoder(packetLength, 0, headerLength, 0, headerLength)) + .addLast("handler", peerChannel) + .addLast("pack", new LengthFieldPrepender(headerLength)); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrap.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrap.java index c70f29125a..978907abae 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrap.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrap.java @@ -67,5 +67,5 @@ import com.radixdlt.network.p2p.RadixNodeUri; public interface PeerOutboundBootstrap { - void initOutboundConnection(RadixNodeUri uri); + void initOutboundConnection(RadixNodeUri uri); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrapImpl.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrapImpl.java index 1e217663ce..9ccd9341a4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrapImpl.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerOutboundBootstrapImpl.java @@ -78,64 +78,63 @@ import io.netty.channel.ChannelOption; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; - import java.security.SecureRandom; import java.util.Objects; import java.util.Optional; public final class PeerOutboundBootstrapImpl implements PeerOutboundBootstrap { - private final P2PConfig config; - private final Addressing addressing; - private final int networkId; - private final SystemCounters counters; - private final Serialization serialization; - private final SecureRandom secureRandom; - private final ECKeyOps ecKeyOps; - private final EventDispatcher peerEventDispatcher; + private final P2PConfig config; + private final Addressing addressing; + private final int networkId; + private final SystemCounters counters; + private final Serialization serialization; + private final SecureRandom secureRandom; + private final ECKeyOps ecKeyOps; + private final EventDispatcher peerEventDispatcher; - private final NioEventLoopGroup clientWorkerGroup; + private final NioEventLoopGroup clientWorkerGroup; - @Inject - public PeerOutboundBootstrapImpl( - P2PConfig config, - Addressing addressing, - @NetworkId int networkId, - SystemCounters counters, - Serialization serialization, - SecureRandom secureRandom, - ECKeyOps ecKeyOps, - EventDispatcher peerEventDispatcher - ) { - this.config = Objects.requireNonNull(config); - this.addressing = Objects.requireNonNull(addressing); - this.networkId = networkId; - this.counters = Objects.requireNonNull(counters); - this.serialization = Objects.requireNonNull(serialization); - this.secureRandom = Objects.requireNonNull(secureRandom); - this.ecKeyOps = Objects.requireNonNull(ecKeyOps); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + @Inject + public PeerOutboundBootstrapImpl( + P2PConfig config, + Addressing addressing, + @NetworkId int networkId, + SystemCounters counters, + Serialization serialization, + SecureRandom secureRandom, + ECKeyOps ecKeyOps, + EventDispatcher peerEventDispatcher) { + this.config = Objects.requireNonNull(config); + this.addressing = Objects.requireNonNull(addressing); + this.networkId = networkId; + this.counters = Objects.requireNonNull(counters); + this.serialization = Objects.requireNonNull(serialization); + this.secureRandom = Objects.requireNonNull(secureRandom); + this.ecKeyOps = Objects.requireNonNull(ecKeyOps); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - this.clientWorkerGroup = new NioEventLoopGroup(); - } + this.clientWorkerGroup = new NioEventLoopGroup(); + } - @Override - public void initOutboundConnection(RadixNodeUri uri) { - final var bootstrap = new Bootstrap(); - bootstrap.group(clientWorkerGroup) - .channel(NioSocketChannel.class) - .option(ChannelOption.TCP_NODELAY, true) - .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new PeerChannelInitializer( - config, - addressing, - networkId, - counters, - serialization, - secureRandom, - ecKeyOps, - peerEventDispatcher, - Optional.of(uri) - )) - .connect(uri.getHost(), uri.getPort()); - } + @Override + public void initOutboundConnection(RadixNodeUri uri) { + final var bootstrap = new Bootstrap(); + bootstrap + .group(clientWorkerGroup) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.SO_KEEPALIVE, true) + .handler( + new PeerChannelInitializer( + config, + addressing, + networkId, + counters, + serialization, + secureRandom, + ecKeyOps, + peerEventDispatcher, + Optional.of(uri))) + .connect(uri.getHost(), uri.getPort()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerServerBootstrap.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerServerBootstrap.java index 3b0e8a0e76..0680be0a9c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerServerBootstrap.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/PeerServerBootstrap.java @@ -68,8 +68,8 @@ import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECKeyOps; import com.radixdlt.environment.EventDispatcher; -import com.radixdlt.network.p2p.PeerEvent; import com.radixdlt.network.p2p.P2PConfig; +import com.radixdlt.network.p2p.PeerEvent; import com.radixdlt.networks.Addressing; import com.radixdlt.networks.NetworkId; import com.radixdlt.serialization.Serialization; @@ -77,65 +77,64 @@ import io.netty.channel.ChannelOption; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; - import java.security.SecureRandom; import java.util.Objects; import java.util.Optional; public final class PeerServerBootstrap { - private static final int BACKLOG_SIZE = 100; + private static final int BACKLOG_SIZE = 100; - private final P2PConfig config; - private final Addressing addressing; - private final int networkId; - private final SystemCounters counters; - private final Serialization serialization; - private final SecureRandom secureRandom; - private final ECKeyOps ecKeyOps; - private final EventDispatcher peerEventDispatcher; + private final P2PConfig config; + private final Addressing addressing; + private final int networkId; + private final SystemCounters counters; + private final Serialization serialization; + private final SecureRandom secureRandom; + private final ECKeyOps ecKeyOps; + private final EventDispatcher peerEventDispatcher; - @Inject - public PeerServerBootstrap( - P2PConfig config, - Addressing addressing, - @NetworkId int networkId, - SystemCounters counters, - Serialization serialization, - SecureRandom secureRandom, - ECKeyOps ecKeyOps, - EventDispatcher peerEventDispatcher - ) { - this.config = Objects.requireNonNull(config); - this.addressing = Objects.requireNonNull(addressing); - this.networkId = networkId; - this.counters = Objects.requireNonNull(counters); - this.serialization = Objects.requireNonNull(serialization); - this.secureRandom = Objects.requireNonNull(secureRandom); - this.ecKeyOps = Objects.requireNonNull(ecKeyOps); - this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); - } + @Inject + public PeerServerBootstrap( + P2PConfig config, + Addressing addressing, + @NetworkId int networkId, + SystemCounters counters, + Serialization serialization, + SecureRandom secureRandom, + ECKeyOps ecKeyOps, + EventDispatcher peerEventDispatcher) { + this.config = Objects.requireNonNull(config); + this.addressing = Objects.requireNonNull(addressing); + this.networkId = networkId; + this.counters = Objects.requireNonNull(counters); + this.serialization = Objects.requireNonNull(serialization); + this.secureRandom = Objects.requireNonNull(secureRandom); + this.ecKeyOps = Objects.requireNonNull(ecKeyOps); + this.peerEventDispatcher = Objects.requireNonNull(peerEventDispatcher); + } - public void start() throws InterruptedException { - final var serverGroup = new NioEventLoopGroup(1); - final var workerGroup = new NioEventLoopGroup(); + public void start() throws InterruptedException { + final var serverGroup = new NioEventLoopGroup(1); + final var workerGroup = new NioEventLoopGroup(); - final var serverBootstrap = new ServerBootstrap(); - serverBootstrap.group(serverGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .option(ChannelOption.SO_BACKLOG, BACKLOG_SIZE) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.peerConnectionTimeout()) - .childHandler(new PeerChannelInitializer( - config, - addressing, - networkId, - counters, - serialization, - secureRandom, - ecKeyOps, - peerEventDispatcher, - Optional.empty() - )); + final var serverBootstrap = new ServerBootstrap(); + serverBootstrap + .group(serverGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, BACKLOG_SIZE) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.peerConnectionTimeout()) + .childHandler( + new PeerChannelInitializer( + config, + addressing, + networkId, + counters, + serialization, + secureRandom, + ecKeyOps, + peerEventDispatcher, + Optional.empty())); - serverBootstrap.bind(config.listenAddress(), config.listenPort()).sync(); - } + serverBootstrap.bind(config.listenAddress(), config.listenPort()).sync(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakeResult.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakeResult.java index 9e70cfe959..fe1eba94d4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakeResult.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakeResult.java @@ -66,52 +66,51 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.network.p2p.NodeId; - import java.util.Optional; public interface AuthHandshakeResult { - static AuthHandshakeSuccess success(ECPublicKey remotePubKey, Secrets secrets) { - return new AuthHandshakeSuccess(NodeId.fromPublicKey(remotePubKey), secrets); - } + static AuthHandshakeSuccess success(ECPublicKey remotePubKey, Secrets secrets) { + return new AuthHandshakeSuccess(NodeId.fromPublicKey(remotePubKey), secrets); + } - static AuthHandshakeError error(String msg, Optional maybeNodeId) { - return new AuthHandshakeError(msg, maybeNodeId); - } + static AuthHandshakeError error(String msg, Optional maybeNodeId) { + return new AuthHandshakeError(msg, maybeNodeId); + } - final class AuthHandshakeSuccess implements AuthHandshakeResult { - private final NodeId remoteNodeId; - private final Secrets secrets; + final class AuthHandshakeSuccess implements AuthHandshakeResult { + private final NodeId remoteNodeId; + private final Secrets secrets; - private AuthHandshakeSuccess(NodeId remoteNodeId, Secrets secrets) { - this.remoteNodeId = remoteNodeId; - this.secrets = secrets; - } + private AuthHandshakeSuccess(NodeId remoteNodeId, Secrets secrets) { + this.remoteNodeId = remoteNodeId; + this.secrets = secrets; + } - public NodeId getRemoteNodeId() { - return remoteNodeId; - } + public NodeId getRemoteNodeId() { + return remoteNodeId; + } - public Secrets getSecrets() { - return secrets; - } - } + public Secrets getSecrets() { + return secrets; + } + } - final class AuthHandshakeError implements AuthHandshakeResult { - private final String msg; - private final Optional maybeNodeId; + final class AuthHandshakeError implements AuthHandshakeResult { + private final String msg; + private final Optional maybeNodeId; - public AuthHandshakeError(String msg, Optional maybeNodeId) { - this.msg = msg; - this.maybeNodeId = maybeNodeId; - } + public AuthHandshakeError(String msg, Optional maybeNodeId) { + this.msg = msg; + this.maybeNodeId = maybeNodeId; + } - public String getMsg() { - return msg; - } + public String getMsg() { + return msg; + } - public Optional getMaybeNodeId() { - return maybeNodeId; - } - } + public Optional getMaybeNodeId() { + return maybeNodeId; + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshaker.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshaker.java index d0f5cb53d9..8bdeba4dfb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshaker.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshaker.java @@ -64,6 +64,10 @@ package com.radixdlt.network.p2p.transport.handshake; +import static com.radixdlt.crypto.HashUtils.kec256; +import static com.radixdlt.utils.Bytes.bigIntegerToBytes; +import static com.radixdlt.utils.Bytes.xor; + import com.google.common.hash.HashCode; import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECIESCoder; @@ -73,250 +77,259 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.network.p2p.NodeId; +import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeSuccess; import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.Serialization; -import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeSuccess; import com.radixdlt.utils.Pair; import io.netty.buffer.ByteBuf; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; -import org.bouncycastle.crypto.digests.KeccakDigest; -import org.bouncycastle.crypto.params.ECPrivateKeyParameters; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; - import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.Objects; import java.util.Optional; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; +import org.bouncycastle.crypto.digests.KeccakDigest; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import static com.radixdlt.crypto.HashUtils.kec256; -import static com.radixdlt.utils.Bytes.bigIntegerToBytes; -import static com.radixdlt.utils.Bytes.xor; - -/** - * Handles the auth handshake to create an encrypted communication channel between peers. - */ +/** Handles the auth handshake to create an encrypted communication channel between peers. */ public final class AuthHandshaker { - private static final byte STATUS_OK = 0x01; - private static final byte STATUS_ERROR = 0x02; - - private static final int NONCE_SIZE = 32; - private static final int MIN_PADDING = 100; - private static final int MAX_PADDING = 300; - private static final int SECRET_SIZE = 32; - private static final int MAC_SIZE = 256; - - private final Serialization serialization; - private final SecureRandom secureRandom; - private final ECKeyOps ecKeyOps; - private final byte[] nonce; - private final ECKeyPair ephemeralKey; - private final int networkId; - private boolean isInitiator = false; - private Optional initiatePacketOpt = Optional.empty(); - private Optional responsePacketOpt = Optional.empty(); - private Optional remotePubKeyOpt; - - public AuthHandshaker( - Serialization serialization, - SecureRandom secureRandom, - ECKeyOps ecKeyOps, - int networkId - ) { - this.serialization = Objects.requireNonNull(serialization); - this.secureRandom = Objects.requireNonNull(secureRandom); - this.ecKeyOps = Objects.requireNonNull(ecKeyOps); - this.nonce = randomBytes(NONCE_SIZE); - this.ephemeralKey = ECKeyPair.generateNew(); - this.networkId = networkId; - } - - public byte[] initiate(ECPublicKey remotePubKey) { - final var message = createAuthInitiateMessage(remotePubKey); - final var encoded = serialization.toDson(message, DsonOutput.Output.WIRE); - final var padding = randomBytes(secureRandom.nextInt(MAX_PADDING - MIN_PADDING) + MIN_PADDING); - final var padded = new byte[encoded.length + padding.length]; - System.arraycopy(encoded, 0, padded, 0, encoded.length); - System.arraycopy(padding, 0, padded, encoded.length, padding.length); - - final var encryptedSize = padded.length + ECIESCoder.OVERHEAD_SIZE; - final var sizePrefix = ByteBuffer.allocate(2).putShort((short) encryptedSize).array(); - final var encryptedPayload = ECIESCoder.encrypt(remotePubKey.getEcPoint(), padded, sizePrefix); - final var packet = new byte[sizePrefix.length + encryptedPayload.length]; - System.arraycopy(sizePrefix, 0, packet, 0, sizePrefix.length); - System.arraycopy(encryptedPayload, 0, packet, sizePrefix.length, encryptedPayload.length); - - this.isInitiator = true; - this.initiatePacketOpt = Optional.of(packet); - this.remotePubKeyOpt = Optional.of(remotePubKey); - - return packet; - } - - private AuthInitiateMessage createAuthInitiateMessage(ECPublicKey remotePubKey) { - final var sharedSecret = bigIntegerToBytes(ecKeyOps.ecdhAgreement(remotePubKey), NONCE_SIZE); - final var messageToSign = xor(sharedSecret, nonce); - final var signature = ephemeralKey.sign(messageToSign); - return new AuthInitiateMessage( - signature, - HashCode.fromBytes(ecKeyOps.nodePubKey().getBytes()), - HashCode.fromBytes(nonce), - networkId - ); - } - - public Pair handleInitialMessage(ByteBuf data) { - try { - final var sizeBytes = new byte[2]; - data.getBytes(0, sizeBytes, 0, sizeBytes.length); - final var encryptedPayload = new byte[data.readableBytes() - sizeBytes.length]; - data.getBytes(sizeBytes.length, encryptedPayload, 0, encryptedPayload.length); - final var plaintext = ecKeyOps.eciesDecrypt(encryptedPayload, sizeBytes); - final var message = serialization.fromDson(plaintext, AuthInitiateMessage.class); - final var remotePubKey = ECPublicKey.fromBytes(message.getPublicKey().asBytes()); - - if (message.getNetworkId() != this.networkId) { - return Pair.of(new byte[] {STATUS_ERROR}, AuthHandshakeResult.error( - String.format("Network ID mismatch (expected %s, got %s)", this.networkId, message.getNetworkId()), - Optional.of(NodeId.fromPublicKey(remotePubKey)) - )); - } - - final var response = new AuthResponseMessage( - HashCode.fromBytes(ephemeralKey.getPublicKey().getBytes()), - HashCode.fromBytes(nonce) - ); - final var encodedResponse = serialization.toDson(response, DsonOutput.Output.WIRE); - - final var encryptedSize = encodedResponse.length + ECIESCoder.OVERHEAD_SIZE; - final var sizePrefix = ByteBuffer.allocate(2).putShort((short) encryptedSize).array(); - final var encryptedResponsePayload = - ECIESCoder.encrypt(remotePubKey.getEcPoint(), encodedResponse, sizePrefix); - - final var packet = new byte[1 + sizePrefix.length + encryptedResponsePayload.length]; - packet[0] = STATUS_OK; - System.arraycopy(sizePrefix, 0, packet, 1, sizePrefix.length); - System.arraycopy(encryptedResponsePayload, 0, packet, 1 + sizePrefix.length, encryptedResponsePayload.length); - - final var remoteEphemeralKey = extractEphemeralKey( - message.getSignature(), - message.getNonce(), - remotePubKey - ); - - final var initiatePacket = new byte[data.readableBytes()]; - data.getBytes(0, initiatePacket); - this.initiatePacketOpt = Optional.of(initiatePacket); - this.responsePacketOpt = Optional.of(packet); - this.remotePubKeyOpt = Optional.of(remotePubKey); - - final var handshakeResult = finalizeHandshake(remoteEphemeralKey, message.getNonce()); - - return Pair.of(packet, handshakeResult); - } catch (PublicKeyException | InvalidCipherTextException | IOException ex) { - return Pair.of(new byte[] {STATUS_ERROR}, AuthHandshakeResult.error( - String.format("Handshake decryption failed (%s)", ex.getMessage()), - Optional.empty()) - ); - } - } - - public AuthHandshakeResult handleResponseMessage(ByteBuf data) throws IOException { - try { - final var statusByte = data.getByte(0); - if (statusByte != STATUS_OK) { - return AuthHandshakeResult.error("Received error response", Optional.empty()); - } - - final var sizeBytes = new byte[2]; - data.getBytes(1, sizeBytes, 0, sizeBytes.length); - final var encryptedPayload = new byte[data.readableBytes() - 3]; - data.getBytes(3, encryptedPayload, 0, encryptedPayload.length); - final var plaintext = ecKeyOps.eciesDecrypt(encryptedPayload, sizeBytes); - final var message = serialization.fromDson(plaintext, AuthResponseMessage.class); - final var responsePacket = new byte[data.readableBytes()]; - data.getBytes(0, responsePacket); - this.responsePacketOpt = Optional.of(responsePacket); - final var remoteEphemeralKey = ECPublicKey.fromBytes(message.getEphemeralPublicKey().asBytes()); - return finalizeHandshake(remoteEphemeralKey, message.getNonce()); - } catch (PublicKeyException | InvalidCipherTextException ex) { - return AuthHandshakeResult.error( - String.format("Handshake decryption failed (%s)", ex.getMessage()), - Optional.empty() - ); - } - } - - private ECPublicKey extractEphemeralKey(ECDSASignature signature, HashCode nonce, ECPublicKey publicKey) { - final var sharedSecret = ecKeyOps.ecdhAgreement(publicKey); - final var token = bigIntegerToBytes(sharedSecret, NONCE_SIZE); - final var signed = xor(token, nonce.asBytes()); - return ECPublicKey.recoverFrom(HashCode.fromBytes(signed), signature).orElseThrow(); - } - - private AuthHandshakeSuccess finalizeHandshake(ECPublicKey remoteEphemeralKey, HashCode remoteNonce) { - final var initiatePacket = initiatePacketOpt.get(); - final var responsePacket = responsePacketOpt.get(); - final var remotePubKey = remotePubKeyOpt.get(); - - final var agreement = new ECDHBasicAgreement(); - agreement.init(new ECPrivateKeyParameters(new BigInteger(1, ephemeralKey.getPrivateKey()), ECKeyUtils.domain())); - final var secretScalar = agreement.calculateAgreement( - new ECPublicKeyParameters(remoteEphemeralKey.getEcPoint(), - ECKeyUtils.domain()) - ); - final var agreedSecret = bigIntegerToBytes(secretScalar, SECRET_SIZE); - - final var sharedSecret = isInitiator - ? kec256(agreedSecret, kec256(remoteNonce.asBytes(), nonce)) - : kec256(agreedSecret, kec256(nonce, remoteNonce.asBytes())); - - final var aesSecret = kec256(agreedSecret, sharedSecret); - - final var macSecrets = isInitiator - ? macSecretSetup(agreedSecret, aesSecret, initiatePacket, nonce, responsePacket, remoteNonce.asBytes()) - : macSecretSetup(agreedSecret, aesSecret, initiatePacket, remoteNonce.asBytes(), responsePacket, nonce); - - final var secrets = new Secrets( - aesSecret, - kec256(agreedSecret, aesSecret), - kec256(sharedSecret), - macSecrets.getFirst(), - macSecrets.getSecond() - ); - - return AuthHandshakeResult.success(remotePubKey, secrets); - } - - private Pair macSecretSetup( - byte[] agreedSecret, - byte[] aesSecret, - byte[] initiatePacket, - byte[] initiateNonce, - byte[] responsePacket, - byte[] responseNonce - ) { - final var macSecret = kec256(agreedSecret, aesSecret); - final var mac1 = new KeccakDigest(MAC_SIZE); - mac1.update(xor(macSecret, responseNonce), 0, macSecret.length); - final var bufSize = 32; - final var buf = new byte[bufSize]; - new KeccakDigest(mac1).doFinal(buf, 0); - mac1.update(initiatePacket, 0, initiatePacket.length); - new KeccakDigest(mac1).doFinal(buf, 0); - final var mac2 = new KeccakDigest(MAC_SIZE); - mac2.update(xor(macSecret, initiateNonce), 0, macSecret.length); - new KeccakDigest(mac2).doFinal(buf, 0); - mac2.update(responsePacket, 0, responsePacket.length); - new KeccakDigest(mac2).doFinal(buf, 0); - return isInitiator ? Pair.of(mac1, mac2) : Pair.of(mac2, mac1); - } - - private byte[] randomBytes(int len) { - final var arr = new byte[len]; - secureRandom.nextBytes(arr); - return arr; - } + private static final byte STATUS_OK = 0x01; + private static final byte STATUS_ERROR = 0x02; + + private static final int NONCE_SIZE = 32; + private static final int MIN_PADDING = 100; + private static final int MAX_PADDING = 300; + private static final int SECRET_SIZE = 32; + private static final int MAC_SIZE = 256; + + private final Serialization serialization; + private final SecureRandom secureRandom; + private final ECKeyOps ecKeyOps; + private final byte[] nonce; + private final ECKeyPair ephemeralKey; + private final int networkId; + private boolean isInitiator = false; + private Optional initiatePacketOpt = Optional.empty(); + private Optional responsePacketOpt = Optional.empty(); + private Optional remotePubKeyOpt; + + public AuthHandshaker( + Serialization serialization, SecureRandom secureRandom, ECKeyOps ecKeyOps, int networkId) { + this.serialization = Objects.requireNonNull(serialization); + this.secureRandom = Objects.requireNonNull(secureRandom); + this.ecKeyOps = Objects.requireNonNull(ecKeyOps); + this.nonce = randomBytes(NONCE_SIZE); + this.ephemeralKey = ECKeyPair.generateNew(); + this.networkId = networkId; + } + + public byte[] initiate(ECPublicKey remotePubKey) { + final var message = createAuthInitiateMessage(remotePubKey); + final var encoded = serialization.toDson(message, DsonOutput.Output.WIRE); + final var padding = randomBytes(secureRandom.nextInt(MAX_PADDING - MIN_PADDING) + MIN_PADDING); + final var padded = new byte[encoded.length + padding.length]; + System.arraycopy(encoded, 0, padded, 0, encoded.length); + System.arraycopy(padding, 0, padded, encoded.length, padding.length); + + final var encryptedSize = padded.length + ECIESCoder.OVERHEAD_SIZE; + final var sizePrefix = ByteBuffer.allocate(2).putShort((short) encryptedSize).array(); + final var encryptedPayload = ECIESCoder.encrypt(remotePubKey.getEcPoint(), padded, sizePrefix); + final var packet = new byte[sizePrefix.length + encryptedPayload.length]; + System.arraycopy(sizePrefix, 0, packet, 0, sizePrefix.length); + System.arraycopy(encryptedPayload, 0, packet, sizePrefix.length, encryptedPayload.length); + + this.isInitiator = true; + this.initiatePacketOpt = Optional.of(packet); + this.remotePubKeyOpt = Optional.of(remotePubKey); + + return packet; + } + + private AuthInitiateMessage createAuthInitiateMessage(ECPublicKey remotePubKey) { + final var sharedSecret = bigIntegerToBytes(ecKeyOps.ecdhAgreement(remotePubKey), NONCE_SIZE); + final var messageToSign = xor(sharedSecret, nonce); + final var signature = ephemeralKey.sign(messageToSign); + return new AuthInitiateMessage( + signature, + HashCode.fromBytes(ecKeyOps.nodePubKey().getBytes()), + HashCode.fromBytes(nonce), + networkId); + } + + public Pair handleInitialMessage(ByteBuf data) { + try { + final var sizeBytes = new byte[2]; + data.getBytes(0, sizeBytes, 0, sizeBytes.length); + final var encryptedPayload = new byte[data.readableBytes() - sizeBytes.length]; + data.getBytes(sizeBytes.length, encryptedPayload, 0, encryptedPayload.length); + final var plaintext = ecKeyOps.eciesDecrypt(encryptedPayload, sizeBytes); + final var message = serialization.fromDson(plaintext, AuthInitiateMessage.class); + final var remotePubKey = ECPublicKey.fromBytes(message.getPublicKey().asBytes()); + + if (message.getNetworkId() != this.networkId) { + return Pair.of( + new byte[] {STATUS_ERROR}, + AuthHandshakeResult.error( + String.format( + "Network ID mismatch (expected %s, got %s)", + this.networkId, message.getNetworkId()), + Optional.of(NodeId.fromPublicKey(remotePubKey)))); + } + + final var response = + new AuthResponseMessage( + HashCode.fromBytes(ephemeralKey.getPublicKey().getBytes()), + HashCode.fromBytes(nonce)); + final var encodedResponse = serialization.toDson(response, DsonOutput.Output.WIRE); + + final var encryptedSize = encodedResponse.length + ECIESCoder.OVERHEAD_SIZE; + final var sizePrefix = ByteBuffer.allocate(2).putShort((short) encryptedSize).array(); + final var encryptedResponsePayload = + ECIESCoder.encrypt(remotePubKey.getEcPoint(), encodedResponse, sizePrefix); + + final var packet = new byte[1 + sizePrefix.length + encryptedResponsePayload.length]; + packet[0] = STATUS_OK; + System.arraycopy(sizePrefix, 0, packet, 1, sizePrefix.length); + System.arraycopy( + encryptedResponsePayload, + 0, + packet, + 1 + sizePrefix.length, + encryptedResponsePayload.length); + + final var remoteEphemeralKey = + extractEphemeralKey(message.getSignature(), message.getNonce(), remotePubKey); + + final var initiatePacket = new byte[data.readableBytes()]; + data.getBytes(0, initiatePacket); + this.initiatePacketOpt = Optional.of(initiatePacket); + this.responsePacketOpt = Optional.of(packet); + this.remotePubKeyOpt = Optional.of(remotePubKey); + + final var handshakeResult = finalizeHandshake(remoteEphemeralKey, message.getNonce()); + + return Pair.of(packet, handshakeResult); + } catch (PublicKeyException | InvalidCipherTextException | IOException ex) { + return Pair.of( + new byte[] {STATUS_ERROR}, + AuthHandshakeResult.error( + String.format("Handshake decryption failed (%s)", ex.getMessage()), + Optional.empty())); + } + } + + public AuthHandshakeResult handleResponseMessage(ByteBuf data) throws IOException { + try { + final var statusByte = data.getByte(0); + if (statusByte != STATUS_OK) { + return AuthHandshakeResult.error("Received error response", Optional.empty()); + } + + final var sizeBytes = new byte[2]; + data.getBytes(1, sizeBytes, 0, sizeBytes.length); + final var encryptedPayload = new byte[data.readableBytes() - 3]; + data.getBytes(3, encryptedPayload, 0, encryptedPayload.length); + final var plaintext = ecKeyOps.eciesDecrypt(encryptedPayload, sizeBytes); + final var message = serialization.fromDson(plaintext, AuthResponseMessage.class); + final var responsePacket = new byte[data.readableBytes()]; + data.getBytes(0, responsePacket); + this.responsePacketOpt = Optional.of(responsePacket); + final var remoteEphemeralKey = + ECPublicKey.fromBytes(message.getEphemeralPublicKey().asBytes()); + return finalizeHandshake(remoteEphemeralKey, message.getNonce()); + } catch (PublicKeyException | InvalidCipherTextException ex) { + return AuthHandshakeResult.error( + String.format("Handshake decryption failed (%s)", ex.getMessage()), Optional.empty()); + } + } + + private ECPublicKey extractEphemeralKey( + ECDSASignature signature, HashCode nonce, ECPublicKey publicKey) { + final var sharedSecret = ecKeyOps.ecdhAgreement(publicKey); + final var token = bigIntegerToBytes(sharedSecret, NONCE_SIZE); + final var signed = xor(token, nonce.asBytes()); + return ECPublicKey.recoverFrom(HashCode.fromBytes(signed), signature).orElseThrow(); + } + + private AuthHandshakeSuccess finalizeHandshake( + ECPublicKey remoteEphemeralKey, HashCode remoteNonce) { + final var initiatePacket = initiatePacketOpt.get(); + final var responsePacket = responsePacketOpt.get(); + final var remotePubKey = remotePubKeyOpt.get(); + + final var agreement = new ECDHBasicAgreement(); + agreement.init( + new ECPrivateKeyParameters( + new BigInteger(1, ephemeralKey.getPrivateKey()), ECKeyUtils.domain())); + final var secretScalar = + agreement.calculateAgreement( + new ECPublicKeyParameters(remoteEphemeralKey.getEcPoint(), ECKeyUtils.domain())); + final var agreedSecret = bigIntegerToBytes(secretScalar, SECRET_SIZE); + + final var sharedSecret = + isInitiator + ? kec256(agreedSecret, kec256(remoteNonce.asBytes(), nonce)) + : kec256(agreedSecret, kec256(nonce, remoteNonce.asBytes())); + + final var aesSecret = kec256(agreedSecret, sharedSecret); + + final var macSecrets = + isInitiator + ? macSecretSetup( + agreedSecret, + aesSecret, + initiatePacket, + nonce, + responsePacket, + remoteNonce.asBytes()) + : macSecretSetup( + agreedSecret, + aesSecret, + initiatePacket, + remoteNonce.asBytes(), + responsePacket, + nonce); + + final var secrets = + new Secrets( + aesSecret, + kec256(agreedSecret, aesSecret), + kec256(sharedSecret), + macSecrets.getFirst(), + macSecrets.getSecond()); + + return AuthHandshakeResult.success(remotePubKey, secrets); + } + + private Pair macSecretSetup( + byte[] agreedSecret, + byte[] aesSecret, + byte[] initiatePacket, + byte[] initiateNonce, + byte[] responsePacket, + byte[] responseNonce) { + final var macSecret = kec256(agreedSecret, aesSecret); + final var mac1 = new KeccakDigest(MAC_SIZE); + mac1.update(xor(macSecret, responseNonce), 0, macSecret.length); + final var bufSize = 32; + final var buf = new byte[bufSize]; + new KeccakDigest(mac1).doFinal(buf, 0); + mac1.update(initiatePacket, 0, initiatePacket.length); + new KeccakDigest(mac1).doFinal(buf, 0); + final var mac2 = new KeccakDigest(MAC_SIZE); + mac2.update(xor(macSecret, initiateNonce), 0, macSecret.length); + new KeccakDigest(mac2).doFinal(buf, 0); + mac2.update(responsePacket, 0, responsePacket.length); + new KeccakDigest(mac2).doFinal(buf, 0); + return isInitiator ? Pair.of(mac1, mac2) : Pair.of(mac2, mac1); + } + + private byte[] randomBytes(int len) { + final var arr = new byte[len]; + secureRandom.nextBytes(arr); + return arr; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthInitiateMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthInitiateMessage.java index 195330753d..d3090071f2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthInitiateMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthInitiateMessage.java @@ -72,80 +72,79 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; @SerializerId2("message.handshake.auth_initiate") public final class AuthInitiateMessage { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(DsonOutput.Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("signature") - @DsonOutput(DsonOutput.Output.ALL) - private final ECDSASignature signature; - - @JsonProperty("publicKey") - @DsonOutput(DsonOutput.Output.ALL) - private final HashCode publicKey; - - @JsonProperty("nonce") - @DsonOutput(DsonOutput.Output.ALL) - private final HashCode nonce; - - @JsonProperty("networkId") - @DsonOutput(DsonOutput.Output.ALL) - private final int networkId; - - @JsonCreator - public static AuthInitiateMessage deserialize( - @JsonProperty(value = "signature", required = true) ECDSASignature signature, - @JsonProperty(value = "publicKey", required = true) HashCode publicKey, - @JsonProperty(value = "nonce", required = true) HashCode nonce, - @JsonProperty("networkId") int networkId - ) { - return new AuthInitiateMessage(signature, publicKey, nonce, networkId); - } - - public AuthInitiateMessage(ECDSASignature signature, HashCode publicKey, HashCode nonce, int networkId) { - this.signature = signature; - this.publicKey = publicKey; - this.nonce = nonce; - this.networkId = networkId; - } - - public ECDSASignature getSignature() { - return signature; - } - - public HashCode getPublicKey() { - return publicKey; - } - - public HashCode getNonce() { - return nonce; - } - - public int getNetworkId() { - return networkId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - return (o instanceof AuthInitiateMessage that) - && Objects.equals(signature, that.signature) - && Objects.equals(publicKey, that.publicKey) - && Objects.equals(nonce, that.nonce) - && networkId == that.networkId; - } - - @Override - public int hashCode() { - return Objects.hash(signature, publicKey, nonce, networkId); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(DsonOutput.Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("signature") + @DsonOutput(DsonOutput.Output.ALL) + private final ECDSASignature signature; + + @JsonProperty("publicKey") + @DsonOutput(DsonOutput.Output.ALL) + private final HashCode publicKey; + + @JsonProperty("nonce") + @DsonOutput(DsonOutput.Output.ALL) + private final HashCode nonce; + + @JsonProperty("networkId") + @DsonOutput(DsonOutput.Output.ALL) + private final int networkId; + + @JsonCreator + public static AuthInitiateMessage deserialize( + @JsonProperty(value = "signature", required = true) ECDSASignature signature, + @JsonProperty(value = "publicKey", required = true) HashCode publicKey, + @JsonProperty(value = "nonce", required = true) HashCode nonce, + @JsonProperty("networkId") int networkId) { + return new AuthInitiateMessage(signature, publicKey, nonce, networkId); + } + + public AuthInitiateMessage( + ECDSASignature signature, HashCode publicKey, HashCode nonce, int networkId) { + this.signature = signature; + this.publicKey = publicKey; + this.nonce = nonce; + this.networkId = networkId; + } + + public ECDSASignature getSignature() { + return signature; + } + + public HashCode getPublicKey() { + return publicKey; + } + + public HashCode getNonce() { + return nonce; + } + + public int getNetworkId() { + return networkId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + return (o instanceof AuthInitiateMessage that) + && Objects.equals(signature, that.signature) + && Objects.equals(publicKey, that.publicKey) + && Objects.equals(nonce, that.nonce) + && networkId == that.networkId; + } + + @Override + public int hashCode() { + return Objects.hash(signature, publicKey, nonce, networkId); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthResponseMessage.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthResponseMessage.java index 1080ea5f88..30a2178498 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthResponseMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/AuthResponseMessage.java @@ -71,58 +71,56 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; @SerializerId2("message.handshake.auth_response") public final class AuthResponseMessage { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(DsonOutput.Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(DsonOutput.Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; - @JsonProperty("ephemeralPublicKey") - @DsonOutput(DsonOutput.Output.ALL) - private final HashCode ephemeralPublicKey; + @JsonProperty("ephemeralPublicKey") + @DsonOutput(DsonOutput.Output.ALL) + private final HashCode ephemeralPublicKey; - @JsonProperty("nonce") - @DsonOutput(DsonOutput.Output.ALL) - private final HashCode nonce; + @JsonProperty("nonce") + @DsonOutput(DsonOutput.Output.ALL) + private final HashCode nonce; - @JsonCreator - public static AuthResponseMessage deserialize( - @JsonProperty(value = "ephemeralPublicKey", required = true) HashCode ephemeralPublicKey, - @JsonProperty(value = "nonce", required = true) HashCode nonce - ) { - return new AuthResponseMessage(ephemeralPublicKey, nonce); - } + @JsonCreator + public static AuthResponseMessage deserialize( + @JsonProperty(value = "ephemeralPublicKey", required = true) HashCode ephemeralPublicKey, + @JsonProperty(value = "nonce", required = true) HashCode nonce) { + return new AuthResponseMessage(ephemeralPublicKey, nonce); + } - public AuthResponseMessage(HashCode ephemeralPublicKey, HashCode nonce) { - this.ephemeralPublicKey = ephemeralPublicKey; - this.nonce = nonce; - } + public AuthResponseMessage(HashCode ephemeralPublicKey, HashCode nonce) { + this.ephemeralPublicKey = ephemeralPublicKey; + this.nonce = nonce; + } - public HashCode getEphemeralPublicKey() { - return ephemeralPublicKey; - } + public HashCode getEphemeralPublicKey() { + return ephemeralPublicKey; + } - public HashCode getNonce() { - return nonce; - } + public HashCode getNonce() { + return nonce; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof AuthResponseMessage that) - && Objects.equals(ephemeralPublicKey, that.ephemeralPublicKey) - && Objects.equals(nonce, that.nonce); - } + return (o instanceof AuthResponseMessage that) + && Objects.equals(ephemeralPublicKey, that.ephemeralPublicKey) + && Objects.equals(nonce, that.nonce); + } - @Override - public int hashCode() { - return Objects.hash(ephemeralPublicKey, nonce); - } + @Override + public int hashCode() { + return Objects.hash(ephemeralPublicKey, nonce); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/Secrets.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/Secrets.java index fb8fe3a5a9..8504cd5180 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/Secrets.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/handshake/Secrets.java @@ -66,41 +66,40 @@ import org.bouncycastle.crypto.digests.KeccakDigest; -/** - * Secrets agreed upon during the auth handshake. Used for encrypted communication (FrameCodec). - */ +/** Secrets agreed upon during the auth handshake. Used for encrypted communication (FrameCodec). */ public final class Secrets { - final byte[] aes; - final byte[] mac; - final byte[] token; - final KeccakDigest egressMac; - final KeccakDigest ingressMac; + final byte[] aes; + final byte[] mac; + final byte[] token; + final KeccakDigest egressMac; + final KeccakDigest ingressMac; - public Secrets(byte[] aes, byte[] mac, byte[] token, KeccakDigest egressMac, KeccakDigest ingressMac) { - this.aes = aes; - this.mac = mac; - this.token = token; - this.egressMac = egressMac; - this.ingressMac = ingressMac; - } + public Secrets( + byte[] aes, byte[] mac, byte[] token, KeccakDigest egressMac, KeccakDigest ingressMac) { + this.aes = aes; + this.mac = mac; + this.token = token; + this.egressMac = egressMac; + this.ingressMac = ingressMac; + } - public byte[] getAes() { - return aes; - } + public byte[] getAes() { + return aes; + } - public byte[] getMac() { - return mac; - } + public byte[] getMac() { + return mac; + } - public byte[] getToken() { - return token; - } + public byte[] getToken() { + return token; + } - public KeccakDigest getEgressMac() { - return egressMac; - } + public KeccakDigest getEgressMac() { + return egressMac; + } - public KeccakDigest getIngressMac() { - return ingressMac; - } + public KeccakDigest getIngressMac() { + return ingressMac; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LogSink.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LogSink.java index 88d5d38ebe..cd69e00d87 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LogSink.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LogSink.java @@ -66,58 +66,54 @@ import org.apache.logging.log4j.Logger; -/** - * A sink that accepts log strings. - */ +/** A sink that accepts log strings. */ public interface LogSink { - /** - * Returns {@code true} if trace logs enabled. - * - * return {@code true} if trace logs enabled. - */ - boolean isTraceEnabled(); - - /** - * Outputs the specified log message at trace level. - * - * @param message The message to output. - */ - void trace(String message); - - /** - * Outputs the specified log message and exception at trace level. - * - * @param message The message to output. - * @param ex The exception to include. - */ - void trace(String message, Throwable ex); + /** + * Returns {@code true} if trace logs enabled. + * + *

return {@code true} if trace logs enabled. + */ + boolean isTraceEnabled(); - /** - * Create a {@link LogSink} using the specified logger. - * - * @param log The logger that will consume log messages - * @return A newly constructed {@link LogSink} - */ - static LogSink using(Logger log) { - return new LogSink() { + /** + * Outputs the specified log message at trace level. + * + * @param message The message to output. + */ + void trace(String message); - @Override - public boolean isTraceEnabled() { - return log.isTraceEnabled(); - } + /** + * Outputs the specified log message and exception at trace level. + * + * @param message The message to output. + * @param ex The exception to include. + */ + void trace(String message, Throwable ex); - @Override - public void trace(String message) { - log.trace(message); - } + /** + * Create a {@link LogSink} using the specified logger. + * + * @param log The logger that will consume log messages + * @return A newly constructed {@link LogSink} + */ + static LogSink using(Logger log) { + return new LogSink() { - @Override - public void trace(String message, Throwable ex) { - log.trace(message, ex); - } + @Override + public boolean isTraceEnabled() { + return log.isTraceEnabled(); + } - }; - } + @Override + public void trace(String message) { + log.trace(message); + } + @Override + public void trace(String message, Throwable ex) { + log.trace(message, ex); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LoggingHandler.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LoggingHandler.java index e411243371..18816643cc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LoggingHandler.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/network/p2p/transport/logging/LoggingHandler.java @@ -64,348 +64,389 @@ package com.radixdlt.network.p2p.transport.logging; +import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump; +import static io.netty.util.internal.StringUtil.NEWLINE; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; - import java.net.SocketAddress; import java.util.Objects; -import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump; -import static io.netty.util.internal.StringUtil.NEWLINE; - -/** - * A {@link ChannelHandler} that logs all events using a supplied log sink. - */ +/** A {@link ChannelHandler} that logs all events using a supplied log sink. */ @Sharable public class LoggingHandler extends ChannelDuplexHandler { - private final LogSink logger; - private final boolean includeData; - - /** - * Creates a new instance where log messages are sent to the specified - * {@link LogSink}. - * - * @param logger the {@code LogSink} to which all messages will be sent - */ - public LoggingHandler(LogSink logger, boolean includeData) { - this.logger = Objects.requireNonNull(logger); - this.includeData = includeData; - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "REGISTERED")); - } - ctx.fireChannelRegistered(); - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "UNREGISTERED")); - } - ctx.fireChannelUnregistered(); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "ACTIVE")); - } - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "INACTIVE")); - } - ctx.fireChannelInactive(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(formatSimpleDetails(ctx, "EXCEPTION", cause), cause); - } - ctx.fireExceptionCaught(cause); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (this.logger.isTraceEnabled()) { - if (this.includeData) { - this.logger.trace(formatDetails(ctx, "USER_EVENT", evt)); - } else { - this.logger.trace(formatSummary(ctx, "USER_EVENT", evt)); - } - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(formatSimpleDetails(ctx, "BIND", localAddress)); - } - ctx.bind(localAddress, promise); - } - - @Override - public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) - throws Exception { - - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "CONNECT", remoteAddress, localAddress)); - } - ctx.connect(remoteAddress, localAddress, promise); - } - - @Override - public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "DISCONNECT")); - } - ctx.disconnect(promise); - } - - @Override - public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "CLOSE")); - } - ctx.close(promise); - } - - @Override - public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "DEREGISTER")); - } - ctx.deregister(promise); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "READ_COMPLETE")); - } - ctx.fireChannelReadComplete(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (this.logger.isTraceEnabled()) { - if (this.includeData) { - this.logger.trace(formatDetails(ctx, "READ", msg)); - } else { - this.logger.trace(formatSummary(ctx, "READ", msg)); - } - } - ctx.fireChannelRead(msg); - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (this.logger.isTraceEnabled()) { - if (this.includeData) { - this.logger.trace(formatDetails(ctx, "WRITE", msg)); - } else { - this.logger.trace(formatSummary(ctx, "WRITE", msg)); - } - } - ctx.write(msg, promise); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "WRITABILITY_CHANGED")); - } - ctx.fireChannelWritabilityChanged(); - } - - @Override - public void flush(ChannelHandlerContext ctx) throws Exception { - if (this.logger.isTraceEnabled()) { - this.logger.trace(format(ctx, "FLUSH")); - } - ctx.flush(); - } - - /** - * Formats an event and returns the formatted message. - * - * @param eventName the name of the event - */ - protected String format(ChannelHandlerContext ctx, String eventName) { - String chStr = ctx.channel().toString(); - return new StringBuilder(chStr.length() + 1 + eventName.length()) - .append(chStr) - .append(' ') - .append(eventName) - .toString(); - } - - /** - * Formats an event and returns the formatted message. - * - * @param eventName the name of the event - * @param arg the argument of the event - */ - protected String formatDetails(ChannelHandlerContext ctx, String eventName, Object arg) { - if (arg instanceof ByteBuf) { - return formatByteBufDetails(ctx, eventName, (ByteBuf) arg); - } else if (arg instanceof ByteBufHolder) { - return formatByteBufHolderDetails(ctx, eventName, (ByteBufHolder) arg); - } else { - return formatSimpleDetails(ctx, eventName, arg); - } - } - - /** - * Formats an event and returns the formatted message. - * - * @param eventName the name of the event - * @param arg the argument of the event - */ - protected String formatSummary(ChannelHandlerContext ctx, String eventName, Object arg) { - if (arg instanceof ByteBuf) { - return formatByteBufSummary(ctx, eventName, (ByteBuf) arg); - } else if (arg instanceof ByteBufHolder) { - return formatByteBufHolderSummary(ctx, eventName, (ByteBufHolder) arg); - } else { - return formatSimpleSummary(ctx, eventName, arg); - } - } - - /** - * Formats an event and returns the formatted message. This method is currently - * only used for formatting - * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}. - * - * @param eventName the name of the event - * @param firstArg the first argument of the event - * @param secondArg the second argument of the event - */ - protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) { - if (secondArg == null) { - return formatSimpleDetails(ctx, eventName, firstArg); - } - - String chStr = ctx.channel().toString(); - String arg1Str = String.valueOf(firstArg); - String arg2Str = secondArg.toString(); - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length()); - buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str); - return buf.toString(); - } - - /** - * Generates the default log message of the specified event whose argument is a - * {@link ByteBuf}. - */ - private static String formatByteBufDetails(ChannelHandlerContext ctx, String eventName, ByteBuf msg) { - String chStr = ctx.channel().toString(); - int length = msg.readableBytes(); - if (length == 0) { - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4); - buf.append(chStr).append(' ').append(eventName).append(": 0B"); - return buf.toString(); - } else { - int rows = (length + 15) / 16 + 4; - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + 10 + 1 + 2 + rows * 80); - - buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B').append(NEWLINE); - appendPrettyHexDump(buf, msg); - - return buf.toString(); - } - } - - /** - * Generates the default log message of the specified event whose argument is a - * {@link ByteBuf}. - */ - private static String formatByteBufSummary(ChannelHandlerContext ctx, String eventName, ByteBuf msg) { - String chStr = ctx.channel().toString(); - int length = msg.readableBytes(); - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 20); - buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B'); - return buf.toString(); - } - - /** - * Generates the default log message of the specified event whose argument is a - * {@link ByteBufHolder}. - */ - private static String formatByteBufHolderDetails(ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) { - String chStr = ctx.channel().toString(); - String msgStr = msg.toString(); - ByteBuf content = msg.content(); - int length = content.readableBytes(); - if (length == 0) { - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 4); - buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).append(", 0B"); - return buf.toString(); - } else { - int rows = (length + 15) / 16 + 4; - var buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 2 + 10 + 1 + 2 + rows * 80); - buf - .append(chStr) - .append(' ') - .append(eventName) - .append(": ") - .append(msgStr) - .append(", ") - .append(length) - .append('B') - .append(NEWLINE); - appendPrettyHexDump(buf, content); - - return buf.toString(); - } - } - - /** - * Generates the default log message of the specified event whose argument is a - * {@link ByteBufHolder}. - */ - private static String formatByteBufHolderSummary(ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) { - String chStr = ctx.channel().toString(); - String msgStr = msg.toString(); - ByteBuf content = msg.content(); - int length = content.readableBytes(); - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 20); - buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).append(", ").append(length).append('B'); - return buf.toString(); - } - - /** - * Generates the default log message of the specified event whose argument is an - * arbitrary object. - */ - private static String formatSimpleDetails(ChannelHandlerContext ctx, String eventName, Object msg) { - String chStr = ctx.channel().toString(); - String msgStr = String.valueOf(msg); - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length()); - return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString(); - } - - /** - * Generates the default log message of the specified event whose argument is an - * arbitrary object. - */ - private static String formatSimpleSummary(ChannelHandlerContext ctx, String eventName, Object msg) { - String chStr = ctx.channel().toString(); - String msgStr = msg == null ? "null" : msg.getClass().getSimpleName(); - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length()); - return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString(); - } + private final LogSink logger; + private final boolean includeData; + + /** + * Creates a new instance where log messages are sent to the specified {@link LogSink}. + * + * @param logger the {@code LogSink} to which all messages will be sent + */ + public LoggingHandler(LogSink logger, boolean includeData) { + this.logger = Objects.requireNonNull(logger); + this.includeData = includeData; + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "REGISTERED")); + } + ctx.fireChannelRegistered(); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "UNREGISTERED")); + } + ctx.fireChannelUnregistered(); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "ACTIVE")); + } + ctx.fireChannelActive(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "INACTIVE")); + } + ctx.fireChannelInactive(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(formatSimpleDetails(ctx, "EXCEPTION", cause), cause); + } + ctx.fireExceptionCaught(cause); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (this.logger.isTraceEnabled()) { + if (this.includeData) { + this.logger.trace(formatDetails(ctx, "USER_EVENT", evt)); + } else { + this.logger.trace(formatSummary(ctx, "USER_EVENT", evt)); + } + } + ctx.fireUserEventTriggered(evt); + } + + @Override + public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) + throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(formatSimpleDetails(ctx, "BIND", localAddress)); + } + ctx.bind(localAddress, promise); + } + + @Override + public void connect( + ChannelHandlerContext ctx, + SocketAddress remoteAddress, + SocketAddress localAddress, + ChannelPromise promise) + throws Exception { + + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "CONNECT", remoteAddress, localAddress)); + } + ctx.connect(remoteAddress, localAddress, promise); + } + + @Override + public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "DISCONNECT")); + } + ctx.disconnect(promise); + } + + @Override + public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "CLOSE")); + } + ctx.close(promise); + } + + @Override + public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "DEREGISTER")); + } + ctx.deregister(promise); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "READ_COMPLETE")); + } + ctx.fireChannelReadComplete(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (this.logger.isTraceEnabled()) { + if (this.includeData) { + this.logger.trace(formatDetails(ctx, "READ", msg)); + } else { + this.logger.trace(formatSummary(ctx, "READ", msg)); + } + } + ctx.fireChannelRead(msg); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) + throws Exception { + if (this.logger.isTraceEnabled()) { + if (this.includeData) { + this.logger.trace(formatDetails(ctx, "WRITE", msg)); + } else { + this.logger.trace(formatSummary(ctx, "WRITE", msg)); + } + } + ctx.write(msg, promise); + } + + @Override + public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "WRITABILITY_CHANGED")); + } + ctx.fireChannelWritabilityChanged(); + } + + @Override + public void flush(ChannelHandlerContext ctx) throws Exception { + if (this.logger.isTraceEnabled()) { + this.logger.trace(format(ctx, "FLUSH")); + } + ctx.flush(); + } + + /** + * Formats an event and returns the formatted message. + * + * @param eventName the name of the event + */ + protected String format(ChannelHandlerContext ctx, String eventName) { + String chStr = ctx.channel().toString(); + return new StringBuilder(chStr.length() + 1 + eventName.length()) + .append(chStr) + .append(' ') + .append(eventName) + .toString(); + } + + /** + * Formats an event and returns the formatted message. + * + * @param eventName the name of the event + * @param arg the argument of the event + */ + protected String formatDetails(ChannelHandlerContext ctx, String eventName, Object arg) { + if (arg instanceof ByteBuf) { + return formatByteBufDetails(ctx, eventName, (ByteBuf) arg); + } else if (arg instanceof ByteBufHolder) { + return formatByteBufHolderDetails(ctx, eventName, (ByteBufHolder) arg); + } else { + return formatSimpleDetails(ctx, eventName, arg); + } + } + + /** + * Formats an event and returns the formatted message. + * + * @param eventName the name of the event + * @param arg the argument of the event + */ + protected String formatSummary(ChannelHandlerContext ctx, String eventName, Object arg) { + if (arg instanceof ByteBuf) { + return formatByteBufSummary(ctx, eventName, (ByteBuf) arg); + } else if (arg instanceof ByteBufHolder) { + return formatByteBufHolderSummary(ctx, eventName, (ByteBufHolder) arg); + } else { + return formatSimpleSummary(ctx, eventName, arg); + } + } + + /** + * Formats an event and returns the formatted message. This method is currently only used for + * formatting {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, + * SocketAddress, ChannelPromise)}. + * + * @param eventName the name of the event + * @param firstArg the first argument of the event + * @param secondArg the second argument of the event + */ + protected String format( + ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) { + if (secondArg == null) { + return formatSimpleDetails(ctx, eventName, firstArg); + } + + String chStr = ctx.channel().toString(); + String arg1Str = String.valueOf(firstArg); + String arg2Str = secondArg.toString(); + StringBuilder buf = + new StringBuilder( + chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length()); + buf.append(chStr) + .append(' ') + .append(eventName) + .append(": ") + .append(arg1Str) + .append(", ") + .append(arg2Str); + return buf.toString(); + } + + /** + * Generates the default log message of the specified event whose argument is a {@link ByteBuf}. + */ + private static String formatByteBufDetails( + ChannelHandlerContext ctx, String eventName, ByteBuf msg) { + String chStr = ctx.channel().toString(); + int length = msg.readableBytes(); + if (length == 0) { + StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4); + buf.append(chStr).append(' ').append(eventName).append(": 0B"); + return buf.toString(); + } else { + int rows = (length + 15) / 16 + 4; + StringBuilder buf = + new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + 10 + 1 + 2 + rows * 80); + + buf.append(chStr) + .append(' ') + .append(eventName) + .append(": ") + .append(length) + .append('B') + .append(NEWLINE); + appendPrettyHexDump(buf, msg); + + return buf.toString(); + } + } + + /** + * Generates the default log message of the specified event whose argument is a {@link ByteBuf}. + */ + private static String formatByteBufSummary( + ChannelHandlerContext ctx, String eventName, ByteBuf msg) { + String chStr = ctx.channel().toString(); + int length = msg.readableBytes(); + StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 20); + buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B'); + return buf.toString(); + } + + /** + * Generates the default log message of the specified event whose argument is a {@link + * ByteBufHolder}. + */ + private static String formatByteBufHolderDetails( + ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) { + String chStr = ctx.channel().toString(); + String msgStr = msg.toString(); + ByteBuf content = msg.content(); + int length = content.readableBytes(); + if (length == 0) { + StringBuilder buf = + new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 4); + buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).append(", 0B"); + return buf.toString(); + } else { + int rows = (length + 15) / 16 + 4; + var buf = + new StringBuilder( + chStr.length() + + 1 + + eventName.length() + + 2 + + msgStr.length() + + 2 + + 10 + + 1 + + 2 + + rows * 80); + buf.append(chStr) + .append(' ') + .append(eventName) + .append(": ") + .append(msgStr) + .append(", ") + .append(length) + .append('B') + .append(NEWLINE); + appendPrettyHexDump(buf, content); + + return buf.toString(); + } + } + + /** + * Generates the default log message of the specified event whose argument is a {@link + * ByteBufHolder}. + */ + private static String formatByteBufHolderSummary( + ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) { + String chStr = ctx.channel().toString(); + String msgStr = msg.toString(); + ByteBuf content = msg.content(); + int length = content.readableBytes(); + StringBuilder buf = + new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 20); + buf.append(chStr) + .append(' ') + .append(eventName) + .append(": ") + .append(msgStr) + .append(", ") + .append(length) + .append('B'); + return buf.toString(); + } + + /** + * Generates the default log message of the specified event whose argument is an arbitrary object. + */ + private static String formatSimpleDetails( + ChannelHandlerContext ctx, String eventName, Object msg) { + String chStr = ctx.channel().toString(); + String msgStr = String.valueOf(msg); + StringBuilder buf = + new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length()); + return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString(); + } + + /** + * Generates the default log message of the specified event whose argument is an arbitrary object. + */ + private static String formatSimpleSummary( + ChannelHandlerContext ctx, String eventName, Object msg) { + String chStr = ctx.channel().toString(); + String msgStr = msg == null ? "null" : msg.getClass().getSimpleName(); + StringBuilder buf = + new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length()); + return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/PersistedProperties.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/PersistedProperties.java index 459a3ee496..45d1353cc5 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/PersistedProperties.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/PersistedProperties.java @@ -64,9 +64,7 @@ package com.radixdlt.properties; -import org.apache.commons.cli.ParseException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import static com.radixdlt.utils.RadixConstants.STANDARD_CHARSET; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -83,224 +81,217 @@ import java.util.Objects; import java.util.Properties; import java.util.function.Function; - -import static com.radixdlt.utils.RadixConstants.STANDARD_CHARSET; +import org.apache.commons.cli.ParseException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * Persisted properties are property sets that are committed to storage - * and may be reloaded on the next application execution. + * Persisted properties are property sets that are committed to storage and may be reloaded on the + * next application execution. */ public class PersistedProperties { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - private final Properties properties; + private final Properties properties; - PersistedProperties() { - this.properties = new Properties(); - } + PersistedProperties() { + this.properties = new Properties(); + } - /** - * Loads properties from specified file. - * - * @param filename the file to load the properties from - * - * @throws ParseException if the properties cannot be loaded - */ - public void load(String filename) throws ParseException { - var loaded = false; - var file = new File(filename); + /** + * Loads properties from specified file. + * + * @param filename the file to load the properties from + * @throws ParseException if the properties cannot be loaded + */ + public void load(String filename) throws ParseException { + var loaded = false; + var file = new File(filename); - try (var propertiesInput = new FileInputStream(file)) { - loadProperties(propertiesInput); - // No need to immediately save, we just loaded them - log.info("Loaded properties from {}", file); - loaded = true; - } catch (FileNotFoundException ex) { - log.info("Can not open properties file {}, using default", file); - } catch (IOException ex) { - log.error(String.format("Can not open properties file %s, using default", file), ex); - } + try (var propertiesInput = new FileInputStream(file)) { + loadProperties(propertiesInput); + // No need to immediately save, we just loaded them + log.info("Loaded properties from {}", file); + loaded = true; + } catch (FileNotFoundException ex) { + log.info("Can not open properties file {}, using default", file); + } catch (IOException ex) { + log.error(String.format("Can not open properties file %s, using default", file), ex); + } - // Try in resource if not loaded - if (!loaded) { - try (var propertiesInput = this.getClass().getResourceAsStream("/" + filename)) { - if (propertiesInput == null) { - throw new FileNotFoundException("Default properties for " + file + " not found"); - } - loadProperties(propertiesInput); - log.info("Loaded default properties for {}", file); - // Save to file, so they can be edited later - save(filename); - log.info("Saved default properties in {}", file); - } catch (FileNotFoundException ex) { - log.error("Can not find properties file {}", file); - throw new ParseException("Can not find properties file " + file); - } catch (IOException ex) { - log.error(String.format("Can not load default properties for %s", file), ex); - throw new ParseException(String.format("Can not load properties file '%s' (%s)", file, ex.getMessage())); - } - } - } + // Try in resource if not loaded + if (!loaded) { + try (var propertiesInput = this.getClass().getResourceAsStream("/" + filename)) { + if (propertiesInput == null) { + throw new FileNotFoundException("Default properties for " + file + " not found"); + } + loadProperties(propertiesInput); + log.info("Loaded default properties for {}", file); + // Save to file, so they can be edited later + save(filename); + log.info("Saved default properties in {}", file); + } catch (FileNotFoundException ex) { + log.error("Can not find properties file {}", file); + throw new ParseException("Can not find properties file " + file); + } catch (IOException ex) { + log.error(String.format("Can not load default properties for %s", file), ex); + throw new ParseException( + String.format("Can not load properties file '%s' (%s)", file, ex.getMessage())); + } + } + } - private void loadProperties(InputStream is) throws IOException { - var out = new ByteArrayOutputStream(); + private void loadProperties(InputStream is) throws IOException { + var out = new ByteArrayOutputStream(); - //Strip redundant whitespace characters - try (var writer = new BufferedWriter(new OutputStreamWriter(out))) { - try (var reader = new BufferedReader(new InputStreamReader(is, STANDARD_CHARSET))) { - for (var line = reader.readLine(); line != null; line = reader.readLine()) { - writer.write(line.trim()); - writer.newLine(); - } - } - } + // Strip redundant whitespace characters + try (var writer = new BufferedWriter(new OutputStreamWriter(out))) { + try (var reader = new BufferedReader(new InputStreamReader(is, STANDARD_CHARSET))) { + for (var line = reader.readLine(); line != null; line = reader.readLine()) { + writer.write(line.trim()); + writer.newLine(); + } + } + } - this.properties.load(new ByteArrayInputStream(out.toByteArray())); - } + this.properties.load(new ByteArrayInputStream(out.toByteArray())); + } - /** - * Saves properties to the specified file. - * - * @param filename The file to save the properties to - * - * @throws IOException if the properties cannot be saved - */ - public void save(String filename) throws IOException { - try (var propertiesOutput = new FileOutputStream(filename)) { - this.properties.store(propertiesOutput, ""); - } - } + /** + * Saves properties to the specified file. + * + * @param filename The file to save the properties to + * @throws IOException if the properties cannot be saved + */ + public void save(String filename) throws IOException { + try (var propertiesOutput = new FileOutputStream(filename)) { + this.properties.store(propertiesOutput, ""); + } + } - /** - * Returns the property if there is a property matching the key, otherwise {@code null}. - * - * @param key the property key - * - * @return the property if there is a property matching the key, otherwise {@code null} - */ - public String get(String key) { - return this.properties.getProperty(key); - } + /** + * Returns the property if there is a property matching the key, otherwise {@code null}. + * + * @param key the property key + * @return the property if there is a property matching the key, otherwise {@code null} + */ + public String get(String key) { + return this.properties.getProperty(key); + } - /** - * Returns a property value as an {@code int}. - * - * @param key the property key - * @param defaultValue the default value returned if no value is set - * - * @return either the property value, or {@code defaultValue} if no value set - */ - public int get(String key, int defaultValue) { - return get(key, defaultValue, Integer::parseInt); - } + /** + * Returns a property value as an {@code int}. + * + * @param key the property key + * @param defaultValue the default value returned if no value is set + * @return either the property value, or {@code defaultValue} if no value set + */ + public int get(String key, int defaultValue) { + return get(key, defaultValue, Integer::parseInt); + } - /** - * Returns a property value as a {@code long}. - * - * @param key the property key - * @param defaultValue the default value returned if no value is set - * - * @return either the property value, or {@code defaultValue} if no value set - */ - public long get(String key, long defaultValue) { - return get(key, defaultValue, Long::parseLong); - } + /** + * Returns a property value as a {@code long}. + * + * @param key the property key + * @param defaultValue the default value returned if no value is set + * @return either the property value, or {@code defaultValue} if no value set + */ + public long get(String key, long defaultValue) { + return get(key, defaultValue, Long::parseLong); + } - /** - * Returns a property value as a {@code double}. - * - * @param key the property key - * @param defaultValue the default value returned if no value is set - * - * @return either the property value, or {@code defaultValue} if no value set - */ - public double get(String key, double defaultValue) { - return get(key, defaultValue, Double::parseDouble); - } + /** + * Returns a property value as a {@code double}. + * + * @param key the property key + * @param defaultValue the default value returned if no value is set + * @return either the property value, or {@code defaultValue} if no value set + */ + public double get(String key, double defaultValue) { + return get(key, defaultValue, Double::parseDouble); + } - /** - * Returns a property value as a {@code boolean}. - * Note that the strings "true", ignoring case, and correctly formatted non-zero - * integers are {@code true} values, all others are {@code false}. - * Empty value handled as if value was missing. - * - * @param key the property key - * @param defaultValue the default value returned if no value is set - * - * @return either the property value, or {@code defaultValue} if no value set - */ - public boolean get(String key, boolean defaultValue) { - var value = get(key); - return (value == null || value.trim().isEmpty()) ? defaultValue : parseBoolean(value); - } + /** + * Returns a property value as a {@code boolean}. Note that the strings "true", ignoring case, and + * correctly formatted non-zero integers are {@code true} values, all others are {@code false}. + * Empty value handled as if value was missing. + * + * @param key the property key + * @param defaultValue the default value returned if no value is set + * @return either the property value, or {@code defaultValue} if no value set + */ + public boolean get(String key, boolean defaultValue) { + var value = get(key); + return (value == null || value.trim().isEmpty()) ? defaultValue : parseBoolean(value); + } - /** - * Returns a property value as a {@code String}. - * - * @param key the property key - * @param defaultValue the default value returned if no value is set - * - * @return either the property value, or {@code defaultValue} if no value set - */ - public String get(String key, String defaultValue) { - var value = get(key); - return value == null ? defaultValue : value; - } + /** + * Returns a property value as a {@code String}. + * + * @param key the property key + * @param defaultValue the default value returned if no value is set + * @return either the property value, or {@code defaultValue} if no value set + */ + public String get(String key, String defaultValue) { + var value = get(key); + return value == null ? defaultValue : value; + } - /** - * Sets a property value. - * - * @param key the property name - * @param value the property value - */ - public void set(String key, Object value) { - this.properties.setProperty(key, value.toString()); - } + /** + * Sets a property value. + * + * @param key the property name + * @param value the property value + */ + public void set(String key, Object value) { + this.properties.setProperty(key, value.toString()); + } - @Override - public int hashCode() { - return Objects.hash(this.properties); - } + @Override + public int hashCode() { + return Objects.hash(this.properties); + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } - if (obj == null || !this.getClass().equals(obj.getClass())) { - return false; - } + if (obj == null || !this.getClass().equals(obj.getClass())) { + return false; + } - var other = (PersistedProperties) obj; - return Objects.equals(this.properties, other.properties); - } + var other = (PersistedProperties) obj; + return Objects.equals(this.properties, other.properties); + } - @Override - public String toString() { - return this.properties.keySet().toString(); - } + @Override + public String toString() { + return this.properties.keySet().toString(); + } - private T get(String key, T defaultValue, Function parser) { - var value = get(key); - try { - return value == null ? defaultValue : parser.apply(value); - } catch (NumberFormatException ex) { - var msg = String.format("Exception while retrieving double value for %s: '%s'", key, value); - throw new IllegalArgumentException(msg, ex); - } - } + private T get(String key, T defaultValue, Function parser) { + var value = get(key); + try { + return value == null ? defaultValue : parser.apply(value); + } catch (NumberFormatException ex) { + var msg = String.format("Exception while retrieving double value for %s: '%s'", key, value); + throw new IllegalArgumentException(msg, ex); + } + } - private boolean parseBoolean(String value) { - if (value.trim().equalsIgnoreCase("true")) { - return true; - } - // Try as 0 = false, not-0 = true - try { - int i = Integer.parseInt(value); - return i != 0; - } catch (NumberFormatException e) { - return false; - } - } + private boolean parseBoolean(String value) { + if (value.trim().equalsIgnoreCase("true")) { + return true; + } + // Try as 0 = false, not-0 = true + try { + int i = Integer.parseInt(value); + return i != 0; + } catch (NumberFormatException e) { + return false; + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/RuntimeProperties.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/RuntimeProperties.java index 47559a1e4c..58b9649797 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/RuntimeProperties.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/properties/RuntimeProperties.java @@ -76,69 +76,74 @@ /** * Runtime properties are temporary property sets that are discarded upon client termination. * - * They combine a set of persisted properties, with a set of command line properties. + *

They combine a set of persisted properties, with a set of command line properties. */ public final class RuntimeProperties extends PersistedProperties { - private final CommandLine commandLine; + private final CommandLine commandLine; - public RuntimeProperties(JSONObject commandLineConfig, String[] commandLineArguments) throws ParseException { - CommandLineParser parser = new DefaultParser(); - Options gnuOptions = new Options(); + public RuntimeProperties(JSONObject commandLineConfig, String[] commandLineArguments) + throws ParseException { + CommandLineParser parser = new DefaultParser(); + Options gnuOptions = new Options(); - for (String clKey : commandLineConfig.keySet()) { - JSONObject clOption = commandLineConfig.getJSONObject(clKey); - gnuOptions.addOption(clOption.getString("short"), clKey, clOption.getBoolean("has_arg"), clOption.optString("desc", "")); - } + for (String clKey : commandLineConfig.keySet()) { + JSONObject clOption = commandLineConfig.getJSONObject(clKey); + gnuOptions.addOption( + clOption.getString("short"), + clKey, + clOption.getBoolean("has_arg"), + clOption.optString("desc", "")); + } - commandLine = parser.parse(gnuOptions, commandLineArguments); + commandLine = parser.parse(gnuOptions, commandLineArguments); - load(commandLine.getOptionValue("config", "default.config")); - } + load(commandLine.getOptionValue("config", "default.config")); + } - @Override - public String get(String key) { - for (Option commandLineOption : commandLine.getOptions()) { - if (commandLineOption.getOpt().equals(key)) { - if (commandLineOption.hasArg()) { - return commandLineOption.getValue(); - } else { - return "1"; - } - } - } + @Override + public String get(String key) { + for (Option commandLineOption : commandLine.getOptions()) { + if (commandLineOption.getOpt().equals(key)) { + if (commandLineOption.hasArg()) { + return commandLineOption.getValue(); + } else { + return "1"; + } + } + } - return super.get(key); - } + return super.get(key); + } - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + Arrays.hashCode(commandLine.getArgs()); - result = prime * result + Arrays.hashCode(commandLine.getOptions()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(commandLine.getArgs()); + result = prime * result + Arrays.hashCode(commandLine.getOptions()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (super.equals(obj) && obj instanceof RuntimeProperties) { - RuntimeProperties other = (RuntimeProperties) obj; - return Arrays.equals(commandLine.getArgs(), other.commandLine.getArgs()) - && Arrays.equals(commandLine.getOptions(), other.commandLine.getOptions()); - } - return false; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (super.equals(obj) && obj instanceof RuntimeProperties) { + RuntimeProperties other = (RuntimeProperties) obj; + return Arrays.equals(commandLine.getArgs(), other.commandLine.getArgs()) + && Arrays.equals(commandLine.getOptions(), other.commandLine.getOptions()); + } + return false; + } - @Override - public String toString() { - return String.format("%s[%s, args=%s, options=%s]", - getClass().getSimpleName(), - super.toString(), - Arrays.toString(commandLine.getArgs()), - Arrays.toString(commandLine.getOptions()) - ); - } + @Override + public String toString() { + return String.format( + "%s[%s, args=%s, options=%s]", + getClass().getSimpleName(), + super.toString(), + Arrays.toString(commandLine.getArgs()), + Arrays.toString(commandLine.getOptions())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LatencyProviderBase.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LatencyProviderBase.java index 15dbde8220..d6c7f6b02b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LatencyProviderBase.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LatencyProviderBase.java @@ -64,21 +64,17 @@ package com.radixdlt.qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import javax.inject.Qualifier; - import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Marker for LatencyProvider - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Marker for LatencyProvider */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface LatencyProviderBase { -} +public @interface LatencyProviderBase {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LocalSigner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LocalSigner.java index 0a38ede171..a47bb7937f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LocalSigner.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/qualifier/LocalSigner.java @@ -64,21 +64,17 @@ package com.radixdlt.qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import javax.inject.Qualifier; - import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Marker for radix engine hash signer. - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Marker for radix engine hash signer. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface LocalSigner { -} +public @interface LocalSigner {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochCeilingView.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochCeilingView.java index f1553eddf9..42a0a9d8bc 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochCeilingView.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochCeilingView.java @@ -73,12 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * Identifies the highest view per epoch until an epoch - * change must occur. - */ +/** Identifies the highest view per epoch until an epoch change must occur. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface EpochCeilingView { -} +public @interface EpochCeilingView {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochProofVerifierV2.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochProofVerifierV2.java index 78bd90f1ae..c78627341f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochProofVerifierV2.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/EpochProofVerifierV2.java @@ -72,55 +72,61 @@ import com.radixdlt.constraintmachine.REProcessedTxn; import com.radixdlt.engine.BatchVerifier; import com.radixdlt.engine.MetadataException; - import java.util.List; import java.util.stream.Collectors; public class EpochProofVerifierV2 implements BatchVerifier { - @Override - public void testMetadata(LedgerAndBFTProof metadata, List txns) throws MetadataException { - NextValidatorSetEvent nextValidatorSetEvent = null; - for (int i = 0; i < txns.size(); i++) { - var processed = txns.get(i); - var nextEpochEvents = processed.getEvents().stream() - .filter(NextValidatorSetEvent.class::isInstance) - .map(NextValidatorSetEvent.class::cast) - .collect(Collectors.toList()); + @Override + public void testMetadata(LedgerAndBFTProof metadata, List txns) + throws MetadataException { + NextValidatorSetEvent nextValidatorSetEvent = null; + for (int i = 0; i < txns.size(); i++) { + var processed = txns.get(i); + var nextEpochEvents = + processed.getEvents().stream() + .filter(NextValidatorSetEvent.class::isInstance) + .map(NextValidatorSetEvent.class::cast) + .collect(Collectors.toList()); - if (!nextEpochEvents.isEmpty()) { - // TODO: Move this check into Meter - if (i != txns.size() - 1) { - throw new MetadataException("Additional txns added to end of epoch."); - } + if (!nextEpochEvents.isEmpty()) { + // TODO: Move this check into Meter + if (i != txns.size() - 1) { + throw new MetadataException("Additional txns added to end of epoch."); + } - // TODO: Move this check into Meter - if (nextEpochEvents.size() != 1) { - throw new MetadataException("Multiple epoch events occurred in batch."); - } + // TODO: Move this check into Meter + if (nextEpochEvents.size() != 1) { + throw new MetadataException("Multiple epoch events occurred in batch."); + } - // TODO: Move this check into Meter - var stateUpdates = processed.getGroupedStateUpdates(); - if (stateUpdates.get(stateUpdates.size() - 1).stream() - .noneMatch(u -> u.getParsed() instanceof EpochData)) { - throw new MetadataException("Epoch update is not the last execution."); - } + // TODO: Move this check into Meter + var stateUpdates = processed.getGroupedStateUpdates(); + if (stateUpdates.get(stateUpdates.size() - 1).stream() + .noneMatch(u -> u.getParsed() instanceof EpochData)) { + throw new MetadataException("Epoch update is not the last execution."); + } - nextValidatorSetEvent = nextEpochEvents.get(0); - } - } + nextValidatorSetEvent = nextEpochEvents.get(0); + } + } - var nextValidatorSetMaybe = metadata.getProof().getNextValidatorSet(); - if (nextValidatorSetEvent == null != nextValidatorSetMaybe.isEmpty()) { - throw new MetadataException("Epoch event does not match proof " + nextValidatorSetEvent + " " + nextValidatorSetMaybe); - } - if (nextValidatorSetEvent != null) { - // TODO: Comparison of ordering as well - var nextValidatorSet = nextValidatorSetEvent.nextValidators().stream() - .map(v -> BFTValidator.from(BFTNode.create(v.getValidatorKey()), v.getAmount())); - var bftValidatorSet = BFTValidatorSet.from(nextValidatorSet); - if (!nextValidatorSetMaybe.orElseThrow().equals(bftValidatorSet)) { - throw new MetadataException("Validator set computed does not match proof."); - } - } - } + var nextValidatorSetMaybe = metadata.getProof().getNextValidatorSet(); + if (nextValidatorSetEvent == null != nextValidatorSetMaybe.isEmpty()) { + throw new MetadataException( + "Epoch event does not match proof " + + nextValidatorSetEvent + + " " + + nextValidatorSetMaybe); + } + if (nextValidatorSetEvent != null) { + // TODO: Comparison of ordering as well + var nextValidatorSet = + nextValidatorSetEvent.nextValidators().stream() + .map(v -> BFTValidator.from(BFTNode.create(v.getValidatorKey()), v.getAmount())); + var bftValidatorSet = BFTValidatorSet.from(nextValidatorSet); + if (!nextValidatorSetMaybe.orElseThrow().equals(bftValidatorSet)) { + throw new MetadataException("Validator set computed does not match proof."); + } + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/InvalidProposedTxn.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/InvalidProposedTxn.java index c9af8f0dae..feb4164bbb 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/InvalidProposedTxn.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/InvalidProposedTxn.java @@ -66,57 +66,56 @@ import com.radixdlt.atom.Txn; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; /** - * An event which signifies that a command has been proposed but which - * does not pass verification. + * An event which signifies that a command has been proposed but which does not pass verification. */ public final class InvalidProposedTxn { - private final ECPublicKey proposer; - private final Txn txn; - private final Exception e; + private final ECPublicKey proposer; + private final Txn txn; + private final Exception e; - private InvalidProposedTxn(ECPublicKey proposer, Txn txn, Exception e) { - this.proposer = proposer; - this.txn = txn; - this.e = e; - } + private InvalidProposedTxn(ECPublicKey proposer, Txn txn, Exception e) { + this.proposer = proposer; + this.txn = txn; + this.e = e; + } - public static InvalidProposedTxn create(ECPublicKey proposer, Txn txn, Exception e) { - Objects.requireNonNull(txn); - Objects.requireNonNull(e); - return new InvalidProposedTxn(proposer, txn, e); - } + public static InvalidProposedTxn create(ECPublicKey proposer, Txn txn, Exception e) { + Objects.requireNonNull(txn); + Objects.requireNonNull(e); + return new InvalidProposedTxn(proposer, txn, e); + } - public ECPublicKey getProposer() { - return proposer; - } + public ECPublicKey getProposer() { + return proposer; + } - public Exception getException() { - return e; - } + public Exception getException() { + return e; + } - @Override - public String toString() { - return String.format("%s{txn=%s ex=%s}", this.getClass().getSimpleName(), this.txn.getId(), this.e.toString()); - } - - @Override - public int hashCode() { - return Objects.hash(proposer, txn, e); - } + @Override + public String toString() { + return String.format( + "%s{txn=%s ex=%s}", this.getClass().getSimpleName(), this.txn.getId(), this.e.toString()); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof InvalidProposedTxn)) { - return false; - } + @Override + public int hashCode() { + return Objects.hash(proposer, txn, e); + } - InvalidProposedTxn other = (InvalidProposedTxn) o; - return Objects.equals(this.proposer, other.proposer) - && Objects.equals(this.e, other.e) - && Objects.equals(this.txn, other.txn); + @Override + public boolean equals(Object o) { + if (!(o instanceof InvalidProposedTxn)) { + return false; } + + InvalidProposedTxn other = (InvalidProposedTxn) o; + return Objects.equals(this.proposer, other.proposer) + && Objects.equals(this.e, other.e) + && Objects.equals(this.txn, other.txn); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/LedgerAndBFTProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/LedgerAndBFTProof.java index b6b85b8adb..fd0c327cca 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/LedgerAndBFTProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/LedgerAndBFTProof.java @@ -66,52 +66,50 @@ import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.VerifiedVertexStoreState; - import java.util.Objects; import java.util.Optional; -/** - * Proof of ledger commit - */ +/** Proof of ledger commit */ public final class LedgerAndBFTProof { - private final LedgerProof ledgerProof; - private final VerifiedVertexStoreState vertexStoreState; + private final LedgerProof ledgerProof; + private final VerifiedVertexStoreState vertexStoreState; - private LedgerAndBFTProof(LedgerProof ledgerProof, VerifiedVertexStoreState vertexStoreState) { - this.ledgerProof = ledgerProof; - this.vertexStoreState = vertexStoreState; - } + private LedgerAndBFTProof(LedgerProof ledgerProof, VerifiedVertexStoreState vertexStoreState) { + this.ledgerProof = ledgerProof; + this.vertexStoreState = vertexStoreState; + } - public static LedgerAndBFTProof create(LedgerProof ledgerProof) { - return create(ledgerProof, null); - } + public static LedgerAndBFTProof create(LedgerProof ledgerProof) { + return create(ledgerProof, null); + } - public static LedgerAndBFTProof create(LedgerProof ledgerProof, VerifiedVertexStoreState vertexStoreState) { - Objects.requireNonNull(ledgerProof); - return new LedgerAndBFTProof(ledgerProof, vertexStoreState); - } + public static LedgerAndBFTProof create( + LedgerProof ledgerProof, VerifiedVertexStoreState vertexStoreState) { + Objects.requireNonNull(ledgerProof); + return new LedgerAndBFTProof(ledgerProof, vertexStoreState); + } - public LedgerProof getProof() { - return ledgerProof; - } + public LedgerProof getProof() { + return ledgerProof; + } - public Optional vertexStoreState() { - return Optional.ofNullable(vertexStoreState); - } + public Optional vertexStoreState() { + return Optional.ofNullable(vertexStoreState); + } - @Override - public int hashCode() { - return Objects.hash(ledgerProof, vertexStoreState); - } + @Override + public int hashCode() { + return Objects.hash(ledgerProof, vertexStoreState); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof LedgerAndBFTProof)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof LedgerAndBFTProof)) { + return false; + } - var other = (LedgerAndBFTProof) o; - return Objects.equals(this.ledgerProof, other.ledgerProof) - && Objects.equals(this.vertexStoreState, other.vertexStoreState); - } + var other = (LedgerAndBFTProof) o; + return Objects.equals(this.ledgerProof, other.ledgerProof) + && Objects.equals(this.vertexStoreState, other.vertexStoreState); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxSigsPerRound.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxSigsPerRound.java index ff3707bb07..f37c476e8a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxSigsPerRound.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxSigsPerRound.java @@ -64,17 +64,16 @@ package com.radixdlt.statecomputer; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MaxSigsPerRound { -} +public @interface MaxSigsPerRound {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxValidators.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxValidators.java index 5a093e953e..87548ab189 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxValidators.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/MaxValidators.java @@ -73,11 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; -/** - * DI annotation used to identify the maximum number of validators. - */ +/** DI annotation used to identify the maximum number of validators. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface MaxValidators { -} +public @interface MaxValidators {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/NoValidatorsException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/NoValidatorsException.java index dd793d1dda..66e9a1b7fa 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/NoValidatorsException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/NoValidatorsException.java @@ -65,7 +65,7 @@ package com.radixdlt.statecomputer; public final class NoValidatorsException extends RuntimeException { - public NoValidatorsException(long epoch) { - super("Fatal no validators at the end of epoch " + epoch); - } + public NoValidatorsException(long epoch) { + super("Fatal no validators at the end of epoch " + epoch); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/REOutput.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/REOutput.java index fb0fec15a7..25d81af263 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/REOutput.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/REOutput.java @@ -65,26 +65,23 @@ package com.radixdlt.statecomputer; import com.radixdlt.constraintmachine.REProcessedTxn; - import java.util.List; import java.util.Objects; -/** - * Event signifying that an atom was committed to ledger successfully - */ +/** Event signifying that an atom was committed to ledger successfully */ public final class REOutput { - private final List processed; + private final List processed; - private REOutput(List processed) { - this.processed = processed; - } + private REOutput(List processed) { + this.processed = processed; + } - public static REOutput create(List processed) { - Objects.requireNonNull(processed); - return new REOutput(processed); - } + public static REOutput create(List processed) { + Objects.requireNonNull(processed); + return new REOutput(processed); + } - public List getProcessedTxns() { - return processed; - } + public List getProcessedTxns() { + return processed; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineMempool.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineMempool.java index f3b51f9b78..cf40e5f560 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineMempool.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineMempool.java @@ -69,22 +69,19 @@ import com.google.inject.Singleton; import com.radixdlt.atom.SubstateId; import com.radixdlt.atom.Txn; -import com.radixdlt.constraintmachine.REStateUpdate; import com.radixdlt.constraintmachine.REProcessedTxn; +import com.radixdlt.constraintmachine.REStateUpdate; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.RadixEngineException; import com.radixdlt.engine.RadixEngineResult; import com.radixdlt.identifiers.AID; import com.radixdlt.mempool.Mempool; -import com.radixdlt.mempool.MempoolMaxSize; -import com.radixdlt.mempool.MempoolMetadata; import com.radixdlt.mempool.MempoolDuplicateException; import com.radixdlt.mempool.MempoolFullException; +import com.radixdlt.mempool.MempoolMaxSize; +import com.radixdlt.mempool.MempoolMetadata; import com.radixdlt.mempool.MempoolRejectedException; import com.radixdlt.utils.Pair; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -96,148 +93,154 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * A mempool which uses internal radix engine to be more efficient. - */ +/** A mempool which uses internal radix engine to be more efficient. */ @Singleton public final class RadixEngineMempool implements Mempool { - private static final Logger logger = LogManager.getLogger(); - - private final ConcurrentHashMap> data = new ConcurrentHashMap<>(); - private final Map> substateIndex = new ConcurrentHashMap<>(); - private final RadixEngine radixEngine; - private final int maxSize; - - @Inject - public RadixEngineMempool( - RadixEngine radixEngine, - @MempoolMaxSize int maxSize - ) { - if (maxSize <= 0) { - throw new IllegalArgumentException("mempool.maxSize must be positive: " + maxSize); - } - this.maxSize = maxSize; - this.radixEngine = radixEngine; - } - - public T getData(Function>, T> mapper) { - return mapper.apply(data); - } - - @Override - public REProcessedTxn add(Txn txn) throws MempoolRejectedException { - if (this.data.size() >= maxSize) { - throw new MempoolFullException(this.data.size(), maxSize); - } - - if (this.data.containsKey(txn.getId())) { - throw new MempoolDuplicateException(String.format("Mempool already has command %s", txn.getId())); - } - - final RadixEngineResult result; - try { - RadixEngine.RadixEngineBranch checker = radixEngine.transientBranch(); - result = checker.execute(List.of(txn)); - } catch (RadixEngineException e) { - // TODO: allow missing dependency atoms to live for a certain amount of time - throw new MempoolRejectedException(e); - } finally { - radixEngine.deleteBranches(); - } - - var mempoolTxn = MempoolMetadata.create(System.currentTimeMillis()); - var data = Pair.of(result.getProcessedTxn(), mempoolTxn); - this.data.put(txn.getId(), data); - result.getProcessedTxn().substateDependencies() - .forEach(substateId -> substateIndex.merge(substateId, Set.of(txn.getId()), Sets::union)); - - return result.getProcessedTxn(); - } - - @Override - public List committed(List transactions) { - final var removed = new ArrayList(); - final var committedIds = transactions.stream() - .map(p -> p.getTxn().getId()) - .collect(Collectors.toSet()); - - transactions.stream() - .flatMap(REProcessedTxn::stateUpdates) - .filter(REStateUpdate::isShutDown) - .forEach(instruction -> { - var substateId = instruction.getId(); - Set txnIds = substateIndex.remove(substateId); - if (txnIds == null) { - return; - } - - for (var txnId : txnIds) { - var toRemove = data.remove(txnId); - // TODO: Cleanup - if (toRemove != null && !committedIds.contains(toRemove.getFirst().getTxn().getId())) { - removed.add(toRemove.getFirst().getTxn()); - } - } - }); - - if (!removed.isEmpty()) { - logger.debug("Evicting {} txns from mempool", removed.size()); - } - - return removed; - } - - @Override - public List getTxns(int count, List prepared) { - // TODO: Order by highest fees paid - var copy = new TreeSet<>(data.keySet()); - prepared.stream() - .flatMap(REProcessedTxn::stateUpdates) - .filter(REStateUpdate::isShutDown) - .flatMap(i -> substateIndex.getOrDefault(i.getId(), Set.of()).stream()) - .distinct() - .forEach(copy::remove); - - var txns = new ArrayList(); - - for (int i = 0; i < count && !copy.isEmpty(); i++) { - var txId = copy.first(); - copy.remove(txId); - var txnData = data.get(txId); - txnData.getFirst().stateUpdates() - .filter(REStateUpdate::isShutDown) - .flatMap(inst -> substateIndex.getOrDefault(inst.getId(), Set.of()).stream()) - .distinct() - .forEach(copy::remove); - - txns.add(txnData.getFirst().getTxn()); - } - - return txns; - } - - public Set getShuttingDownSubstates() { - return new HashSet<>(substateIndex.keySet()); - } - - @Override - public List scanUpdateAndGet(Predicate predicate, Consumer operator) { - return this.data - .values().stream() - .filter(e -> predicate.test(e.getSecond())) - .peek(e -> operator.accept(e.getSecond())) - .map(e -> e.getFirst().getTxn()) - .collect(Collectors.toList()); - } - - public int getCount() { - return this.data.size(); - } - - @Override - public String toString() { - return String.format("%s[%x:%s/%s]", - getClass().getSimpleName(), System.identityHashCode(this), this.data.size(), maxSize); - } + private static final Logger logger = LogManager.getLogger(); + + private final ConcurrentHashMap> data = + new ConcurrentHashMap<>(); + private final Map> substateIndex = new ConcurrentHashMap<>(); + private final RadixEngine radixEngine; + private final int maxSize; + + @Inject + public RadixEngineMempool( + RadixEngine radixEngine, @MempoolMaxSize int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("mempool.maxSize must be positive: " + maxSize); + } + this.maxSize = maxSize; + this.radixEngine = radixEngine; + } + + public T getData(Function>, T> mapper) { + return mapper.apply(data); + } + + @Override + public REProcessedTxn add(Txn txn) throws MempoolRejectedException { + if (this.data.size() >= maxSize) { + throw new MempoolFullException(this.data.size(), maxSize); + } + + if (this.data.containsKey(txn.getId())) { + throw new MempoolDuplicateException( + String.format("Mempool already has command %s", txn.getId())); + } + + final RadixEngineResult result; + try { + RadixEngine.RadixEngineBranch checker = radixEngine.transientBranch(); + result = checker.execute(List.of(txn)); + } catch (RadixEngineException e) { + // TODO: allow missing dependency atoms to live for a certain amount of time + throw new MempoolRejectedException(e); + } finally { + radixEngine.deleteBranches(); + } + + var mempoolTxn = MempoolMetadata.create(System.currentTimeMillis()); + var data = Pair.of(result.getProcessedTxn(), mempoolTxn); + this.data.put(txn.getId(), data); + result + .getProcessedTxn() + .substateDependencies() + .forEach(substateId -> substateIndex.merge(substateId, Set.of(txn.getId()), Sets::union)); + + return result.getProcessedTxn(); + } + + @Override + public List committed(List transactions) { + final var removed = new ArrayList(); + final var committedIds = + transactions.stream().map(p -> p.getTxn().getId()).collect(Collectors.toSet()); + + transactions.stream() + .flatMap(REProcessedTxn::stateUpdates) + .filter(REStateUpdate::isShutDown) + .forEach( + instruction -> { + var substateId = instruction.getId(); + Set txnIds = substateIndex.remove(substateId); + if (txnIds == null) { + return; + } + + for (var txnId : txnIds) { + var toRemove = data.remove(txnId); + // TODO: Cleanup + if (toRemove != null + && !committedIds.contains(toRemove.getFirst().getTxn().getId())) { + removed.add(toRemove.getFirst().getTxn()); + } + } + }); + + if (!removed.isEmpty()) { + logger.debug("Evicting {} txns from mempool", removed.size()); + } + + return removed; + } + + @Override + public List getTxns(int count, List prepared) { + // TODO: Order by highest fees paid + var copy = new TreeSet<>(data.keySet()); + prepared.stream() + .flatMap(REProcessedTxn::stateUpdates) + .filter(REStateUpdate::isShutDown) + .flatMap(i -> substateIndex.getOrDefault(i.getId(), Set.of()).stream()) + .distinct() + .forEach(copy::remove); + + var txns = new ArrayList(); + + for (int i = 0; i < count && !copy.isEmpty(); i++) { + var txId = copy.first(); + copy.remove(txId); + var txnData = data.get(txId); + txnData + .getFirst() + .stateUpdates() + .filter(REStateUpdate::isShutDown) + .flatMap(inst -> substateIndex.getOrDefault(inst.getId(), Set.of()).stream()) + .distinct() + .forEach(copy::remove); + + txns.add(txnData.getFirst().getTxn()); + } + + return txns; + } + + public Set getShuttingDownSubstates() { + return new HashSet<>(substateIndex.keySet()); + } + + @Override + public List scanUpdateAndGet( + Predicate predicate, Consumer operator) { + return this.data.values().stream() + .filter(e -> predicate.test(e.getSecond())) + .peek(e -> operator.accept(e.getSecond())) + .map(e -> e.getFirst().getTxn()) + .collect(Collectors.toList()); + } + + public int getCount() { + return this.data.size(); + } + + @Override + public String toString() { + return String.format( + "%s[%x:%s/%s]", + getClass().getSimpleName(), System.identityHashCode(this), this.data.size(), maxSize); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineModule.java index 34f46670ed..788d0a3a0b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineModule.java @@ -76,79 +76,72 @@ import com.radixdlt.statecomputer.forks.RERules; import com.radixdlt.store.EngineStore; import com.radixdlt.sync.CommittedReader; +import java.util.OptionalInt; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.OptionalInt; - -/** - * Module which manages execution of commands - */ +/** Module which manages execution of commands */ public class RadixEngineModule extends AbstractModule { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - @Provides - @Singleton - RERules reRules( - CommittedReader committedReader, // TODO: This is a hack, remove - Forks forks - ) { - var lastProof = committedReader.getLastProof().orElse(LedgerProof.mock()); - var epoch = lastProof.isEndOfEpoch() ? lastProof.getEpoch() + 1 : lastProof.getEpoch(); - return forks.get(epoch); - } + @Provides + @Singleton + RERules reRules( + CommittedReader committedReader, // TODO: This is a hack, remove + Forks forks) { + var lastProof = committedReader.getLastProof().orElse(LedgerProof.mock()); + var epoch = lastProof.isEndOfEpoch() ? lastProof.getEpoch() + 1 : lastProof.getEpoch(); + return forks.get(epoch); + } - // TODO: Remove - @Provides - @Singleton - private REParser parser(RERules rules) { - return rules.getParser(); - } + // TODO: Remove + @Provides + @Singleton + private REParser parser(RERules rules) { + return rules.getParser(); + } - // TODO: Remove - @Provides - @Singleton - @MaxSigsPerRound - private OptionalInt maxSigsPerRound(RERules rules) { - return rules.getMaxSigsPerRound(); - } + // TODO: Remove + @Provides + @Singleton + @MaxSigsPerRound + private OptionalInt maxSigsPerRound(RERules rules) { + return rules.getMaxSigsPerRound(); + } - // TODO: Remove - @Provides - @Singleton - @EpochCeilingView - private View epochCeilingHighView(RERules rules) { - return rules.getMaxRounds(); - } + // TODO: Remove + @Provides + @Singleton + @EpochCeilingView + private View epochCeilingHighView(RERules rules) { + return rules.getMaxRounds(); + } - // TODO: Remove - @Provides - @Singleton - @MaxValidators - private int maxValidators(RERules rules) { - return rules.getMaxValidators(); - } + // TODO: Remove + @Provides + @Singleton + @MaxValidators + private int maxValidators(RERules rules) { + return rules.getMaxValidators(); + } - @Provides - @Singleton - private RadixEngine getRadixEngine( - EngineStore engineStore, - RERules rules - ) { - var cmConfig = rules.getConstraintMachineConfig(); - var cm = new ConstraintMachine( - cmConfig.getProcedures(), - cmConfig.getDeserialization(), - cmConfig.getVirtualSubstateDeserialization(), - cmConfig.getMeter() - ); - return new RadixEngine<>( - rules.getParser(), - rules.getSerialization(), - rules.getActionConstructors(), - cm, - engineStore, - rules.getBatchVerifier() - ); - } + @Provides + @Singleton + private RadixEngine getRadixEngine( + EngineStore engineStore, RERules rules) { + var cmConfig = rules.getConstraintMachineConfig(); + var cm = + new ConstraintMachine( + cmConfig.getProcedures(), + cmConfig.getDeserialization(), + cmConfig.getVirtualSubstateDeserialization(), + cmConfig.getMeter()); + return new RadixEngine<>( + rules.getParser(), + rules.getSerialization(), + rules.getActionConstructors(), + cm, + engineStore, + rules.getBatchVerifier()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputer.java index 21c2ab6fb9..fc12c3c9a1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputer.java @@ -113,370 +113,374 @@ import com.radixdlt.mempool.MempoolDuplicateException; import com.radixdlt.mempool.MempoolRejectedException; import com.radixdlt.statecomputer.forks.Forks; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.Nullable; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.function.LongFunction; import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Wraps the Radix Engine and emits messages based on success or failure - */ +/** Wraps the Radix Engine and emits messages based on success or failure */ public final class RadixEngineStateComputer implements StateComputer { - private static final Logger log = LogManager.getLogger(); - - private final RadixEngineMempool mempool; - private final RadixEngine radixEngine; - private final EventDispatcher ledgerUpdateDispatcher; - private final EventDispatcher mempoolAddSuccessEventDispatcher; - private final EventDispatcher mempoolAtomsRemovedEventDispatcher; - private final EventDispatcher invalidProposedCommandEventDispatcher; - private final SystemCounters systemCounters; - private final Hasher hasher; - private final Forks forks; - private final Object lock = new Object(); - - private ProposerElection proposerElection; - private View epochCeilingView; - private OptionalInt maxSigsPerRound; - - @Inject - public RadixEngineStateComputer( - ProposerElection proposerElection, // TODO: Should be able to load this directly from state - RadixEngine radixEngine, - Forks forks, - RadixEngineMempool mempool, // TODO: Move this into radixEngine - @EpochCeilingView View epochCeilingView, // TODO: Move this into radixEngine - @MaxSigsPerRound OptionalInt maxSigsPerRound, // TODO: Move this into radixEngine - EventDispatcher mempoolAddedCommandEventDispatcher, - EventDispatcher invalidProposedCommandEventDispatcher, - EventDispatcher mempoolAtomsRemovedEventDispatcher, - EventDispatcher ledgerUpdateDispatcher, - Hasher hasher, - SystemCounters systemCounters - ) { - if (epochCeilingView.isGenesis()) { - throw new IllegalArgumentException("Epoch change view must not be genesis."); - } - - this.radixEngine = Objects.requireNonNull(radixEngine); - this.forks = forks; - this.epochCeilingView = epochCeilingView; - this.maxSigsPerRound = maxSigsPerRound; - this.mempool = Objects.requireNonNull(mempool); - this.mempoolAddSuccessEventDispatcher = Objects.requireNonNull(mempoolAddedCommandEventDispatcher); - this.invalidProposedCommandEventDispatcher = Objects.requireNonNull(invalidProposedCommandEventDispatcher); - this.mempoolAtomsRemovedEventDispatcher = Objects.requireNonNull(mempoolAtomsRemovedEventDispatcher); - this.ledgerUpdateDispatcher = Objects.requireNonNull(ledgerUpdateDispatcher); - this.hasher = Objects.requireNonNull(hasher); - this.systemCounters = Objects.requireNonNull(systemCounters); - this.proposerElection = proposerElection; - } - - public static class RadixEngineTxn implements PreparedTxn { - private final Txn txn; - private final REProcessedTxn processed; - private final PermissionLevel permissionLevel; - - public RadixEngineTxn( - Txn txn, - REProcessedTxn processed, - PermissionLevel permissionLevel - ) { - this.txn = txn; - this.processed = processed; - this.permissionLevel = permissionLevel; - } - - REProcessedTxn processedTxn() { - return processed; - } - - @Override - public Txn txn() { - return txn; - } - } - - public REProcessedTxn test(byte[] payload, boolean isSigned) throws RadixEngineException { - synchronized (lock) { - var txn = isSigned - ? Txn.create(payload) - : TxLowLevelBuilder.newBuilder(payload).sig(ECDSASignature.zeroSignature()).build(); - var checker = radixEngine.transientBranch(); - try { - var result = checker.execute(List.of(txn), !isSigned); - return result.getProcessedTxn(); - } finally { - radixEngine.deleteBranches(); - } - } - } - - public REProcessedTxn addToMempool(Txn txn) throws MempoolRejectedException { - return addToMempool(txn, null); - } - - public REProcessedTxn addToMempool(Txn txn, BFTNode origin) throws MempoolRejectedException { - synchronized (lock) { - REProcessedTxn processed; - try { - processed = mempool.add(txn); - } catch (MempoolDuplicateException e) { - throw e; - } catch (MempoolRejectedException e) { - systemCounters.increment(SystemCounters.CounterType.MEMPOOL_ADD_FAILURE); - throw e; - } - - systemCounters.increment(SystemCounters.CounterType.MEMPOOL_ADD_SUCCESS); - systemCounters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); - var success = MempoolAddSuccess.create(txn, processed, origin); - mempoolAddSuccessEventDispatcher.dispatch(success); - - return processed; - } - } - - @Override - public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { - mempoolAdd.txns().forEach(txn -> { - try { - addToMempool(txn); - } catch (MempoolRejectedException ignored) { - } - }); - } - - @Override - public List getNextTxnsFromMempool(List prepared) { - synchronized (lock) { - var cmds = prepared.stream() - .map(p -> (RadixEngineTxn) p) - .map(RadixEngineTxn::processedTxn) - .collect(Collectors.toList()); - - // TODO: only return commands which will not cause a missing dependency error - return mempool.getTxns(maxSigsPerRound.orElse(50), cmds); - } - } - - private LongFunction getValidatorMapping() { - return l -> proposerElection.getProposer(View.of(l)).getKey(); - } - - private RadixEngineTxn executeSystemUpdate( - RadixEngineBranch branch, - VerifiedVertex vertex, - long timestamp - ) { - var systemActions = TxnConstructionRequest.create(); - var view = vertex.getView(); - if (view.compareTo(epochCeilingView) <= 0) { - systemActions.action(new NextRound( - view.number(), - vertex.isTimeout(), - timestamp, - getValidatorMapping() - )); - } else { - if (vertex.getParentHeader().getView().compareTo(epochCeilingView) < 0) { - systemActions.action(new NextRound( - epochCeilingView.number(), - true, - timestamp, - getValidatorMapping() - )); - } - systemActions.action(new NextEpoch(timestamp)); - } - - final Txn systemUpdate; - final RadixEngineResult result; - try { - // TODO: combine construct/execute - systemUpdate = branch.construct(systemActions).buildWithoutSignature(); - result = branch.execute(List.of(systemUpdate), PermissionLevel.SUPER_USER); - } catch (RadixEngineException | TxBuilderException e) { - throw new IllegalStateException( - String.format("Failed to execute system updates: %s", systemActions), e - ); - } - return new RadixEngineTxn( - systemUpdate, - result.getProcessedTxn(), - PermissionLevel.SUPER_USER - ); - } - - private void executeUserCommands( - BFTNode proposer, - RadixEngineBranch branch, - List nextTxns, - ImmutableList.Builder successBuilder, - ImmutableMap.Builder errorBuilder - ) { - // TODO: This check should probably be done before getting into state computer - this.maxSigsPerRound.ifPresent(max -> { - if (nextTxns.size() > max) { - log.warn("{} proposing {} txns when limit is {}", proposer, nextTxns.size(), max); - } - }); - var numToProcess = Integer.min(nextTxns.size(), this.maxSigsPerRound.orElse(Integer.MAX_VALUE)); - for (int i = 0; i < numToProcess; i++) { - var txn = nextTxns.get(i); - final RadixEngineResult result; - try { - result = branch.execute(List.of(txn)); - } catch (RadixEngineException e) { - errorBuilder.put(txn, e); - invalidProposedCommandEventDispatcher.dispatch(InvalidProposedTxn.create(proposer.getKey(), txn, e)); - return; - } - - var radixEngineCommand = new RadixEngineTxn(txn, result.getProcessedTxn(), PermissionLevel.USER); - successBuilder.add(radixEngineCommand); - } - } - - @Override - public StateComputerResult prepare(List previous, VerifiedVertex vertex, long timestamp) { - synchronized (lock) { - var next = vertex.getTxns(); - var transientBranch = this.radixEngine.transientBranch(); - for (PreparedTxn command : previous) { - // TODO: fix this cast with generics. Currently the fix would become a bit too messy - final var radixEngineCommand = (RadixEngineTxn) command; - try { - transientBranch.execute( - List.of(radixEngineCommand.txn), - radixEngineCommand.permissionLevel - ); - } catch (RadixEngineException e) { - throw new IllegalStateException("Re-execution of already prepared atom failed: " - + radixEngineCommand.processed.getTxn().getId(), e); - } - } - - var systemTxn = this.executeSystemUpdate(transientBranch, vertex, timestamp); - final ImmutableList.Builder successBuilder = ImmutableList.builder(); - successBuilder.add(systemTxn); - final ImmutableMap.Builder exceptionBuilder = ImmutableMap.builder(); - var nextValidatorSet = systemTxn.processedTxn().getEvents().stream() - .filter(NextValidatorSetEvent.class::isInstance) - .map(NextValidatorSetEvent.class::cast) - .findFirst() - .map(e -> BFTValidatorSet.from( - e.nextValidators().stream() - .map(v -> BFTValidator.from(BFTNode.create(v.getValidatorKey()), v.getAmount()))) - ); - // Don't execute command if changing epochs - if (nextValidatorSet.isEmpty()) { - this.executeUserCommands(vertex.getProposer(), transientBranch, next, successBuilder, exceptionBuilder); - } - this.radixEngine.deleteBranches(); - - return new StateComputerResult(successBuilder.build(), exceptionBuilder.build(), nextValidatorSet.orElse(null)); - } - } - - private List commitInternal( - VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState - ) { - var proof = verifiedTxnsAndProof.getProof(); - var ledgerAndBFTProof = LedgerAndBFTProof.create(proof, vertexStoreState); - - final RadixEngineResult result; - try { - result = this.radixEngine.execute( - verifiedTxnsAndProof.getTxns(), - ledgerAndBFTProof, - PermissionLevel.SUPER_USER - ); - } catch (RadixEngineException e) { - throw new CommittedBadTxnException(verifiedTxnsAndProof, e); - } catch (MetadataException e) { - throw new ByzantineQuorumException(e.getMessage(), e); - } - - // Next epoch - if (proof.getNextValidatorSet().isPresent()) { - forks.ifForkGet(proof.getEpoch() + 1) - .ifPresent(rules -> { - log.info("Epoch {} Forking RadixEngine to {}", proof.getEpoch() + 1, rules.name()); - this.radixEngine.replaceConstraintMachine( - rules.getConstraintMachineConfig(), - rules.getSerialization(), - rules.getActionConstructors(), - rules.getBatchVerifier(), - rules.getParser() - ); - this.epochCeilingView = rules.getMaxRounds(); - this.maxSigsPerRound = rules.getMaxSigsPerRound(); - }); - } - - result.getProcessedTxns().forEach(t -> { - if (t.isSystemOnly()) { - systemCounters.increment(SystemCounters.CounterType.RADIX_ENGINE_SYSTEM_TRANSACTIONS); - } else { - systemCounters.increment(SystemCounters.CounterType.RADIX_ENGINE_USER_TRANSACTIONS); - } - }); - - return result.getProcessedTxns(); - } - - @Override - public void commit(VerifiedTxnsAndProof txnsAndProof, VerifiedVertexStoreState vertexStoreState) { - synchronized (lock) { - var txCommitted = commitInternal(txnsAndProof, vertexStoreState); - - // TODO: refactor mempool to be less generic and make this more efficient - // TODO: Move this into engine - List removed = this.mempool.committed(txCommitted); - systemCounters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); - if (!removed.isEmpty()) { - TxnsRemovedFromMempool atomsRemovedFromMempool = TxnsRemovedFromMempool.create(removed); - mempoolAtomsRemovedEventDispatcher.dispatch(atomsRemovedFromMempool); - } - - var epochChangeOptional = txnsAndProof.getProof().getNextValidatorSet().map(validatorSet -> { - var header = txnsAndProof.getProof(); - // TODO: Move vertex stuff somewhere else - var genesisVertex = UnverifiedVertex.createGenesis(header.getRaw()); - var verifiedGenesisVertex = new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); - var nextLedgerHeader = LedgerHeader.create( - header.getEpoch() + 1, - View.genesis(), - header.getAccumulatorState(), - header.timestamp() - ); - var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); - final var initialState = - VerifiedVertexStoreState.create( - HighQC.from(genesisQC), - verifiedGenesisVertex, - Optional.empty(), - hasher - ); - var proposerElection = new WeightedRotatingLeaders(validatorSet); - var bftConfiguration = new BFTConfiguration(proposerElection, validatorSet, initialState); - return new EpochChange(header, bftConfiguration); - }); - var outputBuilder = ImmutableClassToInstanceMap.builder(); - epochChangeOptional.ifPresent(e -> { - this.proposerElection = e.getBFTConfiguration().getProposerElection(); - outputBuilder.put(EpochChange.class, e); - }); - outputBuilder.put(REOutput.class, REOutput.create(txCommitted)); - var ledgerUpdate = new LedgerUpdate(txnsAndProof, outputBuilder.build()); - ledgerUpdateDispatcher.dispatch(ledgerUpdate); - } - } + private static final Logger log = LogManager.getLogger(); + + private final RadixEngineMempool mempool; + private final RadixEngine radixEngine; + private final EventDispatcher ledgerUpdateDispatcher; + private final EventDispatcher mempoolAddSuccessEventDispatcher; + private final EventDispatcher mempoolAtomsRemovedEventDispatcher; + private final EventDispatcher invalidProposedCommandEventDispatcher; + private final SystemCounters systemCounters; + private final Hasher hasher; + private final Forks forks; + private final Object lock = new Object(); + + private ProposerElection proposerElection; + private View epochCeilingView; + private OptionalInt maxSigsPerRound; + + @Inject + public RadixEngineStateComputer( + ProposerElection proposerElection, // TODO: Should be able to load this directly from state + RadixEngine radixEngine, + Forks forks, + RadixEngineMempool mempool, // TODO: Move this into radixEngine + @EpochCeilingView View epochCeilingView, // TODO: Move this into radixEngine + @MaxSigsPerRound OptionalInt maxSigsPerRound, // TODO: Move this into radixEngine + EventDispatcher mempoolAddedCommandEventDispatcher, + EventDispatcher invalidProposedCommandEventDispatcher, + EventDispatcher mempoolAtomsRemovedEventDispatcher, + EventDispatcher ledgerUpdateDispatcher, + Hasher hasher, + SystemCounters systemCounters) { + if (epochCeilingView.isGenesis()) { + throw new IllegalArgumentException("Epoch change view must not be genesis."); + } + + this.radixEngine = Objects.requireNonNull(radixEngine); + this.forks = forks; + this.epochCeilingView = epochCeilingView; + this.maxSigsPerRound = maxSigsPerRound; + this.mempool = Objects.requireNonNull(mempool); + this.mempoolAddSuccessEventDispatcher = + Objects.requireNonNull(mempoolAddedCommandEventDispatcher); + this.invalidProposedCommandEventDispatcher = + Objects.requireNonNull(invalidProposedCommandEventDispatcher); + this.mempoolAtomsRemovedEventDispatcher = + Objects.requireNonNull(mempoolAtomsRemovedEventDispatcher); + this.ledgerUpdateDispatcher = Objects.requireNonNull(ledgerUpdateDispatcher); + this.hasher = Objects.requireNonNull(hasher); + this.systemCounters = Objects.requireNonNull(systemCounters); + this.proposerElection = proposerElection; + } + + public static class RadixEngineTxn implements PreparedTxn { + private final Txn txn; + private final REProcessedTxn processed; + private final PermissionLevel permissionLevel; + + public RadixEngineTxn(Txn txn, REProcessedTxn processed, PermissionLevel permissionLevel) { + this.txn = txn; + this.processed = processed; + this.permissionLevel = permissionLevel; + } + + REProcessedTxn processedTxn() { + return processed; + } + + @Override + public Txn txn() { + return txn; + } + } + + public REProcessedTxn test(byte[] payload, boolean isSigned) throws RadixEngineException { + synchronized (lock) { + var txn = + isSigned + ? Txn.create(payload) + : TxLowLevelBuilder.newBuilder(payload).sig(ECDSASignature.zeroSignature()).build(); + var checker = radixEngine.transientBranch(); + try { + var result = checker.execute(List.of(txn), !isSigned); + return result.getProcessedTxn(); + } finally { + radixEngine.deleteBranches(); + } + } + } + + public REProcessedTxn addToMempool(Txn txn) throws MempoolRejectedException { + return addToMempool(txn, null); + } + + public REProcessedTxn addToMempool(Txn txn, BFTNode origin) throws MempoolRejectedException { + synchronized (lock) { + REProcessedTxn processed; + try { + processed = mempool.add(txn); + } catch (MempoolDuplicateException e) { + throw e; + } catch (MempoolRejectedException e) { + systemCounters.increment(SystemCounters.CounterType.MEMPOOL_ADD_FAILURE); + throw e; + } + + systemCounters.increment(SystemCounters.CounterType.MEMPOOL_ADD_SUCCESS); + systemCounters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); + var success = MempoolAddSuccess.create(txn, processed, origin); + mempoolAddSuccessEventDispatcher.dispatch(success); + + return processed; + } + } + + @Override + public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { + mempoolAdd + .txns() + .forEach( + txn -> { + try { + addToMempool(txn); + } catch (MempoolRejectedException ignored) { + } + }); + } + + @Override + public List getNextTxnsFromMempool(List prepared) { + synchronized (lock) { + var cmds = + prepared.stream() + .map(p -> (RadixEngineTxn) p) + .map(RadixEngineTxn::processedTxn) + .collect(Collectors.toList()); + + // TODO: only return commands which will not cause a missing dependency error + return mempool.getTxns(maxSigsPerRound.orElse(50), cmds); + } + } + + private LongFunction getValidatorMapping() { + return l -> proposerElection.getProposer(View.of(l)).getKey(); + } + + private RadixEngineTxn executeSystemUpdate( + RadixEngineBranch branch, VerifiedVertex vertex, long timestamp) { + var systemActions = TxnConstructionRequest.create(); + var view = vertex.getView(); + if (view.compareTo(epochCeilingView) <= 0) { + systemActions.action( + new NextRound(view.number(), vertex.isTimeout(), timestamp, getValidatorMapping())); + } else { + if (vertex.getParentHeader().getView().compareTo(epochCeilingView) < 0) { + systemActions.action( + new NextRound(epochCeilingView.number(), true, timestamp, getValidatorMapping())); + } + systemActions.action(new NextEpoch(timestamp)); + } + + final Txn systemUpdate; + final RadixEngineResult result; + try { + // TODO: combine construct/execute + systemUpdate = branch.construct(systemActions).buildWithoutSignature(); + result = branch.execute(List.of(systemUpdate), PermissionLevel.SUPER_USER); + } catch (RadixEngineException | TxBuilderException e) { + throw new IllegalStateException( + String.format("Failed to execute system updates: %s", systemActions), e); + } + return new RadixEngineTxn(systemUpdate, result.getProcessedTxn(), PermissionLevel.SUPER_USER); + } + + private void executeUserCommands( + BFTNode proposer, + RadixEngineBranch branch, + List nextTxns, + ImmutableList.Builder successBuilder, + ImmutableMap.Builder errorBuilder) { + // TODO: This check should probably be done before getting into state computer + this.maxSigsPerRound.ifPresent( + max -> { + if (nextTxns.size() > max) { + log.warn("{} proposing {} txns when limit is {}", proposer, nextTxns.size(), max); + } + }); + var numToProcess = Integer.min(nextTxns.size(), this.maxSigsPerRound.orElse(Integer.MAX_VALUE)); + for (int i = 0; i < numToProcess; i++) { + var txn = nextTxns.get(i); + final RadixEngineResult result; + try { + result = branch.execute(List.of(txn)); + } catch (RadixEngineException e) { + errorBuilder.put(txn, e); + invalidProposedCommandEventDispatcher.dispatch( + InvalidProposedTxn.create(proposer.getKey(), txn, e)); + return; + } + + var radixEngineCommand = + new RadixEngineTxn(txn, result.getProcessedTxn(), PermissionLevel.USER); + successBuilder.add(radixEngineCommand); + } + } + + @Override + public StateComputerResult prepare( + List previous, VerifiedVertex vertex, long timestamp) { + synchronized (lock) { + var next = vertex.getTxns(); + var transientBranch = this.radixEngine.transientBranch(); + for (PreparedTxn command : previous) { + // TODO: fix this cast with generics. Currently the fix would become a bit too messy + final var radixEngineCommand = (RadixEngineTxn) command; + try { + transientBranch.execute( + List.of(radixEngineCommand.txn), radixEngineCommand.permissionLevel); + } catch (RadixEngineException e) { + throw new IllegalStateException( + "Re-execution of already prepared atom failed: " + + radixEngineCommand.processed.getTxn().getId(), + e); + } + } + + var systemTxn = this.executeSystemUpdate(transientBranch, vertex, timestamp); + final ImmutableList.Builder successBuilder = ImmutableList.builder(); + successBuilder.add(systemTxn); + final ImmutableMap.Builder exceptionBuilder = ImmutableMap.builder(); + var nextValidatorSet = + systemTxn.processedTxn().getEvents().stream() + .filter(NextValidatorSetEvent.class::isInstance) + .map(NextValidatorSetEvent.class::cast) + .findFirst() + .map( + e -> + BFTValidatorSet.from( + e.nextValidators().stream() + .map( + v -> + BFTValidator.from( + BFTNode.create(v.getValidatorKey()), v.getAmount())))); + // Don't execute command if changing epochs + if (nextValidatorSet.isEmpty()) { + this.executeUserCommands( + vertex.getProposer(), transientBranch, next, successBuilder, exceptionBuilder); + } + this.radixEngine.deleteBranches(); + + return new StateComputerResult( + successBuilder.build(), exceptionBuilder.build(), nextValidatorSet.orElse(null)); + } + } + + private List commitInternal( + VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { + var proof = verifiedTxnsAndProof.getProof(); + var ledgerAndBFTProof = LedgerAndBFTProof.create(proof, vertexStoreState); + + final RadixEngineResult result; + try { + result = + this.radixEngine.execute( + verifiedTxnsAndProof.getTxns(), ledgerAndBFTProof, PermissionLevel.SUPER_USER); + } catch (RadixEngineException e) { + throw new CommittedBadTxnException(verifiedTxnsAndProof, e); + } catch (MetadataException e) { + throw new ByzantineQuorumException(e.getMessage(), e); + } + + // Next epoch + if (proof.getNextValidatorSet().isPresent()) { + forks + .ifForkGet(proof.getEpoch() + 1) + .ifPresent( + rules -> { + log.info("Epoch {} Forking RadixEngine to {}", proof.getEpoch() + 1, rules.name()); + this.radixEngine.replaceConstraintMachine( + rules.getConstraintMachineConfig(), + rules.getSerialization(), + rules.getActionConstructors(), + rules.getBatchVerifier(), + rules.getParser()); + this.epochCeilingView = rules.getMaxRounds(); + this.maxSigsPerRound = rules.getMaxSigsPerRound(); + }); + } + + result + .getProcessedTxns() + .forEach( + t -> { + if (t.isSystemOnly()) { + systemCounters.increment( + SystemCounters.CounterType.RADIX_ENGINE_SYSTEM_TRANSACTIONS); + } else { + systemCounters.increment(SystemCounters.CounterType.RADIX_ENGINE_USER_TRANSACTIONS); + } + }); + + return result.getProcessedTxns(); + } + + @Override + public void commit(VerifiedTxnsAndProof txnsAndProof, VerifiedVertexStoreState vertexStoreState) { + synchronized (lock) { + var txCommitted = commitInternal(txnsAndProof, vertexStoreState); + + // TODO: refactor mempool to be less generic and make this more efficient + // TODO: Move this into engine + List removed = this.mempool.committed(txCommitted); + systemCounters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); + if (!removed.isEmpty()) { + TxnsRemovedFromMempool atomsRemovedFromMempool = TxnsRemovedFromMempool.create(removed); + mempoolAtomsRemovedEventDispatcher.dispatch(atomsRemovedFromMempool); + } + + var epochChangeOptional = + txnsAndProof + .getProof() + .getNextValidatorSet() + .map( + validatorSet -> { + var header = txnsAndProof.getProof(); + // TODO: Move vertex stuff somewhere else + var genesisVertex = UnverifiedVertex.createGenesis(header.getRaw()); + var verifiedGenesisVertex = + new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); + var nextLedgerHeader = + LedgerHeader.create( + header.getEpoch() + 1, + View.genesis(), + header.getAccumulatorState(), + header.timestamp()); + var genesisQC = + QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); + final var initialState = + VerifiedVertexStoreState.create( + HighQC.from(genesisQC), + verifiedGenesisVertex, + Optional.empty(), + hasher); + var proposerElection = new WeightedRotatingLeaders(validatorSet); + var bftConfiguration = + new BFTConfiguration(proposerElection, validatorSet, initialState); + return new EpochChange(header, bftConfiguration); + }); + var outputBuilder = ImmutableClassToInstanceMap.builder(); + epochChangeOptional.ifPresent( + e -> { + this.proposerElection = e.getBFTConfiguration().getProposerElection(); + outputBuilder.put(EpochChange.class, e); + }); + outputBuilder.put(REOutput.class, REOutput.create(txCommitted)); + var ledgerUpdate = new LedgerUpdate(txnsAndProof, outputBuilder.build()); + ledgerUpdateDispatcher.dispatch(ledgerUpdate); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputerModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputerModule.java index 99d7a31223..bfb7d3823d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputerModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RadixEngineStateComputerModule.java @@ -72,12 +72,16 @@ import com.radixdlt.mempool.Mempool; public class RadixEngineStateComputerModule extends AbstractModule { - @Override - protected void configure() { - bind(RadixEngineStateComputer.class).in(Scopes.SINGLETON); - bind(RadixEngineMempool.class).in(Scopes.SINGLETON); - bind(StateComputerLedger.StateComputer.class).to(RadixEngineStateComputer.class).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }).to(RadixEngineMempool.class).in(Scopes.SINGLETON); - bind(new TypeLiteral>() { }).to(RadixEngineMempool.class).in(Scopes.SINGLETON); - } + @Override + protected void configure() { + bind(RadixEngineStateComputer.class).in(Scopes.SINGLETON); + bind(RadixEngineMempool.class).in(Scopes.SINGLETON); + bind(StateComputerLedger.StateComputer.class) + .to(RadixEngineStateComputer.class) + .in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}).to(RadixEngineMempool.class).in(Scopes.SINGLETON); + bind(new TypeLiteral>() {}) + .to(RadixEngineMempool.class) + .in(Scopes.SINGLETON); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/Rewards.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/Rewards.java index 5cec6e9d61..f522fa2f76 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/Rewards.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/Rewards.java @@ -68,91 +68,93 @@ import com.google.common.collect.Maps; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.utils.UInt256; - import java.util.Map; import java.util.Objects; import java.util.stream.Stream; -/** - * Wrapper class for amount staked per node - */ +/** Wrapper class for amount staked per node */ public final class Rewards { - private final ImmutableMap stakedAmounts; - - private Rewards(ImmutableMap stakedAmounts) { - this.stakedAmounts = stakedAmounts; - } - - public static Rewards create() { - return new Rewards(ImmutableMap.of()); - } - - public ImmutableMap toMap() { - return stakedAmounts; - } - - public Rewards add(ECPublicKey delegatedKey, UInt256 amount) { - if (amount.isZero()) { - return this; - } - - final var nextAmount = this.stakedAmounts.getOrDefault(delegatedKey, UInt256.ZERO).add(amount); - final var nextStakedAmounts = Stream.concat( - Stream.of(Maps.immutableEntry(delegatedKey, nextAmount)), - this.stakedAmounts.entrySet().stream().filter(e -> !delegatedKey.equals(e.getKey())) - ).collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - - return new Rewards(nextStakedAmounts); - } - - public Rewards remove(ECPublicKey delegatedKey, UInt256 amount) { - if (!this.stakedAmounts.containsKey(delegatedKey)) { - throw new IllegalStateException("Removing stake which doesn't exist."); - } - - if (amount.isZero()) { - return this; - } - - final var oldAmount = this.stakedAmounts.get(delegatedKey); - final var comparison = amount.compareTo(oldAmount); - - if (comparison == 0) { - // remove stake - final var nextStakedAmounts = this.stakedAmounts.entrySet().stream() - .filter(e -> !delegatedKey.equals(e.getKey())) - .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - return new Rewards(nextStakedAmounts); - } else if (comparison < 0) { - // reduce stake - final var nextAmount = oldAmount.subtract(amount); - final var nextStakedAmounts = Stream.concat( - Stream.of(Maps.immutableEntry(delegatedKey, nextAmount)), - this.stakedAmounts.entrySet().stream().filter(e -> !delegatedKey.equals(e.getKey())) - ).collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - return new Rewards(nextStakedAmounts); - } else { - throw new IllegalStateException("Removing stake which doesn't exist."); - } - } - - @Override - public String toString() { - return this.stakedAmounts.toString(); - } - - @Override - public int hashCode() { - return Objects.hash(stakedAmounts); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Rewards)) { - return false; - } - - var other = (Rewards) o; - return Objects.equals(this.stakedAmounts, other.stakedAmounts); - } + private final ImmutableMap stakedAmounts; + + private Rewards(ImmutableMap stakedAmounts) { + this.stakedAmounts = stakedAmounts; + } + + public static Rewards create() { + return new Rewards(ImmutableMap.of()); + } + + public ImmutableMap toMap() { + return stakedAmounts; + } + + public Rewards add(ECPublicKey delegatedKey, UInt256 amount) { + if (amount.isZero()) { + return this; + } + + final var nextAmount = this.stakedAmounts.getOrDefault(delegatedKey, UInt256.ZERO).add(amount); + final var nextStakedAmounts = + Stream.concat( + Stream.of(Maps.immutableEntry(delegatedKey, nextAmount)), + this.stakedAmounts.entrySet().stream() + .filter(e -> !delegatedKey.equals(e.getKey()))) + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + + return new Rewards(nextStakedAmounts); + } + + public Rewards remove(ECPublicKey delegatedKey, UInt256 amount) { + if (!this.stakedAmounts.containsKey(delegatedKey)) { + throw new IllegalStateException("Removing stake which doesn't exist."); + } + + if (amount.isZero()) { + return this; + } + + final var oldAmount = this.stakedAmounts.get(delegatedKey); + final var comparison = amount.compareTo(oldAmount); + + if (comparison == 0) { + // remove stake + final var nextStakedAmounts = + this.stakedAmounts.entrySet().stream() + .filter(e -> !delegatedKey.equals(e.getKey())) + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + return new Rewards(nextStakedAmounts); + } else if (comparison < 0) { + // reduce stake + final var nextAmount = oldAmount.subtract(amount); + final var nextStakedAmounts = + Stream.concat( + Stream.of(Maps.immutableEntry(delegatedKey, nextAmount)), + this.stakedAmounts.entrySet().stream() + .filter(e -> !delegatedKey.equals(e.getKey()))) + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + return new Rewards(nextStakedAmounts); + } else { + throw new IllegalStateException("Removing stake which doesn't exist."); + } + } + + @Override + public String toString() { + return this.stakedAmounts.toString(); + } + + @Override + public int hashCode() { + return Objects.hash(stakedAmounts); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Rewards)) { + return false; + } + + var other = (Rewards) o; + return Objects.equals(this.stakedAmounts, other.stakedAmounts); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/TxnsRemovedFromMempool.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/TxnsRemovedFromMempool.java index d330917555..b255b44d63 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/TxnsRemovedFromMempool.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/TxnsRemovedFromMempool.java @@ -65,17 +65,13 @@ package com.radixdlt.statecomputer; import com.radixdlt.atom.Txn; - import java.util.List; import java.util.Objects; -/** - * Event describing atoms which have been removed from the mempool - * after a commit. - */ +/** Event describing atoms which have been removed from the mempool after a commit. */ public record TxnsRemovedFromMempool(List removed) { - public static TxnsRemovedFromMempool create(List removed) { - Objects.requireNonNull(removed); - return new TxnsRemovedFromMempool(removed); - } + public static TxnsRemovedFromMempool create(List removed) { + Objects.requireNonNull(removed); + return new TxnsRemovedFromMempool(removed); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/Genesis.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/Genesis.java index 29d7b7d52f..5e2db1ed50 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/Genesis.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/Genesis.java @@ -64,20 +64,17 @@ package com.radixdlt.statecomputer.checkpoint; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies a genesis atom parameter - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Specifies a genesis atom parameter */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface Genesis { -} +public @interface Genesis {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisBuilder.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisBuilder.java index a57d2d3f55..09aee90bad 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisBuilder.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisBuilder.java @@ -86,77 +86,76 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.forks.RERules; import com.radixdlt.store.InMemoryEngineStore; - import java.nio.charset.StandardCharsets; import java.util.List; public final class GenesisBuilder { - private final LedgerAccumulator ledgerAccumulator; - private final RadixEngine radixEngine; + private final LedgerAccumulator ledgerAccumulator; + private final RadixEngine radixEngine; - @Inject - public GenesisBuilder( - RERules rules, - LedgerAccumulator ledgerAccumulator - ) { - this.ledgerAccumulator = ledgerAccumulator; - var cmConfig = rules.getConstraintMachineConfig(); - var cm = new ConstraintMachine( - cmConfig.getProcedures(), - cmConfig.getDeserialization(), - cmConfig.getVirtualSubstateDeserialization(), - cmConfig.getMeter() - ); - this.radixEngine = new RadixEngine<>( - rules.getParser(), - rules.getSerialization(), - rules.getActionConstructors(), - cm, - new InMemoryEngineStore<>(), - rules.getBatchVerifier() - ); - } + @Inject + public GenesisBuilder(RERules rules, LedgerAccumulator ledgerAccumulator) { + this.ledgerAccumulator = ledgerAccumulator; + var cmConfig = rules.getConstraintMachineConfig(); + var cm = + new ConstraintMachine( + cmConfig.getProcedures(), + cmConfig.getDeserialization(), + cmConfig.getVirtualSubstateDeserialization(), + cmConfig.getMeter()); + this.radixEngine = + new RadixEngine<>( + rules.getParser(), + rules.getSerialization(), + rules.getActionConstructors(), + cm, + new InMemoryEngineStore<>(), + rules.getBatchVerifier()); + } - public Txn build(String message, long timestamp, List actions) throws TxBuilderException, RadixEngineException { - var txnConstructionRequest = TxnConstructionRequest.create(); - txnConstructionRequest.msg(message.getBytes(StandardCharsets.UTF_8)); - txnConstructionRequest.action(new CreateSystem(timestamp)); - actions.forEach(txnConstructionRequest::action); - txnConstructionRequest.action(new NextEpoch(timestamp)); - var txn = radixEngine.construct(txnConstructionRequest).buildWithoutSignature(); + public Txn build(String message, long timestamp, List actions) + throws TxBuilderException, RadixEngineException { + var txnConstructionRequest = TxnConstructionRequest.create(); + txnConstructionRequest.msg(message.getBytes(StandardCharsets.UTF_8)); + txnConstructionRequest.action(new CreateSystem(timestamp)); + actions.forEach(txnConstructionRequest::action); + txnConstructionRequest.action(new NextEpoch(timestamp)); + var txn = radixEngine.construct(txnConstructionRequest).buildWithoutSignature(); - // Verify that it executes okay - var branch = radixEngine.transientBranch(); - branch.execute(List.of(txn), PermissionLevel.SYSTEM); - radixEngine.deleteBranches(); + // Verify that it executes okay + var branch = radixEngine.transientBranch(); + branch.execute(List.of(txn), PermissionLevel.SYSTEM); + radixEngine.deleteBranches(); - return txn; - } + return txn; + } - public LedgerProof generateGenesisProof(Txn txn) throws RadixEngineException { - var branch = radixEngine.transientBranch(); - var result = branch.execute(List.of(txn), PermissionLevel.SYSTEM); - radixEngine.deleteBranches(); - var genesisValidatorSet = result.getProcessedTxn().getEvents().stream() - .filter(NextValidatorSetEvent.class::isInstance) - .map(NextValidatorSetEvent.class::cast) - .findFirst() - .map(e -> BFTValidatorSet.from( - e.nextValidators().stream() - .map(v -> BFTValidator.from(BFTNode.create(v.getValidatorKey()), v.getAmount()))) - ).orElseThrow(() -> new IllegalStateException("No validator set in genesis.")); + public LedgerProof generateGenesisProof(Txn txn) throws RadixEngineException { + var branch = radixEngine.transientBranch(); + var result = branch.execute(List.of(txn), PermissionLevel.SYSTEM); + radixEngine.deleteBranches(); + var genesisValidatorSet = + result.getProcessedTxn().getEvents().stream() + .filter(NextValidatorSetEvent.class::isInstance) + .map(NextValidatorSetEvent.class::cast) + .findFirst() + .map( + e -> + BFTValidatorSet.from( + e.nextValidators().stream() + .map( + v -> + BFTValidator.from( + BFTNode.create(v.getValidatorKey()), v.getAmount())))) + .orElseThrow(() -> new IllegalStateException("No validator set in genesis.")); - var init = new AccumulatorState(0, HashUtils.zero256()); - var accumulatorState = ledgerAccumulator.accumulate(init, txn.getId().asHashCode()); - var genesisProof = LedgerProof.genesis( - accumulatorState, - genesisValidatorSet, - 0L - ); - if (!genesisProof.isEndOfEpoch()) { - throw new IllegalStateException("Genesis must be end of epoch"); - } + var init = new AccumulatorState(0, HashUtils.zero256()); + var accumulatorState = ledgerAccumulator.accumulate(init, txn.getId().asHashCode()); + var genesisProof = LedgerProof.genesis(accumulatorState, genesisValidatorSet, 0L); + if (!genesisProof.isEndOfEpoch()) { + throw new IllegalStateException("Genesis must be end of epoch"); + } - return genesisProof; - } + return genesisProof; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisProvider.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisProvider.java index e1a31df024..523fdf686d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisProvider.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/GenesisProvider.java @@ -83,93 +83,88 @@ import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.utils.KeyComparator; import com.radixdlt.utils.UInt256; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.radix.TokenIssuance; - import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.radix.TokenIssuance; -/** - * Generates a genesis atom - */ +/** Generates a genesis atom */ public final class GenesisProvider implements Provider { - private static final Logger logger = LogManager.getLogger(); - private final ImmutableList tokenIssuances; - private final Set validatorKeys; - private final Set stakeTokens; - private final Optional> additionalActions; - private final GenesisBuilder genesisBuilder; - private final long timestamp; + private static final Logger logger = LogManager.getLogger(); + private final ImmutableList tokenIssuances; + private final Set validatorKeys; + private final Set stakeTokens; + private final Optional> additionalActions; + private final GenesisBuilder genesisBuilder; + private final long timestamp; - @Inject - public GenesisProvider( - GenesisBuilder genesisBuilder, - @Genesis long timestamp, - @Genesis ImmutableList tokenIssuances, - @Genesis Set stakeTokens, - @Genesis Set validatorKeys, - @Genesis Optional> additionalActions - ) { - this.genesisBuilder = genesisBuilder; - this.timestamp = timestamp; - this.tokenIssuances = tokenIssuances; - this.validatorKeys = validatorKeys; - this.stakeTokens = stakeTokens; - this.additionalActions = additionalActions; - } + @Inject + public GenesisProvider( + GenesisBuilder genesisBuilder, + @Genesis long timestamp, + @Genesis ImmutableList tokenIssuances, + @Genesis Set stakeTokens, + @Genesis Set validatorKeys, + @Genesis Optional> additionalActions) { + this.genesisBuilder = genesisBuilder; + this.timestamp = timestamp; + this.tokenIssuances = tokenIssuances; + this.validatorKeys = validatorKeys; + this.stakeTokens = stakeTokens; + this.additionalActions = additionalActions; + } - @Override - public VerifiedTxnsAndProof get() { - // Check that issuances are sufficient for delegations - final var issuances = tokenIssuances.stream() - .collect(ImmutableMap.toImmutableMap(TokenIssuance::receiver, TokenIssuance::amount, UInt256::add)); + @Override + public VerifiedTxnsAndProof get() { + // Check that issuances are sufficient for delegations + final var issuances = + tokenIssuances.stream() + .collect( + ImmutableMap.toImmutableMap( + TokenIssuance::receiver, TokenIssuance::amount, UInt256::add)); - var actions = new ArrayList(); - actions.add(new CreateMutableToken( - REAddr.ofNativeToken(), - "xrd", - "Rads", - "Radix Tokens", - "", - "", - null - )); - var rri = REAddr.ofNativeToken(); - try { - for (var e : issuances.entrySet()) { - var addr = REAddr.ofPubKeyAccount(e.getKey()); - actions.add(new MintToken(rri, addr, e.getValue())); - } + var actions = new ArrayList(); + actions.add( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Rads", "Radix Tokens", "", "", null)); + var rri = REAddr.ofNativeToken(); + try { + for (var e : issuances.entrySet()) { + var addr = REAddr.ofPubKeyAccount(e.getKey()); + actions.add(new MintToken(rri, addr, e.getValue())); + } - validatorKeys.stream() - .sorted(KeyComparator.instance()) - .forEach(k -> { - actions.add(new RegisterValidator(k)); - actions.add(new UpdateValidatorFee(k, 0)); - actions.add(new UpdateAllowDelegationFlag(k, true)); - }); + validatorKeys.stream() + .sorted(KeyComparator.instance()) + .forEach( + k -> { + actions.add(new RegisterValidator(k)); + actions.add(new UpdateValidatorFee(k, 0)); + actions.add(new UpdateAllowDelegationFlag(k, true)); + }); - stakeTokens.stream() - .sorted( - Comparator.comparing(t -> t.from().getBytes(), UnsignedBytes.lexicographicalComparator()) - .thenComparing(t -> t.from().getBytes(), UnsignedBytes.lexicographicalComparator()) - .thenComparing(StakeTokens::amount) - ) - .forEach(actions::add); + stakeTokens.stream() + .sorted( + Comparator.comparing( + t -> t.from().getBytes(), UnsignedBytes.lexicographicalComparator()) + .thenComparing( + t -> t.from().getBytes(), UnsignedBytes.lexicographicalComparator()) + .thenComparing(StakeTokens::amount)) + .forEach(actions::add); - additionalActions.ifPresent(actions::addAll); - var genesis = genesisBuilder.build("hello", timestamp, actions); + additionalActions.ifPresent(actions::addAll); + var genesis = genesisBuilder.build("hello", timestamp, actions); - logger.info("gen_create{tx_id={}}", genesis.getId()); + logger.info("gen_create{tx_id={}}", genesis.getId()); - var proof = genesisBuilder.generateGenesisProof(genesis); - return VerifiedTxnsAndProof.create(List.of(genesis), proof); - } catch (TxBuilderException | RadixEngineException e) { - throw new IllegalStateException(e); - } - } + var proof = genesisBuilder.generateGenesisProof(genesis); + return VerifiedTxnsAndProof.create(List.of(genesis), proof); + } catch (TxBuilderException | RadixEngineException e) { + throw new IllegalStateException(e); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/RadixEngineCheckpointModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/RadixEngineCheckpointModule.java index 6a53879eaa..755adc9e6d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/RadixEngineCheckpointModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/checkpoint/RadixEngineCheckpointModule.java @@ -67,12 +67,12 @@ import com.google.inject.AbstractModule; /** - * Configures the module in charge of "weak-subjectivity" or checkpoints - * which the node will always align with + * Configures the module in charge of "weak-subjectivity" or checkpoints which the node will always + * align with */ public class RadixEngineCheckpointModule extends AbstractModule { - public RadixEngineCheckpointModule() { - // Nothing to do here - } + public RadixEngineCheckpointModule() { + // Nothing to do here + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkConfig.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkConfig.java index 4305889ef6..c5d3b9f819 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkConfig.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkConfig.java @@ -64,48 +64,41 @@ package com.radixdlt.statecomputer.forks; -/** - * Configuration used for hard forks - */ +/** Configuration used for hard forks */ public final class ForkConfig { - private final long epoch; - private final String name; - private final RERulesConfig config; - private final RERulesVersion version; + private final long epoch; + private final String name; + private final RERulesConfig config; + private final RERulesVersion version; - public ForkConfig( - long epoch, - String name, - RERulesVersion version, - RERulesConfig config - ) { - this.epoch = epoch; - this.name = name; - this.config = config; - this.version = version; - } + public ForkConfig(long epoch, String name, RERulesVersion version, RERulesConfig config) { + this.epoch = epoch; + this.name = name; + this.config = config; + this.version = version; + } - public long getEpoch() { - return epoch; - } + public long getEpoch() { + return epoch; + } - public RERulesVersion getVersion() { - return version; - } + public RERulesVersion getVersion() { + return version; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public RERulesConfig getConfig() { - return config; - } + public RERulesConfig getConfig() { + return config; + } - public ForkConfig overrideEpoch(long epoch) { - return new ForkConfig(epoch, this.name, this.version, this.config); - } + public ForkConfig overrideEpoch(long epoch) { + return new ForkConfig(epoch, this.name, this.version, this.config); + } - public ForkConfig overrideConfig(RERulesConfig config) { - return new ForkConfig(this.epoch, this.name, this.version, config); - } + public ForkConfig overrideConfig(RERulesConfig config) { + return new ForkConfig(this.epoch, this.name, this.version, config); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesFromPropertiesModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesFromPropertiesModule.java index 18ec6a27bf..2011ff07b6 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesFromPropertiesModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesFromPropertiesModule.java @@ -69,47 +69,59 @@ import com.google.inject.TypeLiteral; import com.google.inject.multibindings.OptionalBinder; import com.radixdlt.properties.RuntimeProperties; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.Set; import java.util.function.UnaryOperator; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ForkOverwritesFromPropertiesModule extends AbstractModule { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private static class ForkOverwrite implements UnaryOperator> { - @Inject - private RuntimeProperties properties; + private static class ForkOverwrite implements UnaryOperator> { + @Inject private RuntimeProperties properties; - @Override - public Set apply(Set forkConfigs) { - return forkConfigs.stream() - .map(c -> { - var epochOverwrite = properties.get("overwrite_forks." + c.getName() + ".epoch", ""); - if (!epochOverwrite.isBlank()) { - var epoch = Long.parseLong(epochOverwrite); - logger.warn("Overwriting epoch of " + c.getName() + " from " + c.getEpoch() + " to " + epoch); - c = c.overrideEpoch(epoch); - } + @Override + public Set apply(Set forkConfigs) { + return forkConfigs.stream() + .map( + c -> { + var epochOverwrite = + properties.get("overwrite_forks." + c.getName() + ".epoch", ""); + if (!epochOverwrite.isBlank()) { + var epoch = Long.parseLong(epochOverwrite); + logger.warn( + "Overwriting epoch of " + + c.getName() + + " from " + + c.getEpoch() + + " to " + + epoch); + c = c.overrideEpoch(epoch); + } - var viewOverwrite = properties.get("overwrite_forks." + c.getName() + ".views", ""); - if (!viewOverwrite.isBlank()) { - var view = Long.parseLong(viewOverwrite); - logger.warn("Overwriting views of " + c.getName() + " from " + c.getConfig().getMaxRounds() + " to " + view); - c = c.overrideConfig(c.getConfig().overrideMaxRounds(view)); - } - return c; - }) - .collect(Collectors.toSet()); - } - } + var viewOverwrite = properties.get("overwrite_forks." + c.getName() + ".views", ""); + if (!viewOverwrite.isBlank()) { + var view = Long.parseLong(viewOverwrite); + logger.warn( + "Overwriting views of " + + c.getName() + + " from " + + c.getConfig().getMaxRounds() + + " to " + + view); + c = c.overrideConfig(c.getConfig().overrideMaxRounds(view)); + } + return c; + }) + .collect(Collectors.toSet()); + } + } - @Override - protected void configure() { - OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() { }) - .setBinding() - .to(ForkOverwrite.class); - } + @Override + protected void configure() { + OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() {}) + .setBinding() + .to(ForkOverwrite.class); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesWithShorterEpochsModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesWithShorterEpochsModule.java index e318a93267..bf2b8b6750 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesWithShorterEpochsModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForkOverwritesWithShorterEpochsModule.java @@ -67,7 +67,6 @@ import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.OptionalBinder; - import java.util.Comparator; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @@ -75,27 +74,24 @@ import java.util.stream.Collectors; public class ForkOverwritesWithShorterEpochsModule extends AbstractModule { - private final RERulesConfig config; + private final RERulesConfig config; - public ForkOverwritesWithShorterEpochsModule(RERulesConfig config) { - this.config = config; - } + public ForkOverwritesWithShorterEpochsModule(RERulesConfig config) { + this.config = config; + } - @Override - protected void configure() { - var epoch = new AtomicLong(0); - OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() { }) - .setBinding() - .toInstance(s -> - s.stream() - .sorted(Comparator.comparingLong(ForkConfig::getEpoch)) - .map(c -> new ForkConfig( - epoch.getAndAdd(5), - c.getName(), - c.getVersion(), - config - )) - .collect(Collectors.toSet()) - ); - } + @Override + protected void configure() { + var epoch = new AtomicLong(0); + OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() {}) + .setBinding() + .toInstance( + s -> + s.stream() + .sorted(Comparator.comparingLong(ForkConfig::getEpoch)) + .map( + c -> + new ForkConfig(epoch.getAndAdd(5), c.getName(), c.getVersion(), config)) + .collect(Collectors.toSet())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/Forks.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/Forks.java index 0d504e2984..2a7024ef99 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/Forks.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/Forks.java @@ -69,25 +69,25 @@ import java.util.function.BiConsumer; public final class Forks { - private final TreeMap forks; + private final TreeMap forks; - public Forks(TreeMap forks) { - if (forks.isEmpty() || forks.get(0L) == null) { - throw new IllegalArgumentException(); - } + public Forks(TreeMap forks) { + if (forks.isEmpty() || forks.get(0L) == null) { + throw new IllegalArgumentException(); + } - this.forks = forks; - } + this.forks = forks; + } - public RERules get(long epoch) { - return this.forks.floorEntry(epoch).getValue(); - } + public RERules get(long epoch) { + return this.forks.floorEntry(epoch).getValue(); + } - public Optional ifForkGet(long epoch) { - return Optional.ofNullable(this.forks.get(epoch)); - } + public Optional ifForkGet(long epoch) { + return Optional.ofNullable(this.forks.get(epoch)); + } - public void forEach(BiConsumer consumer) { - forks.forEach(consumer); - } + public void forEach(BiConsumer consumer) { + forks.forEach(consumer); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForksModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForksModule.java index 3c79b760f5..a3c0ef495f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForksModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/ForksModule.java @@ -69,7 +69,6 @@ import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.OptionalBinder; - import java.util.Collection; import java.util.Optional; import java.util.Set; @@ -79,32 +78,28 @@ import java.util.stream.Collectors; public final class ForksModule extends AbstractModule { - @Override - protected void configure() { - OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() { }); - } + @Override + protected void configure() { + OptionalBinder.newOptionalBinder( + binder(), new TypeLiteral>>() {}); + } - @Provides - @Singleton - private Forks forks(TreeMap forkConfigs) { - return new Forks(asTreeMap(forkConfigs.values(), e -> e.getVersion().create(e.getConfig()))); - } + @Provides + @Singleton + private Forks forks(TreeMap forkConfigs) { + return new Forks(asTreeMap(forkConfigs.values(), e -> e.getVersion().create(e.getConfig()))); + } - @Provides - @Singleton - private TreeMap forkConfigMap( - Set forkConfigs, - Optional>> transformer - ) { - return asTreeMap( - transformer.map(o -> o.apply(forkConfigs)).orElse(forkConfigs), - Function.identity() - ); - } + @Provides + @Singleton + private TreeMap forkConfigMap( + Set forkConfigs, Optional>> transformer) { + return asTreeMap( + transformer.map(o -> o.apply(forkConfigs)).orElse(forkConfigs), Function.identity()); + } - private static TreeMap asTreeMap(Collection input, Function mapper) { - return new TreeMap<>( - input.stream().collect(Collectors.toMap(ForkConfig::getEpoch, mapper)) - ); - } + private static TreeMap asTreeMap( + Collection input, Function mapper) { + return new TreeMap<>(input.stream().collect(Collectors.toMap(ForkConfig::getEpoch, mapper))); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/MainnetForkConfigsModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/MainnetForkConfigsModule.java index 2b2b08ef24..5d6f5772e5 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/MainnetForkConfigsModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/MainnetForkConfigsModule.java @@ -66,97 +66,89 @@ import com.google.inject.AbstractModule; import com.google.inject.multibindings.ProvidesIntoSet; -import com.radixdlt.application.tokens.Amount; import com.radixdlt.application.system.FeeTable; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.application.tokens.state.PreparedStake; import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.validators.state.AllowDelegationFlag; +import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.application.validators.state.ValidatorMetaData; import com.radixdlt.application.validators.state.ValidatorOwnerCopy; -import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; - import java.util.Map; import java.util.OptionalInt; import java.util.Set; import java.util.regex.Pattern; -/** - * The forks for betanet and the epochs at which they will occur. - */ +/** The forks for betanet and the epochs at which they will occur. */ public final class MainnetForkConfigsModule extends AbstractModule { - private static final Set RESERVED_SYMBOLS = Set.of( - "xrd", "xrds", "exrd", "exrds", "rad", "rads", "rdx", "rdxs", "radix" - ); + private static final Set RESERVED_SYMBOLS = + Set.of("xrd", "xrds", "exrd", "exrds", "rad", "rads", "rdx", "rdxs", "radix"); - @ProvidesIntoSet - ForkConfig olympiaFirstEpoch() { - return new ForkConfig( - 0L, - "olympia-first-epoch", - RERulesVersion.OLYMPIA_V1, - new RERulesConfig( - RESERVED_SYMBOLS, - Pattern.compile("[a-z0-9]+"), // Token symbol pattern - FeeTable.create( - Amount.ofMicroTokens(200), // 0.0002XRD per byte fee - Map.of( - TokenResource.class, Amount.ofTokens(100), // 100XRD per resource - ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update - ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update - AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update - PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake - PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake - ) - ), - (long) 1024 * 1024, // 1MB max user transaction size - OptionalInt.of(50), // 50 Txns per round - 8_000_000, // Rounds per epoch - approximately two weeks of epochs - 500, // Two weeks worth of epochs for rake debounce - Amount.ofTokens(90), // Minimum stake - 500, // Two weeks worth of epochs for unstaking delay - Amount.ofTokens(0), // No rewards in first epoch - 9800, // 98.00% threshold for completed proposals to get any rewards, - 100 // 100 max validators - ) - ); - } + @ProvidesIntoSet + ForkConfig olympiaFirstEpoch() { + return new ForkConfig( + 0L, + "olympia-first-epoch", + RERulesVersion.OLYMPIA_V1, + new RERulesConfig( + RESERVED_SYMBOLS, + Pattern.compile("[a-z0-9]+"), // Token symbol pattern + FeeTable.create( + Amount.ofMicroTokens(200), // 0.0002XRD per byte fee + Map.of( + TokenResource.class, Amount.ofTokens(100), // 100XRD per resource + ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update + ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update + AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update + PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake + PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake + )), + (long) 1024 * 1024, // 1MB max user transaction size + OptionalInt.of(50), // 50 Txns per round + 8_000_000, // Rounds per epoch - approximately two weeks of epochs + 500, // Two weeks worth of epochs for rake debounce + Amount.ofTokens(90), // Minimum stake + 500, // Two weeks worth of epochs for unstaking delay + Amount.ofTokens(0), // No rewards in first epoch + 9800, // 98.00% threshold for completed proposals to get any rewards, + 100 // 100 max validators + )); + } - @ProvidesIntoSet - ForkConfig olympia() { - return new ForkConfig( - 2L, - "olympia", - RERulesVersion.OLYMPIA_V1, - new RERulesConfig( - RESERVED_SYMBOLS, - Pattern.compile("[a-z0-9]+"), // Token symbol pattern - FeeTable.create( - Amount.ofMicroTokens(200), // 0.0002XRD per byte fee - Map.of( - TokenResource.class, Amount.ofTokens(100), // 100XRD per resource - ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update - ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update - AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update - PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake - PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake - ) - ), - (long) 1024 * 1024, // 1MB max user transaction size - OptionalInt.of(50), // 50 Txns per round - 10_000, // Rounds per epoch - 500, // Two weeks worth of epochs - Amount.ofTokens(90), // Minimum stake - 500, // Two weeks worth of epochs - Amount.ofMicroTokens(2307700), // 2.3077XRD Rewards per proposal - 9800, // 98.00% threshold for completed proposals to get any rewards - 100 // 100 max validators - ) - ); - } + @ProvidesIntoSet + ForkConfig olympia() { + return new ForkConfig( + 2L, + "olympia", + RERulesVersion.OLYMPIA_V1, + new RERulesConfig( + RESERVED_SYMBOLS, + Pattern.compile("[a-z0-9]+"), // Token symbol pattern + FeeTable.create( + Amount.ofMicroTokens(200), // 0.0002XRD per byte fee + Map.of( + TokenResource.class, Amount.ofTokens(100), // 100XRD per resource + ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update + ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update + AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update + PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake + PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake + )), + (long) 1024 * 1024, // 1MB max user transaction size + OptionalInt.of(50), // 50 Txns per round + 10_000, // Rounds per epoch + 500, // Two weeks worth of epochs + Amount.ofTokens(90), // Minimum stake + 500, // Two weeks worth of epochs + Amount.ofMicroTokens(2307700), // 2.3077XRD Rewards per proposal + 9800, // 98.00% threshold for completed proposals to get any rewards + 100 // 100 max validators + )); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERules.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERules.java index b05ad943d5..5b784c5420 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERules.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERules.java @@ -71,73 +71,71 @@ import com.radixdlt.engine.BatchVerifier; import com.radixdlt.engine.parser.REParser; import com.radixdlt.statecomputer.LedgerAndBFTProof; - import java.util.OptionalInt; public final class RERules { - private final String name; - private final REParser parser; - private final SubstateSerialization serialization; - private final ConstraintMachineConfig constraintMachineConfig; - private final REConstructor actionConstructors; - private final BatchVerifier batchVerifier; - private final RERulesConfig config; + private final String name; + private final REParser parser; + private final SubstateSerialization serialization; + private final ConstraintMachineConfig constraintMachineConfig; + private final REConstructor actionConstructors; + private final BatchVerifier batchVerifier; + private final RERulesConfig config; - public RERules( - String name, - REParser parser, - SubstateSerialization serialization, - ConstraintMachineConfig constraintMachineConfig, - REConstructor actionConstructors, - BatchVerifier batchVerifier, - RERulesConfig config - ) { - this.name = name; - this.parser = parser; - this.serialization = serialization; - this.constraintMachineConfig = constraintMachineConfig; - this.actionConstructors = actionConstructors; - this.batchVerifier = batchVerifier; - this.config = config; - } + public RERules( + String name, + REParser parser, + SubstateSerialization serialization, + ConstraintMachineConfig constraintMachineConfig, + REConstructor actionConstructors, + BatchVerifier batchVerifier, + RERulesConfig config) { + this.name = name; + this.parser = parser; + this.serialization = serialization; + this.constraintMachineConfig = constraintMachineConfig; + this.actionConstructors = actionConstructors; + this.batchVerifier = batchVerifier; + this.config = config; + } - public String name() { - return name; - } + public String name() { + return name; + } - public ConstraintMachineConfig getConstraintMachineConfig() { - return constraintMachineConfig; - } + public ConstraintMachineConfig getConstraintMachineConfig() { + return constraintMachineConfig; + } - public SubstateSerialization getSerialization() { - return serialization; - } + public SubstateSerialization getSerialization() { + return serialization; + } - public REConstructor getActionConstructors() { - return actionConstructors; - } + public REConstructor getActionConstructors() { + return actionConstructors; + } - public BatchVerifier getBatchVerifier() { - return batchVerifier; - } + public BatchVerifier getBatchVerifier() { + return batchVerifier; + } - public REParser getParser() { - return parser; - } + public REParser getParser() { + return parser; + } - public View getMaxRounds() { - return View.of(config.getMaxRounds()); - } + public View getMaxRounds() { + return View.of(config.getMaxRounds()); + } - public OptionalInt getMaxSigsPerRound() { - return config.getMaxSigsPerRound(); - } + public OptionalInt getMaxSigsPerRound() { + return config.getMaxSigsPerRound(); + } - public int getMaxValidators() { - return config.getMaxValidators(); - } + public int getMaxValidators() { + return config.getMaxValidators(); + } - public RERulesConfig getConfig() { - return config; - } + public RERulesConfig getConfig() { + return config; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesConfig.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesConfig.java index e0ce609a08..2188bfa2af 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesConfig.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesConfig.java @@ -64,203 +64,195 @@ package com.radixdlt.statecomputer.forks; -import com.radixdlt.application.tokens.Amount; import com.radixdlt.application.system.FeeTable; - +import com.radixdlt.application.tokens.Amount; import java.util.Map; import java.util.OptionalInt; import java.util.Set; import java.util.regex.Pattern; public final class RERulesConfig { - private final Set reservedSymbols; - private final Pattern tokenSymbolPattern; - private final FeeTable feeTable; - private final long maxTxnSize; - private final long maxRounds; - private final OptionalInt maxSigsPerRound; - private final long rakeIncreaseDebouncerEpochLength; - private final Amount minimumStake; - private final long unstakingEpochDelay; - private final Amount rewardsPerProposal; - private final int minimumCompletedProposalsPercentage; - private final int maxValidators; + private final Set reservedSymbols; + private final Pattern tokenSymbolPattern; + private final FeeTable feeTable; + private final long maxTxnSize; + private final long maxRounds; + private final OptionalInt maxSigsPerRound; + private final long rakeIncreaseDebouncerEpochLength; + private final Amount minimumStake; + private final long unstakingEpochDelay; + private final Amount rewardsPerProposal; + private final int minimumCompletedProposalsPercentage; + private final int maxValidators; - public RERulesConfig( - Set reservedSymbols, - Pattern tokenSymbolPattern, - FeeTable feeTable, - long maxTxnSize, - OptionalInt maxSigsPerRound, - long maxRounds, - long rakeIncreaseDebouncerEpochLength, - Amount minimumStake, - long unstakingEpochDelay, - Amount rewardsPerProposal, - int minimumCompletedProposalsPercentage, - int maxValidators - ) { - this.reservedSymbols = reservedSymbols; - this.tokenSymbolPattern = tokenSymbolPattern; - this.feeTable = feeTable; - this.maxTxnSize = maxTxnSize; - this.maxSigsPerRound = maxSigsPerRound; - this.maxRounds = maxRounds; - this.rakeIncreaseDebouncerEpochLength = rakeIncreaseDebouncerEpochLength; - this.minimumStake = minimumStake; - this.unstakingEpochDelay = unstakingEpochDelay; - this.rewardsPerProposal = rewardsPerProposal; - this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; - this.maxValidators = maxValidators; - } + public RERulesConfig( + Set reservedSymbols, + Pattern tokenSymbolPattern, + FeeTable feeTable, + long maxTxnSize, + OptionalInt maxSigsPerRound, + long maxRounds, + long rakeIncreaseDebouncerEpochLength, + Amount minimumStake, + long unstakingEpochDelay, + Amount rewardsPerProposal, + int minimumCompletedProposalsPercentage, + int maxValidators) { + this.reservedSymbols = reservedSymbols; + this.tokenSymbolPattern = tokenSymbolPattern; + this.feeTable = feeTable; + this.maxTxnSize = maxTxnSize; + this.maxSigsPerRound = maxSigsPerRound; + this.maxRounds = maxRounds; + this.rakeIncreaseDebouncerEpochLength = rakeIncreaseDebouncerEpochLength; + this.minimumStake = minimumStake; + this.unstakingEpochDelay = unstakingEpochDelay; + this.rewardsPerProposal = rewardsPerProposal; + this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; + this.maxValidators = maxValidators; + } - public static RERulesConfig testingDefault() { - return new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.create(Amount.zero(), Map.of()), - (long) 1024 * 1024, - OptionalInt.of(2), - 10, - 1, - Amount.ofTokens(10), - 1, - Amount.ofMicroTokens(2307700), // Rewards per proposal - 9800, - 10 - ); - } + public static RERulesConfig testingDefault() { + return new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.create(Amount.zero(), Map.of()), + (long) 1024 * 1024, + OptionalInt.of(2), + 10, + 1, + Amount.ofTokens(10), + 1, + Amount.ofMicroTokens(2307700), // Rewards per proposal + 9800, + 10); + } - public Pattern getTokenSymbolPattern() { - return tokenSymbolPattern; - } + public Pattern getTokenSymbolPattern() { + return tokenSymbolPattern; + } - public Set getReservedSymbols() { - return reservedSymbols; - } + public Set getReservedSymbols() { + return reservedSymbols; + } - public OptionalInt getMaxSigsPerRound() { - return maxSigsPerRound; - } + public OptionalInt getMaxSigsPerRound() { + return maxSigsPerRound; + } - public Amount getMinimumStake() { - return minimumStake; - } + public Amount getMinimumStake() { + return minimumStake; + } - public long getRakeIncreaseDebouncerEpochLength() { - return rakeIncreaseDebouncerEpochLength; - } + public long getRakeIncreaseDebouncerEpochLength() { + return rakeIncreaseDebouncerEpochLength; + } - public FeeTable getFeeTable() { - return feeTable; - } + public FeeTable getFeeTable() { + return feeTable; + } - public long getMaxTxnSize() { - return maxTxnSize; - } + public long getMaxTxnSize() { + return maxTxnSize; + } - public long getMaxRounds() { - return maxRounds; - } + public long getMaxRounds() { + return maxRounds; + } - public Amount getRewardsPerProposal() { - return rewardsPerProposal; - } + public Amount getRewardsPerProposal() { + return rewardsPerProposal; + } - public long getUnstakingEpochDelay() { - return unstakingEpochDelay; - } + public long getUnstakingEpochDelay() { + return unstakingEpochDelay; + } - public int getMinimumCompletedProposalsPercentage() { - return minimumCompletedProposalsPercentage; - } + public int getMinimumCompletedProposalsPercentage() { + return minimumCompletedProposalsPercentage; + } - public int getMaxValidators() { - return maxValidators; - } + public int getMaxValidators() { + return maxValidators; + } - public RERulesConfig overrideMinimumStake(Amount minimumStake) { - return new RERulesConfig( - this.reservedSymbols, - this.tokenSymbolPattern, - this.feeTable, - this.maxTxnSize, - this.maxSigsPerRound, - this.maxRounds, - this.rakeIncreaseDebouncerEpochLength, - minimumStake, - this.unstakingEpochDelay, - this.rewardsPerProposal, - this.minimumCompletedProposalsPercentage, - this.maxValidators - ); - } + public RERulesConfig overrideMinimumStake(Amount minimumStake) { + return new RERulesConfig( + this.reservedSymbols, + this.tokenSymbolPattern, + this.feeTable, + this.maxTxnSize, + this.maxSigsPerRound, + this.maxRounds, + this.rakeIncreaseDebouncerEpochLength, + minimumStake, + this.unstakingEpochDelay, + this.rewardsPerProposal, + this.minimumCompletedProposalsPercentage, + this.maxValidators); + } - public RERulesConfig overrideMaxSigsPerRound(int maxSigsPerRound) { - return new RERulesConfig( - this.reservedSymbols, - this.tokenSymbolPattern, - this.feeTable, - this.maxTxnSize, - OptionalInt.of(maxSigsPerRound), - this.maxRounds, - this.rakeIncreaseDebouncerEpochLength, - this.minimumStake, - this.unstakingEpochDelay, - this.rewardsPerProposal, - this.minimumCompletedProposalsPercentage, - this.maxValidators - ); - } + public RERulesConfig overrideMaxSigsPerRound(int maxSigsPerRound) { + return new RERulesConfig( + this.reservedSymbols, + this.tokenSymbolPattern, + this.feeTable, + this.maxTxnSize, + OptionalInt.of(maxSigsPerRound), + this.maxRounds, + this.rakeIncreaseDebouncerEpochLength, + this.minimumStake, + this.unstakingEpochDelay, + this.rewardsPerProposal, + this.minimumCompletedProposalsPercentage, + this.maxValidators); + } - public RERulesConfig removeSigsPerRoundLimit() { - return new RERulesConfig( - this.reservedSymbols, - this.tokenSymbolPattern, - this.feeTable, - this.maxTxnSize, - OptionalInt.empty(), - this.maxRounds, - this.rakeIncreaseDebouncerEpochLength, - this.minimumStake, - this.unstakingEpochDelay, - this.rewardsPerProposal, - this.minimumCompletedProposalsPercentage, - this.maxValidators - ); - } + public RERulesConfig removeSigsPerRoundLimit() { + return new RERulesConfig( + this.reservedSymbols, + this.tokenSymbolPattern, + this.feeTable, + this.maxTxnSize, + OptionalInt.empty(), + this.maxRounds, + this.rakeIncreaseDebouncerEpochLength, + this.minimumStake, + this.unstakingEpochDelay, + this.rewardsPerProposal, + this.minimumCompletedProposalsPercentage, + this.maxValidators); + } - public RERulesConfig overrideFeeTable(FeeTable feeTable) { - return new RERulesConfig( - this.reservedSymbols, - this.tokenSymbolPattern, - feeTable, - this.maxTxnSize, - this.maxSigsPerRound, - this.maxRounds, - this.rakeIncreaseDebouncerEpochLength, - this.minimumStake, - this.unstakingEpochDelay, - this.rewardsPerProposal, - this.minimumCompletedProposalsPercentage, - this.maxValidators - ); - } + public RERulesConfig overrideFeeTable(FeeTable feeTable) { + return new RERulesConfig( + this.reservedSymbols, + this.tokenSymbolPattern, + feeTable, + this.maxTxnSize, + this.maxSigsPerRound, + this.maxRounds, + this.rakeIncreaseDebouncerEpochLength, + this.minimumStake, + this.unstakingEpochDelay, + this.rewardsPerProposal, + this.minimumCompletedProposalsPercentage, + this.maxValidators); + } - public RERulesConfig overrideMaxRounds(long maxRounds) { - return new RERulesConfig( - this.reservedSymbols, - this.tokenSymbolPattern, - this.feeTable, - this.maxTxnSize, - this.maxSigsPerRound, - maxRounds, - this.rakeIncreaseDebouncerEpochLength, - this.minimumStake, - this.unstakingEpochDelay, - this.rewardsPerProposal, - this.minimumCompletedProposalsPercentage, - this.maxValidators - ); - } + public RERulesConfig overrideMaxRounds(long maxRounds) { + return new RERulesConfig( + this.reservedSymbols, + this.tokenSymbolPattern, + this.feeTable, + this.maxTxnSize, + this.maxSigsPerRound, + maxRounds, + this.rakeIncreaseDebouncerEpochLength, + this.minimumStake, + this.unstakingEpochDelay, + this.rewardsPerProposal, + this.minimumCompletedProposalsPercentage, + this.maxValidators); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesVersion.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesVersion.java index b638227e92..afca35cb3b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesVersion.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RERulesVersion.java @@ -64,43 +64,19 @@ package com.radixdlt.statecomputer.forks; -import com.radixdlt.application.system.construction.FeeReserveCompleteConstructor; -import com.radixdlt.application.validators.construction.UpdateValidatorSystemMetadataConstructor; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; -import com.radixdlt.atom.REConstructor; -import com.radixdlt.atom.actions.BurnToken; -import com.radixdlt.atom.actions.CreateFixedToken; -import com.radixdlt.atom.actions.CreateMutableToken; -import com.radixdlt.atom.actions.CreateSystem; -import com.radixdlt.atom.actions.FeeReserveComplete; -import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.atom.actions.NextEpoch; -import com.radixdlt.atom.actions.NextRound; -import com.radixdlt.atom.actions.FeeReservePut; -import com.radixdlt.atom.actions.RegisterValidator; -import com.radixdlt.atom.actions.SplitToken; -import com.radixdlt.atom.actions.StakeTokens; -import com.radixdlt.atom.actions.TransferToken; -import com.radixdlt.atom.actions.UnregisterValidator; -import com.radixdlt.atom.actions.UnstakeOwnership; -import com.radixdlt.atom.actions.UnstakeTokens; -import com.radixdlt.atom.actions.UpdateAllowDelegationFlag; -import com.radixdlt.atom.actions.UpdateValidatorFee; -import com.radixdlt.atom.actions.UpdateValidatorMetadata; -import com.radixdlt.atom.actions.UpdateValidatorOwner; +import com.radixdlt.application.misc.SplitTokenConstructor; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; +import com.radixdlt.application.system.construction.FeeReserveCompleteConstructor; +import com.radixdlt.application.system.construction.FeeReservePutConstructor; import com.radixdlt.application.system.construction.NextEpochConstructorV3; import com.radixdlt.application.system.construction.NextViewConstructorV3; -import com.radixdlt.application.system.construction.FeeReservePutConstructor; import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; -import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; +import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; import com.radixdlt.application.tokens.construction.BurnTokenConstructor; import com.radixdlt.application.tokens.construction.CreateFixedTokenConstructor; import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.misc.SplitTokenConstructor; import com.radixdlt.application.tokens.construction.StakeTokensConstructorV3; import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; import com.radixdlt.application.tokens.construction.UnstakeOwnershipConstructor; @@ -114,106 +90,142 @@ import com.radixdlt.application.validators.construction.UpdateRakeConstructor; import com.radixdlt.application.validators.construction.UpdateValidatorMetadataConstructor; import com.radixdlt.application.validators.construction.UpdateValidatorOwnerConstructor; +import com.radixdlt.application.validators.construction.UpdateValidatorSystemMetadataConstructor; import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; +import com.radixdlt.atom.REConstructor; +import com.radixdlt.atom.actions.BurnToken; +import com.radixdlt.atom.actions.CreateFixedToken; +import com.radixdlt.atom.actions.CreateMutableToken; +import com.radixdlt.atom.actions.CreateSystem; +import com.radixdlt.atom.actions.FeeReserveComplete; +import com.radixdlt.atom.actions.FeeReservePut; +import com.radixdlt.atom.actions.MintToken; +import com.radixdlt.atom.actions.NextEpoch; +import com.radixdlt.atom.actions.NextRound; +import com.radixdlt.atom.actions.RegisterValidator; +import com.radixdlt.atom.actions.SplitToken; +import com.radixdlt.atom.actions.StakeTokens; +import com.radixdlt.atom.actions.TransferToken; +import com.radixdlt.atom.actions.UnregisterValidator; +import com.radixdlt.atom.actions.UnstakeOwnership; +import com.radixdlt.atom.actions.UnstakeTokens; +import com.radixdlt.atom.actions.UpdateAllowDelegationFlag; +import com.radixdlt.atom.actions.UpdateValidatorFee; +import com.radixdlt.atom.actions.UpdateValidatorMetadata; +import com.radixdlt.atom.actions.UpdateValidatorOwner; import com.radixdlt.atom.actions.UpdateValidatorSystemMetadata; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.constraintmachine.ConstraintMachineConfig; import com.radixdlt.constraintmachine.meter.Meter; import com.radixdlt.constraintmachine.meter.Meters; -import com.radixdlt.constraintmachine.meter.UpSubstateFeeMeter; import com.radixdlt.constraintmachine.meter.SigsPerRoundMeter; import com.radixdlt.constraintmachine.meter.TxnSizeFeeMeter; +import com.radixdlt.constraintmachine.meter.UpSubstateFeeMeter; import com.radixdlt.engine.parser.REParser; import com.radixdlt.statecomputer.EpochProofVerifierV2; public enum RERulesVersion { - OLYMPIA_V1 { - @Override - public RERules create(RERulesConfig config) { - var maxRounds = config.getMaxRounds(); - var perByteFee = config.getFeeTable().getPerByteFee(); - var perUpSubstateFee = config.getFeeTable().getPerUpSubstateFee(); - var rakeIncreaseDebouncerEpochLength = config.getRakeIncreaseDebouncerEpochLength(); - var tokenSymbolPattern = config.getTokenSymbolPattern(); - - final CMAtomOS v4 = new CMAtomOS(); - v4.load(new ValidatorConstraintScryptV2()); - v4.load(new ValidatorUpdateRakeConstraintScrypt(rakeIncreaseDebouncerEpochLength)); - v4.load(new ValidatorRegisterConstraintScrypt()); - v4.load(new ValidatorUpdateOwnerConstraintScrypt()); - v4.load(new SystemConstraintScrypt()); - v4.load(new TokensConstraintScryptV3(config.getReservedSymbols(), tokenSymbolPattern)); - v4.load(new StakingConstraintScryptV4(config.getMinimumStake().toSubunits())); - v4.load(new MutexConstraintScrypt()); - v4.load(new RoundUpdateConstraintScrypt(maxRounds)); - v4.load(new EpochUpdateConstraintScrypt( - maxRounds, - config.getRewardsPerProposal().toSubunits(), - config.getMinimumCompletedProposalsPercentage(), - config.getUnstakingEpochDelay(), - config.getMaxValidators() - )); - var meter = Meters.combine( - config.getMaxSigsPerRound().stream().mapToObj(SigsPerRoundMeter::create).findAny().orElse(Meter.EMPTY), - Meters.combine( - TxnSizeFeeMeter.create(perByteFee, config.getMaxTxnSize()), - UpSubstateFeeMeter.create(perUpSubstateFee) - ) - ); - var betanet4 = new ConstraintMachineConfig( - v4.getProcedures(), - v4.buildSubstateDeserialization(), - v4.buildVirtualSubstateDeserialization(), - meter - ); - var parser = new REParser(v4.buildSubstateDeserialization()); - var serialization = v4.buildSubstateSerialization(); - var actionConstructors = REConstructor.newBuilder() - .perByteFee(perByteFee) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(BurnToken.class, new BurnTokenConstructor()) - .put(CreateFixedToken.class, new CreateFixedTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(NextEpoch.class, new NextEpochConstructorV3( - config.getRewardsPerProposal().toSubunits(), - config.getMinimumCompletedProposalsPercentage(), - config.getUnstakingEpochDelay(), - config.getMaxValidators() - )) - .put(NextRound.class, new NextViewConstructorV3()) - .put(RegisterValidator.class, new RegisterValidatorConstructor()) - .put(SplitToken.class, new SplitTokenConstructor()) - .put(StakeTokens.class, new StakeTokensConstructorV3(config.getMinimumStake().toSubunits())) - .put(UnstakeTokens.class, new UnstakeTokensConstructorV2()) - .put(UnstakeOwnership.class, new UnstakeOwnershipConstructor()) - .put(TransferToken.class, new TransferTokensConstructorV2()) - .put(UnregisterValidator.class, new UnregisterValidatorConstructor()) - .put(UpdateValidatorMetadata.class, new UpdateValidatorMetadataConstructor()) - .put(FeeReservePut.class, new FeeReservePutConstructor()) - .put(FeeReserveComplete.class, new FeeReserveCompleteConstructor(config.getFeeTable())) - .put(UpdateValidatorFee.class, new UpdateRakeConstructor( - rakeIncreaseDebouncerEpochLength, - ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE - )) - .put(UpdateValidatorOwner.class, new UpdateValidatorOwnerConstructor()) - .put(UpdateAllowDelegationFlag.class, new UpdateAllowDelegationFlagConstructor()) - .put(UpdateValidatorSystemMetadata.class, new UpdateValidatorSystemMetadataConstructor()) - .build(); + OLYMPIA_V1 { + @Override + public RERules create(RERulesConfig config) { + var maxRounds = config.getMaxRounds(); + var perByteFee = config.getFeeTable().getPerByteFee(); + var perUpSubstateFee = config.getFeeTable().getPerUpSubstateFee(); + var rakeIncreaseDebouncerEpochLength = config.getRakeIncreaseDebouncerEpochLength(); + var tokenSymbolPattern = config.getTokenSymbolPattern(); - return new RERules( - "mainnet", - parser, - serialization, - betanet4, - actionConstructors, - new EpochProofVerifierV2(), - config - ); - } - }; + final CMAtomOS v4 = new CMAtomOS(); + v4.load(new ValidatorConstraintScryptV2()); + v4.load(new ValidatorUpdateRakeConstraintScrypt(rakeIncreaseDebouncerEpochLength)); + v4.load(new ValidatorRegisterConstraintScrypt()); + v4.load(new ValidatorUpdateOwnerConstraintScrypt()); + v4.load(new SystemConstraintScrypt()); + v4.load(new TokensConstraintScryptV3(config.getReservedSymbols(), tokenSymbolPattern)); + v4.load(new StakingConstraintScryptV4(config.getMinimumStake().toSubunits())); + v4.load(new MutexConstraintScrypt()); + v4.load(new RoundUpdateConstraintScrypt(maxRounds)); + v4.load( + new EpochUpdateConstraintScrypt( + maxRounds, + config.getRewardsPerProposal().toSubunits(), + config.getMinimumCompletedProposalsPercentage(), + config.getUnstakingEpochDelay(), + config.getMaxValidators())); + var meter = + Meters.combine( + config.getMaxSigsPerRound().stream() + .mapToObj(SigsPerRoundMeter::create) + .findAny() + .orElse(Meter.EMPTY), + Meters.combine( + TxnSizeFeeMeter.create(perByteFee, config.getMaxTxnSize()), + UpSubstateFeeMeter.create(perUpSubstateFee))); + var betanet4 = + new ConstraintMachineConfig( + v4.getProcedures(), + v4.buildSubstateDeserialization(), + v4.buildVirtualSubstateDeserialization(), + meter); + var parser = new REParser(v4.buildSubstateDeserialization()); + var serialization = v4.buildSubstateSerialization(); + var actionConstructors = + REConstructor.newBuilder() + .perByteFee(perByteFee) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(BurnToken.class, new BurnTokenConstructor()) + .put( + CreateFixedToken.class, + new CreateFixedTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put( + NextEpoch.class, + new NextEpochConstructorV3( + config.getRewardsPerProposal().toSubunits(), + config.getMinimumCompletedProposalsPercentage(), + config.getUnstakingEpochDelay(), + config.getMaxValidators())) + .put(NextRound.class, new NextViewConstructorV3()) + .put(RegisterValidator.class, new RegisterValidatorConstructor()) + .put(SplitToken.class, new SplitTokenConstructor()) + .put( + StakeTokens.class, + new StakeTokensConstructorV3(config.getMinimumStake().toSubunits())) + .put(UnstakeTokens.class, new UnstakeTokensConstructorV2()) + .put(UnstakeOwnership.class, new UnstakeOwnershipConstructor()) + .put(TransferToken.class, new TransferTokensConstructorV2()) + .put(UnregisterValidator.class, new UnregisterValidatorConstructor()) + .put(UpdateValidatorMetadata.class, new UpdateValidatorMetadataConstructor()) + .put(FeeReservePut.class, new FeeReservePutConstructor()) + .put( + FeeReserveComplete.class, new FeeReserveCompleteConstructor(config.getFeeTable())) + .put( + UpdateValidatorFee.class, + new UpdateRakeConstructor( + rakeIncreaseDebouncerEpochLength, + ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE)) + .put(UpdateValidatorOwner.class, new UpdateValidatorOwnerConstructor()) + .put(UpdateAllowDelegationFlag.class, new UpdateAllowDelegationFlagConstructor()) + .put( + UpdateValidatorSystemMetadata.class, + new UpdateValidatorSystemMetadataConstructor()) + .build(); + return new RERules( + "mainnet", + parser, + serialization, + betanet4, + actionConstructors, + new EpochProofVerifierV2(), + config); + } + }; - public abstract RERules create(RERulesConfig config); + public abstract RERules create(RERulesConfig config); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RadixEngineForksLatestOnlyModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RadixEngineForksLatestOnlyModule.java index 214877d59b..2b8022bb3a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RadixEngineForksLatestOnlyModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/RadixEngineForksLatestOnlyModule.java @@ -67,34 +67,32 @@ import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.OptionalBinder; - import java.util.Comparator; import java.util.Set; import java.util.function.UnaryOperator; -/** - * For testing only, only tests the latest state computer configuration - */ +/** For testing only, only tests the latest state computer configuration */ public class RadixEngineForksLatestOnlyModule extends AbstractModule { - private final RERulesConfig config; + private final RERulesConfig config; - public RadixEngineForksLatestOnlyModule(RERulesConfig config) { - this.config = config; - } + public RadixEngineForksLatestOnlyModule(RERulesConfig config) { + this.config = config; + } - public RadixEngineForksLatestOnlyModule() { - this(RERulesConfig.testingDefault()); - } + public RadixEngineForksLatestOnlyModule() { + this(RERulesConfig.testingDefault()); + } - @Override - protected void configure() { - OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() { }) - .setBinding() - .toInstance(m -> - Set.of(m.stream() - .max(Comparator.comparingLong(ForkConfig::getEpoch)) - .map(f -> new ForkConfig(0L, f.getName(), f.getVersion(), config)) - .orElseThrow()) - ); - } + @Override + protected void configure() { + OptionalBinder.newOptionalBinder(binder(), new TypeLiteral>>() {}) + .setBinding() + .toInstance( + m -> + Set.of( + m.stream() + .max(Comparator.comparingLong(ForkConfig::getEpoch)) + .map(f -> new ForkConfig(0L, f.getName(), f.getVersion(), config)) + .orElseThrow())); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/StokenetForkConfigsModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/StokenetForkConfigsModule.java index c58621c54e..1b4ac76bc1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/StokenetForkConfigsModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/forks/StokenetForkConfigsModule.java @@ -72,53 +72,49 @@ import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.validators.state.AllowDelegationFlag; +import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.application.validators.state.ValidatorMetaData; import com.radixdlt.application.validators.state.ValidatorOwnerCopy; -import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; - import java.util.Map; import java.util.OptionalInt; import java.util.Set; import java.util.regex.Pattern; public final class StokenetForkConfigsModule extends AbstractModule { - private static final Set RESERVED_SYMBOLS = Set.of( - "xrd", "xrds", "exrd", "exrds", "rad", "rads", "rdx", "rdxs", "radix" - ); + private static final Set RESERVED_SYMBOLS = + Set.of("xrd", "xrds", "exrd", "exrds", "rad", "rads", "rdx", "rdxs", "radix"); - @ProvidesIntoSet - ForkConfig stokenet() { - return new ForkConfig( - 0L, - "stokenet", - RERulesVersion.OLYMPIA_V1, - new RERulesConfig( - RESERVED_SYMBOLS, - Pattern.compile("[a-z0-9]+"), - FeeTable.create( - Amount.ofMicroTokens(200), // 0.0002XRD per byte fee - Map.of( - TokenResource.class, Amount.ofTokens(100), // 100XRD per resource - ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update - ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update - ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update - AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update - PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake - PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake - ) - ), - (long) 1024 * 1024, // 1MB max user transaction size - OptionalInt.of(50), // 50 Txns per round - 10_000, // Rounds per epoch - 500, // Two weeks worth of epochs - Amount.ofTokens(90), // Minimum stake - 500, // Two weeks worth of epochs - Amount.ofMicroTokens(2307700), // Rewards per proposal - 9800, // 98.00% threshold for completed proposals to get any rewards, - 100 // 100 max validators - ) - ); - } + @ProvidesIntoSet + ForkConfig stokenet() { + return new ForkConfig( + 0L, + "stokenet", + RERulesVersion.OLYMPIA_V1, + new RERulesConfig( + RESERVED_SYMBOLS, + Pattern.compile("[a-z0-9]+"), + FeeTable.create( + Amount.ofMicroTokens(200), // 0.0002XRD per byte fee + Map.of( + TokenResource.class, Amount.ofTokens(100), // 100XRD per resource + ValidatorRegisteredCopy.class, Amount.ofTokens(5), // 5XRD per validator update + ValidatorFeeCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorOwnerCopy.class, Amount.ofTokens(5), // 5XRD per register update + ValidatorMetaData.class, Amount.ofTokens(5), // 5XRD per register update + AllowDelegationFlag.class, Amount.ofTokens(5), // 5XRD per register update + PreparedStake.class, Amount.ofMilliTokens(500), // 0.5XRD per stake + PreparedUnstakeOwnership.class, Amount.ofMilliTokens(500) // 0.5XRD per unstake + )), + (long) 1024 * 1024, // 1MB max user transaction size + OptionalInt.of(50), // 50 Txns per round + 10_000, // Rounds per epoch + 500, // Two weeks worth of epochs + Amount.ofTokens(90), // Minimum stake + 500, // Two weeks worth of epochs + Amount.ofMicroTokens(2307700), // Rewards per proposal + 9800, // 98.00% threshold for completed proposals to get any rewards, + 100 // 100 max validators + )); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseCacheSize.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseCacheSize.java index 6f90b4389c..2fc9106183 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseCacheSize.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseCacheSize.java @@ -64,20 +64,17 @@ package com.radixdlt.store; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies the size of the database cache - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Specifies the size of the database cache */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface DatabaseCacheSize { -} +public @interface DatabaseCacheSize {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseEnvironment.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseEnvironment.java index 7b9e3f7123..d683d3388b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseEnvironment.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseEnvironment.java @@ -1,159 +1,153 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.store; - -import com.sleepycat.je.Durability; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.inject.Inject; -import com.sleepycat.je.CacheMode; -import com.sleepycat.je.DatabaseException; -import com.sleepycat.je.Environment; -import com.sleepycat.je.EnvironmentConfig; - -import java.io.File; -import java.text.StringCharacterIterator; -import java.util.concurrent.TimeUnit; - -import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_CHECKPOINTER; -import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_CLEANER; -import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_EVICTOR; -import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_VERIFIER; -import static com.sleepycat.je.EnvironmentConfig.LOG_FILE_CACHE_SIZE; -import static com.sleepycat.je.EnvironmentConfig.TREE_MAX_EMBEDDED_LN; - -public final class DatabaseEnvironment { - private static final Logger log = LogManager.getLogger(); - - private Environment environment; - - @Inject - public DatabaseEnvironment( - @DatabaseLocation String databaseLocation, - @DatabaseCacheSize long cacheSize - ) { - var dbHome = new File(databaseLocation); - dbHome.mkdir(); - - System.setProperty("je.disable.java.adler32", "true"); - - EnvironmentConfig environmentConfig = new EnvironmentConfig(); - environmentConfig.setTransactional(true); - environmentConfig.setAllowCreate(true); - environmentConfig.setLockTimeout(30, TimeUnit.SECONDS); - environmentConfig.setDurability(Durability.COMMIT_SYNC); - environmentConfig.setConfigParam(LOG_FILE_CACHE_SIZE, "256"); - environmentConfig.setConfigParam(ENV_RUN_CHECKPOINTER, "true"); - environmentConfig.setConfigParam(ENV_RUN_CLEANER, "true"); - environmentConfig.setConfigParam(ENV_RUN_EVICTOR, "true"); - environmentConfig.setConfigParam(ENV_RUN_VERIFIER, "false"); - environmentConfig.setConfigParam(TREE_MAX_EMBEDDED_LN, "0"); - environmentConfig.setCacheSize(cacheSize); - environmentConfig.setCacheMode(CacheMode.EVICT_LN); - - environment = new Environment(dbHome, environmentConfig); - - log.info("DB cache size set to {} ({} bytes)", toHumanReadable(cacheSize), cacheSize); - } - - public void stop() { - try { - environment.close(); - } catch (DatabaseException e) { - log.error("Error while closing database. Possible DB corruption."); - } - environment = null; - } - - public Environment getEnvironment() { - if (environment == null) { - throw new IllegalStateException("environment is not started"); - } - - return environment; - } - - private static String toHumanReadable(long bytes) { - var absB = bytes == Long.MIN_VALUE - ? Long.MAX_VALUE - : Math.abs(bytes); - - if (absB < 1024) { - return bytes + " B"; - } - - var value = absB; - var ci = new StringCharacterIterator("KMGTPE"); - - for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { - value >>= 10; - ci.next(); - } - - value *= Long.signum(bytes); - return String.format("%.1f %ciB", value / 1024.0, ci.current()); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.store; + +import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_CHECKPOINTER; +import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_CLEANER; +import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_EVICTOR; +import static com.sleepycat.je.EnvironmentConfig.ENV_RUN_VERIFIER; +import static com.sleepycat.je.EnvironmentConfig.LOG_FILE_CACHE_SIZE; +import static com.sleepycat.je.EnvironmentConfig.TREE_MAX_EMBEDDED_LN; + +import com.google.inject.Inject; +import com.sleepycat.je.CacheMode; +import com.sleepycat.je.DatabaseException; +import com.sleepycat.je.Durability; +import com.sleepycat.je.Environment; +import com.sleepycat.je.EnvironmentConfig; +import java.io.File; +import java.text.StringCharacterIterator; +import java.util.concurrent.TimeUnit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public final class DatabaseEnvironment { + private static final Logger log = LogManager.getLogger(); + + private Environment environment; + + @Inject + public DatabaseEnvironment( + @DatabaseLocation String databaseLocation, @DatabaseCacheSize long cacheSize) { + var dbHome = new File(databaseLocation); + dbHome.mkdir(); + + System.setProperty("je.disable.java.adler32", "true"); + + EnvironmentConfig environmentConfig = new EnvironmentConfig(); + environmentConfig.setTransactional(true); + environmentConfig.setAllowCreate(true); + environmentConfig.setLockTimeout(30, TimeUnit.SECONDS); + environmentConfig.setDurability(Durability.COMMIT_SYNC); + environmentConfig.setConfigParam(LOG_FILE_CACHE_SIZE, "256"); + environmentConfig.setConfigParam(ENV_RUN_CHECKPOINTER, "true"); + environmentConfig.setConfigParam(ENV_RUN_CLEANER, "true"); + environmentConfig.setConfigParam(ENV_RUN_EVICTOR, "true"); + environmentConfig.setConfigParam(ENV_RUN_VERIFIER, "false"); + environmentConfig.setConfigParam(TREE_MAX_EMBEDDED_LN, "0"); + environmentConfig.setCacheSize(cacheSize); + environmentConfig.setCacheMode(CacheMode.EVICT_LN); + + environment = new Environment(dbHome, environmentConfig); + + log.info("DB cache size set to {} ({} bytes)", toHumanReadable(cacheSize), cacheSize); + } + + public void stop() { + try { + environment.close(); + } catch (DatabaseException e) { + log.error("Error while closing database. Possible DB corruption."); + } + environment = null; + } + + public Environment getEnvironment() { + if (environment == null) { + throw new IllegalStateException("environment is not started"); + } + + return environment; + } + + private static String toHumanReadable(long bytes) { + var absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + + if (absB < 1024) { + return bytes + " B"; + } + + var value = absB; + var ci = new StringCharacterIterator("KMGTPE"); + + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + + value *= Long.signum(bytes); + return String.format("%.1f %ciB", value / 1024.0, ci.current()); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseLocation.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseLocation.java index 4ffe832f30..ad2953ec44 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseLocation.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabaseLocation.java @@ -64,20 +64,17 @@ package com.radixdlt.store; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies the folder location of the database - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Specifies the folder location of the database */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface DatabaseLocation { -} +public @interface DatabaseLocation {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabasePropertiesModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabasePropertiesModule.java index 5e49608b6b..b2c1648528 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabasePropertiesModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/DatabasePropertiesModule.java @@ -68,28 +68,26 @@ import com.google.inject.Provides; import com.radixdlt.properties.RuntimeProperties; -/** - * Manages conversion of runtime properties to guice type properties - */ +/** Manages conversion of runtime properties to guice type properties */ public final class DatabasePropertiesModule extends AbstractModule { - private final long maxMemory = Runtime.getRuntime().maxMemory(); - private final long minCacheSizeLimit = Math.max(50_000_000L, (long) (maxMemory * 0.1)); - private final long maxCacheSizeLimit = (long) (maxMemory * 0.25); - private final long defaultCacheSize = (long) (maxMemory * 0.125); + private final long maxMemory = Runtime.getRuntime().maxMemory(); + private final long minCacheSizeLimit = Math.max(50_000_000L, (long) (maxMemory * 0.1)); + private final long maxCacheSizeLimit = (long) (maxMemory * 0.25); + private final long defaultCacheSize = (long) (maxMemory * 0.125); - @Provides - @DatabaseLocation - String databaseLocation(RuntimeProperties properties) { - return properties.get("db.location", ".//RADIXDB"); - } + @Provides + @DatabaseLocation + String databaseLocation(RuntimeProperties properties) { + return properties.get("db.location", ".//RADIXDB"); + } - @Provides - @DatabaseCacheSize - long databaseCacheSize(RuntimeProperties properties) { - var minCacheSize = properties.get("db.cache_size.min", minCacheSizeLimit); - var maxCacheSize = properties.get("db.cache_size.max", maxCacheSizeLimit); - var cacheSize = properties.get("db.cache_size", defaultCacheSize); + @Provides + @DatabaseCacheSize + long databaseCacheSize(RuntimeProperties properties) { + var minCacheSize = properties.get("db.cache_size.min", minCacheSizeLimit); + var maxCacheSize = properties.get("db.cache_size.max", maxCacheSizeLimit); + var cacheSize = properties.get("db.cache_size", defaultCacheSize); - return Math.min(Math.max(cacheSize, minCacheSize), maxCacheSize); - } + return Math.min(Math.max(cacheSize, minCacheSize), maxCacheSize); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastEpochProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastEpochProof.java index 3a73a083b3..b1e9426401 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastEpochProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastEpochProof.java @@ -73,12 +73,8 @@ import java.lang.annotation.Target; import javax.inject.Qualifier; - -/** - * Identifies that the target is the last epoch proof - */ +/** Identifies that the target is the last epoch proof */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface LastEpochProof { -} +public @interface LastEpochProof {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastProof.java index ab2e840762..15c718cdb9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastProof.java @@ -74,13 +74,11 @@ import javax.inject.Qualifier; /** - * Identifies that the target is the last proof. This includes - * the mocked genesis VerifiedLedgerHeaderAndProof. That is, - * the header associated with this annotation should never + * Identifies that the target is the last proof. This includes the mocked genesis + * VerifiedLedgerHeaderAndProof. That is, the header associated with this annotation should never * have isEndOfEpoch() == true. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface LastProof { -} +public @interface LastProof {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastStoredProof.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastStoredProof.java index a9a7382311..f8f55fcb19 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastStoredProof.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/LastStoredProof.java @@ -64,20 +64,17 @@ package com.radixdlt.store; -import javax.inject.Qualifier; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Marks the proof as the last one stored - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Marks the proof as the last one stored */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface LastStoredProof { -} +public @interface LastStoredProof {} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/PersistenceModule.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/PersistenceModule.java index 072aa463da..11f1ce5a45 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/PersistenceModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/PersistenceModule.java @@ -77,56 +77,49 @@ import com.radixdlt.environment.EventProcessor; import com.radixdlt.environment.ProcessOnDispatch; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; -import com.radixdlt.store.berkeley.SerializedVertexStoreState; import com.radixdlt.store.berkeley.BerkeleySafetyStateStore; - +import com.radixdlt.store.berkeley.SerializedVertexStoreState; import java.util.Optional; -/** - * Module which manages persistent storage - */ +/** Module which manages persistent storage */ public class PersistenceModule extends AbstractModule { - @Override - protected void configure() { - // TODO: should be singletons? - bind(ResourceStore.class).to(BerkeleyLedgerEntryStore.class).in(Scopes.SINGLETON); - bind(PersistentVertexStore.class).to(BerkeleyLedgerEntryStore.class); - bind(PersistentSafetyStateStore.class).to(BerkeleySafetyStateStore.class); - bind(BerkeleySafetyStateStore.class).in(Scopes.SINGLETON); - bind(DatabaseEnvironment.class).in(Scopes.SINGLETON); - } + @Override + protected void configure() { + // TODO: should be singletons? + bind(ResourceStore.class).to(BerkeleyLedgerEntryStore.class).in(Scopes.SINGLETON); + bind(PersistentVertexStore.class).to(BerkeleyLedgerEntryStore.class); + bind(PersistentSafetyStateStore.class).to(BerkeleySafetyStateStore.class); + bind(BerkeleySafetyStateStore.class).in(Scopes.SINGLETON); + bind(DatabaseEnvironment.class).in(Scopes.SINGLETON); + } - @Provides - Optional serializedVertexStoreState(BerkeleyLedgerEntryStore store) { - return store.loadLastVertexStoreState(); - } + @Provides + Optional serializedVertexStoreState(BerkeleyLedgerEntryStore store) { + return store.loadLastVertexStoreState(); + } - @Provides - StoreConfig storeConfig() { - return new StoreConfig(1000); - } + @Provides + StoreConfig storeConfig() { + return new StoreConfig(1000); + } - @ProvidesIntoSet - @ProcessOnDispatch - public EventProcessor persistQC( - PersistentVertexStore persistentVertexStore, - SystemCounters systemCounters - ) { - return update -> { - systemCounters.increment(CounterType.PERSISTENCE_VERTEX_STORE_SAVES); - persistentVertexStore.save(update.getVertexStoreState()); - }; - } + @ProvidesIntoSet + @ProcessOnDispatch + public EventProcessor persistQC( + PersistentVertexStore persistentVertexStore, SystemCounters systemCounters) { + return update -> { + systemCounters.increment(CounterType.PERSISTENCE_VERTEX_STORE_SAVES); + persistentVertexStore.save(update.getVertexStoreState()); + }; + } - @ProvidesIntoSet - @ProcessOnDispatch - public EventProcessor persistUpdates( - PersistentVertexStore persistentVertexStore, - SystemCounters systemCounters - ) { - return update -> { - systemCounters.increment(CounterType.PERSISTENCE_VERTEX_STORE_SAVES); - persistentVertexStore.save(update.getVertexStoreState()); - }; - } + @ProvidesIntoSet + @ProcessOnDispatch + public EventProcessor persistUpdates( + PersistentVertexStore persistentVertexStore, SystemCounters systemCounters) { + return update -> { + systemCounters.increment(CounterType.PERSISTENCE_VERTEX_STORE_SAVES); + persistentVertexStore.save(update.getVertexStoreState()); + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/SearchCursor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/SearchCursor.java index fb138b8fef..a0d2b0b82a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/SearchCursor.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/SearchCursor.java @@ -1,82 +1,81 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.store; - -import com.radixdlt.identifiers.AID; - -/** - * A ledger cursor, bound to a specific ledger instance. - */ -public interface SearchCursor { - long getStateVersion(); - - /** - * Gets the current AID at this cursor - * @return The current AID - */ - AID get(); - - SearchCursor next(); -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.store; + +import com.radixdlt.identifiers.AID; + +/** A ledger cursor, bound to a specific ledger instance. */ +public interface SearchCursor { + long getStateVersion(); + + /** + * Gets the current AID at this cursor + * + * @return The current AID + */ + AID get(); + + SearchCursor next(); +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/StoreConfig.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/StoreConfig.java index 462b4d5004..4743a7a558 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/StoreConfig.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/StoreConfig.java @@ -64,19 +64,18 @@ package com.radixdlt.store; -/** - * Specifies high level configuration options for persistent storage - */ +/** Specifies high level configuration options for persistent storage */ public final class StoreConfig { - private final int minimumProofBlockSize; - public StoreConfig(int minimumProofBlockSize) { - if (minimumProofBlockSize < 1) { - throw new IllegalArgumentException("Proof block size must be >= 1."); - } - this.minimumProofBlockSize = minimumProofBlockSize; - } + private final int minimumProofBlockSize; + + public StoreConfig(int minimumProofBlockSize) { + if (minimumProofBlockSize < 1) { + throw new IllegalArgumentException("Proof block size must be >= 1."); + } + this.minimumProofBlockSize = minimumProofBlockSize; + } - public int getMinimumProofBlockSize() { - return minimumProofBlockSize; - } + public int getMinimumProofBlockSize() { + return minimumProofBlockSize; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAdditionalStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAdditionalStore.java index 569b4a17bd..16f4ffe102 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAdditionalStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAdditionalStore.java @@ -69,23 +69,22 @@ import com.radixdlt.constraintmachine.SystemMapKey; import com.radixdlt.store.DatabaseEnvironment; import com.sleepycat.je.Transaction; - import java.util.Optional; import java.util.function.Function; /** - * Simple way to add an additional store - * TODO: perhaps needs to be integrated at a higher level with RadixEngine - * TODO: Make more generic rather than just attachment to BerkeleyLedgerEntryStore - * TODO: Implement all other additional databases with this interface + * Simple way to add an additional store TODO: perhaps needs to be integrated at a higher level with + * RadixEngine TODO: Make more generic rather than just attachment to BerkeleyLedgerEntryStore TODO: + * Implement all other additional databases with this interface */ public interface BerkeleyAdditionalStore { - void open(DatabaseEnvironment dbEnv); - void close(); - void process( - Transaction dbTxn, - REProcessedTxn txn, - long stateVersion, - Function> mapper - ); + void open(DatabaseEnvironment dbEnv); + + void close(); + + void process( + Transaction dbTxn, + REProcessedTxn txn, + long stateVersion, + Function> mapper); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAddressBookPersistence.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAddressBookPersistence.java index 677432186c..c25d055369 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAddressBookPersistence.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyAddressBookPersistence.java @@ -1,230 +1,227 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.store.berkeley; - -import com.google.common.collect.ImmutableList; -import com.google.inject.Inject; -import com.radixdlt.network.p2p.NodeId; -import com.radixdlt.network.p2p.addressbook.AddressBookEntry; -import com.radixdlt.network.p2p.addressbook.AddressBookPersistence; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.Serialization; -import com.radixdlt.store.DatabaseEnvironment; -import com.sleepycat.je.Database; -import com.sleepycat.je.DatabaseConfig; -import com.sleepycat.je.DatabaseEntry; -import com.sleepycat.je.DatabaseException; -import com.sleepycat.je.DatabaseNotFoundException; -import com.sleepycat.je.LockMode; -import com.sleepycat.je.OperationStatus; -import com.sleepycat.je.Transaction; -import com.sleepycat.je.TransactionConfig; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.Objects; - -/** - * Persistence for address book entries. - */ -public final class BerkeleyAddressBookPersistence implements AddressBookPersistence { - private static final Logger log = LogManager.getLogger(); - - private final Serialization serialization; - private final DatabaseEnvironment dbEnv; - private final SystemCounters systemCounters; - private Database entriesDb; - - @Inject - public BerkeleyAddressBookPersistence( - Serialization serialization, - DatabaseEnvironment dbEnv, - SystemCounters systemCounters - ) { - this.serialization = Objects.requireNonNull(serialization); - this.dbEnv = Objects.requireNonNull(dbEnv); - this.systemCounters = Objects.requireNonNull(systemCounters); - - this.open(); - } - - @Override - public void open() { - final var config = new DatabaseConfig(); - config.setAllowCreate(true); - - try { - this.entriesDb = this.dbEnv.getEnvironment().openDatabase(null, "address_book_entries", config); - } catch (DatabaseException | IllegalArgumentException | IllegalStateException ex) { - throw new IllegalStateException("while opening database", ex); - } - } - - @Override - public void reset() { - Transaction transaction = null; - - try { - transaction = this.dbEnv.getEnvironment().beginTransaction(null, new TransactionConfig().setReadUncommitted(true)); - this.dbEnv.getEnvironment().truncateDatabase(transaction, "address_book_entries", false); - transaction.commit(); - } catch (DatabaseNotFoundException dsnfex) { - if (transaction != null) { - transaction.abort(); - } - log.warn(dsnfex.getMessage()); - } catch (Exception ex) { - if (transaction != null) { - transaction.abort(); - } - throw new IllegalStateException("while resetting database", ex); - } - } - - @Override - public void close() { - if (this.entriesDb != null) { - this.entriesDb.close(); - this.entriesDb = null; - } - } - - @Override - public boolean saveEntry(AddressBookEntry entry) { - final var start = System.nanoTime(); - try { - final var key = new DatabaseEntry(entry.getNodeId().getPublicKey().getBytes()); - final var value = new DatabaseEntry(serialization.toDson(entry, Output.PERSIST)); - - if (entriesDb.put(null, key, value) == OperationStatus.SUCCESS) { - addBytesWrite(key.getSize() + value.getSize()); - return true; - } - - return false; - } finally { - addTime(start); - } - } - - @Override - public boolean removeEntry(NodeId nodeId) { - final var start = System.nanoTime(); - try { - final var key = new DatabaseEntry(nodeId.getPublicKey().getBytes()); - - if (entriesDb.delete(null, key) == OperationStatus.SUCCESS) { - systemCounters.increment(CounterType.COUNT_BDB_ADDRESS_BOOK_DELETES); - return true; - } - return false; - } finally { - addTime(start); - } - } - - @Override - public ImmutableList getAllEntries() { - final var start = System.nanoTime(); - try { - try (var cursor = this.entriesDb.openCursor(null, null)) { - final var key = new DatabaseEntry(); - final var value = new DatabaseEntry(); - - final var builder = ImmutableList.builder(); - while (cursor.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) { - addBytesRead(key.getSize() + value.getSize()); - final var entry = serialization.fromDson(value.getData(), AddressBookEntry.class); - builder.add(entry); - } - - return builder.build(); - } catch (IOException ex) { - throw new UncheckedIOException("Error while loading database", ex); - } - } finally { - addTime(start); - } - } - - private void addTime(long start) { - final var elapsed = (System.nanoTime() - start + 500L) / 1000L; - this.systemCounters.add(CounterType.ELAPSED_BDB_ADDRESS_BOOK, elapsed); - this.systemCounters.increment(CounterType.COUNT_BDB_ADDRESS_BOOK_TOTAL); - } - - private void addBytesRead(int bytesRead) { - this.systemCounters.add(CounterType.COUNT_BDB_ADDRESS_BOOK_BYTES_READ, bytesRead); - } - - private void addBytesWrite(int bytesWrite) { - this.systemCounters.add(CounterType.COUNT_BDB_ADDRESS_BOOK_BYTES_WRITE, bytesWrite); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.store.berkeley; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.network.p2p.NodeId; +import com.radixdlt.network.p2p.addressbook.AddressBookEntry; +import com.radixdlt.network.p2p.addressbook.AddressBookPersistence; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.Serialization; +import com.radixdlt.store.DatabaseEnvironment; +import com.sleepycat.je.Database; +import com.sleepycat.je.DatabaseConfig; +import com.sleepycat.je.DatabaseEntry; +import com.sleepycat.je.DatabaseException; +import com.sleepycat.je.DatabaseNotFoundException; +import com.sleepycat.je.LockMode; +import com.sleepycat.je.OperationStatus; +import com.sleepycat.je.Transaction; +import com.sleepycat.je.TransactionConfig; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** Persistence for address book entries. */ +public final class BerkeleyAddressBookPersistence implements AddressBookPersistence { + private static final Logger log = LogManager.getLogger(); + + private final Serialization serialization; + private final DatabaseEnvironment dbEnv; + private final SystemCounters systemCounters; + private Database entriesDb; + + @Inject + public BerkeleyAddressBookPersistence( + Serialization serialization, DatabaseEnvironment dbEnv, SystemCounters systemCounters) { + this.serialization = Objects.requireNonNull(serialization); + this.dbEnv = Objects.requireNonNull(dbEnv); + this.systemCounters = Objects.requireNonNull(systemCounters); + + this.open(); + } + + @Override + public void open() { + final var config = new DatabaseConfig(); + config.setAllowCreate(true); + + try { + this.entriesDb = + this.dbEnv.getEnvironment().openDatabase(null, "address_book_entries", config); + } catch (DatabaseException | IllegalArgumentException | IllegalStateException ex) { + throw new IllegalStateException("while opening database", ex); + } + } + + @Override + public void reset() { + Transaction transaction = null; + + try { + transaction = + this.dbEnv + .getEnvironment() + .beginTransaction(null, new TransactionConfig().setReadUncommitted(true)); + this.dbEnv.getEnvironment().truncateDatabase(transaction, "address_book_entries", false); + transaction.commit(); + } catch (DatabaseNotFoundException dsnfex) { + if (transaction != null) { + transaction.abort(); + } + log.warn(dsnfex.getMessage()); + } catch (Exception ex) { + if (transaction != null) { + transaction.abort(); + } + throw new IllegalStateException("while resetting database", ex); + } + } + + @Override + public void close() { + if (this.entriesDb != null) { + this.entriesDb.close(); + this.entriesDb = null; + } + } + + @Override + public boolean saveEntry(AddressBookEntry entry) { + final var start = System.nanoTime(); + try { + final var key = new DatabaseEntry(entry.getNodeId().getPublicKey().getBytes()); + final var value = new DatabaseEntry(serialization.toDson(entry, Output.PERSIST)); + + if (entriesDb.put(null, key, value) == OperationStatus.SUCCESS) { + addBytesWrite(key.getSize() + value.getSize()); + return true; + } + + return false; + } finally { + addTime(start); + } + } + + @Override + public boolean removeEntry(NodeId nodeId) { + final var start = System.nanoTime(); + try { + final var key = new DatabaseEntry(nodeId.getPublicKey().getBytes()); + + if (entriesDb.delete(null, key) == OperationStatus.SUCCESS) { + systemCounters.increment(CounterType.COUNT_BDB_ADDRESS_BOOK_DELETES); + return true; + } + return false; + } finally { + addTime(start); + } + } + + @Override + public ImmutableList getAllEntries() { + final var start = System.nanoTime(); + try { + try (var cursor = this.entriesDb.openCursor(null, null)) { + final var key = new DatabaseEntry(); + final var value = new DatabaseEntry(); + + final var builder = ImmutableList.builder(); + while (cursor.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) { + addBytesRead(key.getSize() + value.getSize()); + final var entry = serialization.fromDson(value.getData(), AddressBookEntry.class); + builder.add(entry); + } + + return builder.build(); + } catch (IOException ex) { + throw new UncheckedIOException("Error while loading database", ex); + } + } finally { + addTime(start); + } + } + + private void addTime(long start) { + final var elapsed = (System.nanoTime() - start + 500L) / 1000L; + this.systemCounters.add(CounterType.ELAPSED_BDB_ADDRESS_BOOK, elapsed); + this.systemCounters.increment(CounterType.COUNT_BDB_ADDRESS_BOOK_TOTAL); + } + + private void addBytesRead(int bytesRead) { + this.systemCounters.add(CounterType.COUNT_BDB_ADDRESS_BOOK_BYTES_READ, bytesRead); + } + + private void addBytesWrite(int bytesWrite) { + this.systemCounters.add(CounterType.COUNT_BDB_ADDRESS_BOOK_BYTES_WRITE, bytesWrite); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyLedgerEntryStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyLedgerEntryStore.java index f15203a610..4ba2345a46 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyLedgerEntryStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyLedgerEntryStore.java @@ -64,41 +64,40 @@ package com.radixdlt.store.berkeley; +import static com.google.common.primitives.UnsignedBytes.lexicographicalComparator; +import static com.radixdlt.utils.Longs.fromByteArray; +import static com.sleepycat.je.LockMode.DEFAULT; +import static com.sleepycat.je.OperationStatus.NOTFOUND; +import static com.sleepycat.je.OperationStatus.SUCCESS; + import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Streams; +import com.google.inject.Inject; import com.radixdlt.application.system.state.SystemData; import com.radixdlt.application.system.state.VirtualParent; import com.radixdlt.application.tokens.state.ResourceData; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.validators.state.ValidatorData; -import com.radixdlt.atom.SubstateTypeId; -import com.radixdlt.constraintmachine.REProcessedTxn; -import com.radixdlt.constraintmachine.SubstateIndex; -import com.radixdlt.constraintmachine.RawSubstateBytes; -import com.radixdlt.constraintmachine.SystemMapKey; -import com.radixdlt.constraintmachine.exceptions.VirtualParentStateDoesNotExist; -import com.radixdlt.constraintmachine.exceptions.VirtualSubstateAlreadyDownException; -import com.radixdlt.crypto.ECPublicKey; -import com.radixdlt.engine.RadixEngineException; -import com.radixdlt.store.ResourceStore; -import com.radixdlt.utils.UInt256; -import com.sleepycat.je.Get; -import com.sleepycat.je.Transaction; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.common.collect.ImmutableList; -import com.google.inject.Inject; import com.radixdlt.atom.CloseableCursor; import com.radixdlt.atom.SubstateId; +import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.Txn; -import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.PersistentVertexStore; import com.radixdlt.consensus.bft.VerifiedVertexStoreState; -import com.radixdlt.constraintmachine.REStateUpdate; import com.radixdlt.constraintmachine.REOp; +import com.radixdlt.constraintmachine.REProcessedTxn; +import com.radixdlt.constraintmachine.REStateUpdate; +import com.radixdlt.constraintmachine.RawSubstateBytes; +import com.radixdlt.constraintmachine.SubstateIndex; +import com.radixdlt.constraintmachine.SystemMapKey; +import com.radixdlt.constraintmachine.exceptions.VirtualParentStateDoesNotExist; +import com.radixdlt.constraintmachine.exceptions.VirtualSubstateAlreadyDownException; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.crypto.ECPublicKey; +import com.radixdlt.engine.RadixEngineException; import com.radixdlt.identifiers.AID; import com.radixdlt.identifiers.REAddr; import com.radixdlt.ledger.DtoLedgerProof; @@ -109,19 +108,22 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.EngineStore; +import com.radixdlt.store.ResourceStore; import com.radixdlt.store.StoreConfig; import com.radixdlt.store.berkeley.atom.AppendLog; import com.radixdlt.sync.CommittedReader; import com.radixdlt.utils.Longs; +import com.radixdlt.utils.UInt256; import com.sleepycat.je.Cursor; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseEntry; +import com.sleepycat.je.Get; import com.sleepycat.je.OperationStatus; import com.sleepycat.je.SecondaryConfig; import com.sleepycat.je.SecondaryCursor; import com.sleepycat.je.SecondaryDatabase; - +import com.sleepycat.je.Transaction; import java.io.File; import java.io.IOException; import java.math.BigInteger; @@ -137,1057 +139,1084 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.stream.Stream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import static com.google.common.primitives.UnsignedBytes.lexicographicalComparator; -import static com.radixdlt.utils.Longs.fromByteArray; -import static com.sleepycat.je.LockMode.DEFAULT; -import static com.sleepycat.je.OperationStatus.NOTFOUND; -import static com.sleepycat.je.OperationStatus.SUCCESS; - -public final class BerkeleyLedgerEntryStore implements EngineStore, ResourceStore, - CommittedReader, PersistentVertexStore { - private static final Logger log = LogManager.getLogger(); - - private final Serialization serialization; - private final DatabaseEnvironment dbEnv; - private final SystemCounters systemCounters; - private final StoreConfig storeConfig; - - // Engine Store databases - private static final String SUBSTATE_DB_NAME = "radix.substate_db"; - private static final String RESOURCE_DB_NAME = "radix.resource_db"; - private static final String MAP_DB_NAME = "radix.map_db"; - private static final String INDEXED_SUBSTATE_DB_NAME = "radix.indexed_substate_db"; - private Database substatesDatabase; // Write/Delete - private SecondaryDatabase indexedSubstatesDatabase; // Write/Delete - private Database resourceDatabase; // Write-only (Resources are immutable) - private Database mapDatabase; - - // Metadata databases - private static final String VERTEX_STORE_DB_NAME = "radix.vertex_store"; - private static final String TXN_DB_NAME = "radix.txn_db"; - private Database vertexStoreDatabase; // Write/Delete - private Database proofDatabase; // Write/Delete - private SecondaryDatabase epochProofDatabase; - - // Syncing Ledger databases - private static final String PROOF_DB_NAME = "radix.proof_db"; - private static final String EPOCH_PROOF_DB_NAME = "radix.epoch_proof_db"; - private static final String LEDGER_NAME = "radix.ledger"; - private Database txnDatabase; // Txns by state version; Append-only - private AppendLog txnLog; //Atom data append only log - - private final Set additionalStores; - - @Inject - public BerkeleyLedgerEntryStore( - Serialization serialization, - DatabaseEnvironment dbEnv, - StoreConfig storeConfig, - SystemCounters systemCounters, - Set additionalStores - ) { - this.serialization = Objects.requireNonNull(serialization); - this.dbEnv = Objects.requireNonNull(dbEnv); - this.systemCounters = Objects.requireNonNull(systemCounters); - this.storeConfig = storeConfig; - this.additionalStores = additionalStores; - - this.open(); - } - - public void close() { - safeClose(txnDatabase); - safeClose(resourceDatabase); - safeClose(mapDatabase); - - safeClose(indexedSubstatesDatabase); - safeClose(substatesDatabase); - - safeClose(epochProofDatabase); - safeClose(proofDatabase); - - safeClose(vertexStoreDatabase); - - additionalStores.forEach(BerkeleyAdditionalStore::close); - - if (txnLog != null) { - txnLog.close(); - } - } - - private Transaction createTransaction() { - return withTime( - () -> beginTransaction(), - CounterType.ELAPSED_BDB_LEDGER_CREATE_TX, - CounterType.COUNT_BDB_LEDGER_CREATE_TX - ); - } - - @Override - public R transaction(TransactionEngineStoreConsumer consumer) throws RadixEngineException { - var dbTxn = createTransaction(); - try { - var result = consumer.start(new EngineStoreInTransaction<>() { - @Override - public void storeTxn(REProcessedTxn txn) { - BerkeleyLedgerEntryStore.this.storeTxn(dbTxn, txn); - } - - @Override - public void storeMetadata(LedgerAndBFTProof metadata) { - BerkeleyLedgerEntryStore.this.storeMetadata(dbTxn, metadata); - } - - @Override - public ByteBuffer verifyVirtualSubstate(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist { - var parent = substateId.getVirtualParent().orElseThrow(); - - var parentState = BerkeleyLedgerEntryStore.this.loadSubstate(dbTxn, parent); - if (parentState.isEmpty()) { - throw new VirtualParentStateDoesNotExist(parent); - } - - var buf = parentState.get(); - if (buf.get() != SubstateTypeId.VIRTUAL_PARENT.id()) { - throw new VirtualParentStateDoesNotExist(parent); - } - buf.position(buf.position() - 1); - - if (BerkeleyLedgerEntryStore.this.isVirtualDown(dbTxn, substateId)) { - throw new VirtualSubstateAlreadyDownException(substateId); - } - - return buf; - } - - @Override - public Optional loadSubstate(SubstateId substateId) { - return BerkeleyLedgerEntryStore.this.loadSubstate(dbTxn, substateId); - } - - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return BerkeleyLedgerEntryStore.this.openIndexedCursor(dbTxn, index); - } - - @Override - public Optional loadResource(REAddr addr) { - return BerkeleyLedgerEntryStore.this.loadAddr(dbTxn, addr); - } - }); - dbTxn.commit(); - return result; - } catch (Exception e) { - dbTxn.abort(); - throw e; - } - } - - @Override - public LedgerAndBFTProof getMetadata() { - return getLastProof().map(LedgerAndBFTProof::create).orElse(null); - } - - public Stream scanner() { - var cursor = substatesDatabase.openCursor(null, null); - var iterator = new Iterator() { - final DatabaseEntry key = new DatabaseEntry(); - final DatabaseEntry data = new DatabaseEntry(); - OperationStatus status = cursor.getFirst(key, data, null); - - @Override - public boolean hasNext() { - return status == SUCCESS; - } - - @Override - public RawSubstateBytes next() { - if (status != SUCCESS) { - throw new NoSuchElementException(); - } - var next = new RawSubstateBytes(key.getData(), data.getData()); - status = cursor.getNext(key, data, null); - return next; - } - }; - return Streams.stream(iterator).onClose(cursor::close); - } - - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return BerkeleyLedgerEntryStore.this.openIndexedCursor(null, index); - } - - private Optional getInternal(Transaction dbTxn, SystemMapKey mapKey) { - var key = new DatabaseEntry(mapKey.array()); - var substateId = new DatabaseEntry(); - var result = mapDatabase.get(dbTxn, key, substateId, null); - if (result != SUCCESS) { - return Optional.empty(); - } - - var substate = loadSubstate(dbTxn, SubstateId.fromBytes(substateId.getData())).orElseThrow(); - var substateBytes = new RawSubstateBytes(substateId.getData(), substate.array()); - return Optional.of(substateBytes); - } - - @Override - public Optional get(SystemMapKey mapKey) { - return getInternal(null, mapKey); - } - - private void storeTxn(Transaction dbTxn, REProcessedTxn txn) { - withTime(() -> doStore(dbTxn, txn), CounterType.ELAPSED_BDB_LEDGER_STORE, CounterType.COUNT_BDB_LEDGER_STORE); - } - - private void storeMetadata(Transaction dbTxn, LedgerAndBFTProof ledgerAndBFTProof) { - var proof = ledgerAndBFTProof.getProof(); - - try (var atomCursor = txnDatabase.openCursor(dbTxn, null)) { - var key = entry(); - var status = atomCursor.getLast(key, null, DEFAULT); - if (status == NOTFOUND) { - throw new IllegalStateException("No atom found before storing proof."); - } - - long lastVersion = Longs.fromByteArray(key.getData()); - if (lastVersion != proof.getStateVersion()) { - throw new IllegalStateException("Proof version " + proof.getStateVersion() - + " does not match last transaction: " + lastVersion); - } - } - - try (var proofCursor = proofDatabase.openCursor(dbTxn, null)) { - var prevHeaderKey = entry(); - var status = proofCursor.getLast(prevHeaderKey, null, DEFAULT); - // Cannot remove end of epoch proofs - if (status == SUCCESS && headerKeyEpoch(prevHeaderKey).isEmpty()) { - status = proofCursor.getPrev(prevHeaderKey, null, DEFAULT); - if (status == SUCCESS) { - long twoAwayStateVersion = Longs.fromByteArray(prevHeaderKey.getData()); - long versionDiff = proof.getStateVersion() - twoAwayStateVersion; - if (versionDiff <= storeConfig.getMinimumProofBlockSize()) { - executeOrElseThrow(() -> proofCursor.getNext(null, null, DEFAULT), "Missing next."); - executeOrElseThrow(proofCursor::delete, "Could not delete header."); - systemCounters.increment(CounterType.COUNT_BDB_LEDGER_PROOFS_REMOVED); - } - } - } - - final var headerKey = toHeaderKey(proof); - final var headerData = entry(serialize(proof)); - this.putNoOverwriteOrElseThrow( - proofCursor, - headerKey, - headerData, - "Header write failed: " + proof, - CounterType.COUNT_BDB_HEADER_BYTES_WRITE - ); - - systemCounters.increment(CounterType.COUNT_BDB_LEDGER_PROOFS_ADDED); - } - - ledgerAndBFTProof.vertexStoreState().ifPresent(v -> doSave(dbTxn, v)); - } - - public Optional loadLastVertexStoreState() { - return withTime(() -> { - try (var cursor = vertexStoreDatabase.openCursor(null, null)) { - var pKey = entry(); - var value = entry(); - var status = cursor.getLast(pKey, value, DEFAULT); - - if (status == SUCCESS) { - addBytesRead(value, pKey); - try { - return Optional.of(serialization.fromDson(value.getData(), SerializedVertexStoreState.class)); - } catch (DeserializeException e) { - throw new IllegalStateException(e); - } - } else { - return Optional.empty(); - } - } - }, CounterType.ELAPSED_BDB_LEDGER_LAST_VERTEX, CounterType.COUNT_BDB_LEDGER_LAST_VERTEX); - } - - @Override - public void save(VerifiedVertexStoreState vertexStoreState) { - withTime(() -> { - var transaction = beginTransaction(); - doSave(transaction, vertexStoreState); - transaction.commit(); - }, CounterType.ELAPSED_BDB_LEDGER_SAVE, CounterType.COUNT_BDB_LEDGER_SAVE); - } - - private void open() { - var primaryConfig = buildPrimaryConfig(); - var rriConfig = buildRriConfig(); - var pendingConfig = buildPendingConfig(); - - try { - // This SuppressWarnings here is valid, as ownership of the underlying - // resource is not changed here, the resource is just accessed. - @SuppressWarnings("resource") - var env = dbEnv.getEnvironment(); - txnDatabase = env.openDatabase(null, TXN_DB_NAME, primaryConfig); - - resourceDatabase = env.openDatabase(null, RESOURCE_DB_NAME, rriConfig); - mapDatabase = env.openDatabase(null, MAP_DB_NAME, rriConfig); - substatesDatabase = env.openDatabase(null, SUBSTATE_DB_NAME, primaryConfig); - - indexedSubstatesDatabase = env.openSecondaryDatabase( - null, INDEXED_SUBSTATE_DB_NAME, substatesDatabase, - (SecondaryConfig) new SecondaryConfig() - .setKeyCreator( - (secondary, key, data, result) -> { - if (entryToSpin(data) != REOp.UP) { - return false; - } - - var substateTypeId = data.getData()[data.getOffset()]; - final int prefixIndexSize; - if (substateTypeId == SubstateTypeId.TOKENS.id()) { - // Indexing not necessary for verification at the moment but useful for construction - - // 0: Type Byte - // 1: Reserved Byte - // 2-37: Account Address - prefixIndexSize = 2 + (1 + ECPublicKey.COMPRESSED_BYTES); - } else if (substateTypeId == SubstateTypeId.STAKE_OWNERSHIP.id()) { - // Indexing not necessary for verification at the moment but useful for construction - - // This should have had validator keys and account addresses switched so that - // prefix indexing could be done against account addresses rather than validators - // so that actions like "Unstake Everything" could be implemented and queries against - // accounts. A later to do... - - // 0: Type Byte - // 1: Reserved Byte - // 2-36: Validator Key - // 37-69: Account Address - prefixIndexSize = 2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES); - } else if (substateTypeId == SubstateTypeId.EXITING_STAKE.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2-5: Epoch - // 6-40: Validator Key - // 41-73: Account Address - prefixIndexSize = 2 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES); - } else if (substateTypeId == SubstateTypeId.PREPARED_STAKE.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2-36: Validator Key - // 37-69: Account Address - prefixIndexSize = 2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES); - } else if (substateTypeId == SubstateTypeId.PREPARED_UNSTAKE.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2-36: Validator Key - // 37-69: Account Address - prefixIndexSize = 2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES); - } else if (substateTypeId == SubstateTypeId.VALIDATOR_OWNER_COPY.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2: Optional flag - // 3-6: Epoch - // 7-41: Validator Key - prefixIndexSize = 3 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES; - } else if (substateTypeId == SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2: Optional flag - // 3-6: Epoch - // 7-41: Validator Key - prefixIndexSize = 3 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES; - } else if (substateTypeId == SubstateTypeId.VALIDATOR_RAKE_COPY.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2: Optional flag - // 3-6: Epoch - // 7-41: Validator Key - prefixIndexSize = 3 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES; - } else if (substateTypeId == SubstateTypeId.VALIDATOR_STAKE_DATA.id()) { - // 0: Type Byte - // 1: Reserved Byte - // 2: Registered Byte - // 3-34: Stake amount - // 35-67: Validator key - prefixIndexSize = 3 + UInt256.BYTES + ECPublicKey.COMPRESSED_BYTES; - } else { - // 0: Type Byte - prefixIndexSize = 1; - } - // Index by substate type - result.setData(data.getData(), data.getOffset(), prefixIndexSize); - return true; - } - ) - .setBtreeComparator(lexicographicalComparator()) - .setSortedDuplicates(true) - .setAllowCreate(true) - .setTransactional(true) - ); - - proofDatabase = env.openDatabase(null, PROOF_DB_NAME, primaryConfig); - vertexStoreDatabase = env.openDatabase(null, VERTEX_STORE_DB_NAME, pendingConfig); - epochProofDatabase = env.openSecondaryDatabase(null, EPOCH_PROOF_DB_NAME, proofDatabase, buildEpochProofConfig()); - - txnLog = AppendLog.openCompressed(new File(env.getHome(), LEDGER_NAME).getAbsolutePath(), systemCounters); - } catch (Exception e) { - throw new BerkeleyStoreException("Error while opening databases", e); - } - - this.additionalStores.forEach(b -> b.open(dbEnv)); - - if (System.getProperty("db.check_integrity", "1").equals("1")) { - // TODO implement integrity check - // TODO perhaps we should implement recovery instead? - // TODO recovering should be integrated with recovering of ClientApiStore - } - } - - private SecondaryConfig buildEpochProofConfig() { - return (SecondaryConfig) new SecondaryConfig() - .setKeyCreator( - (secondary, key, data, result) -> { - OptionalLong epoch = headerKeyEpoch(key); - epoch.ifPresent(e -> result.setData(Longs.toByteArray(e))); - return epoch.isPresent(); - } - ) - .setAllowCreate(true) - .setTransactional(true); - } - - private DatabaseConfig buildPendingConfig() { - return new DatabaseConfig() - .setBtreeComparator(lexicographicalComparator()) - .setAllowCreate(true) - .setTransactional(true); - } - - private DatabaseConfig buildPrimaryConfig() { - return new DatabaseConfig() - .setAllowCreate(true) - .setTransactional(true) - .setKeyPrefixing(true) - .setBtreeComparator(lexicographicalComparator()); - } - - private DatabaseConfig buildRriConfig() { - return new DatabaseConfig() - .setAllowCreate(true) - .setTransactional(true) - .setKeyPrefixing(true) - .setBtreeComparator(lexicographicalComparator()); - } - - private static void safeClose(Database database) { - if (database != null) { - database.close(); - } - } - - private static void fail(String message) { - log.error(message); - throw new BerkeleyStoreException(message); - } - - private static void fail(String message, Exception cause) { - log.error(message, cause); - throw new BerkeleyStoreException(message, cause); - } - - private void withTime(Runnable runnable, CounterType elapsed, CounterType count) { - withTime( - () -> { - runnable.run(); - return null; - }, - elapsed, - count - ); - } - - private T withTime(Supplier supplier, CounterType elapsed, CounterType count) { - final var start = System.nanoTime(); - try { - return supplier.get(); - } finally { - addTime(start, elapsed, count); - } - } - - private void doSave(com.sleepycat.je.Transaction transaction, VerifiedVertexStoreState vertexStoreState) { - var rootId = vertexStoreState.getRoot().getId(); - var vertexKey = entry(rootId.asBytes()); - var vertexEntry = serializeAll(vertexStoreState.toSerialized()); - - try (var cursor = vertexStoreDatabase.openCursor(transaction, null)) { - var status = cursor.getLast(null, null, DEFAULT); - if (status == SUCCESS) { - cursor.delete(); - } - - this.putNoOverwriteOrElseThrow( - cursor, - vertexKey, - vertexEntry, - "Store of root vertex with ID " + rootId - ); - } catch (Exception e) { - transaction.abort(); - fail("Commit of atom failed", e); - } - } - - private static DatabaseEntry toHeaderKey(LedgerProof header) { - if (header.isEndOfEpoch()) { - return toPKey(header.getStateVersion(), header.getEpoch() + 1); - } else { - return toPKey(header.getStateVersion()); - } - } - - private static OptionalLong headerKeyEpoch(DatabaseEntry entry) { - if (entry.getData().length == Long.BYTES) { - return OptionalLong.empty(); - } - - return OptionalLong.of(Longs.fromByteArray(entry.getData(), Long.BYTES)); - } - - private static class BerkeleySubstateCursor implements CloseableCursor { - private final SecondaryDatabase db; - private final com.sleepycat.je.Transaction dbTxn; - private final byte[] indexableBytes; - private final boolean reverse; - private SecondaryCursor cursor; - private OperationStatus status; - - private DatabaseEntry key; - private DatabaseEntry value = entry(); - private DatabaseEntry substateIdBytes = entry(); - - BerkeleySubstateCursor( - com.sleepycat.je.Transaction dbTxn, - SecondaryDatabase db, - byte[] indexableBytes - ) { - this.dbTxn = dbTxn; - this.db = db; - this.indexableBytes = indexableBytes; - this.reverse = indexableBytes[0] == SubstateTypeId.VALIDATOR_STAKE_DATA.id() - || indexableBytes[0] == SubstateTypeId.VALIDATOR_RAKE_COPY.id() - || indexableBytes[0] == SubstateTypeId.VALIDATOR_OWNER_COPY.id() - || indexableBytes[0] == SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id(); - } - - private void open() { - this.cursor = db.openCursor(dbTxn, null); - if (reverse) { - if ((indexableBytes[0] & 0x80) != 0) { - throw new IllegalStateException("Unexpected first byte."); - } - var copy = new BigInteger(indexableBytes); - var firstKey = copy.add(BigInteger.ONE).toByteArray(); - this.key = entry(firstKey); - cursor.getSearchKeyRange(key, substateIdBytes, value, null); - this.status = cursor.getPrev(key, substateIdBytes, value, null); - } else { - this.key = entry(indexableBytes); - this.status = cursor.getSearchKeyRange(key, substateIdBytes, value, null); - } - } - - @Override - public void close() { - if (cursor != null) { - cursor.close(); - } - } - - @Override - public boolean hasNext() { - if (status != SUCCESS) { - return false; - } - - if (indexableBytes.length > key.getData().length) { - return false; - } - - return Arrays.equals(indexableBytes, 0, indexableBytes.length, key.getData(), 0, indexableBytes.length); - } - - @Override - public RawSubstateBytes next() { - if (status != SUCCESS) { - throw new NoSuchElementException(); - } - - var next = new RawSubstateBytes(substateIdBytes.getData(), value.getData()); - if (reverse) { - status = cursor.getPrev(key, substateIdBytes, value, null); - } else { - status = cursor.getNext(key, substateIdBytes, value, null); - } - return next; - } - } - - private CloseableCursor openIndexedCursor(Transaction dbTxn, SubstateIndex index) { - var cursor = new BerkeleySubstateCursor(dbTxn, indexedSubstatesDatabase, index.getPrefix()); - cursor.open(); - return cursor; - } - - private void upParticle( - com.sleepycat.je.Transaction txn, - ByteBuffer bytes, - SubstateId substateId - ) { - byte[] particleKey = substateId.asBytes(); - var value = new DatabaseEntry(bytes.array(), bytes.position(), bytes.remaining()); - substatesDatabase.putNoOverwrite(txn, entry(particleKey), value); - } - - private void downVirtualSubstate(com.sleepycat.je.Transaction txn, SubstateId substateId) { - var particleKey = substateId.asBytes(); - substatesDatabase.putNoOverwrite(txn, entry(particleKey), downEntry()); - } - - private void downSubstate(com.sleepycat.je.Transaction txn, SubstateId substateId) { - var status = substatesDatabase.delete(txn, entry(substateId.asBytes())); - if (status != SUCCESS) { - throw new IllegalStateException("Downing particle does not exist " + substateId); - } - } - - private DatabaseEntry downEntry() { - return entry(new byte[0]); - } - - private REOp entryToSpin(DatabaseEntry e) { - return e.getData().length == 0 ? REOp.DOWN : REOp.UP; - } - - private Optional entryToSubstate(DatabaseEntry e) { - if (entryToSpin(e) == REOp.DOWN) { - return Optional.empty(); - } - return Optional.of(ByteBuffer.wrap(e.getData())); - } - - private void insertIntoMapDatabaseOrFail(com.sleepycat.je.Transaction txn, SystemMapKey mapKey, SubstateId substateId) { - var key = new DatabaseEntry(mapKey.array()); - var value = new DatabaseEntry(substateId.asBytes()); - var result = mapDatabase.putNoOverwrite(txn, key, value); - if (result != SUCCESS) { - throw new IllegalStateException("Unable to insert into map database"); - } - } - - private void deleteFromMapDatabaseOrFail(com.sleepycat.je.Transaction txn, SystemMapKey mapKey) { - var key = new DatabaseEntry(mapKey.array()); - var result = mapDatabase.delete(txn, key); - if (result != SUCCESS) { - throw new IllegalStateException("Unable to delete from map database"); - } - } - - private void executeStateUpdate(com.sleepycat.je.Transaction txn, REStateUpdate stateUpdate) { - if (stateUpdate.isBootUp()) { - var buf = stateUpdate.getStateBuf(); - upParticle(txn, buf, stateUpdate.getId()); - - // FIXME: Superhack - if (stateUpdate.getParsed() instanceof TokenResource) { - var p = (TokenResource) stateUpdate.getParsed(); - var addr = p.getAddr(); - var buf2 = stateUpdate.getStateBuf(); - var value = new DatabaseEntry(buf2.array(), buf2.position(), buf2.remaining()); - resourceDatabase.putNoOverwrite(txn, new DatabaseEntry(addr.getBytes()), value); - } - - // TODO: The following is not required for verification. Only useful for construction - // TODO: and stateful reads, move this into a separate store at some point. - if (stateUpdate.getParsed() instanceof VirtualParent) { - var p = (VirtualParent) stateUpdate.getParsed(); - var typeByte = p.getData()[0]; - var mapKey = SystemMapKey.ofSystem(typeByte); - insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); - } else if (stateUpdate.getParsed() instanceof ResourceData) { - var p = (ResourceData) stateUpdate.getParsed(); - var mapKey = SystemMapKey.ofResourceData( - p.getAddr(), - stateUpdate.typeByte() - ); - insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); - } else if (stateUpdate.getParsed() instanceof ValidatorData) { - var p = (ValidatorData) stateUpdate.getParsed(); - var mapKey = SystemMapKey.ofSystem( - stateUpdate.typeByte(), - p.getValidatorKey().getCompressedBytes() - ); - insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); - } else if (stateUpdate.getParsed() instanceof SystemData) { - var mapKey = SystemMapKey.ofSystem(stateUpdate.typeByte()); - insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); - } - } else if (stateUpdate.isShutDown()) { - if (stateUpdate.getId().isVirtual()) { - downVirtualSubstate(txn, stateUpdate.getId()); - } else { - downSubstate(txn, stateUpdate.getId()); - - if (stateUpdate.getParsed() instanceof ResourceData) { - var p = (ResourceData) stateUpdate.getParsed(); - var mapKey = SystemMapKey.ofResourceData( - p.getAddr(), - stateUpdate.typeByte() - ); - deleteFromMapDatabaseOrFail(txn, mapKey); - } else if (stateUpdate.getParsed() instanceof ValidatorData) { - var p = (ValidatorData) stateUpdate.getParsed(); - var mapKey = SystemMapKey.ofSystem( - stateUpdate.typeByte(), - p.getValidatorKey().getCompressedBytes() - ); - deleteFromMapDatabaseOrFail(txn, mapKey); - } else if (stateUpdate.getParsed() instanceof SystemData) { - var mapKey = SystemMapKey.ofSystem(stateUpdate.typeByte()); - deleteFromMapDatabaseOrFail(txn, mapKey); - } - } - } else { - throw new IllegalStateException("Must bootup or shutdown to update particle: " + stateUpdate); - } - } - - private void doStore(Transaction dbTxn, REProcessedTxn txn) { - final long stateVersion; - final long expectedOffset; - try (var cursor = txnDatabase.openCursor(dbTxn, null)) { - var key = entry(); - var data = entry(); - var status = cursor.getLast(key, data, DEFAULT); - if (status == OperationStatus.NOTFOUND) { - stateVersion = 1; - expectedOffset = 0; - } else { - stateVersion = Longs.fromByteArray(key.getData()) + 1; - long prevOffset = Longs.fromByteArray(data.getData()); - long prevSize = Longs.fromByteArray(data.getData(), Long.BYTES); - expectedOffset = prevOffset + prevSize; - } - } - - try { - // Transaction / Syncing database - var aid = txn.getTxn().getId(); - // Write atom data as soon as possible - var storedSize = txnLog.write(txn.getTxn().getPayload(), expectedOffset); - // Store atom indices - var pKey = toPKey(stateVersion); - var atomPosData = txnEntry(expectedOffset, storedSize, aid); - failIfNotSuccess(txnDatabase.putNoOverwrite(dbTxn, pKey, atomPosData), "Atom write for", aid); - addBytesWrite(atomPosData, pKey); - systemCounters.increment(CounterType.COUNT_BDB_LEDGER_COMMIT); - - // State database - var elapsed = Stopwatch.createStarted(); - int totalCount = txn.getGroupedStateUpdates().stream().mapToInt(List::size).reduce(Integer::sum).orElse(0); - int count = 0; - for (var group : txn.getGroupedStateUpdates()) { - for (var stateUpdate : group) { - if (count > 0 && count % 100000 == 0) { - log.warn( - "engine_store large_state_update: {}/{} elapsed_time={}s", - count, - totalCount, - elapsed.elapsed(TimeUnit.SECONDS) - ); - } - try { - this.executeStateUpdate(dbTxn, stateUpdate); - count++; - } catch (Exception e) { - if (dbTxn != null) { - dbTxn.abort(); - } - throw new BerkeleyStoreException("Unable to store transaction, failed on stateUpdate " + count + ": " + stateUpdate, e); - } - } - } - - additionalStores.forEach(b -> b.process(dbTxn, txn, stateVersion, k -> getInternal(dbTxn, k))); - - } catch (Exception e) { - if (dbTxn != null) { - dbTxn.abort(); - } - throw new BerkeleyStoreException("Unable to store atom:\n" + txn, e); - } - } - - private com.sleepycat.je.Transaction beginTransaction() { - return dbEnv.getEnvironment().beginTransaction(null, null); - } - - private byte[] serialize(T instance) { - return serialization.toDson(instance, Output.PERSIST); - } - - private DatabaseEntry serializeAll(T instance) { - return entry(serialization.toDson(instance, Output.ALL)); - } - - @Override - public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { - - long stateVersion = start.getLedgerHeader().getAccumulatorState().getStateVersion(); - final var startTime = System.nanoTime(); - - com.sleepycat.je.Transaction txn = beginTransaction(); - final LedgerProof nextHeader; - try (var proofCursor = proofDatabase.openCursor(txn, null)) { - final var headerSearchKey = toPKey(stateVersion + 1); - final var headerValue = entry(); - var headerCursorStatus = proofCursor.getSearchKeyRange(headerSearchKey, headerValue, DEFAULT); - if (headerCursorStatus != SUCCESS) { - return null; - } - nextHeader = deserializeOrElseFail(headerValue.getData(), LedgerProof.class); - } finally { - txn.commit(); - } - - final var txns = ImmutableList.builder(); - final var atomSearchKey = toPKey(stateVersion + 1); - final var atomPosData = entry(); - - try (var txnCursor = txnDatabase.openCursor(null, null)) { - int atomCount = (int) (nextHeader.getStateVersion() - stateVersion); - int count = 0; - var atomCursorStatus = txnCursor.getSearchKeyRange(atomSearchKey, atomPosData, DEFAULT); - do { - if (atomCursorStatus != SUCCESS) { - throw new BerkeleyStoreException("Atom database search failure"); - } - var offset = fromByteArray(atomPosData.getData()); - var txnBytes = txnLog.read(offset); - txns.add(Txn.create(txnBytes)); - atomCursorStatus = txnCursor.getNext(atomSearchKey, atomPosData, DEFAULT); - count++; - } while (count < atomCount); - - return VerifiedTxnsAndProof.create(txns.build(), nextHeader); - } catch (IOException e) { - throw new BerkeleyStoreException("Unable to read from atom store.", e); - } finally { - addTime(startTime, CounterType.ELAPSED_BDB_LEDGER_ENTRIES, CounterType.COUNT_BDB_LEDGER_ENTRIES); - } - } - - public List getCommittedTxns(long stateVersion, long limit) { - try (var txnCursor = txnDatabase.openCursor(null, null)) { - var iterator = new Iterator() { - final DatabaseEntry key = new DatabaseEntry(Longs.toByteArray(stateVersion + 1)); - final DatabaseEntry value = new DatabaseEntry(); - OperationStatus status = txnCursor.get(key, value, Get.SEARCH, null) != null ? SUCCESS : OperationStatus.NOTFOUND; - - @Override - public boolean hasNext() { - return status == SUCCESS; - } - - @Override - public Txn next() { - if (status != SUCCESS) { - throw new NoSuchElementException(); - } - var offset = fromByteArray(value.getData()); - byte[] txnBytes; - try { - txnBytes = txnLog.read(offset); - } catch (IOException e) { - throw new IllegalStateException("Unable to read transaction", e); - } - Txn next = Txn.create(txnBytes); - - status = txnCursor.getNext(key, value, null); - return next; - } - }; - return Streams.stream(iterator) - .limit(limit) - .onClose(txnCursor::close) - .toList(); - } - } - - @Override - public Optional loadResource(REAddr addr) { - return loadAddr(null, addr); - } - - private Optional loadAddr(Transaction dbTxn, REAddr addr) { - var buf = ByteBuffer.allocate(128); - buf.put(addr.getBytes()); - var pos = buf.position(); - var key = new DatabaseEntry(buf.array(), 0, pos); - var value = entry(); - var status = resourceDatabase.get(dbTxn, key, value, DEFAULT); - if (status != SUCCESS) { - return Optional.empty(); - } - - return entryToSubstate(value); - } - - private boolean isVirtualDown(Transaction dbTxn, SubstateId substateId) { - var key = entry(substateId.asBytes()); - var value = entry(); - var status = substatesDatabase.get(dbTxn, key, value, DEFAULT); - return status == SUCCESS; - } - - private Optional loadSubstate(Transaction dbTxn, SubstateId substateId) { - var key = entry(substateId.asBytes()); - var value = entry(); - var status = substatesDatabase.get(dbTxn, key, value, DEFAULT); - if (status != SUCCESS) { - return Optional.empty(); - } - - return entryToSubstate(value); - } - - @Override - public Optional getLastProof() { - return withTime(() -> { - try (var proofCursor = proofDatabase.openCursor(null, null)) { - var pKey = entry(); - var value = entry(); - - return Optional.of(proofCursor.getLast(pKey, value, DEFAULT)) - .filter(status -> status == SUCCESS) - .map(status -> { - addBytesRead(value, pKey); - return deserializeOrElseFail(value.getData(), LedgerProof.class); - }); - } - }, CounterType.ELAPSED_BDB_LEDGER_LAST_COMMITTED, CounterType.COUNT_BDB_LEDGER_LAST_COMMITTED); - } - - @Override - public Optional getEpochProof(long epoch) { - var value = entry(); - var status = epochProofDatabase.get(null, toPKey(epoch), value, null); - if (status != SUCCESS) { - return Optional.empty(); - } - - return Optional.of(deserializeOrElseFail(value.getData(), LedgerProof.class)); - } - - private T deserializeOrElseFail(byte[] data, Class c) { - try { - return serialization.fromDson(data, c); - } catch (DeserializeException e) { - throw new BerkeleyStoreException("Could not deserialize", e); - } - } - - private static void failIfNotSuccess(OperationStatus status, String message, Object object) { - if (status != SUCCESS) { - fail(message + " '" + object + "' failed with status " + status); - } - } - - static DatabaseEntry entry(byte[] data) { - return new DatabaseEntry(data); - } - - private static DatabaseEntry entry() { - return new DatabaseEntry(); - } - - private static DatabaseEntry txnEntry(long offset, long size, AID aid) { - var buf = ByteBuffer.allocate(Long.BYTES + Long.BYTES + AID.BYTES); - buf.putLong(offset); - buf.putLong(size); - buf.put(aid.getBytes()); - return entry(buf.array()); - } - - private static DatabaseEntry toPKey(long stateVersion) { - var pKey = new byte[Long.BYTES]; - Longs.copyTo(stateVersion, pKey, 0); - return entry(pKey); - } - - private static DatabaseEntry toPKey(long stateVersion, long epoch) { - var pKey = new byte[Long.BYTES * 2]; - Longs.copyTo(stateVersion, pKey, 0); - Longs.copyTo(epoch, pKey, Long.BYTES); - return entry(pKey); - } - - private static DatabaseEntry entry(AID aid) { - return entry(aid.getBytes()); - } - - private void addTime(long start, CounterType detailTime, CounterType detailCounter) { - final var elapsed = (System.nanoTime() - start + 500L) / 1000L; - systemCounters.add(CounterType.ELAPSED_BDB_LEDGER_TOTAL, elapsed); - systemCounters.increment(CounterType.COUNT_BDB_LEDGER_TOTAL); - systemCounters.add(detailTime, elapsed); - systemCounters.increment(detailCounter); - } - - private void addBytesRead(DatabaseEntry entryA, DatabaseEntry entryB) { - long amount = (long) entryA.getSize() + (long) entryB.getSize(); - systemCounters.add(CounterType.COUNT_BDB_LEDGER_BYTES_READ, amount); - } - - private void addBytesWrite(DatabaseEntry entryA, DatabaseEntry entryB) { - long amount = (long) entryA.getSize() + (long) entryB.getSize(); - systemCounters.add(CounterType.COUNT_BDB_LEDGER_BYTES_WRITE, amount); - } - - private static void executeOrElseThrow(Supplier execute, String errorMessage) { - OperationStatus status = execute.get(); - if (status != SUCCESS) { - throw new BerkeleyStoreException(errorMessage); - } - } - - private void putNoOverwriteOrElseThrow( - Cursor cursor, - DatabaseEntry key, - DatabaseEntry value, - String errorMessage - ) { - this.putNoOverwriteOrElseThrow(cursor, key, value, errorMessage, null); - } - - private void putNoOverwriteOrElseThrow( - Cursor cursor, - DatabaseEntry key, - DatabaseEntry value, - String errorMessage, - CounterType additionalCounterType - ) { - executeOrElseThrow(() -> cursor.putNoOverwrite(key, value), errorMessage); - long amount = (long) key.getSize() + (long) value.getSize(); - systemCounters.add(CounterType.COUNT_BDB_LEDGER_BYTES_WRITE, amount); - if (additionalCounterType != null) { - systemCounters.add(additionalCounterType, amount); - } - } +public final class BerkeleyLedgerEntryStore + implements EngineStore, + ResourceStore, + CommittedReader, + PersistentVertexStore { + private static final Logger log = LogManager.getLogger(); + + private final Serialization serialization; + private final DatabaseEnvironment dbEnv; + private final SystemCounters systemCounters; + private final StoreConfig storeConfig; + + // Engine Store databases + private static final String SUBSTATE_DB_NAME = "radix.substate_db"; + private static final String RESOURCE_DB_NAME = "radix.resource_db"; + private static final String MAP_DB_NAME = "radix.map_db"; + private static final String INDEXED_SUBSTATE_DB_NAME = "radix.indexed_substate_db"; + private Database substatesDatabase; // Write/Delete + private SecondaryDatabase indexedSubstatesDatabase; // Write/Delete + private Database resourceDatabase; // Write-only (Resources are immutable) + private Database mapDatabase; + + // Metadata databases + private static final String VERTEX_STORE_DB_NAME = "radix.vertex_store"; + private static final String TXN_DB_NAME = "radix.txn_db"; + private Database vertexStoreDatabase; // Write/Delete + private Database proofDatabase; // Write/Delete + private SecondaryDatabase epochProofDatabase; + + // Syncing Ledger databases + private static final String PROOF_DB_NAME = "radix.proof_db"; + private static final String EPOCH_PROOF_DB_NAME = "radix.epoch_proof_db"; + private static final String LEDGER_NAME = "radix.ledger"; + private Database txnDatabase; // Txns by state version; Append-only + private AppendLog txnLog; // Atom data append only log + + private final Set additionalStores; + + @Inject + public BerkeleyLedgerEntryStore( + Serialization serialization, + DatabaseEnvironment dbEnv, + StoreConfig storeConfig, + SystemCounters systemCounters, + Set additionalStores) { + this.serialization = Objects.requireNonNull(serialization); + this.dbEnv = Objects.requireNonNull(dbEnv); + this.systemCounters = Objects.requireNonNull(systemCounters); + this.storeConfig = storeConfig; + this.additionalStores = additionalStores; + + this.open(); + } + + public void close() { + safeClose(txnDatabase); + safeClose(resourceDatabase); + safeClose(mapDatabase); + + safeClose(indexedSubstatesDatabase); + safeClose(substatesDatabase); + + safeClose(epochProofDatabase); + safeClose(proofDatabase); + + safeClose(vertexStoreDatabase); + + additionalStores.forEach(BerkeleyAdditionalStore::close); + + if (txnLog != null) { + txnLog.close(); + } + } + + private Transaction createTransaction() { + return withTime( + () -> beginTransaction(), + CounterType.ELAPSED_BDB_LEDGER_CREATE_TX, + CounterType.COUNT_BDB_LEDGER_CREATE_TX); + } + + @Override + public R transaction(TransactionEngineStoreConsumer consumer) + throws RadixEngineException { + var dbTxn = createTransaction(); + try { + var result = + consumer.start( + new EngineStoreInTransaction<>() { + @Override + public void storeTxn(REProcessedTxn txn) { + BerkeleyLedgerEntryStore.this.storeTxn(dbTxn, txn); + } + + @Override + public void storeMetadata(LedgerAndBFTProof metadata) { + BerkeleyLedgerEntryStore.this.storeMetadata(dbTxn, metadata); + } + + @Override + public ByteBuffer verifyVirtualSubstate(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist { + var parent = substateId.getVirtualParent().orElseThrow(); + + var parentState = BerkeleyLedgerEntryStore.this.loadSubstate(dbTxn, parent); + if (parentState.isEmpty()) { + throw new VirtualParentStateDoesNotExist(parent); + } + + var buf = parentState.get(); + if (buf.get() != SubstateTypeId.VIRTUAL_PARENT.id()) { + throw new VirtualParentStateDoesNotExist(parent); + } + buf.position(buf.position() - 1); + + if (BerkeleyLedgerEntryStore.this.isVirtualDown(dbTxn, substateId)) { + throw new VirtualSubstateAlreadyDownException(substateId); + } + + return buf; + } + + @Override + public Optional loadSubstate(SubstateId substateId) { + return BerkeleyLedgerEntryStore.this.loadSubstate(dbTxn, substateId); + } + + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + return BerkeleyLedgerEntryStore.this.openIndexedCursor(dbTxn, index); + } + + @Override + public Optional loadResource(REAddr addr) { + return BerkeleyLedgerEntryStore.this.loadAddr(dbTxn, addr); + } + }); + dbTxn.commit(); + return result; + } catch (Exception e) { + dbTxn.abort(); + throw e; + } + } + + @Override + public LedgerAndBFTProof getMetadata() { + return getLastProof().map(LedgerAndBFTProof::create).orElse(null); + } + + public Stream scanner() { + var cursor = substatesDatabase.openCursor(null, null); + var iterator = + new Iterator() { + final DatabaseEntry key = new DatabaseEntry(); + final DatabaseEntry data = new DatabaseEntry(); + OperationStatus status = cursor.getFirst(key, data, null); + + @Override + public boolean hasNext() { + return status == SUCCESS; + } + + @Override + public RawSubstateBytes next() { + if (status != SUCCESS) { + throw new NoSuchElementException(); + } + var next = new RawSubstateBytes(key.getData(), data.getData()); + status = cursor.getNext(key, data, null); + return next; + } + }; + return Streams.stream(iterator).onClose(cursor::close); + } + + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + return BerkeleyLedgerEntryStore.this.openIndexedCursor(null, index); + } + + private Optional getInternal(Transaction dbTxn, SystemMapKey mapKey) { + var key = new DatabaseEntry(mapKey.array()); + var substateId = new DatabaseEntry(); + var result = mapDatabase.get(dbTxn, key, substateId, null); + if (result != SUCCESS) { + return Optional.empty(); + } + + var substate = loadSubstate(dbTxn, SubstateId.fromBytes(substateId.getData())).orElseThrow(); + var substateBytes = new RawSubstateBytes(substateId.getData(), substate.array()); + return Optional.of(substateBytes); + } + + @Override + public Optional get(SystemMapKey mapKey) { + return getInternal(null, mapKey); + } + + private void storeTxn(Transaction dbTxn, REProcessedTxn txn) { + withTime( + () -> doStore(dbTxn, txn), + CounterType.ELAPSED_BDB_LEDGER_STORE, + CounterType.COUNT_BDB_LEDGER_STORE); + } + + private void storeMetadata(Transaction dbTxn, LedgerAndBFTProof ledgerAndBFTProof) { + var proof = ledgerAndBFTProof.getProof(); + + try (var atomCursor = txnDatabase.openCursor(dbTxn, null)) { + var key = entry(); + var status = atomCursor.getLast(key, null, DEFAULT); + if (status == NOTFOUND) { + throw new IllegalStateException("No atom found before storing proof."); + } + + long lastVersion = Longs.fromByteArray(key.getData()); + if (lastVersion != proof.getStateVersion()) { + throw new IllegalStateException( + "Proof version " + + proof.getStateVersion() + + " does not match last transaction: " + + lastVersion); + } + } + + try (var proofCursor = proofDatabase.openCursor(dbTxn, null)) { + var prevHeaderKey = entry(); + var status = proofCursor.getLast(prevHeaderKey, null, DEFAULT); + // Cannot remove end of epoch proofs + if (status == SUCCESS && headerKeyEpoch(prevHeaderKey).isEmpty()) { + status = proofCursor.getPrev(prevHeaderKey, null, DEFAULT); + if (status == SUCCESS) { + long twoAwayStateVersion = Longs.fromByteArray(prevHeaderKey.getData()); + long versionDiff = proof.getStateVersion() - twoAwayStateVersion; + if (versionDiff <= storeConfig.getMinimumProofBlockSize()) { + executeOrElseThrow(() -> proofCursor.getNext(null, null, DEFAULT), "Missing next."); + executeOrElseThrow(proofCursor::delete, "Could not delete header."); + systemCounters.increment(CounterType.COUNT_BDB_LEDGER_PROOFS_REMOVED); + } + } + } + + final var headerKey = toHeaderKey(proof); + final var headerData = entry(serialize(proof)); + this.putNoOverwriteOrElseThrow( + proofCursor, + headerKey, + headerData, + "Header write failed: " + proof, + CounterType.COUNT_BDB_HEADER_BYTES_WRITE); + + systemCounters.increment(CounterType.COUNT_BDB_LEDGER_PROOFS_ADDED); + } + + ledgerAndBFTProof.vertexStoreState().ifPresent(v -> doSave(dbTxn, v)); + } + + public Optional loadLastVertexStoreState() { + return withTime( + () -> { + try (var cursor = vertexStoreDatabase.openCursor(null, null)) { + var pKey = entry(); + var value = entry(); + var status = cursor.getLast(pKey, value, DEFAULT); + + if (status == SUCCESS) { + addBytesRead(value, pKey); + try { + return Optional.of( + serialization.fromDson(value.getData(), SerializedVertexStoreState.class)); + } catch (DeserializeException e) { + throw new IllegalStateException(e); + } + } else { + return Optional.empty(); + } + } + }, + CounterType.ELAPSED_BDB_LEDGER_LAST_VERTEX, + CounterType.COUNT_BDB_LEDGER_LAST_VERTEX); + } + + @Override + public void save(VerifiedVertexStoreState vertexStoreState) { + withTime( + () -> { + var transaction = beginTransaction(); + doSave(transaction, vertexStoreState); + transaction.commit(); + }, + CounterType.ELAPSED_BDB_LEDGER_SAVE, + CounterType.COUNT_BDB_LEDGER_SAVE); + } + + private void open() { + var primaryConfig = buildPrimaryConfig(); + var rriConfig = buildRriConfig(); + var pendingConfig = buildPendingConfig(); + + try { + // This SuppressWarnings here is valid, as ownership of the underlying + // resource is not changed here, the resource is just accessed. + @SuppressWarnings("resource") + var env = dbEnv.getEnvironment(); + txnDatabase = env.openDatabase(null, TXN_DB_NAME, primaryConfig); + + resourceDatabase = env.openDatabase(null, RESOURCE_DB_NAME, rriConfig); + mapDatabase = env.openDatabase(null, MAP_DB_NAME, rriConfig); + substatesDatabase = env.openDatabase(null, SUBSTATE_DB_NAME, primaryConfig); + + indexedSubstatesDatabase = + env.openSecondaryDatabase( + null, + INDEXED_SUBSTATE_DB_NAME, + substatesDatabase, + (SecondaryConfig) + new SecondaryConfig() + .setKeyCreator( + (secondary, key, data, result) -> { + if (entryToSpin(data) != REOp.UP) { + return false; + } + + var substateTypeId = data.getData()[data.getOffset()]; + final int prefixIndexSize; + if (substateTypeId == SubstateTypeId.TOKENS.id()) { + // Indexing not necessary for verification at the moment but useful + // for construction + + // 0: Type Byte + // 1: Reserved Byte + // 2-37: Account Address + prefixIndexSize = 2 + (1 + ECPublicKey.COMPRESSED_BYTES); + } else if (substateTypeId == SubstateTypeId.STAKE_OWNERSHIP.id()) { + // Indexing not necessary for verification at the moment but useful + // for construction + + // This should have had validator keys and account addresses switched + // so that + // prefix indexing could be done against account addresses rather than + // validators + // so that actions like "Unstake Everything" could be implemented and + // queries against + // accounts. A later to do... + + // 0: Type Byte + // 1: Reserved Byte + // 2-36: Validator Key + // 37-69: Account Address + prefixIndexSize = + 2 + + ECPublicKey.COMPRESSED_BYTES + + (1 + ECPublicKey.COMPRESSED_BYTES); + } else if (substateTypeId == SubstateTypeId.EXITING_STAKE.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2-5: Epoch + // 6-40: Validator Key + // 41-73: Account Address + prefixIndexSize = + 2 + + Long.BYTES + + ECPublicKey.COMPRESSED_BYTES + + (1 + ECPublicKey.COMPRESSED_BYTES); + } else if (substateTypeId == SubstateTypeId.PREPARED_STAKE.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2-36: Validator Key + // 37-69: Account Address + prefixIndexSize = + 2 + + ECPublicKey.COMPRESSED_BYTES + + (1 + ECPublicKey.COMPRESSED_BYTES); + } else if (substateTypeId == SubstateTypeId.PREPARED_UNSTAKE.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2-36: Validator Key + // 37-69: Account Address + prefixIndexSize = + 2 + + ECPublicKey.COMPRESSED_BYTES + + (1 + ECPublicKey.COMPRESSED_BYTES); + } else if (substateTypeId == SubstateTypeId.VALIDATOR_OWNER_COPY.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2: Optional flag + // 3-6: Epoch + // 7-41: Validator Key + prefixIndexSize = 3 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES; + } else if (substateTypeId + == SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2: Optional flag + // 3-6: Epoch + // 7-41: Validator Key + prefixIndexSize = 3 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES; + } else if (substateTypeId == SubstateTypeId.VALIDATOR_RAKE_COPY.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2: Optional flag + // 3-6: Epoch + // 7-41: Validator Key + prefixIndexSize = 3 + Long.BYTES + ECPublicKey.COMPRESSED_BYTES; + } else if (substateTypeId == SubstateTypeId.VALIDATOR_STAKE_DATA.id()) { + // 0: Type Byte + // 1: Reserved Byte + // 2: Registered Byte + // 3-34: Stake amount + // 35-67: Validator key + prefixIndexSize = 3 + UInt256.BYTES + ECPublicKey.COMPRESSED_BYTES; + } else { + // 0: Type Byte + prefixIndexSize = 1; + } + // Index by substate type + result.setData(data.getData(), data.getOffset(), prefixIndexSize); + return true; + }) + .setBtreeComparator(lexicographicalComparator()) + .setSortedDuplicates(true) + .setAllowCreate(true) + .setTransactional(true)); + + proofDatabase = env.openDatabase(null, PROOF_DB_NAME, primaryConfig); + vertexStoreDatabase = env.openDatabase(null, VERTEX_STORE_DB_NAME, pendingConfig); + epochProofDatabase = + env.openSecondaryDatabase( + null, EPOCH_PROOF_DB_NAME, proofDatabase, buildEpochProofConfig()); + + txnLog = + AppendLog.openCompressed( + new File(env.getHome(), LEDGER_NAME).getAbsolutePath(), systemCounters); + } catch (Exception e) { + throw new BerkeleyStoreException("Error while opening databases", e); + } + + this.additionalStores.forEach(b -> b.open(dbEnv)); + + if (System.getProperty("db.check_integrity", "1").equals("1")) { + // TODO implement integrity check + // TODO perhaps we should implement recovery instead? + // TODO recovering should be integrated with recovering of ClientApiStore + } + } + + private SecondaryConfig buildEpochProofConfig() { + return (SecondaryConfig) + new SecondaryConfig() + .setKeyCreator( + (secondary, key, data, result) -> { + OptionalLong epoch = headerKeyEpoch(key); + epoch.ifPresent(e -> result.setData(Longs.toByteArray(e))); + return epoch.isPresent(); + }) + .setAllowCreate(true) + .setTransactional(true); + } + + private DatabaseConfig buildPendingConfig() { + return new DatabaseConfig() + .setBtreeComparator(lexicographicalComparator()) + .setAllowCreate(true) + .setTransactional(true); + } + + private DatabaseConfig buildPrimaryConfig() { + return new DatabaseConfig() + .setAllowCreate(true) + .setTransactional(true) + .setKeyPrefixing(true) + .setBtreeComparator(lexicographicalComparator()); + } + + private DatabaseConfig buildRriConfig() { + return new DatabaseConfig() + .setAllowCreate(true) + .setTransactional(true) + .setKeyPrefixing(true) + .setBtreeComparator(lexicographicalComparator()); + } + + private static void safeClose(Database database) { + if (database != null) { + database.close(); + } + } + + private static void fail(String message) { + log.error(message); + throw new BerkeleyStoreException(message); + } + + private static void fail(String message, Exception cause) { + log.error(message, cause); + throw new BerkeleyStoreException(message, cause); + } + + private void withTime(Runnable runnable, CounterType elapsed, CounterType count) { + withTime( + () -> { + runnable.run(); + return null; + }, + elapsed, + count); + } + + private T withTime(Supplier supplier, CounterType elapsed, CounterType count) { + final var start = System.nanoTime(); + try { + return supplier.get(); + } finally { + addTime(start, elapsed, count); + } + } + + private void doSave( + com.sleepycat.je.Transaction transaction, VerifiedVertexStoreState vertexStoreState) { + var rootId = vertexStoreState.getRoot().getId(); + var vertexKey = entry(rootId.asBytes()); + var vertexEntry = serializeAll(vertexStoreState.toSerialized()); + + try (var cursor = vertexStoreDatabase.openCursor(transaction, null)) { + var status = cursor.getLast(null, null, DEFAULT); + if (status == SUCCESS) { + cursor.delete(); + } + + this.putNoOverwriteOrElseThrow( + cursor, vertexKey, vertexEntry, "Store of root vertex with ID " + rootId); + } catch (Exception e) { + transaction.abort(); + fail("Commit of atom failed", e); + } + } + + private static DatabaseEntry toHeaderKey(LedgerProof header) { + if (header.isEndOfEpoch()) { + return toPKey(header.getStateVersion(), header.getEpoch() + 1); + } else { + return toPKey(header.getStateVersion()); + } + } + + private static OptionalLong headerKeyEpoch(DatabaseEntry entry) { + if (entry.getData().length == Long.BYTES) { + return OptionalLong.empty(); + } + + return OptionalLong.of(Longs.fromByteArray(entry.getData(), Long.BYTES)); + } + + private static class BerkeleySubstateCursor implements CloseableCursor { + private final SecondaryDatabase db; + private final com.sleepycat.je.Transaction dbTxn; + private final byte[] indexableBytes; + private final boolean reverse; + private SecondaryCursor cursor; + private OperationStatus status; + + private DatabaseEntry key; + private DatabaseEntry value = entry(); + private DatabaseEntry substateIdBytes = entry(); + + BerkeleySubstateCursor( + com.sleepycat.je.Transaction dbTxn, SecondaryDatabase db, byte[] indexableBytes) { + this.dbTxn = dbTxn; + this.db = db; + this.indexableBytes = indexableBytes; + this.reverse = + indexableBytes[0] == SubstateTypeId.VALIDATOR_STAKE_DATA.id() + || indexableBytes[0] == SubstateTypeId.VALIDATOR_RAKE_COPY.id() + || indexableBytes[0] == SubstateTypeId.VALIDATOR_OWNER_COPY.id() + || indexableBytes[0] == SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id(); + } + + private void open() { + this.cursor = db.openCursor(dbTxn, null); + if (reverse) { + if ((indexableBytes[0] & 0x80) != 0) { + throw new IllegalStateException("Unexpected first byte."); + } + var copy = new BigInteger(indexableBytes); + var firstKey = copy.add(BigInteger.ONE).toByteArray(); + this.key = entry(firstKey); + cursor.getSearchKeyRange(key, substateIdBytes, value, null); + this.status = cursor.getPrev(key, substateIdBytes, value, null); + } else { + this.key = entry(indexableBytes); + this.status = cursor.getSearchKeyRange(key, substateIdBytes, value, null); + } + } + + @Override + public void close() { + if (cursor != null) { + cursor.close(); + } + } + + @Override + public boolean hasNext() { + if (status != SUCCESS) { + return false; + } + + if (indexableBytes.length > key.getData().length) { + return false; + } + + return Arrays.equals( + indexableBytes, 0, indexableBytes.length, key.getData(), 0, indexableBytes.length); + } + + @Override + public RawSubstateBytes next() { + if (status != SUCCESS) { + throw new NoSuchElementException(); + } + + var next = new RawSubstateBytes(substateIdBytes.getData(), value.getData()); + if (reverse) { + status = cursor.getPrev(key, substateIdBytes, value, null); + } else { + status = cursor.getNext(key, substateIdBytes, value, null); + } + return next; + } + } + + private CloseableCursor openIndexedCursor( + Transaction dbTxn, SubstateIndex index) { + var cursor = new BerkeleySubstateCursor(dbTxn, indexedSubstatesDatabase, index.getPrefix()); + cursor.open(); + return cursor; + } + + private void upParticle( + com.sleepycat.je.Transaction txn, ByteBuffer bytes, SubstateId substateId) { + byte[] particleKey = substateId.asBytes(); + var value = new DatabaseEntry(bytes.array(), bytes.position(), bytes.remaining()); + substatesDatabase.putNoOverwrite(txn, entry(particleKey), value); + } + + private void downVirtualSubstate(com.sleepycat.je.Transaction txn, SubstateId substateId) { + var particleKey = substateId.asBytes(); + substatesDatabase.putNoOverwrite(txn, entry(particleKey), downEntry()); + } + + private void downSubstate(com.sleepycat.je.Transaction txn, SubstateId substateId) { + var status = substatesDatabase.delete(txn, entry(substateId.asBytes())); + if (status != SUCCESS) { + throw new IllegalStateException("Downing particle does not exist " + substateId); + } + } + + private DatabaseEntry downEntry() { + return entry(new byte[0]); + } + + private REOp entryToSpin(DatabaseEntry e) { + return e.getData().length == 0 ? REOp.DOWN : REOp.UP; + } + + private Optional entryToSubstate(DatabaseEntry e) { + if (entryToSpin(e) == REOp.DOWN) { + return Optional.empty(); + } + return Optional.of(ByteBuffer.wrap(e.getData())); + } + + private void insertIntoMapDatabaseOrFail( + com.sleepycat.je.Transaction txn, SystemMapKey mapKey, SubstateId substateId) { + var key = new DatabaseEntry(mapKey.array()); + var value = new DatabaseEntry(substateId.asBytes()); + var result = mapDatabase.putNoOverwrite(txn, key, value); + if (result != SUCCESS) { + throw new IllegalStateException("Unable to insert into map database"); + } + } + + private void deleteFromMapDatabaseOrFail(com.sleepycat.je.Transaction txn, SystemMapKey mapKey) { + var key = new DatabaseEntry(mapKey.array()); + var result = mapDatabase.delete(txn, key); + if (result != SUCCESS) { + throw new IllegalStateException("Unable to delete from map database"); + } + } + + private void executeStateUpdate(com.sleepycat.je.Transaction txn, REStateUpdate stateUpdate) { + if (stateUpdate.isBootUp()) { + var buf = stateUpdate.getStateBuf(); + upParticle(txn, buf, stateUpdate.getId()); + + // FIXME: Superhack + if (stateUpdate.getParsed() instanceof TokenResource) { + var p = (TokenResource) stateUpdate.getParsed(); + var addr = p.getAddr(); + var buf2 = stateUpdate.getStateBuf(); + var value = new DatabaseEntry(buf2.array(), buf2.position(), buf2.remaining()); + resourceDatabase.putNoOverwrite(txn, new DatabaseEntry(addr.getBytes()), value); + } + + // TODO: The following is not required for verification. Only useful for construction + // TODO: and stateful reads, move this into a separate store at some point. + if (stateUpdate.getParsed() instanceof VirtualParent) { + var p = (VirtualParent) stateUpdate.getParsed(); + var typeByte = p.getData()[0]; + var mapKey = SystemMapKey.ofSystem(typeByte); + insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); + } else if (stateUpdate.getParsed() instanceof ResourceData) { + var p = (ResourceData) stateUpdate.getParsed(); + var mapKey = SystemMapKey.ofResourceData(p.getAddr(), stateUpdate.typeByte()); + insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); + } else if (stateUpdate.getParsed() instanceof ValidatorData) { + var p = (ValidatorData) stateUpdate.getParsed(); + var mapKey = + SystemMapKey.ofSystem(stateUpdate.typeByte(), p.getValidatorKey().getCompressedBytes()); + insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); + } else if (stateUpdate.getParsed() instanceof SystemData) { + var mapKey = SystemMapKey.ofSystem(stateUpdate.typeByte()); + insertIntoMapDatabaseOrFail(txn, mapKey, stateUpdate.getId()); + } + } else if (stateUpdate.isShutDown()) { + if (stateUpdate.getId().isVirtual()) { + downVirtualSubstate(txn, stateUpdate.getId()); + } else { + downSubstate(txn, stateUpdate.getId()); + + if (stateUpdate.getParsed() instanceof ResourceData) { + var p = (ResourceData) stateUpdate.getParsed(); + var mapKey = SystemMapKey.ofResourceData(p.getAddr(), stateUpdate.typeByte()); + deleteFromMapDatabaseOrFail(txn, mapKey); + } else if (stateUpdate.getParsed() instanceof ValidatorData) { + var p = (ValidatorData) stateUpdate.getParsed(); + var mapKey = + SystemMapKey.ofSystem( + stateUpdate.typeByte(), p.getValidatorKey().getCompressedBytes()); + deleteFromMapDatabaseOrFail(txn, mapKey); + } else if (stateUpdate.getParsed() instanceof SystemData) { + var mapKey = SystemMapKey.ofSystem(stateUpdate.typeByte()); + deleteFromMapDatabaseOrFail(txn, mapKey); + } + } + } else { + throw new IllegalStateException("Must bootup or shutdown to update particle: " + stateUpdate); + } + } + + private void doStore(Transaction dbTxn, REProcessedTxn txn) { + final long stateVersion; + final long expectedOffset; + try (var cursor = txnDatabase.openCursor(dbTxn, null)) { + var key = entry(); + var data = entry(); + var status = cursor.getLast(key, data, DEFAULT); + if (status == OperationStatus.NOTFOUND) { + stateVersion = 1; + expectedOffset = 0; + } else { + stateVersion = Longs.fromByteArray(key.getData()) + 1; + long prevOffset = Longs.fromByteArray(data.getData()); + long prevSize = Longs.fromByteArray(data.getData(), Long.BYTES); + expectedOffset = prevOffset + prevSize; + } + } + + try { + // Transaction / Syncing database + var aid = txn.getTxn().getId(); + // Write atom data as soon as possible + var storedSize = txnLog.write(txn.getTxn().getPayload(), expectedOffset); + // Store atom indices + var pKey = toPKey(stateVersion); + var atomPosData = txnEntry(expectedOffset, storedSize, aid); + failIfNotSuccess(txnDatabase.putNoOverwrite(dbTxn, pKey, atomPosData), "Atom write for", aid); + addBytesWrite(atomPosData, pKey); + systemCounters.increment(CounterType.COUNT_BDB_LEDGER_COMMIT); + + // State database + var elapsed = Stopwatch.createStarted(); + int totalCount = + txn.getGroupedStateUpdates().stream().mapToInt(List::size).reduce(Integer::sum).orElse(0); + int count = 0; + for (var group : txn.getGroupedStateUpdates()) { + for (var stateUpdate : group) { + if (count > 0 && count % 100000 == 0) { + log.warn( + "engine_store large_state_update: {}/{} elapsed_time={}s", + count, + totalCount, + elapsed.elapsed(TimeUnit.SECONDS)); + } + try { + this.executeStateUpdate(dbTxn, stateUpdate); + count++; + } catch (Exception e) { + if (dbTxn != null) { + dbTxn.abort(); + } + throw new BerkeleyStoreException( + "Unable to store transaction, failed on stateUpdate " + count + ": " + stateUpdate, + e); + } + } + } + + additionalStores.forEach( + b -> b.process(dbTxn, txn, stateVersion, k -> getInternal(dbTxn, k))); + + } catch (Exception e) { + if (dbTxn != null) { + dbTxn.abort(); + } + throw new BerkeleyStoreException("Unable to store atom:\n" + txn, e); + } + } + + private com.sleepycat.je.Transaction beginTransaction() { + return dbEnv.getEnvironment().beginTransaction(null, null); + } + + private byte[] serialize(T instance) { + return serialization.toDson(instance, Output.PERSIST); + } + + private DatabaseEntry serializeAll(T instance) { + return entry(serialization.toDson(instance, Output.ALL)); + } + + @Override + public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { + + long stateVersion = start.getLedgerHeader().getAccumulatorState().getStateVersion(); + final var startTime = System.nanoTime(); + + com.sleepycat.je.Transaction txn = beginTransaction(); + final LedgerProof nextHeader; + try (var proofCursor = proofDatabase.openCursor(txn, null)) { + final var headerSearchKey = toPKey(stateVersion + 1); + final var headerValue = entry(); + var headerCursorStatus = proofCursor.getSearchKeyRange(headerSearchKey, headerValue, DEFAULT); + if (headerCursorStatus != SUCCESS) { + return null; + } + nextHeader = deserializeOrElseFail(headerValue.getData(), LedgerProof.class); + } finally { + txn.commit(); + } + + final var txns = ImmutableList.builder(); + final var atomSearchKey = toPKey(stateVersion + 1); + final var atomPosData = entry(); + + try (var txnCursor = txnDatabase.openCursor(null, null)) { + int atomCount = (int) (nextHeader.getStateVersion() - stateVersion); + int count = 0; + var atomCursorStatus = txnCursor.getSearchKeyRange(atomSearchKey, atomPosData, DEFAULT); + do { + if (atomCursorStatus != SUCCESS) { + throw new BerkeleyStoreException("Atom database search failure"); + } + var offset = fromByteArray(atomPosData.getData()); + var txnBytes = txnLog.read(offset); + txns.add(Txn.create(txnBytes)); + atomCursorStatus = txnCursor.getNext(atomSearchKey, atomPosData, DEFAULT); + count++; + } while (count < atomCount); + + return VerifiedTxnsAndProof.create(txns.build(), nextHeader); + } catch (IOException e) { + throw new BerkeleyStoreException("Unable to read from atom store.", e); + } finally { + addTime( + startTime, CounterType.ELAPSED_BDB_LEDGER_ENTRIES, CounterType.COUNT_BDB_LEDGER_ENTRIES); + } + } + + public List getCommittedTxns(long stateVersion, long limit) { + try (var txnCursor = txnDatabase.openCursor(null, null)) { + var iterator = + new Iterator() { + final DatabaseEntry key = new DatabaseEntry(Longs.toByteArray(stateVersion + 1)); + final DatabaseEntry value = new DatabaseEntry(); + OperationStatus status = + txnCursor.get(key, value, Get.SEARCH, null) != null + ? SUCCESS + : OperationStatus.NOTFOUND; + + @Override + public boolean hasNext() { + return status == SUCCESS; + } + + @Override + public Txn next() { + if (status != SUCCESS) { + throw new NoSuchElementException(); + } + var offset = fromByteArray(value.getData()); + byte[] txnBytes; + try { + txnBytes = txnLog.read(offset); + } catch (IOException e) { + throw new IllegalStateException("Unable to read transaction", e); + } + Txn next = Txn.create(txnBytes); + + status = txnCursor.getNext(key, value, null); + return next; + } + }; + return Streams.stream(iterator).limit(limit).onClose(txnCursor::close).toList(); + } + } + + @Override + public Optional loadResource(REAddr addr) { + return loadAddr(null, addr); + } + + private Optional loadAddr(Transaction dbTxn, REAddr addr) { + var buf = ByteBuffer.allocate(128); + buf.put(addr.getBytes()); + var pos = buf.position(); + var key = new DatabaseEntry(buf.array(), 0, pos); + var value = entry(); + var status = resourceDatabase.get(dbTxn, key, value, DEFAULT); + if (status != SUCCESS) { + return Optional.empty(); + } + + return entryToSubstate(value); + } + + private boolean isVirtualDown(Transaction dbTxn, SubstateId substateId) { + var key = entry(substateId.asBytes()); + var value = entry(); + var status = substatesDatabase.get(dbTxn, key, value, DEFAULT); + return status == SUCCESS; + } + + private Optional loadSubstate(Transaction dbTxn, SubstateId substateId) { + var key = entry(substateId.asBytes()); + var value = entry(); + var status = substatesDatabase.get(dbTxn, key, value, DEFAULT); + if (status != SUCCESS) { + return Optional.empty(); + } + + return entryToSubstate(value); + } + + @Override + public Optional getLastProof() { + return withTime( + () -> { + try (var proofCursor = proofDatabase.openCursor(null, null)) { + var pKey = entry(); + var value = entry(); + + return Optional.of(proofCursor.getLast(pKey, value, DEFAULT)) + .filter(status -> status == SUCCESS) + .map( + status -> { + addBytesRead(value, pKey); + return deserializeOrElseFail(value.getData(), LedgerProof.class); + }); + } + }, + CounterType.ELAPSED_BDB_LEDGER_LAST_COMMITTED, + CounterType.COUNT_BDB_LEDGER_LAST_COMMITTED); + } + + @Override + public Optional getEpochProof(long epoch) { + var value = entry(); + var status = epochProofDatabase.get(null, toPKey(epoch), value, null); + if (status != SUCCESS) { + return Optional.empty(); + } + + return Optional.of(deserializeOrElseFail(value.getData(), LedgerProof.class)); + } + + private T deserializeOrElseFail(byte[] data, Class c) { + try { + return serialization.fromDson(data, c); + } catch (DeserializeException e) { + throw new BerkeleyStoreException("Could not deserialize", e); + } + } + + private static void failIfNotSuccess(OperationStatus status, String message, Object object) { + if (status != SUCCESS) { + fail(message + " '" + object + "' failed with status " + status); + } + } + + static DatabaseEntry entry(byte[] data) { + return new DatabaseEntry(data); + } + + private static DatabaseEntry entry() { + return new DatabaseEntry(); + } + + private static DatabaseEntry txnEntry(long offset, long size, AID aid) { + var buf = ByteBuffer.allocate(Long.BYTES + Long.BYTES + AID.BYTES); + buf.putLong(offset); + buf.putLong(size); + buf.put(aid.getBytes()); + return entry(buf.array()); + } + + private static DatabaseEntry toPKey(long stateVersion) { + var pKey = new byte[Long.BYTES]; + Longs.copyTo(stateVersion, pKey, 0); + return entry(pKey); + } + + private static DatabaseEntry toPKey(long stateVersion, long epoch) { + var pKey = new byte[Long.BYTES * 2]; + Longs.copyTo(stateVersion, pKey, 0); + Longs.copyTo(epoch, pKey, Long.BYTES); + return entry(pKey); + } + + private static DatabaseEntry entry(AID aid) { + return entry(aid.getBytes()); + } + + private void addTime(long start, CounterType detailTime, CounterType detailCounter) { + final var elapsed = (System.nanoTime() - start + 500L) / 1000L; + systemCounters.add(CounterType.ELAPSED_BDB_LEDGER_TOTAL, elapsed); + systemCounters.increment(CounterType.COUNT_BDB_LEDGER_TOTAL); + systemCounters.add(detailTime, elapsed); + systemCounters.increment(detailCounter); + } + + private void addBytesRead(DatabaseEntry entryA, DatabaseEntry entryB) { + long amount = (long) entryA.getSize() + (long) entryB.getSize(); + systemCounters.add(CounterType.COUNT_BDB_LEDGER_BYTES_READ, amount); + } + + private void addBytesWrite(DatabaseEntry entryA, DatabaseEntry entryB) { + long amount = (long) entryA.getSize() + (long) entryB.getSize(); + systemCounters.add(CounterType.COUNT_BDB_LEDGER_BYTES_WRITE, amount); + } + + private static void executeOrElseThrow(Supplier execute, String errorMessage) { + OperationStatus status = execute.get(); + if (status != SUCCESS) { + throw new BerkeleyStoreException(errorMessage); + } + } + + private void putNoOverwriteOrElseThrow( + Cursor cursor, DatabaseEntry key, DatabaseEntry value, String errorMessage) { + this.putNoOverwriteOrElseThrow(cursor, key, value, errorMessage, null); + } + + private void putNoOverwriteOrElseThrow( + Cursor cursor, + DatabaseEntry key, + DatabaseEntry value, + String errorMessage, + CounterType additionalCounterType) { + executeOrElseThrow(() -> cursor.putNoOverwrite(key, value), errorMessage); + long amount = (long) key.getSize() + (long) value.getSize(); + systemCounters.add(CounterType.COUNT_BDB_LEDGER_BYTES_WRITE, amount); + if (additionalCounterType != null) { + systemCounters.add(additionalCounterType, amount); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStore.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStore.java index 40e106c7c4..0e3fd06f67 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStore.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStore.java @@ -1,284 +1,279 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.store.berkeley; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.inject.Inject; -import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.safety.PersistentSafetyStateStore; -import com.radixdlt.consensus.safety.SafetyState; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.serialization.DeserializeException; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.Serialization; -import com.radixdlt.store.DatabaseEnvironment; -import com.radixdlt.utils.Longs; -import com.sleepycat.je.Cursor; -import com.sleepycat.je.Database; -import com.sleepycat.je.DatabaseConfig; -import com.sleepycat.je.DatabaseEntry; -import com.sleepycat.je.Environment; -import com.sleepycat.je.LockMode; -import com.sleepycat.je.OperationStatus; - -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Store which persists state required to preserve the networks safety in case of a - * node restart. - */ -public final class BerkeleySafetyStateStore implements PersistentSafetyStateStore { - private static final String SAFETY_STORE_NAME = "safety_store"; - private static final Logger logger = LogManager.getLogger(); - private static final long UPPER_THRESHOLD = 1000; - private static final long LOWER_THRESHOLD = 10; - - private final DatabaseEnvironment dbEnv; - private final Database safetyStore; - private final SystemCounters systemCounters; - private final AtomicLong cleanupCounter = new AtomicLong(); - private final Serialization serialization; - - @Inject - public BerkeleySafetyStateStore( - DatabaseEnvironment dbEnv, - Serialization serialization, - SystemCounters systemCounters - ) { - this.dbEnv = Objects.requireNonNull(dbEnv, "dbEnv is required"); - this.serialization = Objects.requireNonNull(serialization); - - this.safetyStore = this.open(); - this.systemCounters = Objects.requireNonNull(systemCounters); - - if (Boolean.valueOf(System.getProperty("db.check_integrity", "true"))) { - // TODO implement integrity check - } - } - - private void fail(String message) { - logger.error(message); - throw new BerkeleyStoreException(message); - } - - private void fail(String message, Exception cause) { - logger.error(message, cause); - throw new BerkeleyStoreException(message, cause); - } - - private Database open() { - DatabaseConfig primaryConfig = new DatabaseConfig(); - primaryConfig.setAllowCreate(true); - primaryConfig.setTransactional(true); - - try { - // This SuppressWarnings here is valid, as ownership of the underlying - // resource is not changed here, the resource is just accessed. - @SuppressWarnings("resource") - Environment env = this.dbEnv.getEnvironment(); - return env.openDatabase(null, SAFETY_STORE_NAME, primaryConfig); - } catch (Exception e) { - throw new BerkeleyStoreException("Error while opening database", e); - } - } - - @Override - public void close() { - if (this.safetyStore != null) { - this.safetyStore.close(); - } - } - - @Override - public Optional get() { - final var start = System.nanoTime(); - try (com.sleepycat.je.Cursor cursor = this.safetyStore.openCursor(null, null)) { - DatabaseEntry pKey = new DatabaseEntry(); - DatabaseEntry value = new DatabaseEntry(); - OperationStatus status = cursor.getLast(pKey, value, LockMode.DEFAULT); - if (status == OperationStatus.SUCCESS) { - addBytesRead(pKey.getSize() + value.getSize()); - try { - final SafetyState deserializedState = - serialization.fromDson(value.getData(), SafetyState.class); - return Optional.of(deserializedState); - } catch (DeserializeException ex) { - logger.error("Failed to deserialize persisted SafetyState", ex); - return Optional.empty(); - } - } else { - return Optional.empty(); - } - } finally { - addTime(start); - } - } - - @Override - public void commitState(SafetyState safetyState) { - this.systemCounters.increment(CounterType.PERSISTENCE_SAFETY_STORE_SAVES); - - final var start = System.nanoTime(); - - final var transaction = dbEnv.getEnvironment().beginTransaction(null, null); - try { - final byte[] serializedState = serialization.toDson(safetyState, DsonOutput.Output.PERSIST); - - final DatabaseEntry key = new DatabaseEntry(keyFor(safetyState)); - final DatabaseEntry data = new DatabaseEntry(serializedState); - - final OperationStatus status = this.safetyStore.put(transaction, key, data); - if (status != OperationStatus.SUCCESS) { - fail("Database returned status " + status + " for put operation"); - } else { - addBytesWrite(key.getSize() + data.getSize()); - } - - transaction.commit(); - - cleanupUnused(); - } catch (Exception e) { - transaction.abort(); - fail("Error while storing safety state for " + safetyState, e); - } finally { - addTime(start); - } - } - - private void cleanupUnused() { - if (cleanupCounter.incrementAndGet() % UPPER_THRESHOLD != 0) { - return; - } - - final var transaction = dbEnv.getEnvironment().beginTransaction(null, null); - try { - long count = safetyStore.count(); - - if (count < UPPER_THRESHOLD) { - transaction.abort(); - return; - } - - try (Cursor cursor = this.safetyStore.openCursor(transaction, null)) { - DatabaseEntry key = new DatabaseEntry(); - DatabaseEntry value = new DatabaseEntry(); - - if (cursor.getFirst(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) { - if (cursor.delete() != OperationStatus.SUCCESS) { - transaction.abort(); - return; - } - addBytesRead(key.getSize() + value.getSize()); - count--; - } - - while (cursor.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS && count > LOWER_THRESHOLD) { - if (cursor.delete() != OperationStatus.SUCCESS) { - transaction.abort(); - return; - } - addBytesRead(key.getSize() + value.getSize()); - count--; - } - } - - transaction.commit(); - } catch (Exception e) { - transaction.abort(); - fail("Error while clearing unused states", e); - } - } - - private void addTime(long start) { - final var elapsed = (System.nanoTime() - start + 500L) / 1000L; - this.systemCounters.add(CounterType.ELAPSED_BDB_SAFETY_STATE, elapsed); - this.systemCounters.increment(CounterType.COUNT_BDB_SAFETY_STATE_TOTAL); - } - - private void addBytesRead(int bytesRead) { - this.systemCounters.add(CounterType.COUNT_BDB_SAFETY_STATE_BYTES_READ, bytesRead); - } - - private void addBytesWrite(int bytesWrite) { - this.systemCounters.add(CounterType.COUNT_BDB_SAFETY_STATE_BYTES_WRITE, bytesWrite); - } - - private byte[] keyFor(SafetyState safetyState) { - long epoch = safetyState.getLastVote().map(Vote::getEpoch).orElse(0L); - long view = safetyState.getLastVote().map(Vote::getView).orElse(View.genesis()).number(); - - byte[] keyBytes = new byte[Long.BYTES * 2]; - Longs.copyTo(epoch, keyBytes, 0); - Longs.copyTo(view, keyBytes, Long.BYTES); - - return keyBytes; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.store.berkeley; + +import com.google.inject.Inject; +import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.safety.PersistentSafetyStateStore; +import com.radixdlt.consensus.safety.SafetyState; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.serialization.DeserializeException; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.Serialization; +import com.radixdlt.store.DatabaseEnvironment; +import com.radixdlt.utils.Longs; +import com.sleepycat.je.Cursor; +import com.sleepycat.je.Database; +import com.sleepycat.je.DatabaseConfig; +import com.sleepycat.je.DatabaseEntry; +import com.sleepycat.je.Environment; +import com.sleepycat.je.LockMode; +import com.sleepycat.je.OperationStatus; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Store which persists state required to preserve the networks safety in case of a node restart. + */ +public final class BerkeleySafetyStateStore implements PersistentSafetyStateStore { + private static final String SAFETY_STORE_NAME = "safety_store"; + private static final Logger logger = LogManager.getLogger(); + private static final long UPPER_THRESHOLD = 1000; + private static final long LOWER_THRESHOLD = 10; + + private final DatabaseEnvironment dbEnv; + private final Database safetyStore; + private final SystemCounters systemCounters; + private final AtomicLong cleanupCounter = new AtomicLong(); + private final Serialization serialization; + + @Inject + public BerkeleySafetyStateStore( + DatabaseEnvironment dbEnv, Serialization serialization, SystemCounters systemCounters) { + this.dbEnv = Objects.requireNonNull(dbEnv, "dbEnv is required"); + this.serialization = Objects.requireNonNull(serialization); + + this.safetyStore = this.open(); + this.systemCounters = Objects.requireNonNull(systemCounters); + + if (Boolean.valueOf(System.getProperty("db.check_integrity", "true"))) { + // TODO implement integrity check + } + } + + private void fail(String message) { + logger.error(message); + throw new BerkeleyStoreException(message); + } + + private void fail(String message, Exception cause) { + logger.error(message, cause); + throw new BerkeleyStoreException(message, cause); + } + + private Database open() { + DatabaseConfig primaryConfig = new DatabaseConfig(); + primaryConfig.setAllowCreate(true); + primaryConfig.setTransactional(true); + + try { + // This SuppressWarnings here is valid, as ownership of the underlying + // resource is not changed here, the resource is just accessed. + @SuppressWarnings("resource") + Environment env = this.dbEnv.getEnvironment(); + return env.openDatabase(null, SAFETY_STORE_NAME, primaryConfig); + } catch (Exception e) { + throw new BerkeleyStoreException("Error while opening database", e); + } + } + + @Override + public void close() { + if (this.safetyStore != null) { + this.safetyStore.close(); + } + } + + @Override + public Optional get() { + final var start = System.nanoTime(); + try (com.sleepycat.je.Cursor cursor = this.safetyStore.openCursor(null, null)) { + DatabaseEntry pKey = new DatabaseEntry(); + DatabaseEntry value = new DatabaseEntry(); + OperationStatus status = cursor.getLast(pKey, value, LockMode.DEFAULT); + if (status == OperationStatus.SUCCESS) { + addBytesRead(pKey.getSize() + value.getSize()); + try { + final SafetyState deserializedState = + serialization.fromDson(value.getData(), SafetyState.class); + return Optional.of(deserializedState); + } catch (DeserializeException ex) { + logger.error("Failed to deserialize persisted SafetyState", ex); + return Optional.empty(); + } + } else { + return Optional.empty(); + } + } finally { + addTime(start); + } + } + + @Override + public void commitState(SafetyState safetyState) { + this.systemCounters.increment(CounterType.PERSISTENCE_SAFETY_STORE_SAVES); + + final var start = System.nanoTime(); + + final var transaction = dbEnv.getEnvironment().beginTransaction(null, null); + try { + final byte[] serializedState = serialization.toDson(safetyState, DsonOutput.Output.PERSIST); + + final DatabaseEntry key = new DatabaseEntry(keyFor(safetyState)); + final DatabaseEntry data = new DatabaseEntry(serializedState); + + final OperationStatus status = this.safetyStore.put(transaction, key, data); + if (status != OperationStatus.SUCCESS) { + fail("Database returned status " + status + " for put operation"); + } else { + addBytesWrite(key.getSize() + data.getSize()); + } + + transaction.commit(); + + cleanupUnused(); + } catch (Exception e) { + transaction.abort(); + fail("Error while storing safety state for " + safetyState, e); + } finally { + addTime(start); + } + } + + private void cleanupUnused() { + if (cleanupCounter.incrementAndGet() % UPPER_THRESHOLD != 0) { + return; + } + + final var transaction = dbEnv.getEnvironment().beginTransaction(null, null); + try { + long count = safetyStore.count(); + + if (count < UPPER_THRESHOLD) { + transaction.abort(); + return; + } + + try (Cursor cursor = this.safetyStore.openCursor(transaction, null)) { + DatabaseEntry key = new DatabaseEntry(); + DatabaseEntry value = new DatabaseEntry(); + + if (cursor.getFirst(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) { + if (cursor.delete() != OperationStatus.SUCCESS) { + transaction.abort(); + return; + } + addBytesRead(key.getSize() + value.getSize()); + count--; + } + + while (cursor.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS + && count > LOWER_THRESHOLD) { + if (cursor.delete() != OperationStatus.SUCCESS) { + transaction.abort(); + return; + } + addBytesRead(key.getSize() + value.getSize()); + count--; + } + } + + transaction.commit(); + } catch (Exception e) { + transaction.abort(); + fail("Error while clearing unused states", e); + } + } + + private void addTime(long start) { + final var elapsed = (System.nanoTime() - start + 500L) / 1000L; + this.systemCounters.add(CounterType.ELAPSED_BDB_SAFETY_STATE, elapsed); + this.systemCounters.increment(CounterType.COUNT_BDB_SAFETY_STATE_TOTAL); + } + + private void addBytesRead(int bytesRead) { + this.systemCounters.add(CounterType.COUNT_BDB_SAFETY_STATE_BYTES_READ, bytesRead); + } + + private void addBytesWrite(int bytesWrite) { + this.systemCounters.add(CounterType.COUNT_BDB_SAFETY_STATE_BYTES_WRITE, bytesWrite); + } + + private byte[] keyFor(SafetyState safetyState) { + long epoch = safetyState.getLastVote().map(Vote::getEpoch).orElse(0L); + long view = safetyState.getLastVote().map(Vote::getView).orElse(View.genesis()).number(); + + byte[] keyBytes = new byte[Long.BYTES * 2]; + Longs.copyTo(epoch, keyBytes, 0); + Longs.copyTo(view, keyBytes, Long.BYTES); + + return keyBytes; + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyStoreException.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyStoreException.java index 7da7379fa1..80756308d7 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyStoreException.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/BerkeleyStoreException.java @@ -1,78 +1,76 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.store.berkeley; - -/** - * A Tempo exception - */ -public class BerkeleyStoreException extends RuntimeException { - public BerkeleyStoreException(String message) { - super(message); - } - - public BerkeleyStoreException(String message, Throwable cause) { - super(message, cause); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.store.berkeley; + +/** A Tempo exception */ +public class BerkeleyStoreException extends RuntimeException { + public BerkeleyStoreException(String message) { + super(message); + } + + public BerkeleyStoreException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/SerializedVertexStoreState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/SerializedVertexStoreState.java index 3f29089634..37f0133368 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/SerializedVertexStoreState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/SerializedVertexStoreState.java @@ -75,91 +75,83 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; - import java.util.Objects; import java.util.Optional; -/** - * Vertex Store State version which can be serialized. - */ +/** Vertex Store State version which can be serialized. */ @SerializerId2("store.vertices") public final class SerializedVertexStoreState { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("root") - @DsonOutput(Output.ALL) - private final UnverifiedVertex root; - - @JsonProperty("vertices") - @DsonOutput(Output.ALL) - private final ImmutableList vertices; - - @JsonProperty("high_qc") - @DsonOutput(Output.ALL) - private final HighQC highQC; - - @JsonProperty("highest_tc") - @DsonOutput(Output.ALL) - private final TimeoutCertificate highestTC; - - @JsonCreator - public SerializedVertexStoreState( - @JsonProperty(value = "high_qc", required = true) HighQC highQC, - @JsonProperty(value = "root", required = true) UnverifiedVertex root, - @JsonProperty(value = "vertices", required = true) ImmutableList vertices, - @JsonProperty("highest_tc") TimeoutCertificate highestTC - ) { - this.root = Objects.requireNonNull(root); - this.vertices = Objects.requireNonNull(vertices); - this.highQC = Objects.requireNonNull(highQC); - this.highestTC = highestTC; - } - - public UnverifiedVertex getRoot() { - return root; - } - - public ImmutableList getVertices() { - return vertices; - } - - public HighQC getHighQC() { - return highQC; - } - - public Optional getHighestTC() { - return Optional.ofNullable(highestTC); - } - - @Override - public int hashCode() { - return Objects.hash(root, vertices, highQC, highestTC); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - return (o instanceof SerializedVertexStoreState other) - && Objects.equals(this.root, other.root) - && Objects.equals(this.vertices, other.vertices) - && Objects.equals(this.highQC, other.highQC) - && Objects.equals(this.highestTC, other.highestTC); - } - - @Override - public String toString() { - return String.format("%s{highQC=%s root=%s vertices=%s highestTc=%s}", - this.getClass().getSimpleName(), - this.highQC, - this.root, - this.vertices, - this.highestTC - ); - } + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("root") + @DsonOutput(Output.ALL) + private final UnverifiedVertex root; + + @JsonProperty("vertices") + @DsonOutput(Output.ALL) + private final ImmutableList vertices; + + @JsonProperty("high_qc") + @DsonOutput(Output.ALL) + private final HighQC highQC; + + @JsonProperty("highest_tc") + @DsonOutput(Output.ALL) + private final TimeoutCertificate highestTC; + + @JsonCreator + public SerializedVertexStoreState( + @JsonProperty(value = "high_qc", required = true) HighQC highQC, + @JsonProperty(value = "root", required = true) UnverifiedVertex root, + @JsonProperty(value = "vertices", required = true) ImmutableList vertices, + @JsonProperty("highest_tc") TimeoutCertificate highestTC) { + this.root = Objects.requireNonNull(root); + this.vertices = Objects.requireNonNull(vertices); + this.highQC = Objects.requireNonNull(highQC); + this.highestTC = highestTC; + } + + public UnverifiedVertex getRoot() { + return root; + } + + public ImmutableList getVertices() { + return vertices; + } + + public HighQC getHighQC() { + return highQC; + } + + public Optional getHighestTC() { + return Optional.ofNullable(highestTC); + } + + @Override + public int hashCode() { + return Objects.hash(root, vertices, highQC, highestTC); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + return (o instanceof SerializedVertexStoreState other) + && Objects.equals(this.root, other.root) + && Objects.equals(this.vertices, other.vertices) + && Objects.equals(this.highQC, other.highQC) + && Objects.equals(this.highestTC, other.highestTC); + } + + @Override + public String toString() { + return String.format( + "%s{highQC=%s root=%s vertices=%s highestTc=%s}", + this.getClass().getSimpleName(), this.highQC, this.root, this.vertices, this.highestTC); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/AppendLog.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/AppendLog.java index 91280017fd..7edda175b9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/AppendLog.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/AppendLog.java @@ -66,97 +66,85 @@ import com.radixdlt.counters.SystemCounters; import com.radixdlt.utils.Pair; - import java.io.IOException; import java.util.function.BiConsumer; /** - * Interface for append-only log file. The file consists of variable length chunks with following format: + * Interface for append-only log file. The file consists of variable length chunks with following + * format: + * *

  *     [size (64-bit little-endian)] [byte0, byte1, ..., byteN]
  * 
*/ public interface AppendLog { - /** - * Open compressed R/W append log. - * - * @param path log file path - * @param counters system counters to use - * - * @return append log - * - * @throws IOException - */ - static AppendLog openCompressed(String path, SystemCounters counters) throws IOException { - return CompressedAppendLog.open(openSimple(path), counters); - } + /** + * Open compressed R/W append log. + * + * @param path log file path + * @param counters system counters to use + * @return append log + * @throws IOException + */ + static AppendLog openCompressed(String path, SystemCounters counters) throws IOException { + return CompressedAppendLog.open(openSimple(path), counters); + } - /** - * Open plain R/W append log. - * - * @param path log file path - * - * @return append log - * - * @throws IOException - */ - static AppendLog openSimple(String path) throws IOException { - return SimpleAppendLog.open(path); - } + /** + * Open plain R/W append log. + * + * @param path log file path + * @return append log + * @throws IOException + */ + static AppendLog openSimple(String path) throws IOException { + return SimpleAppendLog.open(path); + } - /** - * Get position at which next chunk will be written. - */ - long position(); + /** Get position at which next chunk will be written. */ + long position(); - /** - * Truncate the file to specified length. - * - * @param position position to which file should be truncated. - */ - void truncate(long position); + /** + * Truncate the file to specified length. + * + * @param position position to which file should be truncated. + */ + void truncate(long position); - /** - * Write next chunk. - * - * @param data data to write - * - * @return successful result with chunk length or failure with error description. - */ - long write(byte[] data, long expectedOffset) throws IOException; + /** + * Write next chunk. + * + * @param data data to write + * @return successful result with chunk length or failure with error description. + */ + long write(byte[] data, long expectedOffset) throws IOException; - /** - * Read chunk at specified position. - * - * @param offset offset to read from - * - * @return successful result with chunk length or failure with error description. - */ - default byte[] read(long offset) throws IOException { - return readChunk(offset).getFirst(); - } + /** + * Read chunk at specified position. + * + * @param offset offset to read from + * @return successful result with chunk length or failure with error description. + */ + default byte[] read(long offset) throws IOException { + return readChunk(offset).getFirst(); + } - /** - * Read chunk at specified position. - * - * @param offset offset to read from - * - * @return successful result with chunk length or failure with error description. - */ - Pair readChunk(long offset) throws IOException; + /** + * Read chunk at specified position. + * + * @param offset offset to read from + * @return successful result with chunk length or failure with error description. + */ + Pair readChunk(long offset) throws IOException; - /** - * Force flushing data to disk. - */ - void flush() throws IOException; + /** Force flushing data to disk. */ + void flush() throws IOException; - /** - * Close append log. - */ - void close(); + /** Close append log. */ + void close(); - /** - * Scan log from start to end and submit every found chunk and its offset into provided consumer. - */ - void forEach(BiConsumer chunkConsumer); + /** + * Scan log from start to end and submit every found chunk and its offset into provided consumer. + */ + void forEach(BiConsumer chunkConsumer); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/CompressedAppendLog.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/CompressedAppendLog.java index 16f9f2ec63..d9c336eb30 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/CompressedAppendLog.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/CompressedAppendLog.java @@ -64,79 +64,78 @@ package com.radixdlt.store.berkeley.atom; +import static com.radixdlt.counters.SystemCounters.CounterType.PERSISTENCE_ATOM_LOG_WRITE_BYTES; +import static com.radixdlt.counters.SystemCounters.CounterType.PERSISTENCE_ATOM_LOG_WRITE_COMPRESSED; + import com.radixdlt.counters.SystemCounters; import com.radixdlt.utils.Compress; import com.radixdlt.utils.Pair; - import java.io.IOException; import java.util.function.BiConsumer; -import static com.radixdlt.counters.SystemCounters.CounterType.PERSISTENCE_ATOM_LOG_WRITE_BYTES; -import static com.radixdlt.counters.SystemCounters.CounterType.PERSISTENCE_ATOM_LOG_WRITE_COMPRESSED; - public class CompressedAppendLog implements AppendLog { - private final AppendLog delegate; - private final SystemCounters counters; - - private CompressedAppendLog(final AppendLog delegate, final SystemCounters counters) { - this.delegate = delegate; - this.counters = counters; - } - - static CompressedAppendLog open(AppendLog delegate, SystemCounters counters) { - return new CompressedAppendLog(delegate, counters); - } - - @Override - public long position() { - return delegate.position(); - } - - @Override - public void truncate(final long position) { - delegate.truncate(position); - } - - @Override - public long write(final byte[] data, long expectedOffset) throws IOException { - byte[] compressedData = Compress.compress(data); - - counters.add(PERSISTENCE_ATOM_LOG_WRITE_BYTES, data.length); - counters.add(PERSISTENCE_ATOM_LOG_WRITE_COMPRESSED, compressedData.length); - - return delegate.write(compressedData, expectedOffset); - } - - @Override - public Pair readChunk(final long offset) throws IOException { - var result = delegate.readChunk(offset); - return Pair.of(Compress.uncompress(result.getFirst()), result.getSecond()); - } - - @Override - public void flush() throws IOException { - delegate.flush(); - } - - @Override - public void close() { - delegate.close(); - } - - @Override - public void forEach(BiConsumer chunkConsumer) { - var offset = 0L; - synchronized (delegate) { - var end = false; - while (!end) { - try { - var chunk = readChunk(offset); - chunkConsumer.accept(chunk.getFirst(), offset); - offset += chunk.getSecond() + Integer.BYTES; - } catch (IOException exception) { - end = true; - } - } - } - } + private final AppendLog delegate; + private final SystemCounters counters; + + private CompressedAppendLog(final AppendLog delegate, final SystemCounters counters) { + this.delegate = delegate; + this.counters = counters; + } + + static CompressedAppendLog open(AppendLog delegate, SystemCounters counters) { + return new CompressedAppendLog(delegate, counters); + } + + @Override + public long position() { + return delegate.position(); + } + + @Override + public void truncate(final long position) { + delegate.truncate(position); + } + + @Override + public long write(final byte[] data, long expectedOffset) throws IOException { + byte[] compressedData = Compress.compress(data); + + counters.add(PERSISTENCE_ATOM_LOG_WRITE_BYTES, data.length); + counters.add(PERSISTENCE_ATOM_LOG_WRITE_COMPRESSED, compressedData.length); + + return delegate.write(compressedData, expectedOffset); + } + + @Override + public Pair readChunk(final long offset) throws IOException { + var result = delegate.readChunk(offset); + return Pair.of(Compress.uncompress(result.getFirst()), result.getSecond()); + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() { + delegate.close(); + } + + @Override + public void forEach(BiConsumer chunkConsumer) { + var offset = 0L; + synchronized (delegate) { + var end = false; + while (!end) { + try { + var chunk = readChunk(offset); + chunkConsumer.accept(chunk.getFirst(), offset); + offset += chunk.getSecond() + Integer.BYTES; + } catch (IOException exception) { + end = true; + } + } + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/SimpleAppendLog.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/SimpleAppendLog.java index 7a9952ba8c..a063845073 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/SimpleAppendLog.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/store/berkeley/atom/SimpleAppendLog.java @@ -64,10 +64,12 @@ package com.radixdlt.store.berkeley.atom; -import com.radixdlt.utils.Pair; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import static java.nio.ByteBuffer.allocate; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; +import com.radixdlt.utils.Pair; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -75,142 +77,153 @@ import java.nio.file.Path; import java.util.EnumSet; import java.util.function.BiConsumer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import static java.nio.ByteBuffer.allocate; -import static java.nio.file.StandardOpenOption.CREATE; -import static java.nio.file.StandardOpenOption.READ; -import static java.nio.file.StandardOpenOption.WRITE; - -/** - * Implementation of simple append-only log - */ +/** Implementation of simple append-only log */ public class SimpleAppendLog implements AppendLog { - private static final Logger logger = LogManager.getLogger(); - private final FileChannel channel; - private final ByteBuffer sizeBufferW; - private final ByteBuffer sizeBufferR; - - private SimpleAppendLog(final FileChannel channel) { - this.channel = channel; - this.sizeBufferW = allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN); - this.sizeBufferR = allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN); - } - - static AppendLog open(String path) throws IOException { - var channel = FileChannel.open(Path.of(path), EnumSet.of(READ, WRITE, CREATE)); - - channel.position(channel.size()); - return new SimpleAppendLog(channel); - } - - @Override - public long write(byte[] data, long expectedOffset) throws IOException { - synchronized (channel) { - var position = channel.position(); - if (position > expectedOffset) { - logger.warn("Expected position to be " + expectedOffset + " but is " + position - + ". Resetting position to " + expectedOffset); - channel.position(expectedOffset); - } else if (position < expectedOffset) { - throw new IOException("Expected position to be " + expectedOffset + " but is " + position - + ". Cannot recover as there is missing data."); - } - - sizeBufferW.clear().putInt(data.length).clear(); - checkedWrite(Integer.BYTES, sizeBufferW); - checkedWrite(data.length, ByteBuffer.wrap(data)); - return (long) Integer.BYTES + data.length; - } - } - - @Override - public Pair readChunk(long offset) throws IOException { - synchronized (channel) { - checkedRead(offset, sizeBufferR.clear()); - var readLength = sizeBufferR.clear().getInt(); - return Pair.of(checkedRead(offset + Integer.BYTES, allocate(readLength)).array(), readLength); - } - } - - @Override - public void flush() throws IOException { - synchronized (channel) { - channel.force(true); - } - } - - @Override - public long position() { - try { - synchronized (channel) { - return channel.position(); - } - } catch (IOException e) { - throw new IllegalStateException("Unable to obtain current position in log", e); - } - } - - @Override - public void truncate(long position) { - try { - synchronized (channel) { - channel.truncate(position); - } - } catch (IOException e) { - throw new IllegalStateException("Unable to truncate log", e); - } - } - - @Override - public void close() { - try { - synchronized (channel) { - channel.close(); - } - } catch (IOException e) { - throw new RuntimeException("Error while closing log", e); - } - } - - @Override - public void forEach(BiConsumer chunkConsumer) { - var offset = 0L; - - synchronized (channel) { - var end = false; - while (!end) { - try { - var chunk = readChunk(offset); - chunkConsumer.accept(chunk.getFirst(), offset); - offset += chunk.getSecond() + Integer.BYTES; - } catch (IOException exception) { - end = true; - } - } - } - } - - private void checkedWrite(int length, ByteBuffer buffer) throws IOException { - int len = channel.write(buffer); - - if (len != length) { - throw new IOException("Written less bytes than requested: " + len + " vs " + length); - } - } - - private ByteBuffer checkedRead(long offset, ByteBuffer buffer) throws IOException { - int len = channel.read(buffer.clear(), offset); - - if (len != buffer.capacity()) { - // Force flush and try again - channel.force(true); - len = channel.read(buffer.clear(), offset); - } - - if (len != buffer.capacity()) { - throw new IOException("Got less bytes than requested: " + len + " vs " + buffer.capacity() - + " at " + offset + ", size " + channel.size()); - } - return buffer; - } + private static final Logger logger = LogManager.getLogger(); + private final FileChannel channel; + private final ByteBuffer sizeBufferW; + private final ByteBuffer sizeBufferR; + + private SimpleAppendLog(final FileChannel channel) { + this.channel = channel; + this.sizeBufferW = allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN); + this.sizeBufferR = allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN); + } + + static AppendLog open(String path) throws IOException { + var channel = FileChannel.open(Path.of(path), EnumSet.of(READ, WRITE, CREATE)); + + channel.position(channel.size()); + return new SimpleAppendLog(channel); + } + + @Override + public long write(byte[] data, long expectedOffset) throws IOException { + synchronized (channel) { + var position = channel.position(); + if (position > expectedOffset) { + logger.warn( + "Expected position to be " + + expectedOffset + + " but is " + + position + + ". Resetting position to " + + expectedOffset); + channel.position(expectedOffset); + } else if (position < expectedOffset) { + throw new IOException( + "Expected position to be " + + expectedOffset + + " but is " + + position + + ". Cannot recover as there is missing data."); + } + + sizeBufferW.clear().putInt(data.length).clear(); + checkedWrite(Integer.BYTES, sizeBufferW); + checkedWrite(data.length, ByteBuffer.wrap(data)); + return (long) Integer.BYTES + data.length; + } + } + + @Override + public Pair readChunk(long offset) throws IOException { + synchronized (channel) { + checkedRead(offset, sizeBufferR.clear()); + var readLength = sizeBufferR.clear().getInt(); + return Pair.of(checkedRead(offset + Integer.BYTES, allocate(readLength)).array(), readLength); + } + } + + @Override + public void flush() throws IOException { + synchronized (channel) { + channel.force(true); + } + } + + @Override + public long position() { + try { + synchronized (channel) { + return channel.position(); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to obtain current position in log", e); + } + } + + @Override + public void truncate(long position) { + try { + synchronized (channel) { + channel.truncate(position); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to truncate log", e); + } + } + + @Override + public void close() { + try { + synchronized (channel) { + channel.close(); + } + } catch (IOException e) { + throw new RuntimeException("Error while closing log", e); + } + } + + @Override + public void forEach(BiConsumer chunkConsumer) { + var offset = 0L; + + synchronized (channel) { + var end = false; + while (!end) { + try { + var chunk = readChunk(offset); + chunkConsumer.accept(chunk.getFirst(), offset); + offset += chunk.getSecond() + Integer.BYTES; + } catch (IOException exception) { + end = true; + } + } + } + } + + private void checkedWrite(int length, ByteBuffer buffer) throws IOException { + int len = channel.write(buffer); + + if (len != length) { + throw new IOException("Written less bytes than requested: " + len + " vs " + length); + } + } + + private ByteBuffer checkedRead(long offset, ByteBuffer buffer) throws IOException { + int len = channel.read(buffer.clear(), offset); + + if (len != buffer.capacity()) { + // Force flush and try again + channel.force(true); + len = channel.read(buffer.clear(), offset); + } + + if (len != buffer.capacity()) { + throw new IOException( + "Got less bytes than requested: " + + len + + " vs " + + buffer.capacity() + + " at " + + offset + + ", size " + + channel.size()); + } + return buffer; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/CommittedReader.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/CommittedReader.java index 088cf5d08a..a5b3eae4ca 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/CommittedReader.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/CommittedReader.java @@ -67,33 +67,32 @@ import com.radixdlt.consensus.LedgerProof; import com.radixdlt.ledger.DtoLedgerProof; import com.radixdlt.ledger.VerifiedTxnsAndProof; - import java.util.Optional; -/** - * Reader of committed commands - */ +/** Reader of committed commands */ public interface CommittedReader { - VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start); - Optional getEpochProof(long epoch); - Optional getLastProof(); + VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start); + + Optional getEpochProof(long epoch); + + Optional getLastProof(); - static CommittedReader mocked() { - return new CommittedReader() { - @Override - public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { - return null; - } + static CommittedReader mocked() { + return new CommittedReader() { + @Override + public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { + return null; + } - @Override - public Optional getEpochProof(long epoch) { - return Optional.empty(); - } + @Override + public Optional getEpochProof(long epoch) { + return Optional.empty(); + } - @Override - public Optional getLastProof() { - return Optional.empty(); - } - }; - } + @Override + public Optional getLastProof() { + return Optional.empty(); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/LocalSyncService.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/LocalSyncService.java index 0e01c5df3a..8ac8773cc3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/LocalSyncService.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/LocalSyncService.java @@ -76,560 +76,584 @@ import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.RemoteEventProcessor; import com.radixdlt.environment.ScheduledEventDispatcher; +import com.radixdlt.ledger.AccumulatorState; +import com.radixdlt.ledger.LedgerAccumulatorVerifier; +import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.sync.SyncState.IdleState; import com.radixdlt.sync.SyncState.SyncCheckState; import com.radixdlt.sync.SyncState.SyncingState; -import com.radixdlt.ledger.AccumulatorState; -import com.radixdlt.ledger.LedgerUpdate; -import com.radixdlt.ledger.LedgerAccumulatorVerifier; - -import java.util.Map; -import java.util.Comparator; -import java.util.Objects; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.annotation.concurrent.NotThreadSafe; - import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.sync.messages.local.SyncCheckReceiveStatusTimeout; import com.radixdlt.sync.messages.local.SyncCheckTrigger; import com.radixdlt.sync.messages.local.SyncLedgerUpdateTimeout; import com.radixdlt.sync.messages.local.SyncRequestTimeout; -import com.radixdlt.sync.messages.remote.SyncResponse; -import com.radixdlt.sync.messages.remote.StatusResponse; -import com.radixdlt.sync.messages.remote.StatusRequest; import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; +import com.radixdlt.sync.messages.remote.StatusRequest; +import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; +import com.radixdlt.sync.messages.remote.SyncResponse; import com.radixdlt.sync.validation.RemoteSyncResponseSignaturesVerifier; import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; import com.radixdlt.utils.Pair; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** - * Processes sync service messages and manages ledger sync state machine. - * Thread-safety must be handled by caller. + * Processes sync service messages and manages ledger sync state machine. Thread-safety must be + * handled by caller. */ /* TODO: consider extracting some things away from this class (response validation, etc) as it's too monolithic. */ @NotThreadSafe public final class LocalSyncService { - public interface VerifiedSyncResponseHandler { - void handleVerifiedSyncResponse(SyncResponse syncResponse); - } - - public interface InvalidSyncResponseHandler { - void handleInvalidSyncResponse(BFTNode sender, SyncResponse syncResponse); - } - - private static final Logger log = LogManager.getLogger(); - - private final AtomicLong requestIdCounter = new AtomicLong(); - private final RemoteEventDispatcher statusRequestDispatcher; - private final ScheduledEventDispatcher syncCheckReceiveStatusTimeoutDispatcher; - private final RemoteEventDispatcher syncRequestDispatcher; - private final ScheduledEventDispatcher syncRequestTimeoutDispatcher; - private final ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher; - private final SyncConfig syncConfig; - private final SystemCounters systemCounters; - private final PeersView peersView; - private final Comparator accComparator; - private final RemoteSyncResponseValidatorSetVerifier validatorSetVerifier; - private final RemoteSyncResponseSignaturesVerifier signaturesVerifier; - private final LedgerAccumulatorVerifier accumulatorVerifier; - private final VerifiedSyncResponseHandler verifiedSyncResponseHandler; - private final InvalidSyncResponseHandler invalidSyncResponseHandler; - - private final ImmutableMap, ? extends Class>, Handler> handlers; - - private SyncState syncState; - - @Inject - public LocalSyncService( - RemoteEventDispatcher statusRequestDispatcher, - ScheduledEventDispatcher syncCheckReceiveStatusTimeoutDispatcher, - RemoteEventDispatcher syncRequestDispatcher, - ScheduledEventDispatcher syncRequestTimeoutDispatcher, - ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher, - SyncConfig syncConfig, - SystemCounters systemCounters, - PeersView peersView, - Comparator accComparator, - RemoteSyncResponseValidatorSetVerifier validatorSetVerifier, - RemoteSyncResponseSignaturesVerifier signaturesVerifier, - LedgerAccumulatorVerifier accumulatorVerifier, - VerifiedSyncResponseHandler verifiedSyncResponseHandler, - InvalidSyncResponseHandler invalidSyncResponseHandler, - SyncState initialState - ) { - this.statusRequestDispatcher = Objects.requireNonNull(statusRequestDispatcher); - this.syncCheckReceiveStatusTimeoutDispatcher = Objects.requireNonNull(syncCheckReceiveStatusTimeoutDispatcher); - this.syncRequestDispatcher = Objects.requireNonNull(syncRequestDispatcher); - this.syncRequestTimeoutDispatcher = Objects.requireNonNull(syncRequestTimeoutDispatcher); - this.syncLedgerUpdateTimeoutDispatcher = Objects.requireNonNull(syncLedgerUpdateTimeoutDispatcher); - this.syncConfig = Objects.requireNonNull(syncConfig); - this.systemCounters = Objects.requireNonNull(systemCounters); - this.peersView = Objects.requireNonNull(peersView); - this.accComparator = Objects.requireNonNull(accComparator); - this.validatorSetVerifier = Objects.requireNonNull(validatorSetVerifier); - this.signaturesVerifier = Objects.requireNonNull(signaturesVerifier); - this.accumulatorVerifier = Objects.requireNonNull(accumulatorVerifier); - this.verifiedSyncResponseHandler = Objects.requireNonNull(verifiedSyncResponseHandler); - this.invalidSyncResponseHandler = Objects.requireNonNull(invalidSyncResponseHandler); - - this.syncState = initialState; - - this.handlers = new ImmutableMap.Builder, ? extends Class>, Handler>() - .put(handler( - IdleState.class, SyncCheckTrigger.class, - state -> unused -> this.initSyncCheck(state) - )) - .put(remoteHandler( - SyncCheckState.class, StatusResponse.class, - state -> peer -> response -> this.processStatusResponse(state, peer, response) - )) - .put(handler( - SyncCheckState.class, SyncCheckReceiveStatusTimeout.class, - state -> unused -> this.processSyncCheckReceiveStatusTimeout(state) - )) - .put(remoteHandler( - SyncingState.class, SyncResponse.class, - state -> peer -> response -> this.processSyncResponse(state, peer, response) - )) - .put(handler( - SyncingState.class, SyncRequestTimeout.class, - state -> timeout -> this.processSyncRequestTimeout(state, timeout) - )) - .put(handler( - IdleState.class, LedgerUpdate.class, - state -> ledgerUpdate -> this.updateCurrentHeaderIfNeeded(state, ledgerUpdate) - )) - .put(handler( - SyncCheckState.class, LedgerUpdate.class, - state -> ledgerUpdate -> this.updateCurrentHeaderIfNeeded(state, ledgerUpdate) - )) - .put(handler( - SyncingState.class, LedgerUpdate.class, - state -> ledgerUpdate -> { - final var newState = (SyncingState) this.updateCurrentHeaderIfNeeded(state, ledgerUpdate); - return this.processSync(newState); - } - )) - .put(handler( - IdleState.class, LocalSyncRequest.class, - state -> request -> this.startSync(state, request.getTargetNodes(), request.getTarget()) - )) - .put(handler( - SyncCheckState.class, LocalSyncRequest.class, - state -> request -> this.startSync(state, request.getTargetNodes(), request.getTarget()) - )) - .put(handler( - SyncingState.class, LocalSyncRequest.class, - state -> request -> this.updateTargetIfNeeded(state, request.getTargetNodes(), request.getTarget()) - )) - .put(remoteHandler( - IdleState.class, LedgerStatusUpdate.class, - state -> peer -> request -> this.startSync(state, ImmutableList.of(peer), request.getHeader()) - )) - .put(remoteHandler( - SyncingState.class, LedgerStatusUpdate.class, - state -> peer -> ledgerStatusUpdate -> - this.updateTargetIfNeeded(state, ImmutableList.of(peer), ledgerStatusUpdate.getHeader()) - )) - .put(handler( - SyncingState.class, SyncLedgerUpdateTimeout.class, - state -> event -> this.processSyncLedgerUpdateTimeout(state, event) - )) - .build(); - } - - private SyncState initSyncCheck(IdleState currentState) { - final ImmutableSet peersToAsk = this.choosePeersForSyncCheck(); - - log.trace("LocalSync: Initializing sync check, about to ask {} peers for their status", peersToAsk.size()); - - peersToAsk.forEach(peer -> statusRequestDispatcher.dispatch(peer, StatusRequest.create())); - this.syncCheckReceiveStatusTimeoutDispatcher.dispatch( - SyncCheckReceiveStatusTimeout.create(), - this.syncConfig.syncCheckReceiveStatusTimeout() - ); - - return SyncCheckState.init(currentState.getCurrentHeader(), peersToAsk); - } - - private ImmutableSet choosePeersForSyncCheck() { - final var allPeers = this.peersView.peers().collect(Collectors.toList()); - Collections.shuffle(allPeers); - return allPeers.stream() - .limit(this.syncConfig.syncCheckMaxPeers()) - .map(PeersView.PeerInfo::bftNode) - .collect(ImmutableSet.toImmutableSet()); - } - - private SyncState processStatusResponse(SyncCheckState currentState, BFTNode peer, StatusResponse statusResponse) { - log.trace("LocalSync: Received status response {} from peer {}", statusResponse, peer); - - if (!currentState.hasAskedPeer(peer)) { - return currentState; // we didn't ask this peer - } - - if (currentState.receivedResponseFrom(peer)) { - return currentState; // already got the response from this peer - } - - final var newState = currentState.withStatusResponse(peer, statusResponse); - - if (newState.gotAllResponses()) { - return processPeerStatusResponsesAndStartSyncIfNeeded(newState); // we've got all the responses - } else { - return newState; - } - } - - private SyncState processPeerStatusResponsesAndStartSyncIfNeeded(SyncCheckState currentState) { - // get the highest state that we received that is also higher than what we currently have - final var maybeMaxPeerHeader = currentState.responses().values() - .stream() - .map(StatusResponse::getHeader) - .max(Comparator.comparing(LedgerProof::getAccumulatorState, accComparator)) - .filter(h -> - accComparator.compare( - h.getAccumulatorState(), - currentState.getCurrentHeader().getAccumulatorState() - ) > 0 - ); - - return maybeMaxPeerHeader.map(maxPeerHeader -> { - // start sync with all peers that are at the highest received state - final var candidatePeers = currentState.responses() - .entrySet().stream() - .filter(e -> - accComparator.compare( - e.getValue().getHeader().getAccumulatorState(), - maxPeerHeader.getAccumulatorState() - ) == 0 - ) - .map(Map.Entry::getKey) - .collect(ImmutableList.toImmutableList()); - - return this.startSync(currentState, candidatePeers, maxPeerHeader); - }) - .orElseGet(() -> { - // there is no peer ahead of us, go to idle and wait for another sync check - return this.goToIdle(currentState); - }); - } - - private SyncState processSyncCheckReceiveStatusTimeout(SyncCheckState currentState) { - if (!currentState.responses().isEmpty()) { - // we didn't get all the responses but we have some, try to sync with what we have - return this.processPeerStatusResponsesAndStartSyncIfNeeded(currentState); - } else { - // we didn't get any response, go to idle and wait for another sync check - return this.goToIdle(currentState); - } - } - - private SyncState goToIdle(SyncState currentState) { - return IdleState.init(currentState.getCurrentHeader()); - } - - private SyncState startSync( - SyncState currentState, - ImmutableList candidatePeers, - LedgerProof targetHeader - ) { - log.trace("LocalSync: Syncing to target header {}, got {} candidate peers", targetHeader, candidatePeers.size()); - return this.processSync(SyncingState.init(currentState.getCurrentHeader(), candidatePeers, targetHeader)); - } - - private SyncState processSync(SyncingState currentState) { - this.updateSyncTargetDiffCounter(currentState); - - if (isFullySynced(currentState)) { - log.trace("LocalSync: Fully synced to {}", currentState.getTargetHeader()); - // we're fully synced, go to idle and wait for another sync check - return this.goToIdle(currentState); - } - - if (currentState.waitingForResponse()) { - return currentState; // we're already waiting for a response from peer - } - - final var candidatePeerResult = currentState.fetchNextCandidatePeer(); - final var stateWithUpdatedQueue = candidatePeerResult.getFirst(); - final var maybePeerToUse = candidatePeerResult.getSecond(); - - return maybePeerToUse - .map(peerToUse -> this.sendSyncRequest(stateWithUpdatedQueue, peerToUse)) - .orElseGet(() -> { - // there's no connected peer on our candidates list, starting a fresh sync check immediately - return this.initSyncCheck(IdleState.init(stateWithUpdatedQueue.getCurrentHeader())); - }); - } - - private SyncState sendSyncRequest(SyncingState currentState, BFTNode peer) { - log.trace("LocalSync: Sending sync request to {}", peer); - - final var currentHeader = currentState.getCurrentHeader(); - - final var requestId = requestIdCounter.incrementAndGet(); - this.syncRequestDispatcher.dispatch(peer, SyncRequest.create(currentHeader.toDto())); - this.syncRequestTimeoutDispatcher.dispatch( - SyncRequestTimeout.create(peer, requestId), - this.syncConfig.syncRequestTimeout() - ); - - return currentState.withPendingRequest(peer, requestId); - } - - private boolean isFullySynced(SyncState.SyncingState syncingState) { - return accComparator.compare( - syncingState.getCurrentHeader().getAccumulatorState(), - syncingState.getTargetHeader().getAccumulatorState() - ) >= 0; - } - - private SyncState processSyncResponse(SyncingState currentState, BFTNode sender, SyncResponse syncResponse) { - log.trace("LocalSync: Received sync response from {}", sender); - - if (!currentState.waitingForResponseFrom(sender)) { - log.warn("LocalSync: Received unexpected sync response from {}", sender); - return currentState; - } - - // TODO: check validity of response - if (syncResponse.getTxnsAndProof().getTxns().isEmpty()) { - log.warn("LocalSync: Received empty sync response from {}", sender); - // didn't receive any commands, remove from candidate peers and processSync - return this.processSync( - currentState - .clearPendingRequest() - .removeCandidate(sender) - ); - } else if (!this.verifyResponse(syncResponse)) { - log.warn("LocalSync: Received invalid sync response {} from {}", syncResponse, sender); - // validation failed, remove from candidate peers and processSync - invalidSyncResponseHandler.handleInvalidSyncResponse(sender, syncResponse); - return this.processSync( - currentState - .clearPendingRequest() - .removeCandidate(sender) - ); - } else { - this.syncLedgerUpdateTimeoutDispatcher.dispatch( - SyncLedgerUpdateTimeout.create(currentState.getCurrentHeader().getStateVersion()), - 1000L - ); - this.verifiedSyncResponseHandler.handleVerifiedSyncResponse(syncResponse); - return currentState.clearPendingRequest(); - } - } - - private boolean verifyResponse(SyncResponse syncResponse) { - final var commandsAndProof = syncResponse.getTxnsAndProof(); - final var start = commandsAndProof.getHead().getLedgerHeader().getAccumulatorState(); - final var end = commandsAndProof.getTail().getLedgerHeader().getAccumulatorState(); - final var hashes = commandsAndProof.getTxns().stream() - .map(txn -> txn.getId().asHashCode()) - .collect(ImmutableList.toImmutableList()); - - if (!this.validatorSetVerifier.verifyValidatorSet(syncResponse)) { - log.warn("Invalid validator set"); - return false; - } - - if (!this.signaturesVerifier.verifyResponseSignatures(syncResponse)) { - log.warn("Invalid signatures"); - return false; - } - - if (!this.accumulatorVerifier.verify(start, hashes, end)) { - log.warn("Invalid accumulator"); - return false; - } - - return true; - } - - private SyncState processSyncRequestTimeout(SyncingState currentState, SyncRequestTimeout syncRequestTimeout) { - final var timeoutMatchesRequest = currentState.getPendingRequest().stream() - .anyMatch(pr -> pr.getRequestId() == syncRequestTimeout.getRequestId() - && pr.getPeer().equals(syncRequestTimeout.getPeer()) - ); - - if (!timeoutMatchesRequest) { - return currentState; // ignore, this timeout is no longer valid - } - - log.trace("LocalSync: Sync request timeout from peer {}", syncRequestTimeout.getPeer()); - - return this.processSync( - currentState - .clearPendingRequest() - .removeCandidate(syncRequestTimeout.getPeer()) - ); - } - - private SyncState processSyncLedgerUpdateTimeout(SyncingState currentState, SyncLedgerUpdateTimeout event) { - if (event.stateVersion() != currentState.getCurrentHeader().getStateVersion()) { - return currentState; // obsolete timeout event; ignore - } else { - return this.processSync(currentState); - } - } - - private SyncState updateCurrentHeaderIfNeeded(SyncState currentState, LedgerUpdate ledgerUpdate) { - final var updatedHeader = ledgerUpdate.getTail(); - final var isNewerState = accComparator.compare( - updatedHeader.getAccumulatorState(), - currentState.getCurrentHeader().getAccumulatorState() - ) > 0; - - if (isNewerState) { - final var newState = currentState.withCurrentHeader(updatedHeader); - return this.updateSyncTargetDiffCounter(newState); - } else { - return currentState; - } - } - - private SyncingState updateTargetIfNeeded( - SyncingState currentState, - ImmutableList peers, - LedgerProof header - ) { - final var isNewerState = - accComparator.compare( - header.getAccumulatorState(), - currentState.getTargetHeader().getAccumulatorState() - ) > 0; - - if (isNewerState) { - final var newState = currentState - .withTargetHeader(header) - .addCandidatePeers(peers); - return this.updateSyncTargetDiffCounter(newState); - } else { - log.trace("LocalSync: skipping as already targeted {}", currentState.getTargetHeader()); - return currentState; - } - } - - private T updateSyncTargetDiffCounter(T syncState) { - if (syncState instanceof final SyncingState syncingState) { - this.systemCounters.set(CounterType.SYNC_CURRENT_STATE_VERSION, syncingState.getCurrentHeader().getStateVersion()); - this.systemCounters.set( - CounterType.SYNC_TARGET_STATE_VERSION, - Math.max( - syncingState.getTargetHeader().getStateVersion(), - syncingState.getTargetHeader().getAccumulatorState().getStateVersion() - ) - ); - } else { - this.systemCounters.set(CounterType.SYNC_CURRENT_STATE_VERSION, syncState.getCurrentHeader().getStateVersion()); - this.systemCounters.set(CounterType.SYNC_TARGET_STATE_VERSION, syncState.getCurrentHeader().getStateVersion()); - } - - return syncState; - } - - public SyncState getSyncState() { - return this.syncState; - } - - public EventProcessor syncCheckTriggerEventProcessor() { - return (event) -> this.processEvent(SyncCheckTrigger.class, event); - } - - public RemoteEventProcessor statusResponseEventProcessor() { - return (peer, event) -> this.processRemoteEvent(StatusResponse.class, peer, event); - } - - public EventProcessor syncCheckReceiveStatusTimeoutEventProcessor() { - return (event) -> this.processEvent(SyncCheckReceiveStatusTimeout.class, event); - } - - public RemoteEventProcessor syncResponseEventProcessor() { - return (peer, event) -> this.processRemoteEvent(SyncResponse.class, peer, event); - } - - public RemoteEventProcessor ledgerStatusUpdateEventProcessor() { - return (peer, event) -> this.processRemoteEvent(LedgerStatusUpdate.class, peer, event); - } - - public EventProcessor syncRequestTimeoutEventProcessor() { - return (event) -> this.processEvent(SyncRequestTimeout.class, event); - } - - public EventProcessor ledgerUpdateEventProcessor() { - return (event) -> this.processEvent(LedgerUpdate.class, event); - } - - public EventProcessor localSyncRequestEventProcessor() { - return (event) -> this.processEvent(LocalSyncRequest.class, event); - } - - public EventProcessor syncLedgerUpdateTimeoutProcessor() { - return (event) -> this.processEvent(SyncLedgerUpdateTimeout.class, event); - } - - private void processEvent(Class eventClass, T event) { - @SuppressWarnings("unchecked") - final var maybeHandler = - (Handler) this.handlers.get(Pair.of(this.syncState.getClass(), eventClass)); - if (maybeHandler != null) { - this.syncState = maybeHandler.handle(this.syncState, event); - } - } - - private void processRemoteEvent(Class eventClass, BFTNode peer, T event) { - @SuppressWarnings("unchecked") - final var maybeHandler = - (Handler) this.handlers.get(Pair.of(this.syncState.getClass(), eventClass)); - if (maybeHandler != null) { - this.syncState = maybeHandler.handle(this.syncState, peer, event); - } - } - - private Map.Entry, Class>, Handler> handler( - Class stateClass, - Class eventClass, - Function> fn - ) { - return Map.entry(Pair.of(stateClass, eventClass), new Handler<>(fn)); - } - - private Map.Entry, Class>, Handler> remoteHandler( - Class stateClass, - Class eventClass, - Function>> fn - ) { - return Map.entry(Pair.of(stateClass, eventClass), new Handler<>(new Object(), fn)); - } - - private static final class Handler { - private Function> handleEvent; - private Function>> handleRemoteEvent; - - Handler(Function> fn) { - this.handleEvent = fn; - } - - /* need another param to be able to distinguish the methods after type erasure */ - Handler(Object erasureFix, Function>> fn) { - this.handleRemoteEvent = fn; - } - - SyncState handle(S currentState, T event) { - return this.handleEvent.apply(currentState).apply(event); - } - - SyncState handle(S currentState, BFTNode peer, T event) { - return this.handleRemoteEvent.apply(currentState).apply(peer).apply(event); - } - } + public interface VerifiedSyncResponseHandler { + void handleVerifiedSyncResponse(SyncResponse syncResponse); + } + + public interface InvalidSyncResponseHandler { + void handleInvalidSyncResponse(BFTNode sender, SyncResponse syncResponse); + } + + private static final Logger log = LogManager.getLogger(); + + private final AtomicLong requestIdCounter = new AtomicLong(); + private final RemoteEventDispatcher statusRequestDispatcher; + private final ScheduledEventDispatcher + syncCheckReceiveStatusTimeoutDispatcher; + private final RemoteEventDispatcher syncRequestDispatcher; + private final ScheduledEventDispatcher syncRequestTimeoutDispatcher; + private final ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher; + private final SyncConfig syncConfig; + private final SystemCounters systemCounters; + private final PeersView peersView; + private final Comparator accComparator; + private final RemoteSyncResponseValidatorSetVerifier validatorSetVerifier; + private final RemoteSyncResponseSignaturesVerifier signaturesVerifier; + private final LedgerAccumulatorVerifier accumulatorVerifier; + private final VerifiedSyncResponseHandler verifiedSyncResponseHandler; + private final InvalidSyncResponseHandler invalidSyncResponseHandler; + + private final ImmutableMap, ? extends Class>, Handler> handlers; + + private SyncState syncState; + + @Inject + public LocalSyncService( + RemoteEventDispatcher statusRequestDispatcher, + ScheduledEventDispatcher + syncCheckReceiveStatusTimeoutDispatcher, + RemoteEventDispatcher syncRequestDispatcher, + ScheduledEventDispatcher syncRequestTimeoutDispatcher, + ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher, + SyncConfig syncConfig, + SystemCounters systemCounters, + PeersView peersView, + Comparator accComparator, + RemoteSyncResponseValidatorSetVerifier validatorSetVerifier, + RemoteSyncResponseSignaturesVerifier signaturesVerifier, + LedgerAccumulatorVerifier accumulatorVerifier, + VerifiedSyncResponseHandler verifiedSyncResponseHandler, + InvalidSyncResponseHandler invalidSyncResponseHandler, + SyncState initialState) { + this.statusRequestDispatcher = Objects.requireNonNull(statusRequestDispatcher); + this.syncCheckReceiveStatusTimeoutDispatcher = + Objects.requireNonNull(syncCheckReceiveStatusTimeoutDispatcher); + this.syncRequestDispatcher = Objects.requireNonNull(syncRequestDispatcher); + this.syncRequestTimeoutDispatcher = Objects.requireNonNull(syncRequestTimeoutDispatcher); + this.syncLedgerUpdateTimeoutDispatcher = + Objects.requireNonNull(syncLedgerUpdateTimeoutDispatcher); + this.syncConfig = Objects.requireNonNull(syncConfig); + this.systemCounters = Objects.requireNonNull(systemCounters); + this.peersView = Objects.requireNonNull(peersView); + this.accComparator = Objects.requireNonNull(accComparator); + this.validatorSetVerifier = Objects.requireNonNull(validatorSetVerifier); + this.signaturesVerifier = Objects.requireNonNull(signaturesVerifier); + this.accumulatorVerifier = Objects.requireNonNull(accumulatorVerifier); + this.verifiedSyncResponseHandler = Objects.requireNonNull(verifiedSyncResponseHandler); + this.invalidSyncResponseHandler = Objects.requireNonNull(invalidSyncResponseHandler); + + this.syncState = initialState; + + this.handlers = + new ImmutableMap.Builder, ? extends Class>, Handler>() + .put( + handler( + IdleState.class, + SyncCheckTrigger.class, + state -> unused -> this.initSyncCheck(state))) + .put( + remoteHandler( + SyncCheckState.class, + StatusResponse.class, + state -> peer -> response -> this.processStatusResponse(state, peer, response))) + .put( + handler( + SyncCheckState.class, + SyncCheckReceiveStatusTimeout.class, + state -> unused -> this.processSyncCheckReceiveStatusTimeout(state))) + .put( + remoteHandler( + SyncingState.class, + SyncResponse.class, + state -> peer -> response -> this.processSyncResponse(state, peer, response))) + .put( + handler( + SyncingState.class, + SyncRequestTimeout.class, + state -> timeout -> this.processSyncRequestTimeout(state, timeout))) + .put( + handler( + IdleState.class, + LedgerUpdate.class, + state -> ledgerUpdate -> this.updateCurrentHeaderIfNeeded(state, ledgerUpdate))) + .put( + handler( + SyncCheckState.class, + LedgerUpdate.class, + state -> ledgerUpdate -> this.updateCurrentHeaderIfNeeded(state, ledgerUpdate))) + .put( + handler( + SyncingState.class, + LedgerUpdate.class, + state -> + ledgerUpdate -> { + final var newState = + (SyncingState) this.updateCurrentHeaderIfNeeded(state, ledgerUpdate); + return this.processSync(newState); + })) + .put( + handler( + IdleState.class, + LocalSyncRequest.class, + state -> + request -> + this.startSync(state, request.getTargetNodes(), request.getTarget()))) + .put( + handler( + SyncCheckState.class, + LocalSyncRequest.class, + state -> + request -> + this.startSync(state, request.getTargetNodes(), request.getTarget()))) + .put( + handler( + SyncingState.class, + LocalSyncRequest.class, + state -> + request -> + this.updateTargetIfNeeded( + state, request.getTargetNodes(), request.getTarget()))) + .put( + remoteHandler( + IdleState.class, + LedgerStatusUpdate.class, + state -> + peer -> + request -> + this.startSync(state, ImmutableList.of(peer), request.getHeader()))) + .put( + remoteHandler( + SyncingState.class, + LedgerStatusUpdate.class, + state -> + peer -> + ledgerStatusUpdate -> + this.updateTargetIfNeeded( + state, ImmutableList.of(peer), ledgerStatusUpdate.getHeader()))) + .put( + handler( + SyncingState.class, + SyncLedgerUpdateTimeout.class, + state -> event -> this.processSyncLedgerUpdateTimeout(state, event))) + .build(); + } + + private SyncState initSyncCheck(IdleState currentState) { + final ImmutableSet peersToAsk = this.choosePeersForSyncCheck(); + + log.trace( + "LocalSync: Initializing sync check, about to ask {} peers for their status", + peersToAsk.size()); + + peersToAsk.forEach(peer -> statusRequestDispatcher.dispatch(peer, StatusRequest.create())); + this.syncCheckReceiveStatusTimeoutDispatcher.dispatch( + SyncCheckReceiveStatusTimeout.create(), this.syncConfig.syncCheckReceiveStatusTimeout()); + + return SyncCheckState.init(currentState.getCurrentHeader(), peersToAsk); + } + + private ImmutableSet choosePeersForSyncCheck() { + final var allPeers = this.peersView.peers().collect(Collectors.toList()); + Collections.shuffle(allPeers); + return allPeers.stream() + .limit(this.syncConfig.syncCheckMaxPeers()) + .map(PeersView.PeerInfo::bftNode) + .collect(ImmutableSet.toImmutableSet()); + } + + private SyncState processStatusResponse( + SyncCheckState currentState, BFTNode peer, StatusResponse statusResponse) { + log.trace("LocalSync: Received status response {} from peer {}", statusResponse, peer); + + if (!currentState.hasAskedPeer(peer)) { + return currentState; // we didn't ask this peer + } + + if (currentState.receivedResponseFrom(peer)) { + return currentState; // already got the response from this peer + } + + final var newState = currentState.withStatusResponse(peer, statusResponse); + + if (newState.gotAllResponses()) { + return processPeerStatusResponsesAndStartSyncIfNeeded( + newState); // we've got all the responses + } else { + return newState; + } + } + + private SyncState processPeerStatusResponsesAndStartSyncIfNeeded(SyncCheckState currentState) { + // get the highest state that we received that is also higher than what we currently have + final var maybeMaxPeerHeader = + currentState.responses().values().stream() + .map(StatusResponse::getHeader) + .max(Comparator.comparing(LedgerProof::getAccumulatorState, accComparator)) + .filter( + h -> + accComparator.compare( + h.getAccumulatorState(), + currentState.getCurrentHeader().getAccumulatorState()) + > 0); + + return maybeMaxPeerHeader + .map( + maxPeerHeader -> { + // start sync with all peers that are at the highest received state + final var candidatePeers = + currentState.responses().entrySet().stream() + .filter( + e -> + accComparator.compare( + e.getValue().getHeader().getAccumulatorState(), + maxPeerHeader.getAccumulatorState()) + == 0) + .map(Map.Entry::getKey) + .collect(ImmutableList.toImmutableList()); + + return this.startSync(currentState, candidatePeers, maxPeerHeader); + }) + .orElseGet( + () -> { + // there is no peer ahead of us, go to idle and wait for another sync check + return this.goToIdle(currentState); + }); + } + + private SyncState processSyncCheckReceiveStatusTimeout(SyncCheckState currentState) { + if (!currentState.responses().isEmpty()) { + // we didn't get all the responses but we have some, try to sync with what we have + return this.processPeerStatusResponsesAndStartSyncIfNeeded(currentState); + } else { + // we didn't get any response, go to idle and wait for another sync check + return this.goToIdle(currentState); + } + } + + private SyncState goToIdle(SyncState currentState) { + return IdleState.init(currentState.getCurrentHeader()); + } + + private SyncState startSync( + SyncState currentState, ImmutableList candidatePeers, LedgerProof targetHeader) { + log.trace( + "LocalSync: Syncing to target header {}, got {} candidate peers", + targetHeader, + candidatePeers.size()); + return this.processSync( + SyncingState.init(currentState.getCurrentHeader(), candidatePeers, targetHeader)); + } + + private SyncState processSync(SyncingState currentState) { + this.updateSyncTargetDiffCounter(currentState); + + if (isFullySynced(currentState)) { + log.trace("LocalSync: Fully synced to {}", currentState.getTargetHeader()); + // we're fully synced, go to idle and wait for another sync check + return this.goToIdle(currentState); + } + + if (currentState.waitingForResponse()) { + return currentState; // we're already waiting for a response from peer + } + + final var candidatePeerResult = currentState.fetchNextCandidatePeer(); + final var stateWithUpdatedQueue = candidatePeerResult.getFirst(); + final var maybePeerToUse = candidatePeerResult.getSecond(); + + return maybePeerToUse + .map(peerToUse -> this.sendSyncRequest(stateWithUpdatedQueue, peerToUse)) + .orElseGet( + () -> { + // there's no connected peer on our candidates list, starting a fresh sync check + // immediately + return this.initSyncCheck(IdleState.init(stateWithUpdatedQueue.getCurrentHeader())); + }); + } + + private SyncState sendSyncRequest(SyncingState currentState, BFTNode peer) { + log.trace("LocalSync: Sending sync request to {}", peer); + + final var currentHeader = currentState.getCurrentHeader(); + + final var requestId = requestIdCounter.incrementAndGet(); + this.syncRequestDispatcher.dispatch(peer, SyncRequest.create(currentHeader.toDto())); + this.syncRequestTimeoutDispatcher.dispatch( + SyncRequestTimeout.create(peer, requestId), this.syncConfig.syncRequestTimeout()); + + return currentState.withPendingRequest(peer, requestId); + } + + private boolean isFullySynced(SyncState.SyncingState syncingState) { + return accComparator.compare( + syncingState.getCurrentHeader().getAccumulatorState(), + syncingState.getTargetHeader().getAccumulatorState()) + >= 0; + } + + private SyncState processSyncResponse( + SyncingState currentState, BFTNode sender, SyncResponse syncResponse) { + log.trace("LocalSync: Received sync response from {}", sender); + + if (!currentState.waitingForResponseFrom(sender)) { + log.warn("LocalSync: Received unexpected sync response from {}", sender); + return currentState; + } + + // TODO: check validity of response + if (syncResponse.getTxnsAndProof().getTxns().isEmpty()) { + log.warn("LocalSync: Received empty sync response from {}", sender); + // didn't receive any commands, remove from candidate peers and processSync + return this.processSync(currentState.clearPendingRequest().removeCandidate(sender)); + } else if (!this.verifyResponse(syncResponse)) { + log.warn("LocalSync: Received invalid sync response {} from {}", syncResponse, sender); + // validation failed, remove from candidate peers and processSync + invalidSyncResponseHandler.handleInvalidSyncResponse(sender, syncResponse); + return this.processSync(currentState.clearPendingRequest().removeCandidate(sender)); + } else { + this.syncLedgerUpdateTimeoutDispatcher.dispatch( + SyncLedgerUpdateTimeout.create(currentState.getCurrentHeader().getStateVersion()), 1000L); + this.verifiedSyncResponseHandler.handleVerifiedSyncResponse(syncResponse); + return currentState.clearPendingRequest(); + } + } + + private boolean verifyResponse(SyncResponse syncResponse) { + final var commandsAndProof = syncResponse.getTxnsAndProof(); + final var start = commandsAndProof.getHead().getLedgerHeader().getAccumulatorState(); + final var end = commandsAndProof.getTail().getLedgerHeader().getAccumulatorState(); + final var hashes = + commandsAndProof.getTxns().stream() + .map(txn -> txn.getId().asHashCode()) + .collect(ImmutableList.toImmutableList()); + + if (!this.validatorSetVerifier.verifyValidatorSet(syncResponse)) { + log.warn("Invalid validator set"); + return false; + } + + if (!this.signaturesVerifier.verifyResponseSignatures(syncResponse)) { + log.warn("Invalid signatures"); + return false; + } + + if (!this.accumulatorVerifier.verify(start, hashes, end)) { + log.warn("Invalid accumulator"); + return false; + } + + return true; + } + + private SyncState processSyncRequestTimeout( + SyncingState currentState, SyncRequestTimeout syncRequestTimeout) { + final var timeoutMatchesRequest = + currentState.getPendingRequest().stream() + .anyMatch( + pr -> + pr.getRequestId() == syncRequestTimeout.getRequestId() + && pr.getPeer().equals(syncRequestTimeout.getPeer())); + + if (!timeoutMatchesRequest) { + return currentState; // ignore, this timeout is no longer valid + } + + log.trace("LocalSync: Sync request timeout from peer {}", syncRequestTimeout.getPeer()); + + return this.processSync( + currentState.clearPendingRequest().removeCandidate(syncRequestTimeout.getPeer())); + } + + private SyncState processSyncLedgerUpdateTimeout( + SyncingState currentState, SyncLedgerUpdateTimeout event) { + if (event.stateVersion() != currentState.getCurrentHeader().getStateVersion()) { + return currentState; // obsolete timeout event; ignore + } else { + return this.processSync(currentState); + } + } + + private SyncState updateCurrentHeaderIfNeeded(SyncState currentState, LedgerUpdate ledgerUpdate) { + final var updatedHeader = ledgerUpdate.getTail(); + final var isNewerState = + accComparator.compare( + updatedHeader.getAccumulatorState(), + currentState.getCurrentHeader().getAccumulatorState()) + > 0; + + if (isNewerState) { + final var newState = currentState.withCurrentHeader(updatedHeader); + return this.updateSyncTargetDiffCounter(newState); + } else { + return currentState; + } + } + + private SyncingState updateTargetIfNeeded( + SyncingState currentState, ImmutableList peers, LedgerProof header) { + final var isNewerState = + accComparator.compare( + header.getAccumulatorState(), currentState.getTargetHeader().getAccumulatorState()) + > 0; + + if (isNewerState) { + final var newState = currentState.withTargetHeader(header).addCandidatePeers(peers); + return this.updateSyncTargetDiffCounter(newState); + } else { + log.trace("LocalSync: skipping as already targeted {}", currentState.getTargetHeader()); + return currentState; + } + } + + private T updateSyncTargetDiffCounter(T syncState) { + if (syncState instanceof final SyncingState syncingState) { + this.systemCounters.set( + CounterType.SYNC_CURRENT_STATE_VERSION, + syncingState.getCurrentHeader().getStateVersion()); + this.systemCounters.set( + CounterType.SYNC_TARGET_STATE_VERSION, + Math.max( + syncingState.getTargetHeader().getStateVersion(), + syncingState.getTargetHeader().getAccumulatorState().getStateVersion())); + } else { + this.systemCounters.set( + CounterType.SYNC_CURRENT_STATE_VERSION, syncState.getCurrentHeader().getStateVersion()); + this.systemCounters.set( + CounterType.SYNC_TARGET_STATE_VERSION, syncState.getCurrentHeader().getStateVersion()); + } + + return syncState; + } + + public SyncState getSyncState() { + return this.syncState; + } + + public EventProcessor syncCheckTriggerEventProcessor() { + return (event) -> this.processEvent(SyncCheckTrigger.class, event); + } + + public RemoteEventProcessor statusResponseEventProcessor() { + return (peer, event) -> this.processRemoteEvent(StatusResponse.class, peer, event); + } + + public EventProcessor + syncCheckReceiveStatusTimeoutEventProcessor() { + return (event) -> this.processEvent(SyncCheckReceiveStatusTimeout.class, event); + } + + public RemoteEventProcessor syncResponseEventProcessor() { + return (peer, event) -> this.processRemoteEvent(SyncResponse.class, peer, event); + } + + public RemoteEventProcessor ledgerStatusUpdateEventProcessor() { + return (peer, event) -> this.processRemoteEvent(LedgerStatusUpdate.class, peer, event); + } + + public EventProcessor syncRequestTimeoutEventProcessor() { + return (event) -> this.processEvent(SyncRequestTimeout.class, event); + } + + public EventProcessor ledgerUpdateEventProcessor() { + return (event) -> this.processEvent(LedgerUpdate.class, event); + } + + public EventProcessor localSyncRequestEventProcessor() { + return (event) -> this.processEvent(LocalSyncRequest.class, event); + } + + public EventProcessor syncLedgerUpdateTimeoutProcessor() { + return (event) -> this.processEvent(SyncLedgerUpdateTimeout.class, event); + } + + private void processEvent(Class eventClass, T event) { + @SuppressWarnings("unchecked") + final var maybeHandler = + (Handler) this.handlers.get(Pair.of(this.syncState.getClass(), eventClass)); + if (maybeHandler != null) { + this.syncState = maybeHandler.handle(this.syncState, event); + } + } + + private void processRemoteEvent(Class eventClass, BFTNode peer, T event) { + @SuppressWarnings("unchecked") + final var maybeHandler = + (Handler) this.handlers.get(Pair.of(this.syncState.getClass(), eventClass)); + if (maybeHandler != null) { + this.syncState = maybeHandler.handle(this.syncState, peer, event); + } + } + + private Map.Entry, Class>, Handler> handler( + Class stateClass, Class eventClass, Function> fn) { + return Map.entry(Pair.of(stateClass, eventClass), new Handler<>(fn)); + } + + private Map.Entry, Class>, Handler> remoteHandler( + Class stateClass, + Class eventClass, + Function>> fn) { + return Map.entry(Pair.of(stateClass, eventClass), new Handler<>(new Object(), fn)); + } + + private static final class Handler { + private Function> handleEvent; + private Function>> handleRemoteEvent; + + Handler(Function> fn) { + this.handleEvent = fn; + } + + /* need another param to be able to distinguish the methods after type erasure */ + Handler(Object erasureFix, Function>> fn) { + this.handleRemoteEvent = fn; + } + + SyncState handle(S currentState, T event) { + return this.handleEvent.apply(currentState).apply(event); + } + + SyncState handle(S currentState, BFTNode peer, T event) { + return this.handleRemoteEvent.apply(currentState).apply(peer).apply(event); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/RemoteSyncService.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/RemoteSyncService.java index 5ce6ee90ac..62ba7b364a 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/RemoteSyncService.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/RemoteSyncService.java @@ -74,138 +74,141 @@ import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.RemoteEventProcessor; import com.radixdlt.ledger.AccumulatorState; -import com.radixdlt.ledger.DtoTxnsAndProof; import com.radixdlt.ledger.DtoLedgerProof; -import com.radixdlt.ledger.VerifiedTxnsAndProof; +import com.radixdlt.ledger.DtoTxnsAndProof; import com.radixdlt.ledger.LedgerUpdate; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Objects; -import java.util.stream.Collectors; - +import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.store.LastProof; import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; +import com.radixdlt.sync.messages.remote.StatusRequest; import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; -import com.radixdlt.sync.messages.remote.StatusRequest; import com.radixdlt.sync.messages.remote.SyncResponse; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Service which serves remote sync requests. - */ +/** Service which serves remote sync requests. */ public final class RemoteSyncService { - private static final Logger log = LogManager.getLogger(); - - private final PeersView peersView; - private final LocalSyncService localSyncService; // TODO: consider removing this dependency - private final CommittedReader committedReader; - private final RemoteEventDispatcher statusResponseDispatcher; - private final RemoteEventDispatcher syncResponseDispatcher; - private final RemoteEventDispatcher statusUpdateDispatcher; - private final SyncConfig syncConfig; - private final SystemCounters systemCounters; - private final Comparator accComparator; - private final RateLimiter ledgerStatusUpdateSendRateLimiter; - - private LedgerProof currentHeader; - - @Inject - public RemoteSyncService( - PeersView peersView, - LocalSyncService localSyncService, - CommittedReader committedReader, - RemoteEventDispatcher statusResponseDispatcher, - RemoteEventDispatcher syncResponseDispatcher, - RemoteEventDispatcher statusUpdateDispatcher, - SyncConfig syncConfig, - SystemCounters systemCounters, - Comparator accComparator, - @LastProof LedgerProof initialHeader - ) { - this.peersView = Objects.requireNonNull(peersView); - this.localSyncService = Objects.requireNonNull(localSyncService); - this.committedReader = Objects.requireNonNull(committedReader); - this.syncConfig = Objects.requireNonNull(syncConfig); - this.statusResponseDispatcher = Objects.requireNonNull(statusResponseDispatcher); - this.syncResponseDispatcher = Objects.requireNonNull(syncResponseDispatcher); - this.statusUpdateDispatcher = Objects.requireNonNull(statusUpdateDispatcher); - this.systemCounters = systemCounters; - this.accComparator = Objects.requireNonNull(accComparator); - this.ledgerStatusUpdateSendRateLimiter = RateLimiter.create(syncConfig.maxLedgerUpdatesRate()); - - this.currentHeader = initialHeader; - } - - public RemoteEventProcessor syncRequestEventProcessor() { - return this::processSyncRequest; - } - - private void processSyncRequest(BFTNode sender, SyncRequest syncRequest) { - final var remoteCurrentHeader = syncRequest.getHeader(); - final var committedCommands = getCommittedCommandsForSyncRequest(remoteCurrentHeader); - - if (committedCommands == null) { - log.warn("REMOTE_SYNC_REQUEST: Unable to serve sync request {} from sender {}.", remoteCurrentHeader, sender); - return; - } - - final var verifiable = new DtoTxnsAndProof( - committedCommands.getTxns(), - remoteCurrentHeader, - committedCommands.getProof().toDto() - ); - - log.trace("REMOTE_SYNC_REQUEST: Sending response {} to request {} from {}", verifiable, remoteCurrentHeader, sender); - - systemCounters.increment(CounterType.SYNC_REMOTE_REQUESTS_RECEIVED); - syncResponseDispatcher.dispatch(sender, SyncResponse.create(verifiable)); - } - - private VerifiedTxnsAndProof getCommittedCommandsForSyncRequest(DtoLedgerProof startHeader) { - return committedReader.getNextCommittedTxns(startHeader); - } - - public RemoteEventProcessor statusRequestEventProcessor() { - return this::processStatusRequest; - } - - private void processStatusRequest(BFTNode sender, StatusRequest statusRequest) { - statusResponseDispatcher.dispatch(sender, StatusResponse.create(this.currentHeader)); - } - - public EventProcessor ledgerUpdateEventProcessor() { - return this::processLedgerUpdate; - } - - private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { - final LedgerProof updatedHeader = ledgerUpdate.getTail(); - if (accComparator.compare(updatedHeader.getAccumulatorState(), this.currentHeader.getAccumulatorState()) > 0) { - this.currentHeader = updatedHeader; - this.sendStatusUpdateToSomePeers(updatedHeader); - } - } - - private void sendStatusUpdateToSomePeers(LedgerProof header) { - if (!(this.localSyncService.getSyncState() instanceof SyncState.IdleState)) { - return; // not sending any updates if the node is syncing itself - } - - final var statusUpdate = LedgerStatusUpdate.create(header); - - final var currentPeers = this.peersView.peers().collect(Collectors.toList()); - Collections.shuffle(currentPeers); - - currentPeers.stream() - .limit(syncConfig.ledgerStatusUpdateMaxPeersToNotify()) - .map(PeersView.PeerInfo::bftNode) - .forEach(peer -> { - if (this.ledgerStatusUpdateSendRateLimiter.tryAcquire()) { - statusUpdateDispatcher.dispatch(peer, statusUpdate); - } - }); - } + private static final Logger log = LogManager.getLogger(); + + private final PeersView peersView; + private final LocalSyncService localSyncService; // TODO: consider removing this dependency + private final CommittedReader committedReader; + private final RemoteEventDispatcher statusResponseDispatcher; + private final RemoteEventDispatcher syncResponseDispatcher; + private final RemoteEventDispatcher statusUpdateDispatcher; + private final SyncConfig syncConfig; + private final SystemCounters systemCounters; + private final Comparator accComparator; + private final RateLimiter ledgerStatusUpdateSendRateLimiter; + + private LedgerProof currentHeader; + + @Inject + public RemoteSyncService( + PeersView peersView, + LocalSyncService localSyncService, + CommittedReader committedReader, + RemoteEventDispatcher statusResponseDispatcher, + RemoteEventDispatcher syncResponseDispatcher, + RemoteEventDispatcher statusUpdateDispatcher, + SyncConfig syncConfig, + SystemCounters systemCounters, + Comparator accComparator, + @LastProof LedgerProof initialHeader) { + this.peersView = Objects.requireNonNull(peersView); + this.localSyncService = Objects.requireNonNull(localSyncService); + this.committedReader = Objects.requireNonNull(committedReader); + this.syncConfig = Objects.requireNonNull(syncConfig); + this.statusResponseDispatcher = Objects.requireNonNull(statusResponseDispatcher); + this.syncResponseDispatcher = Objects.requireNonNull(syncResponseDispatcher); + this.statusUpdateDispatcher = Objects.requireNonNull(statusUpdateDispatcher); + this.systemCounters = systemCounters; + this.accComparator = Objects.requireNonNull(accComparator); + this.ledgerStatusUpdateSendRateLimiter = RateLimiter.create(syncConfig.maxLedgerUpdatesRate()); + + this.currentHeader = initialHeader; + } + + public RemoteEventProcessor syncRequestEventProcessor() { + return this::processSyncRequest; + } + + private void processSyncRequest(BFTNode sender, SyncRequest syncRequest) { + final var remoteCurrentHeader = syncRequest.getHeader(); + final var committedCommands = getCommittedCommandsForSyncRequest(remoteCurrentHeader); + + if (committedCommands == null) { + log.warn( + "REMOTE_SYNC_REQUEST: Unable to serve sync request {} from sender {}.", + remoteCurrentHeader, + sender); + return; + } + + final var verifiable = + new DtoTxnsAndProof( + committedCommands.getTxns(), remoteCurrentHeader, committedCommands.getProof().toDto()); + + log.trace( + "REMOTE_SYNC_REQUEST: Sending response {} to request {} from {}", + verifiable, + remoteCurrentHeader, + sender); + + systemCounters.increment(CounterType.SYNC_REMOTE_REQUESTS_RECEIVED); + syncResponseDispatcher.dispatch(sender, SyncResponse.create(verifiable)); + } + + private VerifiedTxnsAndProof getCommittedCommandsForSyncRequest(DtoLedgerProof startHeader) { + return committedReader.getNextCommittedTxns(startHeader); + } + + public RemoteEventProcessor statusRequestEventProcessor() { + return this::processStatusRequest; + } + + private void processStatusRequest(BFTNode sender, StatusRequest statusRequest) { + statusResponseDispatcher.dispatch(sender, StatusResponse.create(this.currentHeader)); + } + + public EventProcessor ledgerUpdateEventProcessor() { + return this::processLedgerUpdate; + } + + private void processLedgerUpdate(LedgerUpdate ledgerUpdate) { + final LedgerProof updatedHeader = ledgerUpdate.getTail(); + if (accComparator.compare( + updatedHeader.getAccumulatorState(), this.currentHeader.getAccumulatorState()) + > 0) { + this.currentHeader = updatedHeader; + this.sendStatusUpdateToSomePeers(updatedHeader); + } + } + + private void sendStatusUpdateToSomePeers(LedgerProof header) { + if (!(this.localSyncService.getSyncState() instanceof SyncState.IdleState)) { + return; // not sending any updates if the node is syncing itself + } + + final var statusUpdate = LedgerStatusUpdate.create(header); + + final var currentPeers = this.peersView.peers().collect(Collectors.toList()); + Collections.shuffle(currentPeers); + + currentPeers.stream() + .limit(syncConfig.ledgerStatusUpdateMaxPeersToNotify()) + .map(PeersView.PeerInfo::bftNode) + .forEach( + peer -> { + if (this.ledgerStatusUpdateSendRateLimiter.tryAcquire()) { + statusUpdateDispatcher.dispatch(peer, statusUpdate); + } + }); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncConfig.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncConfig.java index 28b4832c09..c7929c5f33 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncConfig.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncConfig.java @@ -64,81 +64,66 @@ package com.radixdlt.sync; -/** - * Configuration parameters for ledger sync. - */ +/** Configuration parameters for ledger sync. */ public interface SyncConfig { - static SyncConfig of(long requestTimeout, int syncCheckMaxPeers, long syncCheckInterval) { - return of(requestTimeout, syncCheckMaxPeers, syncCheckInterval, 10, 50); - } + static SyncConfig of(long requestTimeout, int syncCheckMaxPeers, long syncCheckInterval) { + return of(requestTimeout, syncCheckMaxPeers, syncCheckInterval, 10, 50); + } - static SyncConfig of( - long requestTimeout, - int syncCheckMaxPeers, - long syncCheckInterval, - int ledgerStatusUpdateMaxPeersToNotify, - double maxLedgerUpdatesRate - ) { - return new SyncConfig() { - @Override - public long syncCheckReceiveStatusTimeout() { - return requestTimeout; - } + static SyncConfig of( + long requestTimeout, + int syncCheckMaxPeers, + long syncCheckInterval, + int ledgerStatusUpdateMaxPeersToNotify, + double maxLedgerUpdatesRate) { + return new SyncConfig() { + @Override + public long syncCheckReceiveStatusTimeout() { + return requestTimeout; + } - @Override - public long syncCheckInterval() { - return syncCheckInterval; - } + @Override + public long syncCheckInterval() { + return syncCheckInterval; + } - @Override - public int syncCheckMaxPeers() { - return syncCheckMaxPeers; - } + @Override + public int syncCheckMaxPeers() { + return syncCheckMaxPeers; + } - @Override - public long syncRequestTimeout() { - return requestTimeout; - } + @Override + public long syncRequestTimeout() { + return requestTimeout; + } - @Override - public int ledgerStatusUpdateMaxPeersToNotify() { - return ledgerStatusUpdateMaxPeersToNotify; - } + @Override + public int ledgerStatusUpdateMaxPeersToNotify() { + return ledgerStatusUpdateMaxPeersToNotify; + } - @Override - public double maxLedgerUpdatesRate() { - return maxLedgerUpdatesRate; - } - }; - } + @Override + public double maxLedgerUpdatesRate() { + return maxLedgerUpdatesRate; + } + }; + } - /** - * A timeout for receiving sync check response messages (StatusResponse). - */ - long syncCheckReceiveStatusTimeout(); + /** A timeout for receiving sync check response messages (StatusResponse). */ + long syncCheckReceiveStatusTimeout(); - /** - * An interval for executing periodic sync checks with peers. - */ - long syncCheckInterval(); + /** An interval for executing periodic sync checks with peers. */ + long syncCheckInterval(); - /** - * Maximum number of peers to use for sync check. - */ - int syncCheckMaxPeers(); + /** Maximum number of peers to use for sync check. */ + int syncCheckMaxPeers(); - /** - * A timeout for peer sync request. - */ - long syncRequestTimeout(); + /** A timeout for peer sync request. */ + long syncRequestTimeout(); - /** - * Maximum number of peers to send the LedgerStatusUpdate message to. - */ - int ledgerStatusUpdateMaxPeersToNotify(); + /** Maximum number of peers to send the LedgerStatusUpdate message to. */ + int ledgerStatusUpdateMaxPeersToNotify(); - /** - * Maximum number of LedgerStatusUpdate messages send by this node per second. - */ - double maxLedgerUpdatesRate(); + /** Maximum number of LedgerStatusUpdate messages send by this node per second. */ + double maxLedgerUpdatesRate(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncState.java index d1f69d70ce..1bb3cdb723 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncState.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/SyncState.java @@ -64,6 +64,9 @@ package com.radixdlt.sync; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; + import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -72,353 +75,333 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.utils.Pair; - import java.util.Objects; import java.util.Optional; -import static com.google.common.base.Predicates.equalTo; -import static com.google.common.base.Predicates.not; - /** - * The current state of the local sync service. - * There are 3 possible states: - * - idle: the service is not waiting for any response; it only processes local ledger updates and sync requests - * - sync check: the service is waiting for StatusResponses; it also processes local messages and timeouts + * The current state of the local sync service. There are 3 possible states: - idle: the service is + * not waiting for any response; it only processes local ledger updates and sync requests - sync + * check: the service is waiting for StatusResponses; it also processes local messages and timeouts * - syncing: the service is waiting for SyncResponse; it also processes local messages and timeouts */ public interface SyncState { - /** - * Gets the current header. - */ - LedgerProof getCurrentHeader(); - - /** - * Returns a SyncState with a new current header. - */ - SyncState withCurrentHeader(LedgerProof newCurrentHeader); - - final class IdleState implements SyncState { - private final LedgerProof currentHeader; - - public static IdleState init(LedgerProof currentHeader) { - return new IdleState(currentHeader); - } - - private IdleState(LedgerProof currentHeader) { - this.currentHeader = currentHeader; - } - - @Override - public LedgerProof getCurrentHeader() { - return this.currentHeader; - } - - @Override - public IdleState withCurrentHeader(LedgerProof newCurrentHeader) { - return new IdleState(newCurrentHeader); - } - - @Override - public String toString() { - return String.format("%s{currentHeader=%s}", this.getClass().getSimpleName(), this.currentHeader); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - IdleState idleState = (IdleState) o; - return Objects.equals(currentHeader, idleState.currentHeader); - } - - @Override - public int hashCode() { - return Objects.hash(currentHeader); - } - } - - final class SyncCheckState implements SyncState { - private final LedgerProof currentHeader; - private final ImmutableSet peersAskedForStatus; - private final ImmutableMap receivedStatusResponses; - - public static SyncCheckState init( - LedgerProof currentHeader, - ImmutableSet peersAskedForStatus - ) { - return new SyncCheckState( - currentHeader, - peersAskedForStatus, - ImmutableMap.of() - ); - } - - private SyncCheckState( - LedgerProof currentHeader, - ImmutableSet peersAskedForStatus, - ImmutableMap receivedStatusResponses - ) { - this.currentHeader = currentHeader; - this.peersAskedForStatus = peersAskedForStatus; - this.receivedStatusResponses = receivedStatusResponses; - } - - public boolean hasAskedPeer(BFTNode peer) { - return this.peersAskedForStatus.contains(peer); - } - - public boolean receivedResponseFrom(BFTNode peer) { - return this.receivedStatusResponses.containsKey(peer); - } - - public boolean gotAllResponses() { - return this.receivedStatusResponses.size() == this.peersAskedForStatus.size(); - } - - public ImmutableMap responses() { - return this.receivedStatusResponses; - } - - public SyncCheckState withStatusResponse(BFTNode peer, StatusResponse statusResponse) { - return new SyncCheckState( - currentHeader, - peersAskedForStatus, - new ImmutableMap.Builder() - .putAll(receivedStatusResponses) - .put(peer, statusResponse) - .build() - ); - } - - @Override - public LedgerProof getCurrentHeader() { - return this.currentHeader; - } - - @Override - public SyncCheckState withCurrentHeader(LedgerProof newCurrentHeader) { - return new SyncCheckState(newCurrentHeader, peersAskedForStatus, receivedStatusResponses); - } - - @Override - public String toString() { - return String.format("%s{currentHeader=%s peersAskedForStatus=%s receivedStatusResponses=%s}", - this.getClass().getSimpleName(), - this.currentHeader, - this.peersAskedForStatus, - this.receivedStatusResponses - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SyncCheckState that = (SyncCheckState) o; - return Objects.equals(currentHeader, that.currentHeader) - && Objects.equals(peersAskedForStatus, that.peersAskedForStatus) - && Objects.equals(receivedStatusResponses, that.receivedStatusResponses); - } - - @Override - public int hashCode() { - return Objects.hash(currentHeader, peersAskedForStatus, receivedStatusResponses); - } - } - - final class PendingRequest { - private final BFTNode peer; - private final long requestId; - - public static PendingRequest create(BFTNode peer, long requestId) { - return new PendingRequest(peer, requestId); - } - - private PendingRequest(BFTNode peer, long requestId) { - this.peer = peer; - this.requestId = requestId; - } - - public BFTNode getPeer() { - return peer; - } - - public long getRequestId() { - return requestId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (PendingRequest) o; - return requestId == that.requestId && Objects.equals(peer, that.peer); - } - - @Override - public int hashCode() { - return Objects.hash(peer, requestId); - } - } - - final class SyncingState implements SyncState { - private final LedgerProof currentHeader; - private final ImmutableList candidatePeersQueue; - private final LedgerProof targetHeader; - private final Optional pendingRequest; - - public static SyncingState init( - LedgerProof currentHeader, - ImmutableList candidatePeersQueue, - LedgerProof targetHeader - ) { - return new SyncingState(currentHeader, candidatePeersQueue, targetHeader, Optional.empty()); - } - - private SyncingState( - LedgerProof currentHeader, - ImmutableList candidatePeersQueue, - LedgerProof targetHeader, - Optional pendingRequest - ) { - this.currentHeader = currentHeader; - this.candidatePeersQueue = candidatePeersQueue; - this.targetHeader = targetHeader; - this.pendingRequest = pendingRequest; - } - - public SyncingState withPendingRequest(BFTNode peer, long requestId) { - return new SyncingState( - currentHeader, - candidatePeersQueue, - targetHeader, - Optional.of(PendingRequest.create(peer, requestId)) - ); - } - - public SyncingState clearPendingRequest() { - return new SyncingState(currentHeader, candidatePeersQueue, targetHeader, Optional.empty()); - } - - public SyncingState removeCandidate(BFTNode peer) { - return new SyncingState( - currentHeader, - ImmutableList.copyOf(Collections2.filter(candidatePeersQueue, not(equalTo(peer)))), - targetHeader, - pendingRequest - ); - } - - public SyncingState withTargetHeader(LedgerProof newTargetHeader) { - return new SyncingState(currentHeader, candidatePeersQueue, newTargetHeader, pendingRequest); - } - - public Pair> fetchNextCandidatePeer() { - final var peerToUse = candidatePeersQueue.stream().findFirst(); - - if (peerToUse.isPresent()) { - final var newState = new SyncingState( - currentHeader, - new ImmutableList.Builder() - .addAll(Collections2.filter(candidatePeersQueue, not(equalTo(peerToUse.get())))) - .add(peerToUse.get()) - .build(), - targetHeader, - pendingRequest - ); - - return Pair.of(newState, peerToUse); - } else { - return Pair.of(this, Optional.empty()); - } - } - - public SyncingState addCandidatePeers(ImmutableList peers) { - return new SyncingState( - currentHeader, - new ImmutableList.Builder() - .addAll(peers) - .addAll(Collections2.filter(candidatePeersQueue, not(peers::contains))) - .build(), - targetHeader, - pendingRequest - ); - } - - public boolean waitingForResponse() { - return this.pendingRequest.isPresent(); - } - - public boolean waitingForResponseFrom(BFTNode peer) { - return this.pendingRequest.stream().anyMatch(pr -> pr.getPeer().equals(peer)); - } - - public Optional getPendingRequest() { - return this.pendingRequest; - } - - public LedgerProof getTargetHeader() { - return this.targetHeader; - } - - public Optional peekNthCandidate(int n) { - var state = this; - Optional candidate = Optional.empty(); - for (int i = 0; i <= n; i++) { - final var res = state.fetchNextCandidatePeer(); - state = res.getFirst(); - candidate = res.getSecond(); - } - return candidate; - } - - @Override - public LedgerProof getCurrentHeader() { - return this.currentHeader; - } - - @Override - public SyncingState withCurrentHeader(LedgerProof newCurrentHeader) { - return new SyncingState(newCurrentHeader, candidatePeersQueue, targetHeader, pendingRequest); - } - - @Override - public String toString() { - return String.format("%s{currentHeader=%s targetHeader=%s}", - getClass().getSimpleName(), currentHeader, targetHeader); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SyncingState that = (SyncingState) o; - return Objects.equals(currentHeader, that.currentHeader) - && Objects.equals(candidatePeersQueue, that.candidatePeersQueue) - && Objects.equals(targetHeader, that.targetHeader) - && Objects.equals(pendingRequest, that.pendingRequest); - } - - @Override - public int hashCode() { - return Objects.hash(currentHeader, candidatePeersQueue, targetHeader, pendingRequest); - } - } + /** Gets the current header. */ + LedgerProof getCurrentHeader(); + + /** Returns a SyncState with a new current header. */ + SyncState withCurrentHeader(LedgerProof newCurrentHeader); + + final class IdleState implements SyncState { + private final LedgerProof currentHeader; + + public static IdleState init(LedgerProof currentHeader) { + return new IdleState(currentHeader); + } + + private IdleState(LedgerProof currentHeader) { + this.currentHeader = currentHeader; + } + + @Override + public LedgerProof getCurrentHeader() { + return this.currentHeader; + } + + @Override + public IdleState withCurrentHeader(LedgerProof newCurrentHeader) { + return new IdleState(newCurrentHeader); + } + + @Override + public String toString() { + return String.format( + "%s{currentHeader=%s}", this.getClass().getSimpleName(), this.currentHeader); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IdleState idleState = (IdleState) o; + return Objects.equals(currentHeader, idleState.currentHeader); + } + + @Override + public int hashCode() { + return Objects.hash(currentHeader); + } + } + + final class SyncCheckState implements SyncState { + private final LedgerProof currentHeader; + private final ImmutableSet peersAskedForStatus; + private final ImmutableMap receivedStatusResponses; + + public static SyncCheckState init( + LedgerProof currentHeader, ImmutableSet peersAskedForStatus) { + return new SyncCheckState(currentHeader, peersAskedForStatus, ImmutableMap.of()); + } + + private SyncCheckState( + LedgerProof currentHeader, + ImmutableSet peersAskedForStatus, + ImmutableMap receivedStatusResponses) { + this.currentHeader = currentHeader; + this.peersAskedForStatus = peersAskedForStatus; + this.receivedStatusResponses = receivedStatusResponses; + } + + public boolean hasAskedPeer(BFTNode peer) { + return this.peersAskedForStatus.contains(peer); + } + + public boolean receivedResponseFrom(BFTNode peer) { + return this.receivedStatusResponses.containsKey(peer); + } + + public boolean gotAllResponses() { + return this.receivedStatusResponses.size() == this.peersAskedForStatus.size(); + } + + public ImmutableMap responses() { + return this.receivedStatusResponses; + } + + public SyncCheckState withStatusResponse(BFTNode peer, StatusResponse statusResponse) { + return new SyncCheckState( + currentHeader, + peersAskedForStatus, + new ImmutableMap.Builder() + .putAll(receivedStatusResponses) + .put(peer, statusResponse) + .build()); + } + + @Override + public LedgerProof getCurrentHeader() { + return this.currentHeader; + } + + @Override + public SyncCheckState withCurrentHeader(LedgerProof newCurrentHeader) { + return new SyncCheckState(newCurrentHeader, peersAskedForStatus, receivedStatusResponses); + } + + @Override + public String toString() { + return String.format( + "%s{currentHeader=%s peersAskedForStatus=%s receivedStatusResponses=%s}", + this.getClass().getSimpleName(), + this.currentHeader, + this.peersAskedForStatus, + this.receivedStatusResponses); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SyncCheckState that = (SyncCheckState) o; + return Objects.equals(currentHeader, that.currentHeader) + && Objects.equals(peersAskedForStatus, that.peersAskedForStatus) + && Objects.equals(receivedStatusResponses, that.receivedStatusResponses); + } + + @Override + public int hashCode() { + return Objects.hash(currentHeader, peersAskedForStatus, receivedStatusResponses); + } + } + + final class PendingRequest { + private final BFTNode peer; + private final long requestId; + + public static PendingRequest create(BFTNode peer, long requestId) { + return new PendingRequest(peer, requestId); + } + + private PendingRequest(BFTNode peer, long requestId) { + this.peer = peer; + this.requestId = requestId; + } + + public BFTNode getPeer() { + return peer; + } + + public long getRequestId() { + return requestId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (PendingRequest) o; + return requestId == that.requestId && Objects.equals(peer, that.peer); + } + + @Override + public int hashCode() { + return Objects.hash(peer, requestId); + } + } + + final class SyncingState implements SyncState { + private final LedgerProof currentHeader; + private final ImmutableList candidatePeersQueue; + private final LedgerProof targetHeader; + private final Optional pendingRequest; + + public static SyncingState init( + LedgerProof currentHeader, + ImmutableList candidatePeersQueue, + LedgerProof targetHeader) { + return new SyncingState(currentHeader, candidatePeersQueue, targetHeader, Optional.empty()); + } + + private SyncingState( + LedgerProof currentHeader, + ImmutableList candidatePeersQueue, + LedgerProof targetHeader, + Optional pendingRequest) { + this.currentHeader = currentHeader; + this.candidatePeersQueue = candidatePeersQueue; + this.targetHeader = targetHeader; + this.pendingRequest = pendingRequest; + } + + public SyncingState withPendingRequest(BFTNode peer, long requestId) { + return new SyncingState( + currentHeader, + candidatePeersQueue, + targetHeader, + Optional.of(PendingRequest.create(peer, requestId))); + } + + public SyncingState clearPendingRequest() { + return new SyncingState(currentHeader, candidatePeersQueue, targetHeader, Optional.empty()); + } + + public SyncingState removeCandidate(BFTNode peer) { + return new SyncingState( + currentHeader, + ImmutableList.copyOf(Collections2.filter(candidatePeersQueue, not(equalTo(peer)))), + targetHeader, + pendingRequest); + } + + public SyncingState withTargetHeader(LedgerProof newTargetHeader) { + return new SyncingState(currentHeader, candidatePeersQueue, newTargetHeader, pendingRequest); + } + + public Pair> fetchNextCandidatePeer() { + final var peerToUse = candidatePeersQueue.stream().findFirst(); + + if (peerToUse.isPresent()) { + final var newState = + new SyncingState( + currentHeader, + new ImmutableList.Builder() + .addAll(Collections2.filter(candidatePeersQueue, not(equalTo(peerToUse.get())))) + .add(peerToUse.get()) + .build(), + targetHeader, + pendingRequest); + + return Pair.of(newState, peerToUse); + } else { + return Pair.of(this, Optional.empty()); + } + } + + public SyncingState addCandidatePeers(ImmutableList peers) { + return new SyncingState( + currentHeader, + new ImmutableList.Builder() + .addAll(peers) + .addAll(Collections2.filter(candidatePeersQueue, not(peers::contains))) + .build(), + targetHeader, + pendingRequest); + } + + public boolean waitingForResponse() { + return this.pendingRequest.isPresent(); + } + + public boolean waitingForResponseFrom(BFTNode peer) { + return this.pendingRequest.stream().anyMatch(pr -> pr.getPeer().equals(peer)); + } + + public Optional getPendingRequest() { + return this.pendingRequest; + } + + public LedgerProof getTargetHeader() { + return this.targetHeader; + } + + public Optional peekNthCandidate(int n) { + var state = this; + Optional candidate = Optional.empty(); + for (int i = 0; i <= n; i++) { + final var res = state.fetchNextCandidatePeer(); + state = res.getFirst(); + candidate = res.getSecond(); + } + return candidate; + } + + @Override + public LedgerProof getCurrentHeader() { + return this.currentHeader; + } + + @Override + public SyncingState withCurrentHeader(LedgerProof newCurrentHeader) { + return new SyncingState(newCurrentHeader, candidatePeersQueue, targetHeader, pendingRequest); + } + + @Override + public String toString() { + return String.format( + "%s{currentHeader=%s targetHeader=%s}", + getClass().getSimpleName(), currentHeader, targetHeader); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SyncingState that = (SyncingState) o; + return Objects.equals(currentHeader, that.currentHeader) + && Objects.equals(candidatePeersQueue, that.candidatePeersQueue) + && Objects.equals(targetHeader, that.targetHeader) + && Objects.equals(pendingRequest, that.pendingRequest); + } + + @Override + public int hashCode() { + return Objects.hash(currentHeader, candidatePeersQueue, targetHeader, pendingRequest); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/LocalSyncRequest.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/LocalSyncRequest.java index fde69613b7..fc64ae930c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/LocalSyncRequest.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/LocalSyncRequest.java @@ -69,46 +69,44 @@ import com.radixdlt.consensus.bft.BFTNode; import java.util.Objects; -/** - * A request to sync ledger to a given version. - */ +/** A request to sync ledger to a given version. */ public final class LocalSyncRequest { - private final LedgerProof target; - private final ImmutableList targetNodes; + private final LedgerProof target; + private final ImmutableList targetNodes; - public LocalSyncRequest(LedgerProof target, ImmutableList targetNodes) { - this.target = Objects.requireNonNull(target); - this.targetNodes = Objects.requireNonNull(targetNodes); - } + public LocalSyncRequest(LedgerProof target, ImmutableList targetNodes) { + this.target = Objects.requireNonNull(target); + this.targetNodes = Objects.requireNonNull(targetNodes); + } - public LedgerProof getTarget() { - return target; - } + public LedgerProof getTarget() { + return target; + } - public ImmutableList getTargetNodes() { - return targetNodes; - } + public ImmutableList getTargetNodes() { + return targetNodes; + } - @Override - public String toString() { - return String.format("%s {%s target=%s}", this.getClass().getSimpleName(), target, targetNodes); - } + @Override + public String toString() { + return String.format("%s {%s target=%s}", this.getClass().getSimpleName(), target, targetNodes); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - LocalSyncRequest that = (LocalSyncRequest) o; - return Objects.equals(target, that.target) && Objects.equals(targetNodes, that.targetNodes); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LocalSyncRequest that = (LocalSyncRequest) o; + return Objects.equals(target, that.target) && Objects.equals(targetNodes, that.targetNodes); + } - @Override - public int hashCode() { - return Objects.hash(target, targetNodes); - } + @Override + public int hashCode() { + return Objects.hash(target, targetNodes); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeout.java index c0d3e4420d..6a9d32865c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeout.java @@ -64,33 +64,30 @@ package com.radixdlt.sync.messages.local; -/** - * A message indicating a timeout on receiving StatusResponse messages during sync check. - */ +/** A message indicating a timeout on receiving StatusResponse messages during sync check. */ public final class SyncCheckReceiveStatusTimeout { - public static SyncCheckReceiveStatusTimeout create() { - return new SyncCheckReceiveStatusTimeout(); - } + public static SyncCheckReceiveStatusTimeout create() { + return new SyncCheckReceiveStatusTimeout(); + } - private SyncCheckReceiveStatusTimeout() { - } + private SyncCheckReceiveStatusTimeout() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o != null && getClass() == o.getClass(); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o != null && getClass() == o.getClass(); + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckTrigger.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckTrigger.java index 02620db7a6..576c258d03 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckTrigger.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncCheckTrigger.java @@ -64,33 +64,30 @@ package com.radixdlt.sync.messages.local; -/** - * A message indicating that the sync service should start the sync check. - */ +/** A message indicating that the sync service should start the sync check. */ public final class SyncCheckTrigger { - public static SyncCheckTrigger create() { - return new SyncCheckTrigger(); - } + public static SyncCheckTrigger create() { + return new SyncCheckTrigger(); + } - private SyncCheckTrigger() { - } + private SyncCheckTrigger() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return o != null && getClass() == o.getClass(); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return o != null && getClass() == o.getClass(); + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeout.java index fe0273c65a..0b929f0366 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeout.java @@ -72,39 +72,39 @@ */ public final class SyncLedgerUpdateTimeout { - public static SyncLedgerUpdateTimeout create(long stateVersion) { - return new SyncLedgerUpdateTimeout(stateVersion); - } + public static SyncLedgerUpdateTimeout create(long stateVersion) { + return new SyncLedgerUpdateTimeout(stateVersion); + } - private final long stateVersion; + private final long stateVersion; - private SyncLedgerUpdateTimeout(long stateVersion) { - this.stateVersion = stateVersion; - } + private SyncLedgerUpdateTimeout(long stateVersion) { + this.stateVersion = stateVersion; + } - public long stateVersion() { - return this.stateVersion; - } + public long stateVersion() { + return this.stateVersion; + } - @Override - public String toString() { - return String.format("%s{stateVersion=%s}", this.getClass().getSimpleName(), stateVersion); - } + @Override + public String toString() { + return String.format("%s{stateVersion=%s}", this.getClass().getSimpleName(), stateVersion); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (SyncLedgerUpdateTimeout) o; - return Objects.equals(stateVersion, that.stateVersion); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (SyncLedgerUpdateTimeout) o; + return Objects.equals(stateVersion, that.stateVersion); + } - @Override - public int hashCode() { - return Objects.hash(stateVersion); - } + @Override + public int hashCode() { + return Objects.hash(stateVersion); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncRequestTimeout.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncRequestTimeout.java index 6984f5a719..34c6506320 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncRequestTimeout.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/local/SyncRequestTimeout.java @@ -65,55 +65,51 @@ package com.radixdlt.sync.messages.local; import com.radixdlt.consensus.bft.BFTNode; - import java.util.Objects; -/** - * A message indicating a timeout on receiving a SyncResponse message. - */ +/** A message indicating a timeout on receiving a SyncResponse message. */ public final class SyncRequestTimeout { - private final BFTNode peer; - private final long requestId; + private final BFTNode peer; + private final long requestId; - public static SyncRequestTimeout create(BFTNode peer, long requestId) { - return new SyncRequestTimeout(peer, requestId); - } + public static SyncRequestTimeout create(BFTNode peer, long requestId) { + return new SyncRequestTimeout(peer, requestId); + } - private SyncRequestTimeout(BFTNode peer, long requestId) { - this.peer = peer; - this.requestId = requestId; - } + private SyncRequestTimeout(BFTNode peer, long requestId) { + this.peer = peer; + this.requestId = requestId; + } - public BFTNode getPeer() { - return peer; - } + public BFTNode getPeer() { + return peer; + } - public long getRequestId() { - return requestId; - } + public long getRequestId() { + return requestId; + } - @Override - public String toString() { - return String.format("%s{peer=%s requestId=%s}", - this.getClass().getSimpleName(), peer, requestId); - } + @Override + public String toString() { + return String.format( + "%s{peer=%s requestId=%s}", this.getClass().getSimpleName(), peer, requestId); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SyncRequestTimeout that = (SyncRequestTimeout) o; - return Objects.equals(peer, that.peer) - && requestId == that.requestId; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SyncRequestTimeout that = (SyncRequestTimeout) o; + return Objects.equals(peer, that.peer) && requestId == that.requestId; + } - @Override - public int hashCode() { - return Objects.hash(peer, requestId); - } + @Override + public int hashCode() { + return Objects.hash(peer, requestId); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdate.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdate.java index 6d90e9989e..f0d911f180 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdate.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdate.java @@ -65,47 +65,47 @@ package com.radixdlt.sync.messages.remote; import com.radixdlt.consensus.LedgerProof; - import java.util.Objects; /** - * A message pushed to a subset of connected non-validator nodes indicating that the ledger state has been updated. + * A message pushed to a subset of connected non-validator nodes indicating that the ledger state + * has been updated. */ public final class LedgerStatusUpdate { - private final LedgerProof header; + private final LedgerProof header; - public static LedgerStatusUpdate create(LedgerProof header) { - return new LedgerStatusUpdate(header); - } + public static LedgerStatusUpdate create(LedgerProof header) { + return new LedgerStatusUpdate(header); + } - private LedgerStatusUpdate(LedgerProof header) { - this.header = header; - } + private LedgerStatusUpdate(LedgerProof header) { + this.header = header; + } - public LedgerProof getHeader() { - return header; - } + public LedgerProof getHeader() { + return header; + } - @Override - public String toString() { - return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.header); - } + @Override + public String toString() { + return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.header); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - LedgerStatusUpdate that = (LedgerStatusUpdate) o; - return Objects.equals(header, that.header); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LedgerStatusUpdate that = (LedgerStatusUpdate) o; + return Objects.equals(header, that.header); + } - @Override - public int hashCode() { - return Objects.hash(header); - } + @Override + public int hashCode() { + return Objects.hash(header); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusRequest.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusRequest.java index de6915460f..63abd2ebc4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusRequest.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusRequest.java @@ -67,33 +67,32 @@ import com.radixdlt.middleware2.network.StatusRequestMessage; /** - * A request to get the current status of a remote node. - * Node should respond with a StatusResponse message. + * A request to get the current status of a remote node. Node should respond with a StatusResponse + * message. */ public final class StatusRequest { - public static StatusRequest create() { - return new StatusRequest(); - } + public static StatusRequest create() { + return new StatusRequest(); + } - private StatusRequest() { - } + private StatusRequest() {} - @Override - public String toString() { - return String.format("%s{}", this.getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s{}", this.getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - return (o instanceof StatusRequestMessage); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof StatusRequestMessage); + } - @Override - public int hashCode() { - return 1; - } + @Override + public int hashCode() { + return 1; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusResponse.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusResponse.java index a9825e9923..87b1e340f2 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusResponse.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/StatusResponse.java @@ -65,47 +65,44 @@ package com.radixdlt.sync.messages.remote; import com.radixdlt.consensus.LedgerProof; - import java.util.Objects; -/** - * Node status response message. - */ +/** Node status response message. */ public final class StatusResponse { - private final LedgerProof header; + private final LedgerProof header; - public static StatusResponse create(LedgerProof header) { - return new StatusResponse(header); - } + public static StatusResponse create(LedgerProof header) { + return new StatusResponse(header); + } - private StatusResponse(LedgerProof header) { - this.header = header; - } + private StatusResponse(LedgerProof header) { + this.header = header; + } - public LedgerProof getHeader() { - return header; - } + public LedgerProof getHeader() { + return header; + } - @Override - public String toString() { - return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.header); - } + @Override + public String toString() { + return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.header); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof StatusResponse)) { - return false; - } - StatusResponse that = (StatusResponse) o; - return Objects.equals(header, that.header); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof StatusResponse)) { + return false; + } + StatusResponse that = (StatusResponse) o; + return Objects.equals(header, that.header); + } - @Override - public int hashCode() { - return Objects.hash(header); - } + @Override + public int hashCode() { + return Objects.hash(header); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncRequest.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncRequest.java index 72f6a2adde..d4e8e5bf7d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncRequest.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncRequest.java @@ -65,48 +65,47 @@ package com.radixdlt.sync.messages.remote; import com.radixdlt.ledger.DtoLedgerProof; - import java.util.Objects; /** - * A request to sync up ledger state, starting at the given header. - * The node should respond with a SyncResponse message. + * A request to sync up ledger state, starting at the given header. The node should respond with a + * SyncResponse message. */ public final class SyncRequest { - private final DtoLedgerProof header; + private final DtoLedgerProof header; - public static SyncRequest create(DtoLedgerProof header) { - return new SyncRequest(header); - } + public static SyncRequest create(DtoLedgerProof header) { + return new SyncRequest(header); + } - private SyncRequest(DtoLedgerProof header) { - this.header = header; - } + private SyncRequest(DtoLedgerProof header) { + this.header = header; + } - public DtoLedgerProof getHeader() { - return header; - } + public DtoLedgerProof getHeader() { + return header; + } - @Override - public String toString() { - return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.header); - } + @Override + public String toString() { + return String.format("%s{header=%s}", this.getClass().getSimpleName(), this.header); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SyncRequest)) { - return false; - } - SyncRequest that = (SyncRequest) o; - return Objects.equals(header, that.header); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SyncRequest)) { + return false; + } + SyncRequest that = (SyncRequest) o; + return Objects.equals(header, that.header); + } - @Override - public int hashCode() { - return Objects.hash(header); - } + @Override + public int hashCode() { + return Objects.hash(header); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncResponse.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncResponse.java index ec2d9059de..a0a575acb8 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncResponse.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/messages/remote/SyncResponse.java @@ -65,47 +65,45 @@ package com.radixdlt.sync.messages.remote; import com.radixdlt.ledger.DtoTxnsAndProof; - import java.util.Objects; -/** - * A response to the SyncRequest message. - */ +/** A response to the SyncRequest message. */ public final class SyncResponse { - private final DtoTxnsAndProof commandsAndProof; + private final DtoTxnsAndProof commandsAndProof; - public static SyncResponse create(DtoTxnsAndProof commandsAndProof) { - return new SyncResponse(commandsAndProof); - } + public static SyncResponse create(DtoTxnsAndProof commandsAndProof) { + return new SyncResponse(commandsAndProof); + } - private SyncResponse(DtoTxnsAndProof commandsAndProof) { - this.commandsAndProof = Objects.requireNonNull(commandsAndProof); - } + private SyncResponse(DtoTxnsAndProof commandsAndProof) { + this.commandsAndProof = Objects.requireNonNull(commandsAndProof); + } - public DtoTxnsAndProof getTxnsAndProof() { - return commandsAndProof; - } + public DtoTxnsAndProof getTxnsAndProof() { + return commandsAndProof; + } - @Override - public String toString() { - return String.format("%s{commandsAndProof=%s}", this.getClass().getSimpleName(), commandsAndProof); - } + @Override + public String toString() { + return String.format( + "%s{commandsAndProof=%s}", this.getClass().getSimpleName(), commandsAndProof); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SyncResponse)) { - return false; - } - SyncResponse that = (SyncResponse) o; - return Objects.equals(commandsAndProof, that.commandsAndProof); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SyncResponse)) { + return false; + } + SyncResponse that = (SyncResponse) o; + return Objects.equals(commandsAndProof, that.commandsAndProof); + } - @Override - public int hashCode() { - return Objects.hash(commandsAndProof); - } + @Override + public int hashCode() { + return Objects.hash(commandsAndProof); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifier.java index b87c330a56..5099f3ea7c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifier.java @@ -65,47 +65,44 @@ package com.radixdlt.sync.validation; import com.google.inject.Inject; +import com.radixdlt.consensus.ConsensusHasher; import com.radixdlt.consensus.HashVerifier; -import com.radixdlt.crypto.Hasher; import com.radixdlt.consensus.TimestampedECDSASignature; -import com.radixdlt.consensus.ConsensusHasher; import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.crypto.Hasher; import com.radixdlt.sync.messages.remote.SyncResponse; - import java.util.Map.Entry; import java.util.Objects; -/** - * Verifies the signatures in a sync response - */ +/** Verifies the signatures in a sync response */ public final class RemoteSyncResponseSignaturesVerifier { - private final Hasher hasher; - private final HashVerifier hashVerifier; - - @Inject - public RemoteSyncResponseSignaturesVerifier(Hasher hasher, HashVerifier hashVerifier) { - this.hasher = Objects.requireNonNull(hasher); - this.hashVerifier = Objects.requireNonNull(hashVerifier); - } + private final Hasher hasher; + private final HashVerifier hashVerifier; - public boolean verifyResponseSignatures(SyncResponse syncResponse) { - var commandsAndProof = syncResponse.getTxnsAndProof(); - var endHeader = commandsAndProof.getTail(); + @Inject + public RemoteSyncResponseSignaturesVerifier(Hasher hasher, HashVerifier hashVerifier) { + this.hasher = Objects.requireNonNull(hasher); + this.hashVerifier = Objects.requireNonNull(hashVerifier); + } - var opaque = endHeader.getOpaque(); - var header = endHeader.getLedgerHeader(); - var signatures = endHeader.getSignatures().getSignatures(); - for (Entry nodeAndSignature : signatures.entrySet()) { - var node = nodeAndSignature.getKey(); - var signature = nodeAndSignature.getValue(); - final var voteDataHash = ConsensusHasher.toHash(opaque, header, signature.timestamp(), hasher); - if (!hashVerifier.verify(node.getKey(), voteDataHash, signature.signature())) { - return false; - } - } + public boolean verifyResponseSignatures(SyncResponse syncResponse) { + var commandsAndProof = syncResponse.getTxnsAndProof(); + var endHeader = commandsAndProof.getTail(); - return true; - } + var opaque = endHeader.getOpaque(); + var header = endHeader.getLedgerHeader(); + var signatures = endHeader.getSignatures().getSignatures(); + for (Entry nodeAndSignature : signatures.entrySet()) { + var node = nodeAndSignature.getKey(); + var signature = nodeAndSignature.getValue(); + final var voteDataHash = + ConsensusHasher.toHash(opaque, header, signature.timestamp(), hasher); + if (!hashVerifier.verify(node.getKey(), voteDataHash, signature.signature())) { + return false; + } + } + return true; + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifier.java index 69b97225f6..8f84d909b4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifier.java @@ -66,29 +66,32 @@ import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.sync.messages.remote.SyncResponse; - import java.util.Objects; /** - * Verifies the signature set of sync remote responses and checks - * whether the signatures form a quorum based on a validatorSet. + * Verifies the signature set of sync remote responses and checks whether the signatures form a + * quorum based on a validatorSet. */ public class RemoteSyncResponseValidatorSetVerifier { - private final BFTValidatorSet validatorSet; + private final BFTValidatorSet validatorSet; - public RemoteSyncResponseValidatorSetVerifier(BFTValidatorSet validatorSet) { - this.validatorSet = Objects.requireNonNull(validatorSet); - } + public RemoteSyncResponseValidatorSetVerifier(BFTValidatorSet validatorSet) { + this.validatorSet = Objects.requireNonNull(validatorSet); + } - public boolean verifyValidatorSet(SyncResponse syncResponse) { - final var dtoCommandsAndProof = syncResponse.getTxnsAndProof(); - final var validationState = validatorSet.newValidationState(); + public boolean verifyValidatorSet(SyncResponse syncResponse) { + final var dtoCommandsAndProof = syncResponse.getTxnsAndProof(); + final var validationState = validatorSet.newValidationState(); - dtoCommandsAndProof.getTail().getSignatures().getSignatures().forEach((node, signature) -> - validationState.addSignature(node, signature.timestamp(), signature.signature()) - ); + dtoCommandsAndProof + .getTail() + .getSignatures() + .getSignatures() + .forEach( + (node, signature) -> + validationState.addSignature(node, signature.timestamp(), signature.signature())); - return validationState.complete(); - } + return validationState.complete(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/InMemorySystemInfo.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/InMemorySystemInfo.java index d5336012df..dc38c62401 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/InMemorySystemInfo.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/InMemorySystemInfo.java @@ -67,83 +67,79 @@ import com.google.inject.Inject; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.bft.BFTHighQCUpdate; -import com.radixdlt.consensus.epoch.EpochChange; -import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.consensus.bft.BFTCommittedUpdate; +import com.radixdlt.consensus.bft.BFTHighQCUpdate; import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.epoch.EpochChange; import com.radixdlt.consensus.epoch.EpochView; +import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.environment.EventProcessor; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.store.LastEpochProof; import com.radixdlt.store.LastProof; - import java.util.concurrent.atomic.AtomicReference; -/** - * Manages system information to be consumed by clients such as the api. - */ +/** Manages system information to be consumed by clients such as the api. */ public final class InMemorySystemInfo { - private final AtomicReference lastTimeout = new AtomicReference<>(); - private final AtomicReference currentView = new AtomicReference<>(EpochView.of(0L, View.genesis())); - private final AtomicReference highQC = new AtomicReference<>(); - private final AtomicReference ledgerProof; - private final AtomicReference epochsLedgerProof; + private final AtomicReference lastTimeout = new AtomicReference<>(); + private final AtomicReference currentView = + new AtomicReference<>(EpochView.of(0L, View.genesis())); + private final AtomicReference highQC = new AtomicReference<>(); + private final AtomicReference ledgerProof; + private final AtomicReference epochsLedgerProof; - @Inject - public InMemorySystemInfo( - @LastProof LedgerProof lastProof, - @LastEpochProof LedgerProof lastEpochProof - ) { - this.ledgerProof = new AtomicReference<>(lastProof); - this.epochsLedgerProof = new AtomicReference<>(lastEpochProof); - } + @Inject + public InMemorySystemInfo( + @LastProof LedgerProof lastProof, @LastEpochProof LedgerProof lastEpochProof) { + this.ledgerProof = new AtomicReference<>(lastProof); + this.epochsLedgerProof = new AtomicReference<>(lastEpochProof); + } - public void processTimeout(EpochLocalTimeoutOccurrence timeout) { - lastTimeout.set(timeout); - } + public void processTimeout(EpochLocalTimeoutOccurrence timeout) { + lastTimeout.set(timeout); + } - public void processView(EpochView epochView) { - currentView.set(epochView); - } + public void processView(EpochView epochView) { + currentView.set(epochView); + } - public EventProcessor ledgerUpdateEventProcessor() { - return update -> { - this.ledgerProof.set(update.getTail()); - var epochChange = update.getStateComputerOutput().getInstance(EpochChange.class); - if (epochChange != null) { - epochsLedgerProof.set(update.getTail()); - } - }; - } + public EventProcessor ledgerUpdateEventProcessor() { + return update -> { + this.ledgerProof.set(update.getTail()); + var epochChange = update.getStateComputerOutput().getInstance(EpochChange.class); + if (epochChange != null) { + epochsLedgerProof.set(update.getTail()); + } + }; + } - public EventProcessor bftHighQCEventProcessor() { - return update -> this.highQC.set(update.getHighQC().highestQC()); - } + public EventProcessor bftHighQCEventProcessor() { + return update -> this.highQC.set(update.getHighQC().highestQC()); + } - public EventProcessor bftCommittedUpdateEventProcessor() { - return update -> { - this.highQC.set(update.getVertexStoreState().getHighQC().highestQC()); - }; - } + public EventProcessor bftCommittedUpdateEventProcessor() { + return update -> { + this.highQC.set(update.getVertexStoreState().getHighQC().highestQC()); + }; + } - public LedgerProof getCurrentProof() { - return ledgerProof.get(); - } + public LedgerProof getCurrentProof() { + return ledgerProof.get(); + } - public LedgerProof getEpochProof() { - return epochsLedgerProof.get(); - } + public LedgerProof getEpochProof() { + return epochsLedgerProof.get(); + } - public EpochView getCurrentView() { - return this.currentView.get(); - } + public EpochView getCurrentView() { + return this.currentView.get(); + } - public EpochLocalTimeoutOccurrence getLastTimeout() { - return this.lastTimeout.get(); - } + public EpochLocalTimeoutOccurrence getLastTimeout() { + return this.lastTimeout.get(); + } - public QuorumCertificate getHighestQC() { - return this.highQC.get(); - } + public QuorumCertificate getHighestQC() { + return this.highQC.get(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/Compress.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/Compress.java index d09a85b0ea..400c5e7305 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/Compress.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/Compress.java @@ -64,53 +64,48 @@ package com.radixdlt.utils; -import org.xerial.snappy.Snappy; -import org.xerial.snappy.SnappyFramedInputStream; -import org.xerial.snappy.SnappyFramedOutputStream; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import org.xerial.snappy.Snappy; +import org.xerial.snappy.SnappyFramedInputStream; +import org.xerial.snappy.SnappyFramedOutputStream; -/** - * Common utility methods for compression/decompression. - */ +/** Common utility methods for compression/decompression. */ public class Compress { - private Compress() { - throw new UnsupportedOperationException("Utility class should not be instantiated."); - } + private Compress() { + throw new UnsupportedOperationException("Utility class should not be instantiated."); + } - /** - * Compresses input byte array into output byte array. - * - * @param input source data to compress - * @return compressed output. - * - * @throws IOException - */ - public static byte[] compress(byte[] input) throws IOException { - try (var out = new ByteArrayOutputStream(); - var os = new SnappyFramedOutputStream(out)) { - os.transferFrom(new ByteArrayInputStream(input)); - os.flush(); - return out.toByteArray(); - } - } + /** + * Compresses input byte array into output byte array. + * + * @param input source data to compress + * @return compressed output. + * @throws IOException + */ + public static byte[] compress(byte[] input) throws IOException { + try (var out = new ByteArrayOutputStream(); + var os = new SnappyFramedOutputStream(out)) { + os.transferFrom(new ByteArrayInputStream(input)); + os.flush(); + return out.toByteArray(); + } + } - /** - * Decompresses input byte array into output byte array. - * - * @param input source data to decompress - * @return decompressed output. - * - * @throws IOException - */ - public static byte[] uncompress(byte[] input) throws IOException { - try (var is = new SnappyFramedInputStream(new ByteArrayInputStream(input)); - var os = new ByteArrayOutputStream(Snappy.uncompressedLength(input))) { - is.transferTo(os); - os.flush(); - return os.toByteArray(); - } - } + /** + * Decompresses input byte array into output byte array. + * + * @param input source data to decompress + * @return decompressed output. + * @throws IOException + */ + public static byte[] uncompress(byte[] input) throws IOException { + try (var is = new SnappyFramedInputStream(new ByteArrayInputStream(input)); + var os = new ByteArrayOutputStream(Snappy.uncompressedLength(input))) { + is.transferTo(os); + os.flush(); + return os.toByteArray(); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/DurationParser.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/DurationParser.java index 1dff565017..2b15d4bb4b 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/DurationParser.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/DurationParser.java @@ -1,134 +1,136 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.utils; - -import com.google.common.collect.ImmutableMap; - -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalUnit; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.util.Optional.ofNullable; - -/** - * Utility method for parsing strings containing description of duration. - * - * Recognized string format: - *
- *     [0-9]+(s|m|h)?
- * 
- * Leading number represents value duration and must be greater than zero. Fractional values are not supported. - * Optional trailing character defines units of duration value: - *
    - *
  • s - second(s)
  • - *
  • m - minute(s)
  • - *
  • h - hour(s)
  • - *
- * If units are omitted or not recognized then seconds are assumed by default. - */ -public final class DurationParser { - private DurationParser() { - } - - private static final Pattern DURATION_PATTERN = Pattern.compile("(\\d+)(\\w)?"); - private static final String DEFAULT_UNITS = "s"; - private static final Map UNIT_MAP = ImmutableMap.of( - "s", ChronoUnit.SECONDS, - "m", ChronoUnit.MINUTES, - "h", ChronoUnit.HOURS - ); - - /** - * Parse input string into duration value. - * - * @param input Input string - * @return {@link Optional} with parsed value. If input is empty or value is less or equal to 0 then - * empty instance is returned. - */ - public static Optional parse(String input) { - Objects.requireNonNull(input); - - final Matcher matcher = DURATION_PATTERN.matcher(input.toLowerCase()); - - if (!matcher.find()) { - return Optional.empty(); - } - - long value = Long.parseLong(matcher.group(1)); - - if (value <= 0) { - return Optional.empty(); - } - - final String units = ofNullable(matcher.group(2)).orElse(DEFAULT_UNITS); - - return ofNullable(UNIT_MAP.get(units)).map(unit -> Duration.of(value, unit)); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +import static java.util.Optional.ofNullable; + +import com.google.common.collect.ImmutableMap; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility method for parsing strings containing description of duration. + * + *

Recognized string format: + * + *

+ *     [0-9]+(s|m|h)?
+ * 
+ * + * Leading number represents value duration and must be greater than zero. Fractional values are not + * supported. Optional trailing character defines units of duration value: + * + *
    + *
  • s - second(s) + *
  • m - minute(s) + *
  • h - hour(s) + *
+ * + * If units are omitted or not recognized then seconds are assumed by default. + */ +public final class DurationParser { + private DurationParser() {} + + private static final Pattern DURATION_PATTERN = Pattern.compile("(\\d+)(\\w)?"); + private static final String DEFAULT_UNITS = "s"; + private static final Map UNIT_MAP = + ImmutableMap.of( + "s", ChronoUnit.SECONDS, + "m", ChronoUnit.MINUTES, + "h", ChronoUnit.HOURS); + + /** + * Parse input string into duration value. + * + * @param input Input string + * @return {@link Optional} with parsed value. If input is empty or value is less or equal to 0 + * then empty instance is returned. + */ + public static Optional parse(String input) { + Objects.requireNonNull(input); + + final Matcher matcher = DURATION_PATTERN.matcher(input.toLowerCase()); + + if (!matcher.find()) { + return Optional.empty(); + } + + long value = Long.parseLong(matcher.group(1)); + + if (value <= 0) { + return Optional.empty(); + } + + final String units = ofNullable(matcher.group(2)).orElse(DEFAULT_UNITS); + + return ofNullable(UNIT_MAP.get(units)).map(unit -> Duration.of(value, unit)); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/MemoryLeakDetector.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/MemoryLeakDetector.java index 9d43b4c3bd..d9f26a6b1f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/MemoryLeakDetector.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/MemoryLeakDetector.java @@ -67,100 +67,102 @@ import com.google.common.collect.Comparators; import com.google.common.collect.EvictingQueue; import com.sun.management.GarbageCollectionNotificationInfo; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.management.ListenerNotFoundException; -import javax.management.Notification; -import javax.management.NotificationEmitter; -import javax.management.NotificationListener; -import javax.management.openmbean.CompositeData; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import javax.management.ListenerNotFoundException; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class MemoryLeakDetector { - private static final Logger log = LogManager.getLogger(); - - private static final String OLD_GEN_GC_NAME = "G1 Old Gen"; - - private static final int CONSECUTIVE_INCREASE_ALERT_THRESHOLD = 3; - - private final EvictingQueue memTrail = EvictingQueue.create(CONSECUTIVE_INCREASE_ALERT_THRESHOLD + 1); - - private final Map registeredListeners = new HashMap<>(); - - public static MemoryLeakDetector start() { - return new MemoryLeakDetector(); - } - - private MemoryLeakDetector() { - log.info("Starting memory leak detector..."); - ManagementFactory.getGarbageCollectorMXBeans() - .forEach(this::registerGCListener); - } - - public void stop() { - ManagementFactory.getGarbageCollectorMXBeans() - .forEach(this::unregisterGCListener); - memTrail.clear(); - registeredListeners.clear(); - } - - private void unregisterGCListener(GarbageCollectorMXBean garbageCollectorMXBean) { - final NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorMXBean; - final NotificationListener listener = registeredListeners.get(garbageCollectorMXBean.getName()); - if (listener != null) { - try { - notificationEmitter.removeNotificationListener(listener); - } catch (ListenerNotFoundException e) { - // nothing - } - } - } - - private void registerGCListener(GarbageCollectorMXBean garbageCollectorMXBean) { - final NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorMXBean; - final NotificationListener listener = this::handleNotification; - registeredListeners.put(garbageCollectorMXBean.getName(), listener); - notificationEmitter.addNotificationListener(listener, null, null); - } - - private void handleNotification(Notification notification, Object handback) { - if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { - final GarbageCollectionNotificationInfo info = - GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); - - final long oldGenMemUsageBeforeGc = info.getGcInfo() - .getMemoryUsageBeforeGc().get(OLD_GEN_GC_NAME).getUsed(); - - final long oldGenMemUsageAfterGc = info.getGcInfo() - .getMemoryUsageAfterGc().get(OLD_GEN_GC_NAME).getUsed(); - - if (oldGenMemUsageAfterGc < oldGenMemUsageBeforeGc) { // we're only considering GC runs where old gen was cleaned - memTrail.offer(oldGenMemUsageAfterGc); - - final boolean consecutivelyIncreasing = Comparators.isInStrictOrder(memTrail, Long::compare); - - if (consecutivelyIncreasing && memTrail.remainingCapacity() == 0) { - final String memTrailStr = memTrail.stream() - .map(n -> Long.toString(toMiB(n))) - .collect(Collectors.joining(", ")); - log.info( - "Memory (old gen) after current GC: {}MiB (steady increase since {} runs)." - + " Previous values (MiB): [{}]", - toMiB(oldGenMemUsageAfterGc), CONSECUTIVE_INCREASE_ALERT_THRESHOLD, memTrailStr - ); - memTrail.clear(); // reset the state - } - } - } - } - - private long toMiB(long valueInBytes) { - return valueInBytes / (1024L * 1024L); - } + private static final Logger log = LogManager.getLogger(); + + private static final String OLD_GEN_GC_NAME = "G1 Old Gen"; + + private static final int CONSECUTIVE_INCREASE_ALERT_THRESHOLD = 3; + + private final EvictingQueue memTrail = + EvictingQueue.create(CONSECUTIVE_INCREASE_ALERT_THRESHOLD + 1); + + private final Map registeredListeners = new HashMap<>(); + + public static MemoryLeakDetector start() { + return new MemoryLeakDetector(); + } + + private MemoryLeakDetector() { + log.info("Starting memory leak detector..."); + ManagementFactory.getGarbageCollectorMXBeans().forEach(this::registerGCListener); + } + + public void stop() { + ManagementFactory.getGarbageCollectorMXBeans().forEach(this::unregisterGCListener); + memTrail.clear(); + registeredListeners.clear(); + } + + private void unregisterGCListener(GarbageCollectorMXBean garbageCollectorMXBean) { + final NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorMXBean; + final NotificationListener listener = registeredListeners.get(garbageCollectorMXBean.getName()); + if (listener != null) { + try { + notificationEmitter.removeNotificationListener(listener); + } catch (ListenerNotFoundException e) { + // nothing + } + } + } + + private void registerGCListener(GarbageCollectorMXBean garbageCollectorMXBean) { + final NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorMXBean; + final NotificationListener listener = this::handleNotification; + registeredListeners.put(garbageCollectorMXBean.getName(), listener); + notificationEmitter.addNotificationListener(listener, null, null); + } + + private void handleNotification(Notification notification, Object handback) { + if (notification + .getType() + .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { + final GarbageCollectionNotificationInfo info = + GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); + + final long oldGenMemUsageBeforeGc = + info.getGcInfo().getMemoryUsageBeforeGc().get(OLD_GEN_GC_NAME).getUsed(); + + final long oldGenMemUsageAfterGc = + info.getGcInfo().getMemoryUsageAfterGc().get(OLD_GEN_GC_NAME).getUsed(); + + if (oldGenMemUsageAfterGc + < oldGenMemUsageBeforeGc) { // we're only considering GC runs where old gen was cleaned + memTrail.offer(oldGenMemUsageAfterGc); + + final boolean consecutivelyIncreasing = + Comparators.isInStrictOrder(memTrail, Long::compare); + + if (consecutivelyIncreasing && memTrail.remainingCapacity() == 0) { + final String memTrailStr = + memTrail.stream().map(n -> Long.toString(toMiB(n))).collect(Collectors.joining(", ")); + log.info( + "Memory (old gen) after current GC: {}MiB (steady increase since {} runs)." + + " Previous values (MiB): [{}]", + toMiB(oldGenMemUsageAfterGc), + CONSECUTIVE_INCREASE_ALERT_THRESHOLD, + memTrailStr); + memTrail.clear(); // reset the state + } + } + } + } + + private long toMiB(long valueInBytes) { + return valueInBytes / (1024L * 1024L); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RTTStatistics.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RTTStatistics.java index e0fffde4af..9a13275cdd 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RTTStatistics.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RTTStatistics.java @@ -64,86 +64,88 @@ package com.radixdlt.utils; -/** - * Class to keep track of round-trip-time statistics. - */ +/** Class to keep track of round-trip-time statistics. */ public final class RTTStatistics { - private double minRTT = Double.MAX_VALUE; - private double maxRTT = 0.0; - private double sumRTT = 0.0; - private double sumSquareRTT = 0.0; - private long countRTT = 0L; + private double minRTT = Double.MAX_VALUE; + private double maxRTT = 0.0; + private double sumRTT = 0.0; + private double sumSquareRTT = 0.0; + private long countRTT = 0L; - /** - * Return the minimum seen RTT by the {@link #update(double)} method, - * or {@code Double.MAX_VALUE} if {@link #count()} is zero. - * - * @return The minimum seen RTT value - */ - public double min() { - return this.minRTT; - } + /** + * Return the minimum seen RTT by the {@link #update(double)} method, or {@code Double.MAX_VALUE} + * if {@link #count()} is zero. + * + * @return The minimum seen RTT value + */ + public double min() { + return this.minRTT; + } - /** - * Return the maximum seen RTT by the {@link #update(double)} method, - * or {@code 0.0} if {@link #count()} is zero. - * - * @return The maximum seen RTT value - */ - public double max() { - return this.maxRTT; - } + /** + * Return the maximum seen RTT by the {@link #update(double)} method, or {@code 0.0} if {@link + * #count()} is zero. + * + * @return The maximum seen RTT value + */ + public double max() { + return this.maxRTT; + } - /** - * Return the mean RTT by the {@link #update(double)} method, - * or {@code Double.MAX_VALUE} if {@link #count()} is zero. - * - * @return The mean RTT values - */ - public double mean() { - if (this.countRTT == 0) { - return Double.MAX_VALUE; - } - return this.sumRTT / this.countRTT; - } + /** + * Return the mean RTT by the {@link #update(double)} method, or {@code Double.MAX_VALUE} if + * {@link #count()} is zero. + * + * @return The mean RTT values + */ + public double mean() { + if (this.countRTT == 0) { + return Double.MAX_VALUE; + } + return this.sumRTT / this.countRTT; + } - /** - * Return the second central moment of the RTT values seen by the - * {@link #update(double)} method, or {@code Double.MAX_VALUE} if - * {@link #count()} is zero. - * - * @return The second central moment RTT values - */ - public double sigma() { - return Math.sqrt(this.sumSquareRTT / this.countRTT - Math.pow(mean(), 2.0)); - } + /** + * Return the second central moment of the RTT values seen by the {@link #update(double)} method, + * or {@code Double.MAX_VALUE} if {@link #count()} is zero. + * + * @return The second central moment RTT values + */ + public double sigma() { + return Math.sqrt(this.sumSquareRTT / this.countRTT - Math.pow(mean(), 2.0)); + } - /** - * Return the count of the RTT values accumulated so far. - * - * @return the count of the RTT values accumulated so far - */ - public long count() { - return this.countRTT; - } + /** + * Return the count of the RTT values accumulated so far. + * + * @return the count of the RTT values accumulated so far + */ + public long count() { + return this.countRTT; + } - /** - * Update the statistics with a new sample time. Exact units are left - * to the caller to decide. - * - * @param duration the duration in units specified by the caller - */ - public void update(double duration) { - this.minRTT = Math.min(this.minRTT, duration); - this.maxRTT = Math.max(this.maxRTT, duration); - this.sumRTT += duration; - this.sumSquareRTT += Math.pow(duration, 2.0); - this.countRTT += 1; - } + /** + * Update the statistics with a new sample time. Exact units are left to the caller to decide. + * + * @param duration the duration in units specified by the caller + */ + public void update(double duration) { + this.minRTT = Math.min(this.minRTT, duration); + this.maxRTT = Math.max(this.maxRTT, duration); + this.sumRTT += duration; + this.sumSquareRTT += Math.pow(duration, 2.0); + this.countRTT += 1; + } - @Override - public String toString() { - return String.format("%s[min %s/max %s/mean %s/sigma %s/count %s]", - getClass().getSimpleName(), this.min(), this.max(), this.mean(), this.sigma(), this.count()); - } -} \ No newline at end of file + @Override + public String toString() { + return String.format( + "%s[min %s/max %s/mean %s/sigma %s/count %s]", + getClass().getSimpleName(), + this.min(), + this.max(), + this.mean(), + this.sigma(), + this.count()); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RateCalculator.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RateCalculator.java index 4d7fc360a5..1c094044b3 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RateCalculator.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/RateCalculator.java @@ -65,27 +65,23 @@ package com.radixdlt.utils; import com.google.common.collect.EvictingQueue; - import java.time.Duration; public final class RateCalculator { - private final Duration interval; - private final EvictingQueue ticks; - - public RateCalculator(Duration interval, int maxEntries) { - this.interval = interval; - this.ticks = EvictingQueue.create(maxEntries); - } + private final Duration interval; + private final EvictingQueue ticks; - public void tick() { - ticks.offer(System.currentTimeMillis()); - } + public RateCalculator(Duration interval, int maxEntries) { + this.interval = interval; + this.ticks = EvictingQueue.create(maxEntries); + } - public long currentRate() { - final var minTime = System.currentTimeMillis() - interval.toMillis(); - return ticks.stream() - .filter(v -> v >= minTime) - .count(); - } + public void tick() { + ticks.offer(System.currentTimeMillis()); + } + public long currentRate() { + final var minTime = System.currentTimeMillis() - interval.toMillis(); + return ticks.stream().filter(v -> v >= minTime).count(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/ThreadFactories.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/ThreadFactories.java index d6553ef920..78dc4ea7c1 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/ThreadFactories.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/ThreadFactories.java @@ -67,71 +67,63 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; -/** - * Utility methods for creating thread factories. - */ +/** Utility methods for creating thread factories. */ public final class ThreadFactories { - private ThreadFactories() { - throw new IllegalStateException("Can't construct"); - } + private ThreadFactories() { + throw new IllegalStateException("Can't construct"); + } - static class CountingThreadFactory implements ThreadFactory { - private final String nameFormat; - private final boolean daemon; - private final AtomicLong counter = new AtomicLong(1L); + static class CountingThreadFactory implements ThreadFactory { + private final String nameFormat; + private final boolean daemon; + private final AtomicLong counter = new AtomicLong(1L); - CountingThreadFactory(String nameFormat, boolean daemon) { - this.nameFormat = nameFormat; - this.daemon = daemon; - } + CountingThreadFactory(String nameFormat, boolean daemon) { + this.nameFormat = nameFormat; + this.daemon = daemon; + } - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r, String.format(nameFormat, counter.getAndIncrement())); - t.setDaemon(this.daemon); - return t; - } + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, String.format(nameFormat, counter.getAndIncrement())); + t.setDaemon(this.daemon); + return t; + } - @Override - public String toString() { - return String.format("%s[%s:%s]", getClass().getSimpleName(), this.daemon, this.nameFormat); - } - } + @Override + public String toString() { + return String.format("%s[%s:%s]", getClass().getSimpleName(), this.daemon, this.nameFormat); + } + } - /** - * Creates a {@link ThreadFactory} that uses the specified format string - * to create threads with identifiable names. The threads created by the - * factory will be daemon threads. - * - * @param nameFormat a {@link String#format(String, Object...)}-compatible - * format {@link String}, to which a unique integer (0, 1, etc.) will - * be supplied as the single parameter. This integer will be unique - * and assigned sequentially as each thread is constructed. For example, - * {@code "rpc-pool-%d"} will generate thread names like - * {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, - * etc. - * @return the newly constructed {@link ThreadFactory} - */ - public static ThreadFactory daemonThreads(String nameFormat) { - return new CountingThreadFactory(nameFormat, true); - } + /** + * Creates a {@link ThreadFactory} that uses the specified format string to create threads with + * identifiable names. The threads created by the factory will be daemon threads. + * + * @param nameFormat a {@link String#format(String, Object...)}-compatible format {@link String}, + * to which a unique integer (0, 1, etc.) will be supplied as the single parameter. This + * integer will be unique and assigned sequentially as each thread is constructed. For + * example, {@code "rpc-pool-%d"} will generate thread names like {@code "rpc-pool-0"}, {@code + * "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * @return the newly constructed {@link ThreadFactory} + */ + public static ThreadFactory daemonThreads(String nameFormat) { + return new CountingThreadFactory(nameFormat, true); + } - /** - * Creates a {@link ThreadFactory} that uses the specified format string - * to create threads with identifiable names. The threads created by the - * factory will be non-daemon threads. - * - * @param nameFormat a {@link String#format(String, Object...)}-compatible - * format {@link String}, to which a unique integer (0, 1, etc.) will - * be supplied as the single parameter. This integer will be unique - * and assigned sequentially as each thread is constructed. For example, - * {@code "rpc-pool-%d"} will generate thread names like - * {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, - * etc. - * @return the newly constructed {@link ThreadFactory} - */ - public static ThreadFactory threads(String nameFormat) { - return new CountingThreadFactory(nameFormat, false); - } + /** + * Creates a {@link ThreadFactory} that uses the specified format string to create threads with + * identifiable names. The threads created by the factory will be non-daemon threads. + * + * @param nameFormat a {@link String#format(String, Object...)}-compatible format {@link String}, + * to which a unique integer (0, 1, etc.) will be supplied as the single parameter. This + * integer will be unique and assigned sequentially as each thread is constructed. For + * example, {@code "rpc-pool-%d"} will generate thread names like {@code "rpc-pool-0"}, {@code + * "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * @return the newly constructed {@link ThreadFactory} + */ + public static ThreadFactory threads(String nameFormat) { + return new CountingThreadFactory(nameFormat, false); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/TimeSupplier.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/TimeSupplier.java index 34cbf154f5..6f43be1b91 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/TimeSupplier.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/utils/TimeSupplier.java @@ -64,18 +64,13 @@ package com.radixdlt.utils; -/** - * Represents a supplier of time in milliseconds since 0:00, January 1, - * 1970 UTC. - */ +/** Represents a supplier of time in milliseconds since 0:00, January 1, 1970 UTC. */ @FunctionalInterface public interface TimeSupplier { - /** - * Returns the current time as the number of milliseconds since - * 00:00, January 1, 1970 UTC. - * - * @return the difference in milliseconds between the current - * time and 00:00, January 1, 1970 UTC. - */ - long currentTime(); + /** + * Returns the current time as the number of milliseconds since 00:00, January 1, 1970 UTC. + * + * @return the difference in milliseconds between the current time and 00:00, January 1, 1970 UTC. + */ + long currentTime(); } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/GenerateUniverses.java b/radixdlt-core/radixdlt/src/main/java/org/radix/GenerateUniverses.java index e77f9eb950..dd60429979 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/GenerateUniverses.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/GenerateUniverses.java @@ -64,46 +64,37 @@ package org.radix; +import com.google.common.collect.ImmutableList; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; import com.google.inject.multibindings.OptionalBinder; -import com.radixdlt.atom.actions.StakeTokens; +import com.radixdlt.CryptoModule; import com.radixdlt.application.tokens.Amount; +import com.radixdlt.atom.TxAction; +import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; -import com.radixdlt.networks.Addressing; -import com.radixdlt.networks.Network; -import com.radixdlt.statecomputer.forks.Forks; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.utils.PrivateKeys; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.json.JSONObject; - -import com.google.common.collect.ImmutableList; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.TypeLiteral; -import com.radixdlt.CryptoModule; -import com.radixdlt.atom.TxAction; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.ledger.LedgerAccumulator; import com.radixdlt.ledger.SimpleLedgerAccumulatorAndVerifier; +import com.radixdlt.networks.Addressing; +import com.radixdlt.networks.Network; import com.radixdlt.statecomputer.MaxValidators; import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.statecomputer.checkpoint.GenesisProvider; +import com.radixdlt.statecomputer.forks.Forks; import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERules; import com.radixdlt.utils.Bytes; +import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; - import java.io.BufferedWriter; import java.io.FileWriter; import java.security.Security; @@ -113,108 +104,139 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.json.JSONObject; public final class GenerateUniverses { - private GenerateUniverses() { } - - private static final UInt256 DEFAULT_ISSUANCE = Amount.ofTokens(1000000000).toSubunits(); // 1 Billion! - private static final UInt256 DEFAULT_STAKE = Amount.ofTokens(100).toSubunits(); - private static final String mnemomicKeyHex = "0236856ea9fa8c243e45fc94ec27c29cf3f17e3a9e19a410ee4a41f4858e379918"; - - public static void main(String[] args) throws Exception { - Security.insertProviderAt(new BouncyCastleProvider(), 1); - - Options options = new Options(); - options.addOption("h", "help", false, "Show usage information (this message)"); - options.addOption("p", "public-keys", true, "Specify validator keys"); - options.addOption("v", "validator-count", true, "Specify number of validators to generate"); - - CommandLineParser parser = new DefaultParser(); - CommandLine cmd = parser.parse(options, args); - if (!cmd.getArgList().isEmpty()) { - System.err.println("Extra arguments: " + String.join(" ", cmd.getArgList())); - usage(options); - return; - } - - if (cmd.hasOption('h')) { - usage(options); - return; - } - - var validatorKeys = new HashSet(); - if (cmd.getOptionValue("p") != null) { - var hexKeys = cmd.getOptionValue("p").split(","); - for (var hexKey : hexKeys) { - validatorKeys.add(ECPublicKey.fromHex(hexKey)); - } - } - final int validatorsCount = cmd.getOptionValue("v") != null ? Integer.parseInt(cmd.getOptionValue("v")) : 0; - var generatedValidatorKeys = PrivateKeys.numeric(6) - .limit(validatorsCount) - .collect(Collectors.toList()); - generatedValidatorKeys.stream().map(ECKeyPair::getPublicKey).forEach(validatorKeys::add); - - // Issuances to mnemomic account, keys 1-5, and 1st validator - final var mnemomicKey = ECPublicKey.fromHex(mnemomicKeyHex); - final ImmutableList.Builder tokenIssuancesBuilder = ImmutableList.builder(); - tokenIssuancesBuilder.add(TokenIssuance.of(mnemomicKey, DEFAULT_ISSUANCE)); - PrivateKeys.numeric(1) - .limit(5) - .map(k -> TokenIssuance.of(k.getPublicKey(), DEFAULT_ISSUANCE)) - .forEach(tokenIssuancesBuilder::add); - // Issue tokens to initial validators for now to support application services - validatorKeys.forEach(pk -> tokenIssuancesBuilder.add(TokenIssuance.of(pk, DEFAULT_ISSUANCE))); - - // Stakes issued by mnemomic account - var stakes = validatorKeys.stream() - .map(pk -> new StakeTokens(REAddr.ofPubKeyAccount(mnemomicKey), pk, DEFAULT_STAKE)) - .collect(Collectors.toSet()); - - var timestamp = String.valueOf(Instant.now().getEpochSecond()); - - var genesisProvider = Guice.createInjector(new AbstractModule() { - @Provides - @Singleton - RERules reRules(Forks forks) { - return forks.get(0); - } - - @Override - protected void configure() { - install(new CryptoModule()); - install(new MainnetForkConfigsModule()); - install(new ForksModule()); - bind(new TypeLiteral>() {}).annotatedWith(Genesis.class).toInstance(List.of()); - bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); - bind(SystemCounters.class).toInstance(new SystemCountersImpl()); - bindConstant().annotatedWith(Genesis.class).to(timestamp); - bind(new TypeLiteral>() { }).annotatedWith(Genesis.class).toInstance(stakes); - bind(new TypeLiteral>() {}).annotatedWith(Genesis.class) - .toInstance(tokenIssuancesBuilder.build()); - bind(new TypeLiteral>() {}).annotatedWith(Genesis.class).toInstance(validatorKeys); - bindConstant().annotatedWith(MaxValidators.class).to(100); - OptionalBinder.newOptionalBinder(binder(), Key.get(new TypeLiteral>() { }, Genesis.class)); - - } - }).getInstance(GenesisProvider.class); - - var genesis = genesisProvider.get().getTxns().get(0); - IntStream.range(0, generatedValidatorKeys.size()).forEach(i -> { - System.out.format("export RADIXDLT_VALIDATOR_%s_PRIVKEY=%s%n", i, Bytes.toBase64String(generatedValidatorKeys.get(i).getPrivateKey())); - System.out.format("export RADIXDLT_VALIDATOR_%s_PUBKEY=%s%n", i, Addressing.ofNetwork(Network.LOCALNET).forNodes().of(generatedValidatorKeys.get(i).getPublicKey())); - }); - if (validatorsCount > 0) { - System.out.format("export RADIXDLT_GENESIS_TXN=%s%n", Bytes.toHexString(genesis.getPayload())); - } else { - try (var writer = new BufferedWriter(new FileWriter("genesis.json"))) { - writer.write(new JSONObject().put("genesis", Bytes.toHexString(genesis.getPayload())).toString()); - } - } - } - - private static void usage(Options options) { - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp(GenerateUniverses.class.getSimpleName(), options, true); - } -} \ No newline at end of file + private GenerateUniverses() {} + + private static final UInt256 DEFAULT_ISSUANCE = + Amount.ofTokens(1000000000).toSubunits(); // 1 Billion! + private static final UInt256 DEFAULT_STAKE = Amount.ofTokens(100).toSubunits(); + private static final String mnemomicKeyHex = + "0236856ea9fa8c243e45fc94ec27c29cf3f17e3a9e19a410ee4a41f4858e379918"; + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + + Options options = new Options(); + options.addOption("h", "help", false, "Show usage information (this message)"); + options.addOption("p", "public-keys", true, "Specify validator keys"); + options.addOption("v", "validator-count", true, "Specify number of validators to generate"); + + CommandLineParser parser = new DefaultParser(); + CommandLine cmd = parser.parse(options, args); + if (!cmd.getArgList().isEmpty()) { + System.err.println("Extra arguments: " + String.join(" ", cmd.getArgList())); + usage(options); + return; + } + + if (cmd.hasOption('h')) { + usage(options); + return; + } + + var validatorKeys = new HashSet(); + if (cmd.getOptionValue("p") != null) { + var hexKeys = cmd.getOptionValue("p").split(","); + for (var hexKey : hexKeys) { + validatorKeys.add(ECPublicKey.fromHex(hexKey)); + } + } + final int validatorsCount = + cmd.getOptionValue("v") != null ? Integer.parseInt(cmd.getOptionValue("v")) : 0; + var generatedValidatorKeys = + PrivateKeys.numeric(6).limit(validatorsCount).collect(Collectors.toList()); + generatedValidatorKeys.stream().map(ECKeyPair::getPublicKey).forEach(validatorKeys::add); + + // Issuances to mnemomic account, keys 1-5, and 1st validator + final var mnemomicKey = ECPublicKey.fromHex(mnemomicKeyHex); + final ImmutableList.Builder tokenIssuancesBuilder = ImmutableList.builder(); + tokenIssuancesBuilder.add(TokenIssuance.of(mnemomicKey, DEFAULT_ISSUANCE)); + PrivateKeys.numeric(1) + .limit(5) + .map(k -> TokenIssuance.of(k.getPublicKey(), DEFAULT_ISSUANCE)) + .forEach(tokenIssuancesBuilder::add); + // Issue tokens to initial validators for now to support application services + validatorKeys.forEach(pk -> tokenIssuancesBuilder.add(TokenIssuance.of(pk, DEFAULT_ISSUANCE))); + + // Stakes issued by mnemomic account + var stakes = + validatorKeys.stream() + .map(pk -> new StakeTokens(REAddr.ofPubKeyAccount(mnemomicKey), pk, DEFAULT_STAKE)) + .collect(Collectors.toSet()); + + var timestamp = String.valueOf(Instant.now().getEpochSecond()); + + var genesisProvider = + Guice.createInjector( + new AbstractModule() { + @Provides + @Singleton + RERules reRules(Forks forks) { + return forks.get(0); + } + + @Override + protected void configure() { + install(new CryptoModule()); + install(new MainnetForkConfigsModule()); + install(new ForksModule()); + bind(new TypeLiteral>() {}) + .annotatedWith(Genesis.class) + .toInstance(List.of()); + bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); + bind(SystemCounters.class).toInstance(new SystemCountersImpl()); + bindConstant().annotatedWith(Genesis.class).to(timestamp); + bind(new TypeLiteral>() {}) + .annotatedWith(Genesis.class) + .toInstance(stakes); + bind(new TypeLiteral>() {}) + .annotatedWith(Genesis.class) + .toInstance(tokenIssuancesBuilder.build()); + bind(new TypeLiteral>() {}) + .annotatedWith(Genesis.class) + .toInstance(validatorKeys); + bindConstant().annotatedWith(MaxValidators.class).to(100); + OptionalBinder.newOptionalBinder( + binder(), Key.get(new TypeLiteral>() {}, Genesis.class)); + } + }) + .getInstance(GenesisProvider.class); + + var genesis = genesisProvider.get().getTxns().get(0); + IntStream.range(0, generatedValidatorKeys.size()) + .forEach( + i -> { + System.out.format( + "export RADIXDLT_VALIDATOR_%s_PRIVKEY=%s%n", + i, Bytes.toBase64String(generatedValidatorKeys.get(i).getPrivateKey())); + System.out.format( + "export RADIXDLT_VALIDATOR_%s_PUBKEY=%s%n", + i, + Addressing.ofNetwork(Network.LOCALNET) + .forNodes() + .of(generatedValidatorKeys.get(i).getPublicKey())); + }); + if (validatorsCount > 0) { + System.out.format( + "export RADIXDLT_GENESIS_TXN=%s%n", Bytes.toHexString(genesis.getPayload())); + } else { + try (var writer = new BufferedWriter(new FileWriter("genesis.json"))) { + writer.write( + new JSONObject().put("genesis", Bytes.toHexString(genesis.getPayload())).toString()); + } + } + } + + private static void usage(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(GenerateUniverses.class.getSimpleName(), options, true); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/Radix.java b/radixdlt-core/radixdlt/src/main/java/org/radix/Radix.java index cb8e70466e..ce7084cf0e 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/Radix.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/Radix.java @@ -64,14 +64,6 @@ package org.radix; -import io.undertow.Undertow; -import org.apache.commons.cli.ParseException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.json.JSONObject; -import org.radix.utils.IOUtils; - import com.google.common.annotations.VisibleForTesting; import com.google.inject.Guice; import com.google.inject.Key; @@ -85,7 +77,7 @@ import com.radixdlt.network.p2p.transport.PeerServerBootstrap; import com.radixdlt.properties.RuntimeProperties; import com.radixdlt.utils.MemoryLeakDetector; - +import io.undertow.Undertow; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; @@ -95,193 +87,207 @@ import java.util.Map; import java.util.Objects; import java.util.Properties; +import org.apache.commons.cli.ParseException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.json.JSONObject; +import org.radix.utils.IOUtils; public final class Radix { - private static final Logger log = LogManager.getLogger(); - - private Radix() { } - - private static final String SYSTEM_VERSION_DISPLAY; - private static final String SYSTEM_VERSION_BRANCH; - private static final String SYSTEM_VERSION_COMMIT; - private static final Map> SYSTEM_VERSION_INFO; - - public static final String SYSTEM_VERSION_KEY = "system_version"; - public static final String VERSION_STRING_KEY = "version_string"; - - static { - System.setProperty("java.net.preferIPv4Stack", "true"); - - var branch = "unknown-branch"; - var commit = "unknown-commit"; - var display = "unknown-version"; - var map = new HashMap(); - - try (var is = Radix.class.getResourceAsStream("/version.properties")) { - if (is != null) { - var p = new Properties(); - p.load(is); - branch = p.getProperty("VERSION_BRANCH", branch); - commit = p.getProperty("VERSION_COMMIT", commit); - display = p.getProperty("VERSION_DISPLAY", display); - - for (var key : p.stringPropertyNames()) { - var mapKey = key.split("_", 2)[1].toLowerCase(Locale.US); - var defaultValue = "unknown-" + mapKey; - - map.put(mapKey, p.getProperty(key, defaultValue)); - } - } - } catch (IOException e) { - // Ignore exception - } - - SYSTEM_VERSION_DISPLAY = display; - SYSTEM_VERSION_BRANCH = branch; - SYSTEM_VERSION_COMMIT = commit; - - map.put(VERSION_STRING_KEY, calculateVersionString(map)); - - SYSTEM_VERSION_INFO = Map.of(SYSTEM_VERSION_KEY, Map.copyOf(map)); - } - - private static final Object BC_LOCK = new Object(); - private static boolean bcInitialised; - - private static void setupBouncyCastle() { - synchronized (BC_LOCK) { - if (bcInitialised) { - log.warn("Bouncy castle is already initialised"); - return; - } - - Security.insertProviderAt(new BouncyCastleProvider(), 1); - bcInitialised = true; - } - } - - public static void main(String[] args) { - try { - MemoryLeakDetector.start(); - - logVersion(); - dumpExecutionLocation(); - // Bouncy Castle is required for loading the node key, so set it up now. - setupBouncyCastle(); - - RuntimeProperties properties = loadProperties(args); - start(properties); - } catch (Exception ex) { - log.fatal("Unable to start", ex); - LogManager.shutdown(); // Flush any async logs - java.lang.System.exit(-1); - } - } - - private static void logVersion() { - log.always().log( - "Radix distributed ledger '{}' from branch '{}' commit '{}'", - SYSTEM_VERSION_DISPLAY, SYSTEM_VERSION_BRANCH, SYSTEM_VERSION_COMMIT - ); - } - - public static Map> systemVersionInfo() { - return SYSTEM_VERSION_INFO; - } - - public static void start(RuntimeProperties properties) { - long start = System.currentTimeMillis(); - var injector = Guice.createInjector(new RadixNodeModule(properties)); - - final Map moduleRunners = injector.getInstance(Key.get(new TypeLiteral>() { })); - - final var p2pNetworkRunner = moduleRunners.get(Runners.P2P_NETWORK); - p2pNetworkRunner.start(); - - final var systemInfoRunner = moduleRunners.get(Runners.SYSTEM_INFO); - systemInfoRunner.start(); - - final var syncRunner = moduleRunners.get(Runners.SYNC); - syncRunner.start(); - - final var mempoolReceiverRunner = moduleRunners.get(Runners.MEMPOOL); - mempoolReceiverRunner.start(); - - final var peerServer = injector.getInstance(PeerServerBootstrap.class); - try { - peerServer.start(); - } catch (InterruptedException e) { - log.error("Cannot start p2p server", e); - } - - final var undertow = injector.getInstance(Undertow.class); - undertow.start(); - - final var consensusRunner = moduleRunners.get(Runners.CONSENSUS); - consensusRunner.start(); - - final BFTNode self = injector.getInstance(Key.get(BFTNode.class, Self.class)); - long finish = System.currentTimeMillis(); - var systemCounters = injector.getInstance(SystemCounters.class); - systemCounters.set(SystemCounters.CounterType.STARTUP_TIME_MS, finish - start); - log.info("Node '{}' started successfully in {} seconds", self, (finish - start) / 1000); - } - - private static void dumpExecutionLocation() { - try { - String jarFile = Radix.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); - System.setProperty("radix.jar", jarFile); - - String jarPath = jarFile; - - if (jarPath.toLowerCase().endsWith(".jar")) { - jarPath = jarPath.substring(0, jarPath.lastIndexOf('/')); - } - System.setProperty("radix.jar.path", jarPath); - - log.debug("Execution file: {}", System.getProperty("radix.jar")); - log.debug("Execution path: {}", System.getProperty("radix.jar.path")); - } catch (URISyntaxException e) { - throw new IllegalStateException("Error while fetching execution location", e); - } - } - - private static RuntimeProperties loadProperties(String[] args) throws IOException, ParseException { - JSONObject runtimeConfigurationJSON = new JSONObject(); - try (InputStream is = Radix.class.getResourceAsStream("/runtime_options.json")) { - if (is != null) { - runtimeConfigurationJSON = new JSONObject(IOUtils.toString(is)); - } - } - return new RuntimeProperties(runtimeConfigurationJSON, args); - } - - @VisibleForTesting - static String calculateVersionString(Map details) { - if (isCleanTag(details)) { - return lastTag(details); - } else { - var version = branchName(details) == null - ? "detached-head-" + gitHash(details) - : (lastTag(details) + "-" + branchName(details)).replace('/', '~') + "-" + gitHash(details); - - return version; - } - } - - private static boolean isCleanTag(Map details) { - return Objects.equals(details.get("tag"), details.get("last_tag")); - } - - private static String lastTag(Map details) { - return (String) details.get("last_tag"); - } - - private static String gitHash(Map details) { - return (String) details.get("build"); - } - - private static String branchName(Map details) { - return (String) details.get("branch"); - } + private static final Logger log = LogManager.getLogger(); + + private Radix() {} + + private static final String SYSTEM_VERSION_DISPLAY; + private static final String SYSTEM_VERSION_BRANCH; + private static final String SYSTEM_VERSION_COMMIT; + private static final Map> SYSTEM_VERSION_INFO; + + public static final String SYSTEM_VERSION_KEY = "system_version"; + public static final String VERSION_STRING_KEY = "version_string"; + + static { + System.setProperty("java.net.preferIPv4Stack", "true"); + + var branch = "unknown-branch"; + var commit = "unknown-commit"; + var display = "unknown-version"; + var map = new HashMap(); + + try (var is = Radix.class.getResourceAsStream("/version.properties")) { + if (is != null) { + var p = new Properties(); + p.load(is); + branch = p.getProperty("VERSION_BRANCH", branch); + commit = p.getProperty("VERSION_COMMIT", commit); + display = p.getProperty("VERSION_DISPLAY", display); + + for (var key : p.stringPropertyNames()) { + var mapKey = key.split("_", 2)[1].toLowerCase(Locale.US); + var defaultValue = "unknown-" + mapKey; + + map.put(mapKey, p.getProperty(key, defaultValue)); + } + } + } catch (IOException e) { + // Ignore exception + } + + SYSTEM_VERSION_DISPLAY = display; + SYSTEM_VERSION_BRANCH = branch; + SYSTEM_VERSION_COMMIT = commit; + + map.put(VERSION_STRING_KEY, calculateVersionString(map)); + + SYSTEM_VERSION_INFO = Map.of(SYSTEM_VERSION_KEY, Map.copyOf(map)); + } + + private static final Object BC_LOCK = new Object(); + private static boolean bcInitialised; + + private static void setupBouncyCastle() { + synchronized (BC_LOCK) { + if (bcInitialised) { + log.warn("Bouncy castle is already initialised"); + return; + } + + Security.insertProviderAt(new BouncyCastleProvider(), 1); + bcInitialised = true; + } + } + + public static void main(String[] args) { + try { + MemoryLeakDetector.start(); + + logVersion(); + dumpExecutionLocation(); + // Bouncy Castle is required for loading the node key, so set it up now. + setupBouncyCastle(); + + RuntimeProperties properties = loadProperties(args); + start(properties); + } catch (Exception ex) { + log.fatal("Unable to start", ex); + LogManager.shutdown(); // Flush any async logs + java.lang.System.exit(-1); + } + } + + private static void logVersion() { + log.always() + .log( + "Radix distributed ledger '{}' from branch '{}' commit '{}'", + SYSTEM_VERSION_DISPLAY, + SYSTEM_VERSION_BRANCH, + SYSTEM_VERSION_COMMIT); + } + + public static Map> systemVersionInfo() { + return SYSTEM_VERSION_INFO; + } + + public static void start(RuntimeProperties properties) { + long start = System.currentTimeMillis(); + var injector = Guice.createInjector(new RadixNodeModule(properties)); + + final Map moduleRunners = + injector.getInstance(Key.get(new TypeLiteral>() {})); + + final var p2pNetworkRunner = moduleRunners.get(Runners.P2P_NETWORK); + p2pNetworkRunner.start(); + + final var systemInfoRunner = moduleRunners.get(Runners.SYSTEM_INFO); + systemInfoRunner.start(); + + final var syncRunner = moduleRunners.get(Runners.SYNC); + syncRunner.start(); + + final var mempoolReceiverRunner = moduleRunners.get(Runners.MEMPOOL); + mempoolReceiverRunner.start(); + + final var peerServer = injector.getInstance(PeerServerBootstrap.class); + try { + peerServer.start(); + } catch (InterruptedException e) { + log.error("Cannot start p2p server", e); + } + + final var undertow = injector.getInstance(Undertow.class); + undertow.start(); + + final var consensusRunner = moduleRunners.get(Runners.CONSENSUS); + consensusRunner.start(); + + final BFTNode self = injector.getInstance(Key.get(BFTNode.class, Self.class)); + long finish = System.currentTimeMillis(); + var systemCounters = injector.getInstance(SystemCounters.class); + systemCounters.set(SystemCounters.CounterType.STARTUP_TIME_MS, finish - start); + log.info("Node '{}' started successfully in {} seconds", self, (finish - start) / 1000); + } + + private static void dumpExecutionLocation() { + try { + String jarFile = + Radix.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + System.setProperty("radix.jar", jarFile); + + String jarPath = jarFile; + + if (jarPath.toLowerCase().endsWith(".jar")) { + jarPath = jarPath.substring(0, jarPath.lastIndexOf('/')); + } + System.setProperty("radix.jar.path", jarPath); + + log.debug("Execution file: {}", System.getProperty("radix.jar")); + log.debug("Execution path: {}", System.getProperty("radix.jar.path")); + } catch (URISyntaxException e) { + throw new IllegalStateException("Error while fetching execution location", e); + } + } + + private static RuntimeProperties loadProperties(String[] args) + throws IOException, ParseException { + JSONObject runtimeConfigurationJSON = new JSONObject(); + try (InputStream is = Radix.class.getResourceAsStream("/runtime_options.json")) { + if (is != null) { + runtimeConfigurationJSON = new JSONObject(IOUtils.toString(is)); + } + } + return new RuntimeProperties(runtimeConfigurationJSON, args); + } + + @VisibleForTesting + static String calculateVersionString(Map details) { + if (isCleanTag(details)) { + return lastTag(details); + } else { + var version = + branchName(details) == null + ? "detached-head-" + gitHash(details) + : (lastTag(details) + "-" + branchName(details)).replace('/', '~') + + "-" + + gitHash(details); + + return version; + } + } + + private static boolean isCleanTag(Map details) { + return Objects.equals(details.get("tag"), details.get("last_tag")); + } + + private static String lastTag(Map details) { + return (String) details.get("last_tag"); + } + + private static String gitHash(Map details) { + return (String) details.get("build"); + } + + private static String branchName(Map details) { + return (String) details.get("branch"); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/TokenIssuance.java b/radixdlt-core/radixdlt/src/main/java/org/radix/TokenIssuance.java index 36cc87b817..a26e1d601b 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/TokenIssuance.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/TokenIssuance.java @@ -64,37 +64,34 @@ package org.radix; -import java.util.Objects; - import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.utils.UInt256; +import java.util.Objects; -/** - * An initial issuance of tokens to the specified key. - */ +/** An initial issuance of tokens to the specified key. */ public final class TokenIssuance { - private final ECPublicKey receiver; - private final UInt256 amount; + private final ECPublicKey receiver; + private final UInt256 amount; - private TokenIssuance(ECPublicKey receiver, UInt256 amount) { - this.receiver = Objects.requireNonNull(receiver); - this.amount = Objects.requireNonNull(amount); - } + private TokenIssuance(ECPublicKey receiver, UInt256 amount) { + this.receiver = Objects.requireNonNull(receiver); + this.amount = Objects.requireNonNull(amount); + } - public static TokenIssuance of(ECPublicKey receiver, UInt256 amount) { - return new TokenIssuance(receiver, amount); - } + public static TokenIssuance of(ECPublicKey receiver, UInt256 amount) { + return new TokenIssuance(receiver, amount); + } - public ECPublicKey receiver() { - return this.receiver; - } + public ECPublicKey receiver() { + return this.receiver; + } - public UInt256 amount() { - return this.amount; - } + public UInt256 amount() { + return this.amount; + } - @Override - public String toString() { - return String.format("%s[->%s:%s]", getClass().getSimpleName(), this.receiver, this.amount); - } -} \ No newline at end of file + @Override + public String toString() { + return String.format("%s[->%s:%s]", getClass().getSimpleName(), this.receiver, this.amount); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/containers/BasicContainer.java b/radixdlt-core/radixdlt/src/main/java/org/radix/containers/BasicContainer.java index 5b611b1a60..5b43d942fc 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/containers/BasicContainer.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/containers/BasicContainer.java @@ -1,91 +1,89 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.containers; - -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.SerializeWithHid; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; - -import com.fasterxml.jackson.annotation.JsonProperty; - -@SerializeWithHid -public abstract class BasicContainer { - // Placeholder for the serializer ID - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - protected BasicContainer() { - } - - @Override - public String toString() { - return this.getClass().toString() + " " + this.hashCode(); - } - - public abstract short version(); -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.containers; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.SerializeWithHid; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; + +@SerializeWithHid +public abstract class BasicContainer { + // Placeholder for the serializer ID + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + protected BasicContainer() {} + + @Override + public String toString() { + return this.getClass().toString() + " " + this.hashCode(); + } + + public abstract short version(); +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SSLFix.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SSLFix.java index e949fab583..78e12a72c9 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SSLFix.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SSLFix.java @@ -67,101 +67,106 @@ import java.net.Socket; import java.security.GeneralSecurityException; import java.security.cert.CertificateException; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.X509ExtendedTrustManager; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; class SSLFix { - private SSLFix() { - throw new IllegalStateException("Can't construct"); - } - - private static final Logger log = LogManager.getLogger(); - - // This, and the code that uses it, is here to placate sonar. - // It should always be true, unless you do not want to use TLS etc to connect to - // other sites - static boolean standardVerifyResult = true; - - static X509ExtendedTrustManager[] trustAllHosts() { - try { - X509ExtendedTrustManager[] trustAllCerts = new X509ExtendedTrustManager[] { - new X509ExtendedTrustManager() { - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[0]; - } - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) - throws CertificateException { - checkClient(); - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) - throws CertificateException { - checkServer(); - } - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) - throws CertificateException { - checkClient(); - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) - throws CertificateException { - checkServer(); - } - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) - throws CertificateException { - checkClient(); - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) - throws CertificateException { - checkServer(); - } - - private void checkClient() throws CertificateException { - if (!standardVerifyResult) { - throw new CertificateException("Client not trusted by default."); - } - } - - private void checkServer() throws CertificateException { - if (!standardVerifyResult) { - throw new CertificateException("Server not trusted by default."); - } - } - } - }; - - SSLContext sc = SSLContext.getInstance("TLSv1.2"); - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - - // Create all-trusting host name verifier - HostnameVerifier allHostsValid = (hostname, session) -> standardVerifyResult; - - // Install the all-trusting host verifier - HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); - return trustAllCerts; - } catch (GeneralSecurityException e) { - log.error("Error occurred", e); - throw new IllegalStateException("Error occurred installing trust manager", e); - } - } + private SSLFix() { + throw new IllegalStateException("Can't construct"); + } + + private static final Logger log = LogManager.getLogger(); + + // This, and the code that uses it, is here to placate sonar. + // It should always be true, unless you do not want to use TLS etc to connect to + // other sites + static boolean standardVerifyResult = true; + + static X509ExtendedTrustManager[] trustAllHosts() { + try { + X509ExtendedTrustManager[] trustAllCerts = + new X509ExtendedTrustManager[] { + new X509ExtendedTrustManager() { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[0]; + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + checkClient(); + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + checkServer(); + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] xcs, String string, Socket socket) + throws CertificateException { + checkClient(); + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] xcs, String string, Socket socket) + throws CertificateException { + checkServer(); + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) + throws CertificateException { + checkClient(); + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) + throws CertificateException { + checkServer(); + } + + private void checkClient() throws CertificateException { + if (!standardVerifyResult) { + throw new CertificateException("Client not trusted by default."); + } + } + + private void checkServer() throws CertificateException { + if (!standardVerifyResult) { + throw new CertificateException("Server not trusted by default."); + } + } + } + }; + + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + + // Create all-trusting host name verifier + HostnameVerifier allHostsValid = (hostname, session) -> standardVerifyResult; + + // Install the all-trusting host verifier + HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); + return trustAllCerts; + } catch (GeneralSecurityException e) { + log.error("Error occurred", e); + throw new IllegalStateException("Error occurred installing trust manager", e); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SeedNodesConfigParser.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SeedNodesConfigParser.java index dfca9100a4..17fb9e5fed 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SeedNodesConfigParser.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/SeedNodesConfigParser.java @@ -64,6 +64,14 @@ package org.radix.network.discovery; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.radixdlt.network.p2p.P2PConfig; +import com.radixdlt.network.p2p.RadixNodeUri; +import com.radixdlt.networks.Addressing; +import com.radixdlt.networks.NetworkId; +import com.radixdlt.serialization.DeserializeException; +import com.radixdlt.utils.Pair; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; @@ -72,73 +80,62 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import com.google.inject.Inject; -import com.radixdlt.identifiers.NodeAddressing; -import com.radixdlt.network.p2p.RadixNodeUri; -import com.radixdlt.network.p2p.P2PConfig; -import com.radixdlt.networks.Addressing; -import com.radixdlt.networks.NetworkId; -import com.radixdlt.serialization.DeserializeException; -import com.radixdlt.utils.Pair; -import com.google.common.collect.ImmutableSet; // TODO: move to PeerDiscovery public final class SeedNodesConfigParser { - private final int defaultPort; - private final Set unresolvedUris = new HashSet<>(); - private final Set resolvedSeedNodes = new HashSet<>(); - private final Addressing addressing; - private final int networkId; + private final int defaultPort; + private final Set unresolvedUris = new HashSet<>(); + private final Set resolvedSeedNodes = new HashSet<>(); + private final Addressing addressing; + private final int networkId; - @Inject - public SeedNodesConfigParser(P2PConfig config, @NetworkId int networkId, Addressing addressing) { - this.networkId = networkId; - this.addressing = addressing; - this.defaultPort = config.defaultPort(); - this.unresolvedUris.addAll(config.seedNodes()); - this.resolveHostNames(); - } + @Inject + public SeedNodesConfigParser(P2PConfig config, @NetworkId int networkId, Addressing addressing) { + this.networkId = networkId; + this.addressing = addressing; + this.defaultPort = config.defaultPort(); + this.unresolvedUris.addAll(config.seedNodes()); + this.resolveHostNames(); + } - public Set getResolvedSeedNodes() { - this.resolveHostNames(); - return this.resolvedSeedNodes; - } + public Set getResolvedSeedNodes() { + this.resolveHostNames(); + return this.resolvedSeedNodes; + } - private void resolveHostNames() { - if (this.unresolvedUris.isEmpty()) { - return; - } + private void resolveHostNames() { + if (this.unresolvedUris.isEmpty()) { + return; + } - final var newlyResolvedHosts = this.unresolvedUris.stream() - .map(host -> Pair.of(host, resolveRadixNodeUri(host))) - .filter(p -> p.getSecond().isPresent()) - .collect(ImmutableSet.toImmutableSet()); + final var newlyResolvedHosts = + this.unresolvedUris.stream() + .map(host -> Pair.of(host, resolveRadixNodeUri(host))) + .filter(p -> p.getSecond().isPresent()) + .collect(ImmutableSet.toImmutableSet()); - final var newlyResolvedHostsNames = newlyResolvedHosts.stream().map(Pair::getFirst) - .collect(ImmutableSet.toImmutableSet()); + final var newlyResolvedHostsNames = + newlyResolvedHosts.stream().map(Pair::getFirst).collect(ImmutableSet.toImmutableSet()); - this.unresolvedUris.removeAll(newlyResolvedHostsNames); + this.unresolvedUris.removeAll(newlyResolvedHostsNames); - this.resolvedSeedNodes.addAll( - newlyResolvedHosts.stream() - .map(p -> p.getSecond().get()) - .collect(Collectors.toList()) - ); - } + this.resolvedSeedNodes.addAll( + newlyResolvedHosts.stream().map(p -> p.getSecond().get()).collect(Collectors.toList())); + } - private Optional resolveRadixNodeUri(String rawUri) { - try { - final var parsedUri = new URI(rawUri); - final var resolved = InetAddress.getByName(parsedUri.getHost()); - // FIXME: This is a bit messy, should have clearer logic on the checks - return Optional.of(RadixNodeUri.fromPubKeyAndAddress( - networkId, - addressing.forNodes().parse(parsedUri.getUserInfo()), - resolved.getHostAddress(), - parsedUri.getPort() > 0 ? parsedUri.getPort() : defaultPort - )); - } catch (UnknownHostException | URISyntaxException | DeserializeException e) { - return Optional.empty(); - } - } + private Optional resolveRadixNodeUri(String rawUri) { + try { + final var parsedUri = new URI(rawUri); + final var resolved = InetAddress.getByName(parsedUri.getHost()); + // FIXME: This is a bit messy, should have clearer logic on the checks + return Optional.of( + RadixNodeUri.fromPubKeyAndAddress( + networkId, + addressing.forNodes().parse(parsedUri.getUserInfo()), + resolved.getHostAddress(), + parsedUri.getPort() > 0 ? parsedUri.getPort() : defaultPort)); + } catch (UnknownHostException | URISyntaxException | DeserializeException e) { + return Optional.empty(); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/Whitelist.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/Whitelist.java index dcb8ef5efa..48ec2b5493 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/Whitelist.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/discovery/Whitelist.java @@ -1,215 +1,213 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.network.discovery; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import com.radixdlt.properties.RuntimeProperties; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class Whitelist { - private static final Logger networkLog = LogManager.getLogger(); - - private Set parameters = new HashSet<>(); - - public Whitelist(String parameters) { - if (parameters == null) { - return; - } - - String[] split = parameters.split(","); - - for (String parameter : split) { - if (parameter.trim().length() == 0) { - continue; - } - - this.parameters.add(parameter.trim()); - } - } - - private int[] convert(String host) { - String[] segments; - int[] output; - - if (host.contains(".")) { // IPV4 // - output = new int[4]; - segments = host.split("\\."); - } else if (host.contains(":")) { // IPV6 // - output = new int[8]; - segments = host.split(":"); - } else { - return new int[]{0, 0, 0, 0}; - } - - Arrays.fill(output, Integer.MAX_VALUE); - for (int s = 0; s < segments.length; s++) { - if (segments[s].equalsIgnoreCase("*")) { - break; - } - - output[s] = Integer.valueOf(segments[s]); - } - - return output; - } - - private boolean isRange(String parameter) { - if (parameter.contains("-")) { - return true; - } - - return false; - } - - private boolean isInRange(String parameter, String address) { - String[] hosts = parameter.split("-"); - - if (hosts.length != 2) { - throw new IllegalStateException("Range is invalid"); - } - - int[] target = convert(address); - int[] low = convert(hosts[0]); - int[] high = convert(hosts[1]); - - if (low.length != high.length || target.length != low.length) { - return false; - } - - for (int s = 0; s < low.length; s++) { - if (low[s] < high[s]) { - int[] swap = low; - low = high; - high = swap; - break; - } - - if (target[s] < low[s] || target[s] > high[s]) { - return false; - } - } - - return true; - } - - private boolean isMask(String parameter) { - if (parameter.contains("*") || parameter.contains("::")) { - return true; - } - - return false; - } - - private boolean isMasked(String parameter, String address) { - int[] target = convert(address); - int[] mask = convert(parameter); - - if (target.length != mask.length) { - return false; - } - - for (int s = 0; s < mask.length; s++) { - if (mask[s] == Integer.MAX_VALUE) { - return true; - } else if (target[s] != mask[s]) { - return false; - } - } - - return false; - } - - public boolean isWhitelisted(String hostName) { - if (parameters.isEmpty()) { - return true; - } - - try { - String hostAddress = InetAddress.getByName(hostName).getHostAddress(); - for (String parameter : parameters) { - if (parameter.equalsIgnoreCase(hostName) - || isRange(parameter) && isInRange(parameter, hostAddress) - || isMask(parameter) && isMasked(parameter, hostAddress)) { - return true; - } - } - } catch (UnknownHostException ex) { - networkLog.error("While checking whitelist", ex); - } - - return false; - } - - public static Whitelist from(RuntimeProperties properties) { - return new Whitelist(properties.get("network.whitelist", "")); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.network.discovery; + +import com.radixdlt.properties.RuntimeProperties; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Whitelist { + private static final Logger networkLog = LogManager.getLogger(); + + private Set parameters = new HashSet<>(); + + public Whitelist(String parameters) { + if (parameters == null) { + return; + } + + String[] split = parameters.split(","); + + for (String parameter : split) { + if (parameter.trim().length() == 0) { + continue; + } + + this.parameters.add(parameter.trim()); + } + } + + private int[] convert(String host) { + String[] segments; + int[] output; + + if (host.contains(".")) { // IPV4 // + output = new int[4]; + segments = host.split("\\."); + } else if (host.contains(":")) { // IPV6 // + output = new int[8]; + segments = host.split(":"); + } else { + return new int[] {0, 0, 0, 0}; + } + + Arrays.fill(output, Integer.MAX_VALUE); + for (int s = 0; s < segments.length; s++) { + if (segments[s].equalsIgnoreCase("*")) { + break; + } + + output[s] = Integer.valueOf(segments[s]); + } + + return output; + } + + private boolean isRange(String parameter) { + if (parameter.contains("-")) { + return true; + } + + return false; + } + + private boolean isInRange(String parameter, String address) { + String[] hosts = parameter.split("-"); + + if (hosts.length != 2) { + throw new IllegalStateException("Range is invalid"); + } + + int[] target = convert(address); + int[] low = convert(hosts[0]); + int[] high = convert(hosts[1]); + + if (low.length != high.length || target.length != low.length) { + return false; + } + + for (int s = 0; s < low.length; s++) { + if (low[s] < high[s]) { + int[] swap = low; + low = high; + high = swap; + break; + } + + if (target[s] < low[s] || target[s] > high[s]) { + return false; + } + } + + return true; + } + + private boolean isMask(String parameter) { + if (parameter.contains("*") || parameter.contains("::")) { + return true; + } + + return false; + } + + private boolean isMasked(String parameter, String address) { + int[] target = convert(address); + int[] mask = convert(parameter); + + if (target.length != mask.length) { + return false; + } + + for (int s = 0; s < mask.length; s++) { + if (mask[s] == Integer.MAX_VALUE) { + return true; + } else if (target[s] != mask[s]) { + return false; + } + } + + return false; + } + + public boolean isWhitelisted(String hostName) { + if (parameters.isEmpty()) { + return true; + } + + try { + String hostAddress = InetAddress.getByName(hostName).getHostAddress(); + for (String parameter : parameters) { + if (parameter.equalsIgnoreCase(hostName) + || isRange(parameter) && isInRange(parameter, hostAddress) + || isMask(parameter) && isMasked(parameter, hostAddress)) { + return true; + } + } + } catch (UnknownHostException ex) { + networkLog.error("While checking whitelist", ex); + } + + return false; + } + + public static Whitelist from(RuntimeProperties properties) { + return new Whitelist(properties.get("network.whitelist", "")); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/GetPeersMessage.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/GetPeersMessage.java index 08d8d39d31..290fc39bc8 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/GetPeersMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/GetPeersMessage.java @@ -66,33 +66,31 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; @SerializerId2("p2p.discovery.get_peers") public final class GetPeersMessage extends Message { - @JsonCreator - public GetPeersMessage() { - } + @JsonCreator + public GetPeersMessage() {} - @Override - public String toString() { - return getClass().getSimpleName(); - } + @Override + public String toString() { + return getClass().getSimpleName(); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof GetPeersMessage that) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof GetPeersMessage that) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hashCode(getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hashCode(getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPingMessage.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPingMessage.java index 35b6c54b78..d074652bbb 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPingMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPingMessage.java @@ -66,33 +66,31 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; @SerializerId2("p2p.liveness.ping") public final class PeerPingMessage extends Message { - @JsonCreator - public PeerPingMessage() { - } + @JsonCreator + public PeerPingMessage() {} - @Override - public String toString() { - return String.format("%s[]", getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s[]", getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof PeerPingMessage that) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof PeerPingMessage that) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPongMessage.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPongMessage.java index c11a90f3cd..7b1097b74b 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPongMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeerPongMessage.java @@ -66,33 +66,31 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; @SerializerId2("p2p.liveness.pong") public final class PeerPongMessage extends Message { - @JsonCreator - public PeerPongMessage() { - } + @JsonCreator + public PeerPongMessage() {} - @Override - public String toString() { - return String.format("%s[]", getClass().getSimpleName()); - } + @Override + public String toString() { + return String.format("%s[]", getClass().getSimpleName()); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof PeerPongMessage that) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof PeerPongMessage that) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeersResponseMessage.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeersResponseMessage.java index eb08c91943..154110fe96 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeersResponseMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messages/PeersResponseMessage.java @@ -71,45 +71,42 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; -import org.radix.network.messaging.Message; - import java.util.Objects; +import org.radix.network.messaging.Message; @SerializerId2("p2p.discovery.peers_response") public final class PeersResponseMessage extends Message { - @JsonProperty("peers") - @DsonOutput(Output.ALL) - private final ImmutableSet peers; + @JsonProperty("peers") + @DsonOutput(Output.ALL) + private final ImmutableSet peers; - @JsonCreator - public PeersResponseMessage( - @JsonProperty("peers") ImmutableSet peers - ) { - this.peers = peers == null ? ImmutableSet.of() : peers; - } + @JsonCreator + public PeersResponseMessage(@JsonProperty("peers") ImmutableSet peers) { + this.peers = peers == null ? ImmutableSet.of() : peers; + } - public ImmutableSet getPeers() { - return peers; - } + public ImmutableSet getPeers() { + return peers; + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), peers); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), peers); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return (o instanceof PeersResponseMessage that) - && Objects.equals(peers, that.peers) - && Objects.equals(getTimestamp(), that.getTimestamp()); - } + return (o instanceof PeersResponseMessage that) + && Objects.equals(peers, that.peers) + && Objects.equals(getTimestamp(), that.getTimestamp()); + } - @Override - public int hashCode() { - return Objects.hash(peers, getTimestamp()); - } + @Override + public int hashCode() { + return Objects.hash(peers, getTimestamp()); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messaging/Message.java b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messaging/Message.java index c7f44ecfa3..dd29a0d2ae 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/network/messaging/Message.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/network/messaging/Message.java @@ -1,116 +1,121 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.network.messaging; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.Serialization; -import com.radixdlt.utils.Compress; -import com.radixdlt.utils.Ints; -import org.radix.containers.BasicContainer; -import org.radix.time.Time; - -import java.io.IOException; -import java.util.concurrent.atomic.AtomicLong; - -public abstract class Message extends BasicContainer { - @Override - public short version() { - return 100; - } - - private static final AtomicLong instances = new AtomicLong(); - - private long instance = Message.instances.incrementAndGet(); - - @JsonProperty("timestamp") - @DsonOutput(value = {Output.API, Output.PERSIST}) - private final long timestamp; - - protected Message() { - this.timestamp = Time.currentTimestamp(); - } - - public long getTimestamp() { - return this.timestamp; - } - - public byte[] toByteArray(Serialization serialization) throws IOException { - byte[] bytes = serialization.toDson(this, Output.WIRE); - byte[] data = Compress.compress(bytes); - - byte[] byteArray = new byte[data.length + Integer.BYTES]; - Ints.copyTo(data.length, byteArray, 0); - System.arraycopy(data, 0, byteArray, Integer.BYTES, data.length); - - return byteArray; - } - - @Override - public String toString() { - return this.instance + " -> " + this.getClass().getSimpleName() + ":" + this.hashCode() + " @ " + this.getTimestamp(); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.network.messaging; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.Serialization; +import com.radixdlt.utils.Compress; +import com.radixdlt.utils.Ints; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; +import org.radix.containers.BasicContainer; +import org.radix.time.Time; + +public abstract class Message extends BasicContainer { + @Override + public short version() { + return 100; + } + + private static final AtomicLong instances = new AtomicLong(); + + private long instance = Message.instances.incrementAndGet(); + + @JsonProperty("timestamp") + @DsonOutput(value = {Output.API, Output.PERSIST}) + private final long timestamp; + + protected Message() { + this.timestamp = Time.currentTimestamp(); + } + + public long getTimestamp() { + return this.timestamp; + } + + public byte[] toByteArray(Serialization serialization) throws IOException { + byte[] bytes = serialization.toDson(this, Output.WIRE); + byte[] data = Compress.compress(bytes); + + byte[] byteArray = new byte[data.length + Integer.BYTES]; + Ints.copyTo(data.length, byteArray, 0); + System.arraycopy(data, 0, byteArray, Integer.BYTES, data.length); + + return byteArray; + } + + @Override + public String toString() { + return this.instance + + " -> " + + this.getClass().getSimpleName() + + ":" + + this.hashCode() + + " @ " + + this.getTimestamp(); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpException.java b/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpException.java index 93f62a76bf..8e57f3e413 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpException.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpException.java @@ -65,11 +65,11 @@ package org.radix.time; public class NtpException extends RuntimeException { - public NtpException(String message) { - super(message); - } + public NtpException(String message) { + super(message); + } - public NtpException(String message, Throwable cause) { - super(message, cause); - } + public NtpException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpMessage.java b/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpMessage.java index e18b401d44..7c38928c31 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpMessage.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpMessage.java @@ -1,494 +1,460 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.time; - -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - - -/** - * This class represents a NTP message, as specified in RFC 2030. The message - * format is compatible with all versions of NTP and SNTP. - *

- * This class does not support the optional authentication protocol, and - * ignores the key ID and message digest fields. - *

- * For convenience, this class exposes message values as native Java types, not - * the NTP-specified data formats. For example, timestamps are - * stored as doubles (as opposed to the NTP unsigned 64-bit fixed point - * format). - *

- * However, the contructor NtpMessage(byte[]) and the method toByteArray() - * allow the import and export of the raw NTP message format. - *

- *

- * Usage example - *

- * // Send message - * DatagramSocket socket = new DatagramSocket(); - * InetAddress address = InetAddress.getByName("ntp.cais.rnp.br"); - * byte[] buf = new NtpMessage().toByteArray(); - * DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 123); - * socket.send(packet); - *

- * // Get response - * socket.receive(packet); - * System.out.println(msg.toString()); - *

- *

- * This code is copyright (c) Adam Buckley 2004 - *

- * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. A HTML version of the GNU General Public License can be - * seen at http://www.gnu.org/licenses/gpl.html - *

- * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - *

- *

- * Comments for member variables are taken from RFC2030 by David Mills, - * University of Delaware. - *

- * Number format conversion code in NtpMessage(byte[] array) and toByteArray() - * inspired by http://www.pps.jussieu.fr/~jch/enseignement/reseaux/ - * NTPMessage.java which is copyright (c) 2003 by Juliusz Chroboczek - * - * @author Dan Hughes - */ -class NtpMessage { - /** - * This is a two-bit code warning of an impending leap second to be - * inserted/deleted in the last minute of the current day. It's values - * may be as follows: - *

- * Value Meaning - * ----- ------- - * 0 no warning - * 1 last minute has 61 seconds - * 2 last minute has 59 seconds) - * 3 alarm condition (clock not synchronized) - */ - byte leapIndicator = 0; - - /** - * This value indicates the NTP/SNTP version number. The version number - * is 3 for Version 3 (IPv4 only) and 4 for Version 4 (IPv4, IPv6 and OSI). - * If necessary to distinguish between IPv4, IPv6 and OSI, the - * encapsulating context must be inspected. - */ - byte version = 3; - - /** - * This value indicates the mode, with values defined as follows: - *

- * Mode Meaning - * ---- ------- - * 0 reserved - * 1 symmetric active - * 2 symmetric passive - * 3 client - * 4 server - * 5 broadcast - * 6 reserved for NTP control message - * 7 reserved for private use - *

- * In unicast and anycast modes, the client sets this field to 3 (client) - * in the request and the server sets it to 4 (server) in the reply. In - * multicast mode, the server sets this field to 5 (broadcast). - */ - byte mode = 0; - - /** - * This value indicates the stratum level of the local clock, with values - * defined as follows: - *

- * Stratum Meaning - * ---------------------------------------------- - * 0 unspecified or unavailable - * 1 primary reference (e.g., radio clock) - * 2-15 secondary reference (via NTP or SNTP) - * 16-255 reserved - */ - short stratum = 0; - - /** - * This value indicates the maximum interval between successive messages, - * in seconds to the nearest power of two. The values that can appear in - * this field presently range from 4 (16 s) to 14 (16284 s); however, most - * applications use only the sub-range 6 (64 s) to 10 (1024 s). - */ - byte pollInterval = 0; - - /** - * This value indicates the precision of the local clock, in seconds to - * the nearest power of two. The values that normally appear in this field - * range from -6 for mains-frequency clocks to -20 for microsecond clocks - * found in some workstations. - */ - byte precision = 0; - - /** - * This value indicates the total roundtrip delay to the primary reference - * source, in seconds. Note that this variable can take on both positive - * and negative values, depending on the relative time and frequency - * offsets. The values that normally appear in this field range from - * negative values of a few milliseconds to positive values of several - * hundred milliseconds. - */ - double rootDelay = 0; - - /** - * This value indicates the nominal error relative to the primary reference - * source, in seconds. The values that normally appear in this field - * range from 0 to several hundred milliseconds. - */ - double rootDispersion = 0; - - /** - * This is a 4-byte array identifying the particular reference source. - * In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) or - * stratum-1 (primary) servers, this is a four-character ASCII string, left - * justified and zero padded to 32 bits. In NTP Version 3 secondary - * servers, this is the 32-bit IPv4 address of the reference source. In NTP - * Version 4 secondary servers, this is the low order 32 bits of the latest - * transmit timestamp of the reference source. NTP primary (stratum 1) - * servers should set this field to a code identifying the external - * reference source according to the following list. If the external - * reference is one of those listed, the associated code should be used. - * Codes for sources not listed can be contrived as appropriate. - *

- * Code External Reference Source - * ---- ------------------------- - * LOCL uncalibrated local clock used as a primary reference for - * a subnet without external means of synchronization - * PPS atomic clock or other pulse-per-second source - * individually calibrated to national standards - * ACTS NIST dialup modem service - * USNO USNO modem service - * PTB PTB (Germany) modem service - * TDF Allouis (France) Radio 164 kHz - * DCF Mainflingen (Germany) Radio 77.5 kHz - * MSF Rugby (UK) Radio 60 kHz - * WWV Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz - * WWVB Boulder (US) Radio 60 kHz - * WWVH Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz - * CHU Ottawa (Canada) Radio 3330, 7335, 14670 kHz - * LORC LORAN-C radionavigation system - * OMEG OMEGA radionavigation system - * GPS Global Positioning Service - * GOES Geostationary Orbit Environment Satellite - */ - byte[] referenceIdentifier = {0, 0, 0, 0}; - - /** - * This is the time at which the local clock was last set or corrected, in - * seconds since 00:00 1-Jan-1900. - */ - double referenceTimestamp = 0; - - /** - * This is the time at which the request departed the client for the - * server, in seconds since 00:00 1-Jan-1900. - */ - double originateTimestamp = 0; - - /** - * This is the time at which the request arrived at the server, in seconds - * since 00:00 1-Jan-1900. - */ - double receiveTimestamp = 0; - - /** - * This is the time at which the reply departed the server for the client, - * in seconds since 00:00 1-Jan-1900. - */ - double transmitTimestamp = 0; - - /** - * Constructs a new NtpMessage from an array of bytes. - */ - NtpMessage(byte[] array) { - // See the packet format diagram in RFC 2030 for details - leapIndicator = (byte) ((array[0] >> 6) & 0x3); - version = (byte) ((array[0] >> 3) & 0x7); - mode = (byte) (array[0] & 0x7); - stratum = unsignedByteToShort(array[1]); - pollInterval = array[2]; - precision = array[3]; - - rootDelay = (array[4] * 256.0) - + unsignedByteToShort(array[5]) - + (unsignedByteToShort(array[6]) / 256.0) - + (unsignedByteToShort(array[7]) / 65536.0); - - rootDispersion = (unsignedByteToShort(array[8]) * 256.0) - + unsignedByteToShort(array[9]) - + (unsignedByteToShort(array[10]) / 256.0) - + (unsignedByteToShort(array[11]) / 65536.0); - - referenceIdentifier[0] = array[12]; - referenceIdentifier[1] = array[13]; - referenceIdentifier[2] = array[14]; - referenceIdentifier[3] = array[15]; - - referenceTimestamp = decodeTimestamp(array, 16); - originateTimestamp = decodeTimestamp(array, 24); - receiveTimestamp = decodeTimestamp(array, 32); - transmitTimestamp = decodeTimestamp(array, 40); - } - - - /** - * Constructs a new NtpMessage in client -> server mode, and sets the - * transmit timestamp to the current time. - */ - NtpMessage() { - // Note that all the other member variables are already set with - // appropriate default values. - this.mode = 3; - this.transmitTimestamp = (System.currentTimeMillis() / 1000.0) + 2208988800.0; - } - - - /** - * This method constructs the data bytes of a raw NTP packet. - */ - byte[] toByteArray() { - // All bytes are automatically set to 0 - byte[] p = new byte[48]; - - p[0] = (byte) (leapIndicator << 6 | version << 3 | mode); - p[1] = (byte) stratum; - p[2] = pollInterval; - p[3] = precision; - - // root delay is a signed 16.16-bit FP, in Java an int is 32-bits - int l = (int) (rootDelay * 65536.0); - p[4] = (byte) ((l >> 24) & 0xFF); - p[5] = (byte) ((l >> 16) & 0xFF); - p[6] = (byte) ((l >> 8) & 0xFF); - p[7] = (byte) (l & 0xFF); - - // root dispersion is an unsigned 16.16-bit FP, in Java there are no - // unsigned primitive types, so we use a long which is 64-bits - long ul = (long) (rootDispersion * 65536.0); - p[8] = (byte) ((ul >> 24) & 0xFF); - p[9] = (byte) ((ul >> 16) & 0xFF); - p[10] = (byte) ((ul >> 8) & 0xFF); - p[11] = (byte) (ul & 0xFF); - - p[12] = referenceIdentifier[0]; - p[13] = referenceIdentifier[1]; - p[14] = referenceIdentifier[2]; - p[15] = referenceIdentifier[3]; - - encodeTimestamp(p, 16, referenceTimestamp); - encodeTimestamp(p, 24, originateTimestamp); - encodeTimestamp(p, 32, receiveTimestamp); - encodeTimestamp(p, 40, transmitTimestamp); - - return p; - } - - - /** - * Returns a string representation of a NtpMessage - */ - @Override - public String toString() { - String precisionStr = - new DecimalFormat("0.#E0").format(Math.pow(2, precision)); - - return "Leap indicator: " + leapIndicator + "\n" - + "Version: " + version + "\n" - + "Mode: " + mode + "\n" - + "Stratum: " + stratum + "\n" - + "Poll: " + pollInterval + "\n" - + "Precision: " + precision + " (" + precisionStr + " seconds)\n" - + "Root delay: " + new DecimalFormat("0.00").format(rootDelay * 1000) + " ms\n" - + "Root dispersion: " + new DecimalFormat("0.00").format(rootDispersion * 1000) + " ms\n" - + "Reference identifier: " + referenceIdentifierToString(referenceIdentifier, stratum, version) + "\n" - + "Reference timestamp: " + timestampToString(referenceTimestamp) + "\n" - + "Originate timestamp: " + timestampToString(originateTimestamp) + "\n" - + "Receive timestamp: " + timestampToString(receiveTimestamp) + "\n" - + "Transmit timestamp: " + timestampToString(transmitTimestamp); - } - - - /** - * Converts an unsigned byte to a short. By default, Java assumes that - * a byte is signed. - */ - static short unsignedByteToShort(byte b) { - if ((b & 0x80) == 0x80) { - return (short) (128 + (b & 0x7f)); - } else { - return b; - } - } - - - /** - * Will read 8 bytes of a message beginning at pointer - * and return it as a double, according to the NTP 64-bit timestamp - * format. - */ - static double decodeTimestamp(byte[] array, int pointer) { - double r = 0.0; - - for (int i = 0; i < 8; i++) { - r += unsignedByteToShort(array[pointer + i]) * Math.pow(2.0, (3.0 - i) * 8.0); - } - - return r; - } - - - /** - * Encodes a timestamp in the specified position in the message - */ - static void encodeTimestamp(byte[] array, int pointer, double timestamp) { - // Converts a double into a 64-bit fixed point - for (int i = 0; i < 8; i++) { - // 2^24, 2^16, 2^8, .. 2^-32 - double base = Math.pow(2.0, (3.0 - i) * 8.0); - - // Capture byte value - array[pointer + i] = (byte) (timestamp / base); - - // Subtract captured value from remaining total - timestamp = timestamp - unsignedByteToShort(array[pointer + i]) * base; - } - - // From RFC 2030: It is advisable to fill the non-significant - // low order bits of the timestamp with a random, unbiased - // bitstring, both to avoid systematic roundoff errors and as - // a means of loop detection and replay detection. - array[7] = (byte) (Math.random() * 255.0); - } - - - /** - * Returns a timestamp (number of seconds since 00:00 1-Jan-1900) as a - * formatted date/time string. - */ - static String timestampToString(double timestamp) { - if (timestamp == 0) { - return "0"; - } - - // timestamp is relative to 1900, utc is used by Java and is relative - // to 1970 - double utc = timestamp - (2208988800.0); - - // milliseconds - long ms = (long) (utc * 1000.0); - - // date/time - String date = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss").format(new Date(ms)); - - // fraction - double fraction = timestamp - ((long) timestamp); - String fractionSting = new DecimalFormat(".000000").format(fraction); - - return date + fractionSting; - } - - - /** - * Returns a string representation of a reference identifier according - * to the rules set out in RFC 2030. - */ - static String referenceIdentifierToString(byte[] ref, short stratum, byte version) { - // From the RFC 2030: - // In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) - // or stratum-1 (primary) servers, this is a four-character ASCII - // string, left justified and zero padded to 32 bits. - if (stratum == 0 || stratum == 1) { - return new String(ref); - } else if (version == 3) { - // In NTP Version 3 secondary servers, this is the 32-bit IPv4 - // address of the reference source. - return unsignedByteToShort(ref[0]) + "." - + unsignedByteToShort(ref[1]) + "." - + unsignedByteToShort(ref[2]) + "." - + unsignedByteToShort(ref[3]); - } else if (version == 4) { - // In NTP Version 4 secondary servers, this is the low order 32 bits - // of the latest transmit timestamp of the reference source. - return "" + ((unsignedByteToShort(ref[0]) / 256.0) - + (unsignedByteToShort(ref[1]) / 65536.0) - + (unsignedByteToShort(ref[2]) / 16777216.0) - + (unsignedByteToShort(ref[3]) / 4294967296.0)); - } - - return ""; - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.time; + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * This class represents a NTP message, as specified in RFC 2030. The message format is compatible + * with all versions of NTP and SNTP. + * + *

This class does not support the optional authentication protocol, and ignores the key ID and + * message digest fields. + * + *

For convenience, this class exposes message values as native Java types, not the NTP-specified + * data formats. For example, timestamps are stored as doubles (as opposed to the NTP unsigned + * 64-bit fixed point format). + * + *

However, the contructor NtpMessage(byte[]) and the method toByteArray() allow the import and + * export of the raw NTP message format. + * + *

+ * + *

Usage example + * + *

// Send message DatagramSocket socket = new DatagramSocket(); InetAddress address = + * InetAddress.getByName("ntp.cais.rnp.br"); byte[] buf = new NtpMessage().toByteArray(); + * DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 123); socket.send(packet); + * + *

// Get response socket.receive(packet); System.out.println(msg.toString()); + * + *

+ * + *

This code is copyright (c) Adam Buckley 2004 + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. A HTML version of the GNU General Public License + * can be seen at http://www.gnu.org/licenses/gpl.html + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

+ * + *

Comments for member variables are taken from RFC2030 by David Mills, University of Delaware. + * + *

Number format conversion code in NtpMessage(byte[] array) and toByteArray() inspired by + * http://www.pps.jussieu.fr/~jch/enseignement/reseaux/ NTPMessage.java which is copyright (c) 2003 + * by Juliusz Chroboczek + * + * @author Dan Hughes + */ +class NtpMessage { + /** + * This is a two-bit code warning of an impending leap second to be inserted/deleted in the last + * minute of the current day. It's values may be as follows: + * + *

Value Meaning ----- ------- 0 no warning 1 last minute has 61 seconds 2 last minute has 59 + * seconds) 3 alarm condition (clock not synchronized) + */ + byte leapIndicator = 0; + + /** + * This value indicates the NTP/SNTP version number. The version number is 3 for Version 3 (IPv4 + * only) and 4 for Version 4 (IPv4, IPv6 and OSI). If necessary to distinguish between IPv4, IPv6 + * and OSI, the encapsulating context must be inspected. + */ + byte version = 3; + + /** + * This value indicates the mode, with values defined as follows: + * + *

Mode Meaning ---- ------- 0 reserved 1 symmetric active 2 symmetric passive 3 client 4 + * server 5 broadcast 6 reserved for NTP control message 7 reserved for private use + * + *

In unicast and anycast modes, the client sets this field to 3 (client) in the request and + * the server sets it to 4 (server) in the reply. In multicast mode, the server sets this field to + * 5 (broadcast). + */ + byte mode = 0; + + /** + * This value indicates the stratum level of the local clock, with values defined as follows: + * + *

Stratum Meaning ---------------------------------------------- 0 unspecified or unavailable + * 1 primary reference (e.g., radio clock) 2-15 secondary reference (via NTP or SNTP) 16-255 + * reserved + */ + short stratum = 0; + + /** + * This value indicates the maximum interval between successive messages, in seconds to the + * nearest power of two. The values that can appear in this field presently range from 4 (16 s) to + * 14 (16284 s); however, most applications use only the sub-range 6 (64 s) to 10 (1024 s). + */ + byte pollInterval = 0; + + /** + * This value indicates the precision of the local clock, in seconds to the nearest power of two. + * The values that normally appear in this field range from -6 for mains-frequency clocks to -20 + * for microsecond clocks found in some workstations. + */ + byte precision = 0; + + /** + * This value indicates the total roundtrip delay to the primary reference source, in seconds. + * Note that this variable can take on both positive and negative values, depending on the + * relative time and frequency offsets. The values that normally appear in this field range from + * negative values of a few milliseconds to positive values of several hundred milliseconds. + */ + double rootDelay = 0; + + /** + * This value indicates the nominal error relative to the primary reference source, in seconds. + * The values that normally appear in this field range from 0 to several hundred milliseconds. + */ + double rootDispersion = 0; + + /** + * This is a 4-byte array identifying the particular reference source. In the case of NTP Version + * 3 or Version 4 stratum-0 (unspecified) or stratum-1 (primary) servers, this is a four-character + * ASCII string, left justified and zero padded to 32 bits. In NTP Version 3 secondary servers, + * this is the 32-bit IPv4 address of the reference source. In NTP Version 4 secondary servers, + * this is the low order 32 bits of the latest transmit timestamp of the reference source. NTP + * primary (stratum 1) servers should set this field to a code identifying the external reference + * source according to the following list. If the external reference is one of those listed, the + * associated code should be used. Codes for sources not listed can be contrived as appropriate. + * + *

Code External Reference Source ---- ------------------------- LOCL uncalibrated local clock + * used as a primary reference for a subnet without external means of synchronization PPS atomic + * clock or other pulse-per-second source individually calibrated to national standards ACTS NIST + * dialup modem service USNO USNO modem service PTB PTB (Germany) modem service TDF Allouis + * (France) Radio 164 kHz DCF Mainflingen (Germany) Radio 77.5 kHz MSF Rugby (UK) Radio 60 kHz WWV + * Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz WWVB Boulder (US) Radio 60 kHz WWVH Kaui Hawaii + * (US) Radio 2.5, 5, 10, 15 MHz CHU Ottawa (Canada) Radio 3330, 7335, 14670 kHz LORC LORAN-C + * radionavigation system OMEG OMEGA radionavigation system GPS Global Positioning Service GOES + * Geostationary Orbit Environment Satellite + */ + byte[] referenceIdentifier = {0, 0, 0, 0}; + + /** + * This is the time at which the local clock was last set or corrected, in seconds since 00:00 + * 1-Jan-1900. + */ + double referenceTimestamp = 0; + + /** + * This is the time at which the request departed the client for the server, in seconds since + * 00:00 1-Jan-1900. + */ + double originateTimestamp = 0; + + /** + * This is the time at which the request arrived at the server, in seconds since 00:00 1-Jan-1900. + */ + double receiveTimestamp = 0; + + /** + * This is the time at which the reply departed the server for the client, in seconds since 00:00 + * 1-Jan-1900. + */ + double transmitTimestamp = 0; + + /** Constructs a new NtpMessage from an array of bytes. */ + NtpMessage(byte[] array) { + // See the packet format diagram in RFC 2030 for details + leapIndicator = (byte) ((array[0] >> 6) & 0x3); + version = (byte) ((array[0] >> 3) & 0x7); + mode = (byte) (array[0] & 0x7); + stratum = unsignedByteToShort(array[1]); + pollInterval = array[2]; + precision = array[3]; + + rootDelay = + (array[4] * 256.0) + + unsignedByteToShort(array[5]) + + (unsignedByteToShort(array[6]) / 256.0) + + (unsignedByteToShort(array[7]) / 65536.0); + + rootDispersion = + (unsignedByteToShort(array[8]) * 256.0) + + unsignedByteToShort(array[9]) + + (unsignedByteToShort(array[10]) / 256.0) + + (unsignedByteToShort(array[11]) / 65536.0); + + referenceIdentifier[0] = array[12]; + referenceIdentifier[1] = array[13]; + referenceIdentifier[2] = array[14]; + referenceIdentifier[3] = array[15]; + + referenceTimestamp = decodeTimestamp(array, 16); + originateTimestamp = decodeTimestamp(array, 24); + receiveTimestamp = decodeTimestamp(array, 32); + transmitTimestamp = decodeTimestamp(array, 40); + } + + /** + * Constructs a new NtpMessage in client -> server mode, and sets the transmit timestamp to the + * current time. + */ + NtpMessage() { + // Note that all the other member variables are already set with + // appropriate default values. + this.mode = 3; + this.transmitTimestamp = (System.currentTimeMillis() / 1000.0) + 2208988800.0; + } + + /** This method constructs the data bytes of a raw NTP packet. */ + byte[] toByteArray() { + // All bytes are automatically set to 0 + byte[] p = new byte[48]; + + p[0] = (byte) (leapIndicator << 6 | version << 3 | mode); + p[1] = (byte) stratum; + p[2] = pollInterval; + p[3] = precision; + + // root delay is a signed 16.16-bit FP, in Java an int is 32-bits + int l = (int) (rootDelay * 65536.0); + p[4] = (byte) ((l >> 24) & 0xFF); + p[5] = (byte) ((l >> 16) & 0xFF); + p[6] = (byte) ((l >> 8) & 0xFF); + p[7] = (byte) (l & 0xFF); + + // root dispersion is an unsigned 16.16-bit FP, in Java there are no + // unsigned primitive types, so we use a long which is 64-bits + long ul = (long) (rootDispersion * 65536.0); + p[8] = (byte) ((ul >> 24) & 0xFF); + p[9] = (byte) ((ul >> 16) & 0xFF); + p[10] = (byte) ((ul >> 8) & 0xFF); + p[11] = (byte) (ul & 0xFF); + + p[12] = referenceIdentifier[0]; + p[13] = referenceIdentifier[1]; + p[14] = referenceIdentifier[2]; + p[15] = referenceIdentifier[3]; + + encodeTimestamp(p, 16, referenceTimestamp); + encodeTimestamp(p, 24, originateTimestamp); + encodeTimestamp(p, 32, receiveTimestamp); + encodeTimestamp(p, 40, transmitTimestamp); + + return p; + } + + /** Returns a string representation of a NtpMessage */ + @Override + public String toString() { + String precisionStr = new DecimalFormat("0.#E0").format(Math.pow(2, precision)); + + return "Leap indicator: " + + leapIndicator + + "\n" + + "Version: " + + version + + "\n" + + "Mode: " + + mode + + "\n" + + "Stratum: " + + stratum + + "\n" + + "Poll: " + + pollInterval + + "\n" + + "Precision: " + + precision + + " (" + + precisionStr + + " seconds)\n" + + "Root delay: " + + new DecimalFormat("0.00").format(rootDelay * 1000) + + " ms\n" + + "Root dispersion: " + + new DecimalFormat("0.00").format(rootDispersion * 1000) + + " ms\n" + + "Reference identifier: " + + referenceIdentifierToString(referenceIdentifier, stratum, version) + + "\n" + + "Reference timestamp: " + + timestampToString(referenceTimestamp) + + "\n" + + "Originate timestamp: " + + timestampToString(originateTimestamp) + + "\n" + + "Receive timestamp: " + + timestampToString(receiveTimestamp) + + "\n" + + "Transmit timestamp: " + + timestampToString(transmitTimestamp); + } + + /** Converts an unsigned byte to a short. By default, Java assumes that a byte is signed. */ + static short unsignedByteToShort(byte b) { + if ((b & 0x80) == 0x80) { + return (short) (128 + (b & 0x7f)); + } else { + return b; + } + } + + /** + * Will read 8 bytes of a message beginning at pointer and return it as a double, + * according to the NTP 64-bit timestamp format. + */ + static double decodeTimestamp(byte[] array, int pointer) { + double r = 0.0; + + for (int i = 0; i < 8; i++) { + r += unsignedByteToShort(array[pointer + i]) * Math.pow(2.0, (3.0 - i) * 8.0); + } + + return r; + } + + /** Encodes a timestamp in the specified position in the message */ + static void encodeTimestamp(byte[] array, int pointer, double timestamp) { + // Converts a double into a 64-bit fixed point + for (int i = 0; i < 8; i++) { + // 2^24, 2^16, 2^8, .. 2^-32 + double base = Math.pow(2.0, (3.0 - i) * 8.0); + + // Capture byte value + array[pointer + i] = (byte) (timestamp / base); + + // Subtract captured value from remaining total + timestamp = timestamp - unsignedByteToShort(array[pointer + i]) * base; + } + + // From RFC 2030: It is advisable to fill the non-significant + // low order bits of the timestamp with a random, unbiased + // bitstring, both to avoid systematic roundoff errors and as + // a means of loop detection and replay detection. + array[7] = (byte) (Math.random() * 255.0); + } + + /** + * Returns a timestamp (number of seconds since 00:00 1-Jan-1900) as a formatted date/time string. + */ + static String timestampToString(double timestamp) { + if (timestamp == 0) { + return "0"; + } + + // timestamp is relative to 1900, utc is used by Java and is relative + // to 1970 + double utc = timestamp - (2208988800.0); + + // milliseconds + long ms = (long) (utc * 1000.0); + + // date/time + String date = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss").format(new Date(ms)); + + // fraction + double fraction = timestamp - ((long) timestamp); + String fractionSting = new DecimalFormat(".000000").format(fraction); + + return date + fractionSting; + } + + /** + * Returns a string representation of a reference identifier according to the rules set out in RFC + * 2030. + */ + static String referenceIdentifierToString(byte[] ref, short stratum, byte version) { + // From the RFC 2030: + // In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) + // or stratum-1 (primary) servers, this is a four-character ASCII + // string, left justified and zero padded to 32 bits. + if (stratum == 0 || stratum == 1) { + return new String(ref); + } else if (version == 3) { + // In NTP Version 3 secondary servers, this is the 32-bit IPv4 + // address of the reference source. + return unsignedByteToShort(ref[0]) + + "." + + unsignedByteToShort(ref[1]) + + "." + + unsignedByteToShort(ref[2]) + + "." + + unsignedByteToShort(ref[3]); + } else if (version == 4) { + // In NTP Version 4 secondary servers, this is the low order 32 bits + // of the latest transmit timestamp of the reference source. + return "" + + ((unsignedByteToShort(ref[0]) / 256.0) + + (unsignedByteToShort(ref[1]) / 65536.0) + + (unsignedByteToShort(ref[2]) / 16777216.0) + + (unsignedByteToShort(ref[3]) / 4294967296.0)); + } + + return ""; + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpService.java b/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpService.java index e5888af990..f2f9199f19 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpService.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/time/NtpService.java @@ -1,198 +1,203 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.time; - -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class NtpService { - private static final Logger log = LogManager.getLogger(); - - private String server = null; - private double roundTripDelay = 0; - private double localClockOffset = 0; - - private int attempts = 0; - private int offset = 0; - - public NtpService(String server) { - this.server = server; - - roundTripDelay = 0; - localClockOffset = 0; - - if (server != null) { - initFromServer(); - } - } - - private void initFromServer() { - if (server != null) { - boolean success = false; - - while (attempts < 3 && !success) { - try (DatagramSocket socket = new DatagramSocket()) { - // Send request - socket.setSoTimeout(5000); - - var address = InetAddress.getByName(server); - var buf = new NtpMessage().toByteArray(); - var packet = new DatagramPacket(buf, buf.length, address, 123); - - // Set the transmit timestamp *just* before sending the packet - // ToDo: Does this actually improve performance or not? - NtpMessage.encodeTimestamp(packet.getData(), 40, (System.currentTimeMillis() / 1000.0) + 2208988800.0); - - socket.send(packet); - - // Get response - log.info("NTP request sent, waiting for response...\n"); - packet = new DatagramPacket(buf, buf.length); - socket.receive(packet); - - // Immediately record the incoming timestamp - double destinationTimestamp = (System.currentTimeMillis() / 1000.0) + 2208988800.0; - - // Process response - NtpMessage msg = new NtpMessage(packet.getData()); - - // Corrected, according to RFC2030 errata - roundTripDelay = - (destinationTimestamp - msg.originateTimestamp) - - (msg.transmitTimestamp - msg.receiveTimestamp); - localClockOffset = - ((msg.receiveTimestamp - msg.originateTimestamp) - + (msg.transmitTimestamp - destinationTimestamp)) / 2; - - log.info(msg.toString()); - success = true; - } catch (Exception ex) { - if (attempts >= 3) { - throw new NtpException("failed to start NTP service", ex); - } - } finally { - attempts++; - } - } - - if (!success) { - throw new NtpException("Unable to start NTP service using " + server); - } - } - } - - public boolean isSynchronized() { - return server != null; - } - - /** - * Returns the offset in seconds set in this NtpService - * - * @return - */ - public int getOffset() { - return offset; - } - - /** - * Sets the offset in seconds for this NtpService - * - * @param offset - */ - public void setOffset(int offset) { - this.offset = offset; - } - - /** - * Returns a corrected System time - * - * @return - */ - public long getSystemTime() { - return (long) (System.currentTimeMillis() + (localClockOffset * 1000.0)); - } - - /** - * Returns a corrected UTC time in seconds - * - * @return - */ - public synchronized int getUTCTimeSeconds() { - return (int) (getUTCTimeMS() / 1000L); - } - - /** - * Returns a corrected UTC time in milliseconds - * - * @return - */ - public synchronized long getUTCTimeMS() { - return (long) (System.currentTimeMillis() + (localClockOffset * 1000.0) + (roundTripDelay * 1000.0) + (offset * 1000L)); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.time; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NtpService { + private static final Logger log = LogManager.getLogger(); + + private String server = null; + private double roundTripDelay = 0; + private double localClockOffset = 0; + + private int attempts = 0; + private int offset = 0; + + public NtpService(String server) { + this.server = server; + + roundTripDelay = 0; + localClockOffset = 0; + + if (server != null) { + initFromServer(); + } + } + + private void initFromServer() { + if (server != null) { + boolean success = false; + + while (attempts < 3 && !success) { + try (DatagramSocket socket = new DatagramSocket()) { + // Send request + socket.setSoTimeout(5000); + + var address = InetAddress.getByName(server); + var buf = new NtpMessage().toByteArray(); + var packet = new DatagramPacket(buf, buf.length, address, 123); + + // Set the transmit timestamp *just* before sending the packet + // ToDo: Does this actually improve performance or not? + NtpMessage.encodeTimestamp( + packet.getData(), 40, (System.currentTimeMillis() / 1000.0) + 2208988800.0); + + socket.send(packet); + + // Get response + log.info("NTP request sent, waiting for response...\n"); + packet = new DatagramPacket(buf, buf.length); + socket.receive(packet); + + // Immediately record the incoming timestamp + double destinationTimestamp = (System.currentTimeMillis() / 1000.0) + 2208988800.0; + + // Process response + NtpMessage msg = new NtpMessage(packet.getData()); + + // Corrected, according to RFC2030 errata + roundTripDelay = + (destinationTimestamp - msg.originateTimestamp) + - (msg.transmitTimestamp - msg.receiveTimestamp); + localClockOffset = + ((msg.receiveTimestamp - msg.originateTimestamp) + + (msg.transmitTimestamp - destinationTimestamp)) + / 2; + + log.info(msg.toString()); + success = true; + } catch (Exception ex) { + if (attempts >= 3) { + throw new NtpException("failed to start NTP service", ex); + } + } finally { + attempts++; + } + } + + if (!success) { + throw new NtpException("Unable to start NTP service using " + server); + } + } + } + + public boolean isSynchronized() { + return server != null; + } + + /** + * Returns the offset in seconds set in this NtpService + * + * @return + */ + public int getOffset() { + return offset; + } + + /** + * Sets the offset in seconds for this NtpService + * + * @param offset + */ + public void setOffset(int offset) { + this.offset = offset; + } + + /** + * Returns a corrected System time + * + * @return + */ + public long getSystemTime() { + return (long) (System.currentTimeMillis() + (localClockOffset * 1000.0)); + } + + /** + * Returns a corrected UTC time in seconds + * + * @return + */ + public synchronized int getUTCTimeSeconds() { + return (int) (getUTCTimeMS() / 1000L); + } + + /** + * Returns a corrected UTC time in milliseconds + * + * @return + */ + public synchronized long getUTCTimeMS() { + return (long) + (System.currentTimeMillis() + + (localClockOffset * 1000.0) + + (roundTripDelay * 1000.0) + + (offset * 1000L)); + } +} diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/time/Time.java b/radixdlt-core/radixdlt/src/main/java/org/radix/time/Time.java index 0de8c614de..fd67232ac1 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/time/Time.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/time/Time.java @@ -68,19 +68,21 @@ public final class Time { - private static NtpService ntpServiceInstance; + private static NtpService ntpServiceInstance; - private Time() { - throw new IllegalStateException("Can't construct"); - } + private Time() { + throw new IllegalStateException("Can't construct"); + } - public static void start(RuntimeProperties properties) { - if (properties.get("ntp", false)) { - ntpServiceInstance = new NtpService(properties.get("ntp.pool")); - } - } + public static void start(RuntimeProperties properties) { + if (properties.get("ntp", false)) { + ntpServiceInstance = new NtpService(properties.get("ntp.pool")); + } + } - public static long currentTimestamp() { - return ntpServiceInstance != null ? ntpServiceInstance.getUTCTimeMS() : System.currentTimeMillis(); - } + public static long currentTimestamp() { + return ntpServiceInstance != null + ? ntpServiceInstance.getUTCTimeMS() + : System.currentTimeMillis(); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/IOUtils.java b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/IOUtils.java index f4bb9f3e11..bbdde2188a 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/IOUtils.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/IOUtils.java @@ -64,81 +64,74 @@ package org.radix.utils; +import com.google.common.io.CharStreams; +import com.radixdlt.utils.RadixConstants; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; - -import com.google.common.io.CharStreams; -import com.radixdlt.utils.RadixConstants; - import org.apache.logging.log4j.Logger; -/** - * Some utility methods dealing with streams and closeables. - */ +/** Some utility methods dealing with streams and closeables. */ public final class IOUtils { - private IOUtils() { - throw new IllegalStateException("Can't construct"); - } + private IOUtils() { + throw new IllegalStateException("Can't construct"); + } - /** - * Reads the specified input stream as a {@code String} using the default - * character set specified in {@link RadixConstants#STANDARD_CHARSET}. - * The input stream is closed before this method completes. - * - * @param inputStream The input stream to read from - * @return the contents of the input stream as a {@code String} - * @throws IOException If an I/O error occurs - */ - public static String toString(InputStream inputStream) throws IOException { - return toString(inputStream, RadixConstants.STANDARD_CHARSET); - } + /** + * Reads the specified input stream as a {@code String} using the default character set specified + * in {@link RadixConstants#STANDARD_CHARSET}. The input stream is closed before this method + * completes. + * + * @param inputStream The input stream to read from + * @return the contents of the input stream as a {@code String} + * @throws IOException If an I/O error occurs + */ + public static String toString(InputStream inputStream) throws IOException { + return toString(inputStream, RadixConstants.STANDARD_CHARSET); + } - /** - * Reads the specified input stream as a {@code String}, using the - * specified character set. - * The input stream is closed before this method completes. - * - * @param inputStream The input stream to read from - * @param charset The character set to use for reading - * @return the contents of the input stream as a {@code String} - * @throws IOException If an I/O error occurs - */ - public static String toString(InputStream inputStream, Charset charset) throws IOException { - try (Reader reader = new InputStreamReader(inputStream, charset)) { - return CharStreams.toString(reader); - } - } + /** + * Reads the specified input stream as a {@code String}, using the specified character set. The + * input stream is closed before this method completes. + * + * @param inputStream The input stream to read from + * @param charset The character set to use for reading + * @return the contents of the input stream as a {@code String} + * @throws IOException If an I/O error occurs + */ + public static String toString(InputStream inputStream, Charset charset) throws IOException { + try (Reader reader = new InputStreamReader(inputStream, charset)) { + return CharStreams.toString(reader); + } + } - /** - * Closes the specified {@link Closeable} suppressing any - * {@link IOException} thrown. - * - * @param c The {@link Closeable} to close - */ - public static void closeSafely(Closeable c) { - closeSafely(c, null); - } + /** + * Closes the specified {@link Closeable} suppressing any {@link IOException} thrown. + * + * @param c The {@link Closeable} to close + */ + public static void closeSafely(Closeable c) { + closeSafely(c, null); + } - /** - * Closes the specified {@link Closeable} suppressing any - * {@link IOException} and logging to the specified {@link Logger}. - * Log messages are suppressed if {@code log} is {@code null}. - * - * @param c The {@link Closeable} to close - * @param log The {@link Logger} to output exception information on, - * or {@code null} if no exception logs are required - */ - public static void closeSafely(Closeable c, Logger log) { - try { - c.close(); - } catch (IOException e) { - if (log != null) { - log.error("While closing", e); - } - } - } + /** + * Closes the specified {@link Closeable} suppressing any {@link IOException} and logging to the + * specified {@link Logger}. Log messages are suppressed if {@code log} is {@code null}. + * + * @param c The {@link Closeable} to close + * @param log The {@link Logger} to output exception information on, or {@code null} if no + * exception logs are required + */ + public static void closeSafely(Closeable c, Logger log) { + try { + c.close(); + } catch (IOException e) { + if (log != null) { + log.error("While closing", e); + } + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/InterruptibleSupplier.java b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/InterruptibleSupplier.java index eecea01caf..9398bcb6f4 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/InterruptibleSupplier.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/InterruptibleSupplier.java @@ -66,5 +66,5 @@ @FunctionalInterface public interface InterruptibleSupplier { - T get() throws InterruptedException; + T get() throws InterruptedException; } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/LedgerFileSync.java b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/LedgerFileSync.java index 6f0d24c900..957a32c68f 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/LedgerFileSync.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/LedgerFileSync.java @@ -78,119 +78,111 @@ import com.radixdlt.serialization.SerializerId2; import com.radixdlt.sync.CommittedReader; import com.radixdlt.utils.Compress; - -import javax.annotation.concurrent.Immutable; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Objects; +import javax.annotation.concurrent.Immutable; -/** - * Utility class to write/restore ledger sync data from a file. - */ +/** Utility class to write/restore ledger sync data from a file. */ public final class LedgerFileSync { - /** - * Writes node's ledger sync data to a file. - */ - public static void writeToFile( - String fileName, - Serialization serialization, - CommittedReader committedReader - ) throws IOException { - final var initialProof = committedReader.getEpochProof(1L); - final var endProofOpt = committedReader.getLastProof(); - if (initialProof.isPresent() && endProofOpt.isPresent()) { - final var endProof = endProofOpt.get(); - try (var out = new FileOutputStream(fileName)) { - var currentProof = initialProof.get(); - var nextCommands = committedReader.getNextCommittedTxns(currentProof.toDto()); - while (nextCommands != null && nextCommands.getProof().getStateVersion() <= endProof.getStateVersion()) { - final var commandsAndProof = new CommandsAndProof(nextCommands.getTxns(), nextCommands.getProof().toDto()); - final var serialized = Compress.compress(serialization.toDson(commandsAndProof, DsonOutput.Output.WIRE)); - out.write(ByteBuffer.allocate(4).putInt(serialized.length).array()); - out.write(serialized); - currentProof = nextCommands.getProof(); - nextCommands = committedReader.getNextCommittedTxns(currentProof.toDto()); - } - } - } - } + /** Writes node's ledger sync data to a file. */ + public static void writeToFile( + String fileName, Serialization serialization, CommittedReader committedReader) + throws IOException { + final var initialProof = committedReader.getEpochProof(1L); + final var endProofOpt = committedReader.getLastProof(); + if (initialProof.isPresent() && endProofOpt.isPresent()) { + final var endProof = endProofOpt.get(); + try (var out = new FileOutputStream(fileName)) { + var currentProof = initialProof.get(); + var nextCommands = committedReader.getNextCommittedTxns(currentProof.toDto()); + while (nextCommands != null + && nextCommands.getProof().getStateVersion() <= endProof.getStateVersion()) { + final var commandsAndProof = + new CommandsAndProof(nextCommands.getTxns(), nextCommands.getProof().toDto()); + final var serialized = + Compress.compress(serialization.toDson(commandsAndProof, DsonOutput.Output.WIRE)); + out.write(ByteBuffer.allocate(4).putInt(serialized.length).array()); + out.write(serialized); + currentProof = nextCommands.getProof(); + nextCommands = committedReader.getNextCommittedTxns(currentProof.toDto()); + } + } + } + } - /** - * Reads and processes ledger sync data from a file. - */ - public static void restoreFromFile( - String fileName, - Serialization serialization, - EventDispatcher verifiedTxnsAndProofDispatcher - ) throws IOException { - try (var in = new FileInputStream(fileName)) { - while (in.available() > 0) { - final var len = ByteBuffer.wrap(in.readNBytes(4)).getInt(); - final var data = in.readNBytes(len); - final var wrapper = serialization.fromDson(Compress.uncompress(data), CommandsAndProof.class); - final var proof = wrapper.getProof(); - // TODO: verify the proof - final var verifiedTxnsAndProof = VerifiedTxnsAndProof.create( - wrapper.getTxns(), - new LedgerProof(proof.getOpaque(), proof.getLedgerHeader(), proof.getSignatures()) - ); - verifiedTxnsAndProofDispatcher.dispatch(verifiedTxnsAndProof); - } - } - } + /** Reads and processes ledger sync data from a file. */ + public static void restoreFromFile( + String fileName, + Serialization serialization, + EventDispatcher verifiedTxnsAndProofDispatcher) + throws IOException { + try (var in = new FileInputStream(fileName)) { + while (in.available() > 0) { + final var len = ByteBuffer.wrap(in.readNBytes(4)).getInt(); + final var data = in.readNBytes(len); + final var wrapper = + serialization.fromDson(Compress.uncompress(data), CommandsAndProof.class); + final var proof = wrapper.getProof(); + // TODO: verify the proof + final var verifiedTxnsAndProof = + VerifiedTxnsAndProof.create( + wrapper.getTxns(), + new LedgerProof(proof.getOpaque(), proof.getLedgerHeader(), proof.getSignatures())); + verifiedTxnsAndProofDispatcher.dispatch(verifiedTxnsAndProof); + } + } + } - @Immutable - @SerializerId2("ledger_file_sync.commands_and_proof") - private static final class CommandsAndProof { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {DsonOutput.Output.API, DsonOutput.Output.WIRE, DsonOutput.Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; + @Immutable + @SerializerId2("ledger_file_sync.commands_and_proof") + private static final class CommandsAndProof { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(value = {DsonOutput.Output.API, DsonOutput.Output.WIRE, DsonOutput.Output.PERSIST}) + SerializerDummy serializer = SerializerDummy.DUMMY; - @JsonProperty("txns") - @DsonOutput(DsonOutput.Output.ALL) - private final List txns; + @JsonProperty("txns") + @DsonOutput(DsonOutput.Output.ALL) + private final List txns; - @JsonProperty("proof") - @DsonOutput(DsonOutput.Output.ALL) - private final DtoLedgerProof proof; + @JsonProperty("proof") + @DsonOutput(DsonOutput.Output.ALL) + private final DtoLedgerProof proof; - @JsonCreator - public CommandsAndProof( - @JsonProperty("txns") List txns, - @JsonProperty("proof") DtoLedgerProof proof - ) { - this.txns = Objects.requireNonNull(txns); - this.proof = Objects.requireNonNull(proof); - } + @JsonCreator + public CommandsAndProof( + @JsonProperty("txns") List txns, @JsonProperty("proof") DtoLedgerProof proof) { + this.txns = Objects.requireNonNull(txns); + this.proof = Objects.requireNonNull(proof); + } - public List getTxns() { - return txns; - } + public List getTxns() { + return txns; + } - public DtoLedgerProof getProof() { - return proof; - } + public DtoLedgerProof getProof() { + return proof; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final var that = (CommandsAndProof) o; - return Objects.equals(txns, that.txns) - && Objects.equals(proof, that.proof); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final var that = (CommandsAndProof) o; + return Objects.equals(txns, that.txns) && Objects.equals(proof, that.proof); + } - @Override - public int hashCode() { - return Objects.hash(txns, proof); - } - } + @Override + public int hashCode() { + return Objects.hash(txns, proof); + } + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/SimpleThreadPool.java b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/SimpleThreadPool.java index 0ac6f55010..a124c4203b 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/SimpleThreadPool.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/SimpleThreadPool.java @@ -66,113 +66,110 @@ import java.util.Objects; import java.util.function.Consumer; - import org.apache.logging.log4j.Logger; /** - * Simple thread pool that copies values of a specified type from a source - * {@link InterruptibleSupplier} to a {@link Consumer} with the specified - * number of threads. + * Simple thread pool that copies values of a specified type from a source {@link + * InterruptibleSupplier} to a {@link Consumer} with the specified number of threads. * * @param The type this class will be handling */ public class SimpleThreadPool { - private final Logger log; + private final Logger log; - private final String name; - private final InterruptibleSupplier source; - private final Consumer sink; - private final Thread[] threads; - private volatile boolean running = false; + private final String name; + private final InterruptibleSupplier source; + private final Consumer sink; + private final Thread[] threads; + private volatile boolean running = false; - /** - * Constructs a {@code SimpleThreadPool} with the specified name, number - * of threads, source, destination and log. - *

- * After construction has complete, the thread pool is in a dormant, - * ready-to-run state. Call {@link #start()} to start processing objects. - * - * @param name The name of the thread pool - * @param numThreads The number of threads in the thread pool - * @param source The source of objects - * @param sink The consumer of objects - * @param log Where log messages should be output - */ - public SimpleThreadPool(String name, int numThreads, InterruptibleSupplier source, Consumer sink, Logger log) { - this.log = Objects.requireNonNull(log); - this.name = Objects.requireNonNull(name); - this.source = Objects.requireNonNull(source); - this.sink = Objects.requireNonNull(sink); - this.threads = new Thread[numThreads]; - } + /** + * Constructs a {@code SimpleThreadPool} with the specified name, number of threads, source, + * destination and log. + * + *

After construction has complete, the thread pool is in a dormant, ready-to-run state. Call + * {@link #start()} to start processing objects. + * + * @param name The name of the thread pool + * @param numThreads The number of threads in the thread pool + * @param source The source of objects + * @param sink The consumer of objects + * @param log Where log messages should be output + */ + public SimpleThreadPool( + String name, int numThreads, InterruptibleSupplier source, Consumer sink, Logger log) { + this.log = Objects.requireNonNull(log); + this.name = Objects.requireNonNull(name); + this.source = Objects.requireNonNull(source); + this.sink = Objects.requireNonNull(sink); + this.threads = new Thread[numThreads]; + } - /** - * Starts a dormant thread pool running. - * Note that {@link #start()} and {@link #stop()} may be called - * multiple times in order to start and stop processing at will. - */ - public void start() { - synchronized (this.threads) { - stop(); - if (!this.running) { - this.running = true; - for (int i = 0; i < this.threads.length; ++i) { - this.threads[i] = new Thread(this::process, this.name + "-" + (i + 1)); - this.threads[i].setDaemon(true); - this.threads[i].start(); - } - } - } - } + /** + * Starts a dormant thread pool running. Note that {@link #start()} and {@link #stop()} may be + * called multiple times in order to start and stop processing at will. + */ + public void start() { + synchronized (this.threads) { + stop(); + if (!this.running) { + this.running = true; + for (int i = 0; i < this.threads.length; ++i) { + this.threads[i] = new Thread(this::process, this.name + "-" + (i + 1)); + this.threads[i].setDaemon(true); + this.threads[i].start(); + } + } + } + } - /** - * Stops a running thread pool, returning it to the dormant state. - * Note that {@link #start()} and {@link #stop()} may be called - * multiple times in order to start and stop processing at will. - */ - public void stop() { - synchronized (this.threads) { - if (this.running) { - this.running = false; - // Interrupt all first, and then join - for (int i = 0; i < this.threads.length; ++i) { - if (this.threads[i] != null) { - this.threads[i].interrupt(); - } - } - for (int i = 0; i < this.threads.length; ++i) { - if (this.threads[i] != null) { - try { - this.threads[i].join(); - } catch (InterruptedException e) { - log.error(this.threads[i].getName() + " did not exit before interrupt"); - // Other threads will not be joined either, as this will re-interrupt - Thread.currentThread().interrupt(); - } - this.threads[i] = null; - } - } - } - } - } + /** + * Stops a running thread pool, returning it to the dormant state. Note that {@link #start()} and + * {@link #stop()} may be called multiple times in order to start and stop processing at will. + */ + public void stop() { + synchronized (this.threads) { + if (this.running) { + this.running = false; + // Interrupt all first, and then join + for (int i = 0; i < this.threads.length; ++i) { + if (this.threads[i] != null) { + this.threads[i].interrupt(); + } + } + for (int i = 0; i < this.threads.length; ++i) { + if (this.threads[i] != null) { + try { + this.threads[i].join(); + } catch (InterruptedException e) { + log.error(this.threads[i].getName() + " did not exit before interrupt"); + // Other threads will not be joined either, as this will re-interrupt + Thread.currentThread().interrupt(); + } + this.threads[i] = null; + } + } + } + } + } - private void process() { - while (this.running) { - try { - sink.accept(source.get()); - } catch (InterruptedException e) { - // Exit - Thread.currentThread().interrupt(); - break; - } catch (Exception e) { - // Don't want to exit this loop if other exception occurs - log.error("While processing events", e); - } - } - } + private void process() { + while (this.running) { + try { + sink.accept(source.get()); + } catch (InterruptedException e) { + // Exit + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + // Don't want to exit this loop if other exception occurs + log.error("While processing events", e); + } + } + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), name); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), name); + } } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/shell/RadixShell.java b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/shell/RadixShell.java index 89953c26b3..ab7d781314 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/utils/shell/RadixShell.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/utils/shell/RadixShell.java @@ -100,13 +100,6 @@ import com.radixdlt.serialization.Serialization; import com.radixdlt.sync.CommittedReader; import io.reactivex.rxjava3.disposables.Disposable; -import org.apache.commons.cli.ParseException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.json.JSONObject; -import org.radix.network.messaging.Message; -import org.radix.utils.LedgerFileSync; - import java.io.File; import java.io.IOException; import java.net.URI; @@ -116,264 +109,276 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import org.apache.commons.cli.ParseException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.json.JSONObject; +import org.radix.network.messaging.Message; +import org.radix.utils.LedgerFileSync; public final class RadixShell { - private static final Logger log = LogManager.getLogger(); - - public static void log(String msg, Object... params) { - log.info(msg, params); - } - - public static NodeBuilder nodeBuilder() throws ParseException { - return new NodeBuilder(); - } - - public static class NodeBuilder { - private Network network = Network.LOCALNET; - private RuntimeProperties properties; - private int p2pServerPort = 0; - private ImmutableSet.Builder moduleRunnersBuilder = new ImmutableSet.Builder<>(); - private ImmutableMap.Builder customProperties = new ImmutableMap.Builder<>(); - private Optional dataDir = Optional.empty(); - private final String nodeKeyPass = System.getenv("RADIX_NODE_KEYSTORE_PASSWORD"); - - public NodeBuilder() throws ParseException { - properties = new RuntimeProperties(new JSONObject(), new String[] { }); - } - - public NodeBuilder p2pServer(int p2pServerPort) { - this.p2pServerPort = p2pServerPort; - moduleRunnersBuilder.add(Runners.P2P_NETWORK); - properties.set("network.p2p.listen_port", p2pServerPort); - properties.set("network.p2p.listen_address", "127.0.0.1"); - properties.set("network.p2p.seed_nodes", ""); - return this; - } - - public NodeBuilder ledgerSync() { - moduleRunnersBuilder.add(Runners.SYNC); - return this; - } - - public NodeBuilder network(Network network) { - this.network = network; - return this; - } - - public NodeBuilder dataDir(String path) { - this.dataDir = Optional.of(path); - return this; - } - - public NodeBuilder prop(String name, String value) { - customProperties.put(name, value); - return this; - } - - public Node build() throws Exception { - final File dataDir; - if (this.dataDir.isPresent()) { - dataDir = new File(this.dataDir.get()); - if (!dataDir.exists()) { - dataDir.mkdirs(); - } - } else { - dataDir = new File(Files.createTempDirectory("radix-shell-node-").toString()); - } - - customProperties.build().forEach((k, v) -> properties.set(k, v)); - - properties.set("db.location", dataDir.toString()); - - if (properties.get("node.key.path", "").isEmpty()) { - properties.set("node.key.path", new File(dataDir, "node-keystore.ks").getAbsolutePath()); - } - - if (properties.get("network.host_ip", "").isEmpty()) { - properties.set("network.host_ip", "127.0.0.1"); - } - - log.info("Node data dir: {}", dataDir); - - final var nodeKeyFile = new File(properties.get("node.key.path")); - if (!nodeKeyFile.exists()) { - final var newKeyPair = ECKeyPair.generateNew(); - RadixKeyStore.fromFile(nodeKeyFile, nodeKeyPass.toCharArray(), true) - .writeKeyPair("node", newKeyPair); - } - - properties.set("network.id", network.getId()); - if (network.genesisTxn().isEmpty() && properties.get("network.genesis_txn", "").isEmpty()) { - properties.set("network.genesis_txn", Network.STOKENET.genesisTxn().get()); // default to stokenet genesis - } - - final var injector = Guice.createInjector(new RadixNodeModule(properties)); - final var node = new Node(injector); - - moduleRunnersBuilder.build().forEach(node::startRunner); - - if (p2pServerPort > 0) { - node.startP2PServer(); - } - - return node; - } - } - - public static final class Node { - private final Injector injector; - private final Map moduleRunners; - private final Set msgConsumers = new HashSet<>(); - private final Set eventConsumers = new HashSet<>(); - private final Set remoteEventConsumers = new HashSet<>(); - - public Node(Injector injector) { - this.injector = injector; - this.moduleRunners = injector.getInstance(Key.get(new TypeLiteral>() { })); - } - - public T getInstance(Key key) { - return injector.getInstance(key); - } - - public T getInstance(Class clz) { - return injector.getInstance(clz); - } - - public void startRunner(String runner) { - moduleRunners.get(runner).start(); - } - - public void stopRunner(String runner) { - moduleRunners.get(runner).stop(); - } - - public void startP2PServer() { - final var peerServer = injector.getInstance(PeerServerBootstrap.class); - try { - peerServer.start(); - log.info("P2P server started: " + self()); - } catch (InterruptedException e) { - log.error("Cannot start p2p server", e); - } - } - - public RadixNodeUri self() { - return injector.getInstance(Key.get(RadixNodeUri.class, Self.class)); - } - - public void connectTo(String uri) throws DeserializeException { - connectTo(RadixNodeUri.fromUri(URI.create(uri))); - } - - public void connectTo(RadixNodeUri uri) { - final var bootstrap = getInstance(PeerOutboundBootstrap.class); - bootstrap.initOutboundConnection(uri); - } - - public ImmutableList peers() { - return ImmutableList.builder() - .addAll(getInstance(PeerManager.class).activeChannels()) - .build(); - } - - @SuppressWarnings("unchecked") - public void dispatch(T t) { - ((EventDispatcher) injector.getInstance(Environment.class).getDispatcher(t.getClass())) - .dispatch(t); - } - - public Disposable onEvent(Class eventClass, Consumer consumer) { - final var disposable = - injector.getInstance(RxEnvironment.class).getObservable(eventClass).subscribe(consumer::accept); - - eventConsumers.add(disposable); - return disposable; - } - - public void cleanEventConsumers() { - eventConsumers.forEach(Disposable::dispose); - eventConsumers.clear(); - } - - public void dispatchRemote(PeerChannel receiver, T t) { - dispatchRemote(BFTNode.create(receiver.getRemoteNodeId().getPublicKey()), t); - } - - public void dispatchRemote(RadixNodeUri receiver, T t) { - dispatchRemote(BFTNode.create(receiver.getNodeId().getPublicKey()), t); - } - - public Disposable onRemoteEvent(Class eventClass, Consumer> consumer) { - final var disposable = - injector.getInstance(RxRemoteEnvironment.class).remoteEvents(eventClass) - .subscribe(consumer::accept); - - remoteEventConsumers.add(disposable); - return disposable; - } - - public void cleanRemoteEventConsumers() { - remoteEventConsumers.forEach(Disposable::dispose); - remoteEventConsumers.clear(); - } - - @SuppressWarnings("unchecked") - public void dispatchRemote(BFTNode receiver, T t) { - ((RemoteEventDispatcher) injector.getInstance(Environment.class).getRemoteDispatcher(t.getClass())) - .dispatch(receiver, t); - } - - public void sendMsg(RadixNodeUri uri, Message message) { - sendMsg(uri.getNodeId(), message); - } - - public void sendMsg(String nodeId, Message message) throws DeserializeException { - final var addressing = injector.getInstance(Addressing.class); - sendMsg(NodeId.fromPublicKey(addressing.forNodes().parse(nodeId)), message); - } - - public void sendMsg(NodeId nodeId, Message message) { - getInstance(MessageCentral.class).send(nodeId, message); - } - - public Disposable onMsg(Class msgClass, Consumer> consumer) { - final var disposable = getInstance(MessageCentral.class).messagesOf(msgClass) - .subscribe(consumer::accept); - msgConsumers.add(disposable); - return disposable; - } - - public void cleanMsgConsumers() { - msgConsumers.forEach(Disposable::dispose); - msgConsumers.clear(); - } - - public void writeLedgerSyncToFile(String fileName) throws IOException { - final var start = System.currentTimeMillis(); - LedgerFileSync.writeToFile( - fileName, - getInstance(Serialization.class), - getInstance(CommittedReader.class) - ); - final var time = System.currentTimeMillis() - start; - System.out.printf("Dump finished. Took %ss%n", time / 1000); - } - - public void restoreLedgerFromFile(String fileName) throws IOException { - final var start = System.currentTimeMillis(); - LedgerFileSync.restoreFromFile( - fileName, - getInstance(Serialization.class), - getInstance(Key.get(new TypeLiteral>() { })) - ); - final var time = System.currentTimeMillis() - start; - System.out.printf("Restore finished. Took %ss%n", time / 1000); - } - - @Override - public String toString() { - return self().toString(); - } - } + private static final Logger log = LogManager.getLogger(); + + public static void log(String msg, Object... params) { + log.info(msg, params); + } + + public static NodeBuilder nodeBuilder() throws ParseException { + return new NodeBuilder(); + } + + public static class NodeBuilder { + private Network network = Network.LOCALNET; + private RuntimeProperties properties; + private int p2pServerPort = 0; + private ImmutableSet.Builder moduleRunnersBuilder = new ImmutableSet.Builder<>(); + private ImmutableMap.Builder customProperties = new ImmutableMap.Builder<>(); + private Optional dataDir = Optional.empty(); + private final String nodeKeyPass = System.getenv("RADIX_NODE_KEYSTORE_PASSWORD"); + + public NodeBuilder() throws ParseException { + properties = new RuntimeProperties(new JSONObject(), new String[] {}); + } + + public NodeBuilder p2pServer(int p2pServerPort) { + this.p2pServerPort = p2pServerPort; + moduleRunnersBuilder.add(Runners.P2P_NETWORK); + properties.set("network.p2p.listen_port", p2pServerPort); + properties.set("network.p2p.listen_address", "127.0.0.1"); + properties.set("network.p2p.seed_nodes", ""); + return this; + } + + public NodeBuilder ledgerSync() { + moduleRunnersBuilder.add(Runners.SYNC); + return this; + } + + public NodeBuilder network(Network network) { + this.network = network; + return this; + } + + public NodeBuilder dataDir(String path) { + this.dataDir = Optional.of(path); + return this; + } + + public NodeBuilder prop(String name, String value) { + customProperties.put(name, value); + return this; + } + + public Node build() throws Exception { + final File dataDir; + if (this.dataDir.isPresent()) { + dataDir = new File(this.dataDir.get()); + if (!dataDir.exists()) { + dataDir.mkdirs(); + } + } else { + dataDir = new File(Files.createTempDirectory("radix-shell-node-").toString()); + } + + customProperties.build().forEach((k, v) -> properties.set(k, v)); + + properties.set("db.location", dataDir.toString()); + + if (properties.get("node.key.path", "").isEmpty()) { + properties.set("node.key.path", new File(dataDir, "node-keystore.ks").getAbsolutePath()); + } + + if (properties.get("network.host_ip", "").isEmpty()) { + properties.set("network.host_ip", "127.0.0.1"); + } + + log.info("Node data dir: {}", dataDir); + + final var nodeKeyFile = new File(properties.get("node.key.path")); + if (!nodeKeyFile.exists()) { + final var newKeyPair = ECKeyPair.generateNew(); + RadixKeyStore.fromFile(nodeKeyFile, nodeKeyPass.toCharArray(), true) + .writeKeyPair("node", newKeyPair); + } + + properties.set("network.id", network.getId()); + if (network.genesisTxn().isEmpty() && properties.get("network.genesis_txn", "").isEmpty()) { + properties.set( + "network.genesis_txn", + Network.STOKENET.genesisTxn().get()); // default to stokenet genesis + } + + final var injector = Guice.createInjector(new RadixNodeModule(properties)); + final var node = new Node(injector); + + moduleRunnersBuilder.build().forEach(node::startRunner); + + if (p2pServerPort > 0) { + node.startP2PServer(); + } + + return node; + } + } + + public static final class Node { + private final Injector injector; + private final Map moduleRunners; + private final Set msgConsumers = new HashSet<>(); + private final Set eventConsumers = new HashSet<>(); + private final Set remoteEventConsumers = new HashSet<>(); + + public Node(Injector injector) { + this.injector = injector; + this.moduleRunners = + injector.getInstance(Key.get(new TypeLiteral>() {})); + } + + public T getInstance(Key key) { + return injector.getInstance(key); + } + + public T getInstance(Class clz) { + return injector.getInstance(clz); + } + + public void startRunner(String runner) { + moduleRunners.get(runner).start(); + } + + public void stopRunner(String runner) { + moduleRunners.get(runner).stop(); + } + + public void startP2PServer() { + final var peerServer = injector.getInstance(PeerServerBootstrap.class); + try { + peerServer.start(); + log.info("P2P server started: " + self()); + } catch (InterruptedException e) { + log.error("Cannot start p2p server", e); + } + } + + public RadixNodeUri self() { + return injector.getInstance(Key.get(RadixNodeUri.class, Self.class)); + } + + public void connectTo(String uri) throws DeserializeException { + connectTo(RadixNodeUri.fromUri(URI.create(uri))); + } + + public void connectTo(RadixNodeUri uri) { + final var bootstrap = getInstance(PeerOutboundBootstrap.class); + bootstrap.initOutboundConnection(uri); + } + + public ImmutableList peers() { + return ImmutableList.builder() + .addAll(getInstance(PeerManager.class).activeChannels()) + .build(); + } + + @SuppressWarnings("unchecked") + public void dispatch(T t) { + ((EventDispatcher) injector.getInstance(Environment.class).getDispatcher(t.getClass())) + .dispatch(t); + } + + public Disposable onEvent(Class eventClass, Consumer consumer) { + final var disposable = + injector + .getInstance(RxEnvironment.class) + .getObservable(eventClass) + .subscribe(consumer::accept); + + eventConsumers.add(disposable); + return disposable; + } + + public void cleanEventConsumers() { + eventConsumers.forEach(Disposable::dispose); + eventConsumers.clear(); + } + + public void dispatchRemote(PeerChannel receiver, T t) { + dispatchRemote(BFTNode.create(receiver.getRemoteNodeId().getPublicKey()), t); + } + + public void dispatchRemote(RadixNodeUri receiver, T t) { + dispatchRemote(BFTNode.create(receiver.getNodeId().getPublicKey()), t); + } + + public Disposable onRemoteEvent(Class eventClass, Consumer> consumer) { + final var disposable = + injector + .getInstance(RxRemoteEnvironment.class) + .remoteEvents(eventClass) + .subscribe(consumer::accept); + + remoteEventConsumers.add(disposable); + return disposable; + } + + public void cleanRemoteEventConsumers() { + remoteEventConsumers.forEach(Disposable::dispose); + remoteEventConsumers.clear(); + } + + @SuppressWarnings("unchecked") + public void dispatchRemote(BFTNode receiver, T t) { + ((RemoteEventDispatcher) + injector.getInstance(Environment.class).getRemoteDispatcher(t.getClass())) + .dispatch(receiver, t); + } + + public void sendMsg(RadixNodeUri uri, Message message) { + sendMsg(uri.getNodeId(), message); + } + + public void sendMsg(String nodeId, Message message) throws DeserializeException { + final var addressing = injector.getInstance(Addressing.class); + sendMsg(NodeId.fromPublicKey(addressing.forNodes().parse(nodeId)), message); + } + + public void sendMsg(NodeId nodeId, Message message) { + getInstance(MessageCentral.class).send(nodeId, message); + } + + public Disposable onMsg( + Class msgClass, Consumer> consumer) { + final var disposable = + getInstance(MessageCentral.class).messagesOf(msgClass).subscribe(consumer::accept); + msgConsumers.add(disposable); + return disposable; + } + + public void cleanMsgConsumers() { + msgConsumers.forEach(Disposable::dispose); + msgConsumers.clear(); + } + + public void writeLedgerSyncToFile(String fileName) throws IOException { + final var start = System.currentTimeMillis(); + LedgerFileSync.writeToFile( + fileName, getInstance(Serialization.class), getInstance(CommittedReader.class)); + final var time = System.currentTimeMillis() - start; + System.out.printf("Dump finished. Took %ss%n", time / 1000); + } + + public void restoreLedgerFromFile(String fileName) throws IOException { + final var start = System.currentTimeMillis(); + LedgerFileSync.restoreFromFile( + fileName, + getInstance(Serialization.class), + getInstance(Key.get(new TypeLiteral>() {}))); + final var time = System.currentTimeMillis() - start; + System.out.printf("Restore finished. Took %ss%n", time / 1000); + } + + @Override + public String toString() { + return self().toString(); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ConsensusModuleTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ConsensusModuleTest.java index adf481f2f7..3098c3910c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ConsensusModuleTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ConsensusModuleTest.java @@ -64,6 +64,15 @@ package com.radixdlt; +import static com.radixdlt.crypto.ECDSASignature.zeroSignature; +import static com.radixdlt.utils.TypedMocks.rmock; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.RateLimiter; import com.google.inject.AbstractModule; @@ -132,223 +141,249 @@ import com.radixdlt.utils.Pair; import com.radixdlt.utils.TimeSupplier; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; - import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; - -import static com.radixdlt.crypto.ECDSASignature.zeroSignature; -import static com.radixdlt.utils.TypedMocks.rmock; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import org.junit.Before; +import org.junit.Test; public class ConsensusModuleTest { - @Inject - private BFTSync bftSync; - - @Inject - private VertexStore vertexStore; - - private Hasher hasher = Sha256Hasher.withDefaultSerialization(); - - private BFTConfiguration bftConfiguration; - - private ECKeyPair ecKeyPair; - private RemoteEventDispatcher requestSender; - private RemoteEventDispatcher responseSender; - private RemoteEventDispatcher errorResponseSender; - - @Before - public void setup() { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - var genesis = UnverifiedVertex.createGenesis(LedgerHeader.genesis(accumulatorState, null, 0)); - var hashedGenesis = new VerifiedVertex(genesis, HashUtils.zero256()); - var qc = QuorumCertificate.ofGenesis(hashedGenesis, LedgerHeader.genesis(accumulatorState, null, 0)); - var validatorSet = BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))); - var vertexStoreState = VerifiedVertexStoreState.create(HighQC.from(qc), hashedGenesis, Optional.empty(), hasher); - var proposerElection = new WeightedRotatingLeaders(validatorSet); - this.bftConfiguration = new BFTConfiguration(proposerElection, validatorSet, vertexStoreState); - this.ecKeyPair = ECKeyPair.generateNew(); - this.requestSender = rmock(RemoteEventDispatcher.class); - this.responseSender = rmock(RemoteEventDispatcher.class); - this.errorResponseSender = rmock(RemoteEventDispatcher.class); - - Guice.createInjector( - new ConsensusModule(), - new CryptoModule(), - getExternalModule() - ).injectMembers(this); - } - - private Module getExternalModule() { - return new AbstractModule() { - @Override - protected void configure() { - bind(Ledger.class).toInstance(mock(Ledger.class)); - - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(ScheduledEventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(ScheduledEventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(RemoteEventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(RemoteEventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(requestSender); - bind(new TypeLiteral>() { }).toInstance(responseSender); - bind(new TypeLiteral>() { }).toInstance(errorResponseSender); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(ScheduledEventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(ScheduledEventDispatcher.class)); - - bind(PersistentVertexStore.class).toInstance(mock(PersistentVertexStore.class)); - bind(PersistentSafetyStateStore.class).toInstance(mock(PersistentSafetyStateStore.class)); - bind(NextTxnsGenerator.class).toInstance(mock(NextTxnsGenerator.class)); - bind(SystemCounters.class).toInstance(mock(SystemCounters.class)); - bind(TimeSupplier.class).toInstance(mock(TimeSupplier.class)); - bind(BFTConfiguration.class).toInstance(bftConfiguration); - LedgerProof proof = mock(LedgerProof.class); - when(proof.getView()).thenReturn(View.genesis()); - bind(LedgerProof.class).annotatedWith(LastProof.class).toInstance(proof); - bind(RateLimiter.class).annotatedWith(GetVerticesRequestRateLimit.class) - .toInstance(RateLimiter.create(Double.MAX_VALUE)); - bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(200); - bindConstant().annotatedWith(PacemakerTimeout.class).to(1000L); - bindConstant().annotatedWith(PacemakerRate.class).to(2.0); - bindConstant().annotatedWith(PacemakerMaxExponent.class).to(6); - - ECKeyPair ecKeyPair = ECKeyPair.generateNew(); - bind(HashSigner.class).toInstance(ecKeyPair::sign); - } - - @Provides - ViewUpdate viewUpdate(@Self BFTNode node) { - return ViewUpdate.create(View.of(1), mock(HighQC.class), node, node); - } - - @Provides - @Self - private BFTNode bftNode() { - return BFTNode.create(ecKeyPair.getPublicKey()); - } - }; - } - - - private Pair createNextVertex(QuorumCertificate parent, BFTNode bftNode) { - return createNextVertex(parent, bftNode, Txn.create(new byte[] {0})); - } - - private Pair createNextVertex(QuorumCertificate parent, BFTNode bftNode, Txn txn) { - var unverifiedVertex = UnverifiedVertex.create(parent, View.of(1), List.of(txn), bftNode); - var hash = hasher.hash(unverifiedVertex); - var verifiedVertex = new VerifiedVertex(unverifiedVertex, hash); - var next = new BFTHeader( - View.of(1), - verifiedVertex.getId(), - LedgerHeader.create(1, View.of(1), new AccumulatorState(1, HashUtils.zero256()), 1) - ); - var voteData = new VoteData( - next, - parent.getProposed(), - parent.getParent() - ); - var unsyncedQC = new QuorumCertificate( - voteData, - new TimestampedECDSASignatures(Map.of(bftNode, TimestampedECDSASignature.from(1, zeroSignature()))) - ); - - return Pair.of(unsyncedQC, verifiedVertex); - } - - @Test - public void on_sync_request_timeout_should_retry() { - // Arrange - BFTNode bftNode = BFTNode.random(); - QuorumCertificate parent = vertexStore.highQC().highestQC(); - Pair nextVertex = createNextVertex(parent, bftNode); - HighQC unsyncedHighQC = HighQC.from(nextVertex.getFirst(), nextVertex.getFirst(), Optional.empty()); - bftSync.syncToQC(unsyncedHighQC, bftNode); - GetVerticesRequest request = new GetVerticesRequest(nextVertex.getSecond().getId(), 1); - VertexRequestTimeout timeout = VertexRequestTimeout.create(request); - - // Act - nothrowSleep(100); // FIXME: Remove when rate limit on send removed - bftSync.vertexRequestTimeoutEventProcessor().process(timeout); - - // Assert - verify(requestSender, times(2)) - .dispatch(eq(bftNode), argThat(r -> r.getCount() == 1 && r.getVertexId().equals(nextVertex.getSecond().getId()))); - } - - @Test - public void on_synced_to_vertex_should_request_for_parent() { - // Arrange - BFTNode bftNode = BFTNode.random(); - QuorumCertificate parent = vertexStore.highQC().highestQC(); - Pair nextVertex = createNextVertex(parent, bftNode); - Pair nextNextVertex = createNextVertex(nextVertex.getFirst(), bftNode); - HighQC unsyncedHighQC = HighQC.from(nextNextVertex.getFirst(), nextNextVertex.getFirst(), Optional.empty()); - bftSync.syncToQC(unsyncedHighQC, bftNode); - - // Act - nothrowSleep(100); // FIXME: Remove when rate limit on send removed - GetVerticesResponse response = new GetVerticesResponse(ImmutableList.of(nextNextVertex.getSecond())); - bftSync.responseProcessor().process(bftNode, response); - - // Assert - verify(requestSender, times(1)) - .dispatch(eq(bftNode), argThat(r -> r.getCount() == 1 && r.getVertexId().equals(nextVertex.getSecond().getId()))); - } - - @Test - public void bft_sync_should_sync_two_different_QCs_with_the_same_parent() { - final var bftNode1 = BFTNode.random(); - final var bftNode2 = BFTNode.random(); - final var parentQc = vertexStore.highQC().highestQC(); - final var parentVertex = createNextVertex(parentQc, bftNode1); - final var proposedVertex1 = createNextVertex(parentVertex.getFirst(), bftNode1, Txn.create(new byte[] {1})); - final var proposedVertex2 = createNextVertex(parentVertex.getFirst(), bftNode2, Txn.create(new byte[] {2})); - final var unsyncedHighQC1 = HighQC.from(proposedVertex1.getFirst(), proposedVertex1.getFirst(), Optional.empty()); - final var unsyncedHighQC2 = HighQC.from(proposedVertex2.getFirst(), proposedVertex2.getFirst(), Optional.empty()); - - bftSync.syncToQC(unsyncedHighQC1, bftNode1); - bftSync.syncToQC(unsyncedHighQC2, bftNode2); - - nothrowSleep(100); - final var response1 = new GetVerticesResponse(ImmutableList.of(proposedVertex1.getSecond())); - bftSync.responseProcessor().process(bftNode1, response1); - - final var response2 = new GetVerticesResponse(ImmutableList.of(proposedVertex2.getSecond())); - bftSync.responseProcessor().process(bftNode2, response2); - - verify(requestSender, times(1)) - .dispatch(eq(bftNode1), argThat(r -> r.getCount() == 1 && r.getVertexId().equals(proposedVertex1.getSecond().getId()))); - - verify(requestSender, times(1)) - .dispatch(eq(bftNode2), argThat(r -> r.getCount() == 1 && r.getVertexId().equals(proposedVertex2.getSecond().getId()))); - } - - private void nothrowSleep(long milliseconds) { - try { - Thread.sleep(milliseconds); - } catch (InterruptedException e) { - // Ignore - Thread.currentThread().interrupt(); - } - } + @Inject private BFTSync bftSync; + + @Inject private VertexStore vertexStore; + + private Hasher hasher = Sha256Hasher.withDefaultSerialization(); + + private BFTConfiguration bftConfiguration; + + private ECKeyPair ecKeyPair; + private RemoteEventDispatcher requestSender; + private RemoteEventDispatcher responseSender; + private RemoteEventDispatcher errorResponseSender; + + @Before + public void setup() { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + var genesis = UnverifiedVertex.createGenesis(LedgerHeader.genesis(accumulatorState, null, 0)); + var hashedGenesis = new VerifiedVertex(genesis, HashUtils.zero256()); + var qc = + QuorumCertificate.ofGenesis(hashedGenesis, LedgerHeader.genesis(accumulatorState, null, 0)); + var validatorSet = + BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))); + var vertexStoreState = + VerifiedVertexStoreState.create(HighQC.from(qc), hashedGenesis, Optional.empty(), hasher); + var proposerElection = new WeightedRotatingLeaders(validatorSet); + this.bftConfiguration = new BFTConfiguration(proposerElection, validatorSet, vertexStoreState); + this.ecKeyPair = ECKeyPair.generateNew(); + this.requestSender = rmock(RemoteEventDispatcher.class); + this.responseSender = rmock(RemoteEventDispatcher.class); + this.errorResponseSender = rmock(RemoteEventDispatcher.class); + + Guice.createInjector(new ConsensusModule(), new CryptoModule(), getExternalModule()) + .injectMembers(this); + } + + private Module getExternalModule() { + return new AbstractModule() { + @Override + protected void configure() { + bind(Ledger.class).toInstance(mock(Ledger.class)); + + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(RemoteEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(RemoteEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(requestSender); + bind(new TypeLiteral>() {}) + .toInstance(responseSender); + bind(new TypeLiteral>() {}) + .toInstance(errorResponseSender); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + + bind(PersistentVertexStore.class).toInstance(mock(PersistentVertexStore.class)); + bind(PersistentSafetyStateStore.class).toInstance(mock(PersistentSafetyStateStore.class)); + bind(NextTxnsGenerator.class).toInstance(mock(NextTxnsGenerator.class)); + bind(SystemCounters.class).toInstance(mock(SystemCounters.class)); + bind(TimeSupplier.class).toInstance(mock(TimeSupplier.class)); + bind(BFTConfiguration.class).toInstance(bftConfiguration); + LedgerProof proof = mock(LedgerProof.class); + when(proof.getView()).thenReturn(View.genesis()); + bind(LedgerProof.class).annotatedWith(LastProof.class).toInstance(proof); + bind(RateLimiter.class) + .annotatedWith(GetVerticesRequestRateLimit.class) + .toInstance(RateLimiter.create(Double.MAX_VALUE)); + bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(200); + bindConstant().annotatedWith(PacemakerTimeout.class).to(1000L); + bindConstant().annotatedWith(PacemakerRate.class).to(2.0); + bindConstant().annotatedWith(PacemakerMaxExponent.class).to(6); + + ECKeyPair ecKeyPair = ECKeyPair.generateNew(); + bind(HashSigner.class).toInstance(ecKeyPair::sign); + } + + @Provides + ViewUpdate viewUpdate(@Self BFTNode node) { + return ViewUpdate.create(View.of(1), mock(HighQC.class), node, node); + } + + @Provides + @Self + private BFTNode bftNode() { + return BFTNode.create(ecKeyPair.getPublicKey()); + } + }; + } + + private Pair createNextVertex( + QuorumCertificate parent, BFTNode bftNode) { + return createNextVertex(parent, bftNode, Txn.create(new byte[] {0})); + } + + private Pair createNextVertex( + QuorumCertificate parent, BFTNode bftNode, Txn txn) { + var unverifiedVertex = UnverifiedVertex.create(parent, View.of(1), List.of(txn), bftNode); + var hash = hasher.hash(unverifiedVertex); + var verifiedVertex = new VerifiedVertex(unverifiedVertex, hash); + var next = + new BFTHeader( + View.of(1), + verifiedVertex.getId(), + LedgerHeader.create(1, View.of(1), new AccumulatorState(1, HashUtils.zero256()), 1)); + var voteData = new VoteData(next, parent.getProposed(), parent.getParent()); + var unsyncedQC = + new QuorumCertificate( + voteData, + new TimestampedECDSASignatures( + Map.of(bftNode, TimestampedECDSASignature.from(1, zeroSignature())))); + + return Pair.of(unsyncedQC, verifiedVertex); + } + + @Test + public void on_sync_request_timeout_should_retry() { + // Arrange + BFTNode bftNode = BFTNode.random(); + QuorumCertificate parent = vertexStore.highQC().highestQC(); + Pair nextVertex = createNextVertex(parent, bftNode); + HighQC unsyncedHighQC = + HighQC.from(nextVertex.getFirst(), nextVertex.getFirst(), Optional.empty()); + bftSync.syncToQC(unsyncedHighQC, bftNode); + GetVerticesRequest request = new GetVerticesRequest(nextVertex.getSecond().getId(), 1); + VertexRequestTimeout timeout = VertexRequestTimeout.create(request); + + // Act + nothrowSleep(100); // FIXME: Remove when rate limit on send removed + bftSync.vertexRequestTimeoutEventProcessor().process(timeout); + + // Assert + verify(requestSender, times(2)) + .dispatch( + eq(bftNode), + argThat( + r -> r.getCount() == 1 && r.getVertexId().equals(nextVertex.getSecond().getId()))); + } + + @Test + public void on_synced_to_vertex_should_request_for_parent() { + // Arrange + BFTNode bftNode = BFTNode.random(); + QuorumCertificate parent = vertexStore.highQC().highestQC(); + Pair nextVertex = createNextVertex(parent, bftNode); + Pair nextNextVertex = + createNextVertex(nextVertex.getFirst(), bftNode); + HighQC unsyncedHighQC = + HighQC.from(nextNextVertex.getFirst(), nextNextVertex.getFirst(), Optional.empty()); + bftSync.syncToQC(unsyncedHighQC, bftNode); + + // Act + nothrowSleep(100); // FIXME: Remove when rate limit on send removed + GetVerticesResponse response = + new GetVerticesResponse(ImmutableList.of(nextNextVertex.getSecond())); + bftSync.responseProcessor().process(bftNode, response); + + // Assert + verify(requestSender, times(1)) + .dispatch( + eq(bftNode), + argThat( + r -> r.getCount() == 1 && r.getVertexId().equals(nextVertex.getSecond().getId()))); + } + + @Test + public void bft_sync_should_sync_two_different_QCs_with_the_same_parent() { + final var bftNode1 = BFTNode.random(); + final var bftNode2 = BFTNode.random(); + final var parentQc = vertexStore.highQC().highestQC(); + final var parentVertex = createNextVertex(parentQc, bftNode1); + final var proposedVertex1 = + createNextVertex(parentVertex.getFirst(), bftNode1, Txn.create(new byte[] {1})); + final var proposedVertex2 = + createNextVertex(parentVertex.getFirst(), bftNode2, Txn.create(new byte[] {2})); + final var unsyncedHighQC1 = + HighQC.from(proposedVertex1.getFirst(), proposedVertex1.getFirst(), Optional.empty()); + final var unsyncedHighQC2 = + HighQC.from(proposedVertex2.getFirst(), proposedVertex2.getFirst(), Optional.empty()); + + bftSync.syncToQC(unsyncedHighQC1, bftNode1); + bftSync.syncToQC(unsyncedHighQC2, bftNode2); + + nothrowSleep(100); + final var response1 = new GetVerticesResponse(ImmutableList.of(proposedVertex1.getSecond())); + bftSync.responseProcessor().process(bftNode1, response1); + + final var response2 = new GetVerticesResponse(ImmutableList.of(proposedVertex2.getSecond())); + bftSync.responseProcessor().process(bftNode2, response2); + + verify(requestSender, times(1)) + .dispatch( + eq(bftNode1), + argThat( + r -> + r.getCount() == 1 + && r.getVertexId().equals(proposedVertex1.getSecond().getId()))); + + verify(requestSender, times(1)) + .dispatch( + eq(bftNode2), + argThat( + r -> + r.getCount() == 1 + && r.getVertexId().equals(proposedVertex2.getSecond().getId()))); + } + + private void nothrowSleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + // Ignore + Thread.currentThread().interrupt(); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/FunctionalNodeModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/FunctionalNodeModule.java index c54eacc830..77d4623728 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/FunctionalNodeModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/FunctionalNodeModule.java @@ -80,105 +80,102 @@ import com.radixdlt.statecomputer.checkpoint.RadixEngineCheckpointModule; import com.radixdlt.sync.MockedSyncServiceModule; -/** - * Manages the functional components of a node - */ +/** Manages the functional components of a node */ public final class FunctionalNodeModule extends AbstractModule { - private final boolean hasConsensus; - private final boolean hasSync; - - // State manager - private final boolean hasLedger; - private final boolean hasMempool; - private final boolean hasRadixEngine; - - private final boolean hasMempoolRelayer; - - private final boolean hasEpochs; - - // FIXME: This is required for now for shared syncing, remove after refactor - private final Module mockedSyncServiceModule = new MockedSyncServiceModule(); - - public FunctionalNodeModule() { - this(true, true, true, true, true, true, true); - } - - public FunctionalNodeModule( - boolean hasConsensus, - boolean hasLedger, - boolean hasMempool, - boolean hasMempoolRelayer, - boolean hasRadixEngine, - boolean hasEpochs, - boolean hasSync - ) { - this.hasConsensus = hasConsensus; - this.hasLedger = hasLedger; - this.hasMempool = hasMempool; - this.hasMempoolRelayer = hasMempoolRelayer; - this.hasRadixEngine = hasRadixEngine; - this.hasEpochs = hasEpochs; - this.hasSync = hasSync; - } - - @Override - public void configure() { - install(new EventLoggerModule()); - install(new DispatcherModule()); - - // Consensus - if (hasConsensus) { - install(new ConsensusModule()); - if (hasEpochs) { - install(new EpochsConsensusModule()); - } else { - install(new NoEpochsConsensusModule()); - } - } - - // Sync - if (hasLedger) { - if (!hasSync) { - install(mockedSyncServiceModule); - } else { - install(new SyncServiceModule()); - if (hasEpochs) { - install(new EpochsSyncModule()); - } else { - install(new NoEpochsSyncModule()); - } - } - } - - // State Manager - if (!hasLedger) { - install(new MockedLedgerModule()); - } else { - install(new LedgerModule()); - - if (!hasMempool) { - install(new MockedCommandGeneratorModule()); - - if (!hasEpochs) { - install(new MockedStateComputerModule()); - } else { - install(new MockedStateComputerWithEpochsModule()); - } - } else { - install(new MempoolReceiverModule()); - - if (hasMempoolRelayer) { - install(new MempoolRelayerModule()); - } - - if (!hasRadixEngine) { - install(new MockedMempoolStateComputerModule()); - } else { - install(new RadixEngineStateComputerModule()); - install(new RadixEngineModule()); - install(new RadixEngineCheckpointModule()); - } - } - } - } + private final boolean hasConsensus; + private final boolean hasSync; + + // State manager + private final boolean hasLedger; + private final boolean hasMempool; + private final boolean hasRadixEngine; + + private final boolean hasMempoolRelayer; + + private final boolean hasEpochs; + + // FIXME: This is required for now for shared syncing, remove after refactor + private final Module mockedSyncServiceModule = new MockedSyncServiceModule(); + + public FunctionalNodeModule() { + this(true, true, true, true, true, true, true); + } + + public FunctionalNodeModule( + boolean hasConsensus, + boolean hasLedger, + boolean hasMempool, + boolean hasMempoolRelayer, + boolean hasRadixEngine, + boolean hasEpochs, + boolean hasSync) { + this.hasConsensus = hasConsensus; + this.hasLedger = hasLedger; + this.hasMempool = hasMempool; + this.hasMempoolRelayer = hasMempoolRelayer; + this.hasRadixEngine = hasRadixEngine; + this.hasEpochs = hasEpochs; + this.hasSync = hasSync; + } + + @Override + public void configure() { + install(new EventLoggerModule()); + install(new DispatcherModule()); + + // Consensus + if (hasConsensus) { + install(new ConsensusModule()); + if (hasEpochs) { + install(new EpochsConsensusModule()); + } else { + install(new NoEpochsConsensusModule()); + } + } + + // Sync + if (hasLedger) { + if (!hasSync) { + install(mockedSyncServiceModule); + } else { + install(new SyncServiceModule()); + if (hasEpochs) { + install(new EpochsSyncModule()); + } else { + install(new NoEpochsSyncModule()); + } + } + } + + // State Manager + if (!hasLedger) { + install(new MockedLedgerModule()); + } else { + install(new LedgerModule()); + + if (!hasMempool) { + install(new MockedCommandGeneratorModule()); + + if (!hasEpochs) { + install(new MockedStateComputerModule()); + } else { + install(new MockedStateComputerWithEpochsModule()); + } + } else { + install(new MempoolReceiverModule()); + + if (hasMempoolRelayer) { + install(new MempoolRelayerModule()); + } + + if (!hasRadixEngine) { + install(new MockedMempoolStateComputerModule()); + } else { + install(new RadixEngineStateComputerModule()); + install(new RadixEngineModule()); + install(new RadixEngineCheckpointModule()); + } + } + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/GenesisTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/GenesisTest.java index c4b5065996..4f7ab4890b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/GenesisTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/GenesisTest.java @@ -83,61 +83,60 @@ import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RERules; import com.radixdlt.utils.Bytes; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.util.Arrays; -import java.util.Collection; -import java.util.stream.Collectors; - @RunWith(Parameterized.class) public class GenesisTest { - @Parameterized.Parameters - public static Collection parameters() { - return Arrays.stream(Network.values()) - .flatMap(n -> n.genesisTxn().stream()) - .map(Bytes::fromHexString) - .map(Txn::create) - .map(txn -> new Object[] {txn}) - .collect(Collectors.toList()); - } + @Parameterized.Parameters + public static Collection parameters() { + return Arrays.stream(Network.values()) + .flatMap(n -> n.genesisTxn().stream()) + .map(Bytes::fromHexString) + .map(Txn::create) + .map(txn -> new Object[] {txn}) + .collect(Collectors.toList()); + } + + private static final Logger logger = LogManager.getLogger(); + private final Txn genesis; - private static final Logger logger = LogManager.getLogger(); - private final Txn genesis; + @Inject private GenesisBuilder genesisBuilder; - @Inject - private GenesisBuilder genesisBuilder; + public GenesisTest(Txn genesis) { + this.genesis = genesis; + } - public GenesisTest(Txn genesis) { - this.genesis = genesis; - } + @Test + public void genesis_should_be_a_valid_transaction() throws RadixEngineException { + Guice.createInjector( + new MainnetForkConfigsModule(), + new ForksModule(), + new CryptoModule(), + new AbstractModule() { + @Override + public void configure() { + bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); + bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); + } - @Test - public void genesis_should_be_a_valid_transaction() throws RadixEngineException { - Guice.createInjector( - new MainnetForkConfigsModule(), - new ForksModule(), - new CryptoModule(), - new AbstractModule() { - @Override - public void configure() { - bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); - } - @Provides - RERules rules(Forks forks) { - return forks.get(0); - } - } - ).injectMembers(this); - var proof = genesisBuilder.generateGenesisProof(genesis); - var validatorSet = proof.getNextValidatorSet().orElseThrow(); - logger.info("validator_set{size={} stake={}}", - validatorSet.getValidators().size(), - Amount.ofSubunits(validatorSet.getTotalPower()) - ); - } + @Provides + RERules rules(Forks forks) { + return forks.get(0); + } + }) + .injectMembers(this); + var proof = genesisBuilder.generateGenesisProof(genesis); + var validatorSet = proof.getNextValidatorSet().orElseThrow(); + logger.info( + "validator_set{size={} stake={}}", + validatorSet.getValidators().size(), + Amount.ofSubunits(validatorSet.getTotalPower())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/JSONFormatterTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/JSONFormatterTest.java index 4f27623887..9ccc6a3923 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/JSONFormatterTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/JSONFormatterTest.java @@ -64,42 +64,41 @@ package com.radixdlt; +import static org.junit.Assert.assertEquals; + import com.radixdlt.utils.JSONFormatter; import org.junit.Test; -import static org.junit.Assert.assertEquals; - public class JSONFormatterTest { - @Test - public void test_sort_and_pretty_print_json() { - String jsonString = "{\n" - + " \"hid\": \":uid:85561567e9dc96578362806ce9e136f2\",\n" - + " \"destinations\": [\n" - + " \":uid:dfd7c486570a7ad40eb948c80cb89376\"\n" - + " ],\n" - + " \"rri\": \":rri:/JFLKeSQmBZ73YkzWiesdEr2fRT14qCB1DQUvj8KxYQC6m8UTCcF/XRD\",\n" - + " \"serializer\": \"radix.particles.rri\",\n" - + " \"version\": 100,\n" - + " \"nonce\": 0\n" - + "}"; - - - String jsonStringManuallySorted = "{\n" - + " \"destinations\": [\n" - + " \":uid:dfd7c486570a7ad40eb948c80cb89376\"\n" - + " ],\n" - + " \"hid\": \":uid:85561567e9dc96578362806ce9e136f2\",\n" - + " \"nonce\": 0,\n" - + " \"rri\": \":rri:/JFLKeSQmBZ73YkzWiesdEr2fRT14qCB1DQUvj8KxYQC6m8UTCcF/XRD\",\n" - + " \"serializer\": \"radix.particles.rri\",\n" - + " \"version\": 100\n" - + "}"; - - String jsonStringSorted = JSONFormatter.sortPrettyPrintJSONString(jsonString); + @Test + public void test_sort_and_pretty_print_json() { + String jsonString = + "{\n" + + " \"hid\": \":uid:85561567e9dc96578362806ce9e136f2\",\n" + + " \"destinations\": [\n" + + " \":uid:dfd7c486570a7ad40eb948c80cb89376\"\n" + + " ],\n" + + " \"rri\": \":rri:/JFLKeSQmBZ73YkzWiesdEr2fRT14qCB1DQUvj8KxYQC6m8UTCcF/XRD\",\n" + + " \"serializer\": \"radix.particles.rri\",\n" + + " \"version\": 100,\n" + + " \"nonce\": 0\n" + + "}"; - assertEquals(jsonStringManuallySorted, jsonStringSorted); + String jsonStringManuallySorted = + "{\n" + + " \"destinations\": [\n" + + " \":uid:dfd7c486570a7ad40eb948c80cb89376\"\n" + + " ],\n" + + " \"hid\": \":uid:85561567e9dc96578362806ce9e136f2\",\n" + + " \"nonce\": 0,\n" + + " \"rri\": \":rri:/JFLKeSQmBZ73YkzWiesdEr2fRT14qCB1DQUvj8KxYQC6m8UTCcF/XRD\",\n" + + " \"serializer\": \"radix.particles.rri\",\n" + + " \"version\": 100\n" + + "}"; - } + String jsonStringSorted = JSONFormatter.sortPrettyPrintJSONString(jsonString); + assertEquals(jsonStringManuallySorted, jsonStringSorted); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedCryptoModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedCryptoModule.java index b13f6a3fbb..8f0e0d0541 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedCryptoModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedCryptoModule.java @@ -64,9 +64,6 @@ package com.radixdlt; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; @@ -78,73 +75,73 @@ import com.radixdlt.crypto.Hasher; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.Serialization; - import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * For testing where verification and signing is skipped - */ +/** For testing where verification and signing is skipped */ public class MockedCryptoModule extends AbstractModule { - private static final Logger log = LogManager.getLogger(); - private static final HashFunction hashFunction = Hashing.goodFastHash(8 * 32); + private static final Logger log = LogManager.getLogger(); + private static final HashFunction hashFunction = Hashing.goodFastHash(8 * 32); - @Override - public void configure() { - bind(Serialization.class).toInstance(DefaultSerialization.getInstance()); - bind(HashFunction.class).toInstance(hashFunction); - } + @Override + public void configure() { + bind(Serialization.class).toInstance(DefaultSerialization.getInstance()); + bind(HashFunction.class).toInstance(hashFunction); + } - @Provides - private HashVerifier hashVerifier(SystemCounters counters) { - return (pubKey, hash, sig) -> { - byte[] concat = new byte[64]; - System.arraycopy(hash.asBytes(), 0, concat, 0, hash.asBytes().length); - System.arraycopy(pubKey.getBytes(), 0, concat, 32, 32); - long hashCode = hashFunction.hashBytes(concat).asLong(); - counters.increment(SystemCounters.CounterType.SIGNATURES_VERIFIED); - return sig.getR().longValue() == hashCode; - }; - } + @Provides + private HashVerifier hashVerifier(SystemCounters counters) { + return (pubKey, hash, sig) -> { + byte[] concat = new byte[64]; + System.arraycopy(hash.asBytes(), 0, concat, 0, hash.asBytes().length); + System.arraycopy(pubKey.getBytes(), 0, concat, 32, 32); + long hashCode = hashFunction.hashBytes(concat).asLong(); + counters.increment(SystemCounters.CounterType.SIGNATURES_VERIFIED); + return sig.getR().longValue() == hashCode; + }; + } - @Provides - private Hasher hasher(Serialization serialization, SystemCounters counters) { - AtomicBoolean running = new AtomicBoolean(false); - Hasher hasher = new Hasher() { - @Override - public int bytes() { - return 32; - } + @Provides + private Hasher hasher(Serialization serialization, SystemCounters counters) { + AtomicBoolean running = new AtomicBoolean(false); + Hasher hasher = + new Hasher() { + @Override + public int bytes() { + return 32; + } - @Override - public HashCode hash(Object o) { - byte[] dson = timeWhinge("Serialization", () -> serialization.toDson(o, Output.HASH)); - return this.hashBytes(dson); - } + @Override + public HashCode hash(Object o) { + byte[] dson = timeWhinge("Serialization", () -> serialization.toDson(o, Output.HASH)); + return this.hashBytes(dson); + } - @Override - public HashCode hashBytes(byte[] bytes) { - byte[] hashCode = timeWhinge("Hashing", () -> hashFunction.hashBytes(bytes).asBytes()); - return HashCode.fromBytes(hashCode); - } + @Override + public HashCode hashBytes(byte[] bytes) { + byte[] hashCode = timeWhinge("Hashing", () -> hashFunction.hashBytes(bytes).asBytes()); + return HashCode.fromBytes(hashCode); + } - private T timeWhinge(String what, Supplier exec) { - long start = System.nanoTime(); - T result = exec.get(); - long end = System.nanoTime(); - long durationMs = (end - start) / 1_000_000L; - if (durationMs > 50) { - log.warn("{} took {}ms", what, durationMs); - } - return result; - } - }; + private T timeWhinge(String what, Supplier exec) { + long start = System.nanoTime(); + T result = exec.get(); + long end = System.nanoTime(); + long durationMs = (end - start) / 1_000_000L; + if (durationMs > 50) { + log.warn("{} took {}ms", what, durationMs); + } + return result; + } + }; - // Make sure classes etc loaded, as first use seems to take some time - Object dummyObject = ECDSASignature.zeroSignature(); // Arbitrary serializable class - hasher.hash(dummyObject); - running.set(true); + // Make sure classes etc loaded, as first use seems to take some time + Object dummyObject = ECDSASignature.zeroSignature(); // Arbitrary serializable class + hasher.hash(dummyObject); + running.set(true); - return hasher; - } + return hasher; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedKeyModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedKeyModule.java index 2faeb16649..24b27ff1ab 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedKeyModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedKeyModule.java @@ -72,32 +72,28 @@ import com.radixdlt.consensus.bft.Self; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECDSASignature; - import java.math.BigInteger; import java.util.function.Function; public final class MockedKeyModule extends AbstractModule { - @Provides - @Self - String name(Function nodeToString, @Self BFTNode self) { - return nodeToString.apply(self); - } + @Provides + @Self + String name(Function nodeToString, @Self BFTNode self) { + return nodeToString.apply(self); + } - @Provides - private HashSigner hashSigner( - @Self BFTNode node, - SystemCounters counters, - HashFunction hashFunction - ) { - return h -> { - var concat = new byte[64]; - System.arraycopy(h, 0, concat, 0, 32); - System.arraycopy(node.getKey().getBytes(), 0, concat, 32, 32); + @Provides + private HashSigner hashSigner( + @Self BFTNode node, SystemCounters counters, HashFunction hashFunction) { + return h -> { + var concat = new byte[64]; + System.arraycopy(h, 0, concat, 0, 32); + System.arraycopy(node.getKey().getBytes(), 0, concat, 32, 32); - var hashCode = hashFunction.hashBytes(concat).asLong(); - counters.increment(SystemCounters.CounterType.SIGNATURES_SIGNED); + var hashCode = hashFunction.hashBytes(concat).asLong(); + counters.increment(SystemCounters.CounterType.SIGNATURES_SIGNED); - return ECDSASignature.create(BigInteger.valueOf(hashCode), BigInteger.valueOf(hashCode), 0); - }; - } + return ECDSASignature.create(BigInteger.valueOf(hashCode), BigInteger.valueOf(hashCode), 0); + }; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedPersistenceStoreModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedPersistenceStoreModule.java index 5b09f2fd02..e542100631 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedPersistenceStoreModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/MockedPersistenceStoreModule.java @@ -71,39 +71,38 @@ import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.store.berkeley.SerializedVertexStoreState; - import java.util.Optional; public class MockedPersistenceStoreModule extends AbstractModule { - @Override - public void configure() { - bind(PersistentSafetyStateStore.class).to(MockedPersistenceStore.class); - bind(PersistentVertexStore.class).to(MockedPersistentVertexStore.class); - OptionalBinder.newOptionalBinder(binder(), SerializedVertexStoreState.class); - } + @Override + public void configure() { + bind(PersistentSafetyStateStore.class).to(MockedPersistenceStore.class); + bind(PersistentVertexStore.class).to(MockedPersistentVertexStore.class); + OptionalBinder.newOptionalBinder(binder(), SerializedVertexStoreState.class); + } - private static class MockedPersistenceStore implements PersistentSafetyStateStore { - @Override - public Optional get() { - return Optional.empty(); - } + private static class MockedPersistenceStore implements PersistentSafetyStateStore { + @Override + public Optional get() { + return Optional.empty(); + } - @Override - public void commitState(SafetyState safetyState) { - // Nothing to do here - } + @Override + public void commitState(SafetyState safetyState) { + // Nothing to do here + } - @Override - public void close() { - // Nothing to do here - } - } + @Override + public void close() { + // Nothing to do here + } + } - private static class MockedPersistentVertexStore implements PersistentVertexStore { - @Override - public void save(VerifiedVertexStoreState vertexStoreState) { - // Nothing to do here - } - } + private static class MockedPersistentVertexStore implements PersistentVertexStore { + @Override + public void save(VerifiedVertexStoreState vertexStoreState) { + // Nothing to do here + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/PersistedNodeForTestingModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/PersistedNodeForTestingModule.java index a30e7e3b12..4f6d48bb30 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/PersistedNodeForTestingModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/PersistedNodeForTestingModule.java @@ -67,53 +67,53 @@ import com.google.common.util.concurrent.RateLimiter; import com.google.inject.AbstractModule; import com.google.inject.Scopes; -import com.radixdlt.keys.InMemoryBFTKeyModule; -import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; -import com.radixdlt.network.p2p.NoOpPeerControl; -import com.radixdlt.network.p2p.PeerControl; import com.radixdlt.consensus.bft.PacemakerMaxExponent; import com.radixdlt.consensus.bft.PacemakerRate; import com.radixdlt.consensus.bft.PacemakerTimeout; import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; +import com.radixdlt.keys.InMemoryBFTKeyModule; +import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; +import com.radixdlt.network.p2p.NoOpPeerControl; +import com.radixdlt.network.p2p.PeerControl; import com.radixdlt.networks.Addressing; import com.radixdlt.networks.Network; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.store.DatabaseCacheSize; import com.radixdlt.store.PersistenceModule; import com.radixdlt.sync.SyncConfig; +import com.radixdlt.utils.TimeSupplier; -/** - * Helper class for modules to be used for recovery tests. - */ +/** Helper class for modules to be used for recovery tests. */ public final class PersistedNodeForTestingModule extends AbstractModule { - @Override - public void configure() { - bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); - bind(SyncConfig.class).toInstance(SyncConfig.of(500, 10, 3000, 10, Long.MAX_VALUE)); - bind(Integer.class).annotatedWith(BFTSyncPatienceMillis.class).toInstance(200); - bind(Long.class).annotatedWith(PacemakerTimeout.class).toInstance(1000L); - bind(Double.class).annotatedWith(PacemakerRate.class).toInstance(2.0); - bind(Integer.class).annotatedWith(PacemakerMaxExponent.class).toInstance(6); - bind(RateLimiter.class).annotatedWith(GetVerticesRequestRateLimit.class) - .toInstance(RateLimiter.create(Double.MAX_VALUE)); - bindConstant().annotatedWith(DatabaseCacheSize.class) - .to((long) (Runtime.getRuntime().maxMemory() * 0.125)); + @Override + public void configure() { + bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); + bind(SyncConfig.class).toInstance(SyncConfig.of(500, 10, 3000, 10, Long.MAX_VALUE)); + bind(Integer.class).annotatedWith(BFTSyncPatienceMillis.class).toInstance(200); + bind(Long.class).annotatedWith(PacemakerTimeout.class).toInstance(1000L); + bind(Double.class).annotatedWith(PacemakerRate.class).toInstance(2.0); + bind(Integer.class).annotatedWith(PacemakerMaxExponent.class).toInstance(6); + bind(RateLimiter.class) + .annotatedWith(GetVerticesRequestRateLimit.class) + .toInstance(RateLimiter.create(Double.MAX_VALUE)); + bindConstant() + .annotatedWith(DatabaseCacheSize.class) + .to((long) (Runtime.getRuntime().maxMemory() * 0.125)); - // System - bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - bind(TimeSupplier.class).toInstance(System::currentTimeMillis); + // System + bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); + bind(TimeSupplier.class).toInstance(System::currentTimeMillis); - // P2P - bind(PeerControl.class).toInstance(new NoOpPeerControl()); + // P2P + bind(PeerControl.class).toInstance(new NoOpPeerControl()); - install(new InMemoryBFTKeyModule()); - install(new CryptoModule()); - install(new FunctionalNodeModule()); - install(new RadixEngineStoreModule()); - install(new PersistenceModule()); - install(new ConsensusRecoveryModule()); - install(new LedgerRecoveryModule()); - } + install(new InMemoryBFTKeyModule()); + install(new CryptoModule()); + install(new FunctionalNodeModule()); + install(new RadixEngineStoreModule()); + install(new PersistenceModule()); + install(new ConsensusRecoveryModule()); + install(new LedgerRecoveryModule()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixNodeModuleTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixNodeModuleTest.java index 8c8e611ef6..e70c224504 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixNodeModuleTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixNodeModuleTest.java @@ -64,59 +64,55 @@ package com.radixdlt; -import com.radixdlt.networks.NetworkId; -import org.assertj.core.util.Files; -import org.junit.BeforeClass; -import org.junit.Test; -import org.radix.serialization.TestSetupUtils; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.inject.Guice; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.RadixKeyStore; +import com.radixdlt.networks.NetworkId; import com.radixdlt.properties.RuntimeProperties; - import java.io.File; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.assertj.core.util.Files; +import org.junit.BeforeClass; +import org.junit.Test; +import org.radix.serialization.TestSetupUtils; public class RadixNodeModuleTest { - @NetworkId - private int networkId; + @NetworkId private int networkId; - @BeforeClass - public static void beforeClass() { - TestSetupUtils.installBouncyCastleProvider(); - } + @BeforeClass + public static void beforeClass() { + TestSetupUtils.installBouncyCastleProvider(); + } - @Test - public void testInjectorNotNullToken() { - final var properties = createDefaultProperties(); - when(properties.get(eq("network.id"))).thenReturn("99"); - when(properties.get(eq("network.genesis_txn"))).thenReturn("00"); - Guice.createInjector(new RadixNodeModule(properties)).injectMembers(this); - } + @Test + public void testInjectorNotNullToken() { + final var properties = createDefaultProperties(); + when(properties.get(eq("network.id"))).thenReturn("99"); + when(properties.get(eq("network.genesis_txn"))).thenReturn("00"); + Guice.createInjector(new RadixNodeModule(properties)).injectMembers(this); + } - private RuntimeProperties createDefaultProperties() { - final var properties = mock(RuntimeProperties.class); - doReturn("127.0.0.1").when(properties).get(eq("host.ip"), any()); - var keyStore = new File("nonesuch.ks"); - Files.delete(keyStore); - generateKeystore(keyStore); + private RuntimeProperties createDefaultProperties() { + final var properties = mock(RuntimeProperties.class); + doReturn("127.0.0.1").when(properties).get(eq("host.ip"), any()); + var keyStore = new File("nonesuch.ks"); + Files.delete(keyStore); + generateKeystore(keyStore); - when(properties.get(eq("node.key.path"), any(String.class))).thenReturn("nonesuch.ks"); - return properties; - } + when(properties.get(eq("node.key.path"), any(String.class))).thenReturn("nonesuch.ks"); + return properties; + } - private void generateKeystore(File keyStore) { - try { - RadixKeyStore.fromFile(keyStore, null, true) - .writeKeyPair("node", ECKeyPair.generateNew()); - } catch (Exception e) { - throw new IllegalStateException("Unable to create keystore"); - } - } + private void generateKeystore(File keyStore) { + try { + RadixKeyStore.fromFile(keyStore, null, true).writeKeyPair("node", ECKeyPair.generateNew()); + } catch (Exception e) { + throw new IllegalStateException("Unable to create keystore"); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/SingleNodeAndPeersDeterministicNetworkModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/SingleNodeAndPeersDeterministicNetworkModule.java index 30defb134c..a6d820f31c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/SingleNodeAndPeersDeterministicNetworkModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/SingleNodeAndPeersDeterministicNetworkModule.java @@ -76,60 +76,55 @@ import com.radixdlt.environment.deterministic.network.MessageMutator; import com.radixdlt.environment.deterministic.network.MessageSelector; import com.radixdlt.network.p2p.PeersView; - import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -/** - * Module which injects a full one node network - */ +/** Module which injects a full one node network */ public final class SingleNodeAndPeersDeterministicNetworkModule extends AbstractModule { - private final ECKeyPair self; - private final int numPeers; + private final ECKeyPair self; + private final int numPeers; - public SingleNodeAndPeersDeterministicNetworkModule(ECKeyPair self, int numPeers) { - this.self = self; - this.numPeers = numPeers; - } + public SingleNodeAndPeersDeterministicNetworkModule(ECKeyPair self, int numPeers) { + this.self = self; + this.numPeers = numPeers; + } - @Override - protected void configure() { - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(self); - install(new PersistedNodeForTestingModule()); - } + @Override + protected void configure() { + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(self); + install(new PersistedNodeForTestingModule()); + } - @Provides - @Singleton - public PeersView peers() { - final var peers = Stream.generate(BFTNode::random) + @Provides + @Singleton + public PeersView peers() { + final var peers = + Stream.generate(BFTNode::random) .limit(numPeers) .map(PeersView.PeerInfo::fromBftNode) .collect(ImmutableList.toImmutableList()); - return peers::stream; - } + return peers::stream; + } - @Provides - public List nodes(@Self BFTNode self) { - return List.of(self); - } + @Provides + public List nodes(@Self BFTNode self) { + return List.of(self); + } - @Provides - @Singleton - public DeterministicNetwork network(@Self BFTNode self, PeersView peersView) { - return new DeterministicNetwork( - Stream.concat( - Stream.of(self), - peersView.peers().map(PeersView.PeerInfo::bftNode) - ).collect(Collectors.toList()), - MessageSelector.firstSelector(), - MessageMutator.nothing() - ); - } + @Provides + @Singleton + public DeterministicNetwork network(@Self BFTNode self, PeersView peersView) { + return new DeterministicNetwork( + Stream.concat(Stream.of(self), peersView.peers().map(PeersView.PeerInfo::bftNode)) + .collect(Collectors.toList()), + MessageSelector.firstSelector(), + MessageMutator.nothing()); + } - @Provides - @Singleton - Environment environment(@Self BFTNode self, DeterministicNetwork network) { - return network.createSender(self); - } + @Provides + @Singleton + Environment environment(@Self BFTNode self, DeterministicNetwork network) { + return network.createSender(self); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/ApiTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/ApiTest.java index 228737868f..19d424d618 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/ApiTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/ApiTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,9 @@ package com.radixdlt.api; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; import com.google.inject.Guice; @@ -70,9 +74,9 @@ import com.google.inject.Scopes; import com.google.inject.multibindings.Multibinder; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.api.core.openapitools.JSON; import com.radixdlt.api.core.openapitools.model.NetworkIdentifier; import com.radixdlt.api.core.reconstruction.BerkeleyRecoverableProcessedTxnStore; -import com.radixdlt.api.core.openapitools.JSON; import com.radixdlt.application.system.FeeTable; import com.radixdlt.application.tokens.Amount; import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; @@ -104,187 +108,199 @@ import io.undertow.server.HttpServerExchange; import io.undertow.server.handlers.ExceptionHandler; import io.undertow.util.HeaderMap; -import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.TemporaryFolder; - import java.io.ByteArrayInputStream; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; - -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; public abstract class ApiTest { - private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); + private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - @Inject - private SingleNodeDeterministicRunner runner; - private final Amount totalTokenAmount = Amount.ofTokens(110); - private final Amount stakeAmount = Amount.ofTokens(10); - private final Amount liquidAmount = Amount.ofSubunits( - totalTokenAmount.toSubunits().subtract(stakeAmount.toSubunits()) - ); - private final int mempoolMaxSize; + @Rule public TemporaryFolder folder = new TemporaryFolder(); + @Inject private SingleNodeDeterministicRunner runner; + private final Amount totalTokenAmount = Amount.ofTokens(110); + private final Amount stakeAmount = Amount.ofTokens(10); + private final Amount liquidAmount = + Amount.ofSubunits(totalTokenAmount.toSubunits().subtract(stakeAmount.toSubunits())); + private final int mempoolMaxSize; - protected ApiTest(int mempoolMaxSize) { - this.mempoolMaxSize = mempoolMaxSize; - } + protected ApiTest(int mempoolMaxSize) { + this.mempoolMaxSize = mempoolMaxSize; + } - protected ApiTest() { - this.mempoolMaxSize = 10; - } + protected ApiTest() { + this.mempoolMaxSize = 10; + } - @Before - public void setup() { - var injector = Guice.createInjector( - MempoolConfig.asModule(mempoolMaxSize, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - RERulesConfig.testingDefault().overrideFeeTable(FeeTable.create( - Amount.ofSubunits(UInt256.ONE), - Map.of(ValidatorRegisteredCopy.class, Amount.ofSubunits(UInt256.ONE)) - )) - ), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 1), - new MockedGenesisModule( - Set.of(TEST_KEY.getPublicKey()), - totalTokenAmount, - stakeAmount - ), - new AbstractModule() { - @Override - protected void configure() { - bind(BerkeleyRecoverableProcessedTxnStore.class).in(Scopes.SINGLETON); - Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class) - .addBinding().to(BerkeleyRecoverableProcessedTxnStore.class); - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - bindConstant().annotatedWith(NetworkId.class).to(99); - bind(P2PConfig.class).toInstance(mock(P2PConfig.class)); - bind(AddressBook.class).in(Scopes.SINGLETON); - var selfUri = RadixNodeUri.fromPubKeyAndAddress(99, TEST_KEY.getPublicKey(), "localhost", 23456); - bind(RadixNodeUri.class).annotatedWith(Self.class).toInstance(selfUri); - var addressBookPersistence = mock(AddressBookPersistence.class); - when(addressBookPersistence.getAllEntries()).thenReturn(ImmutableList.of()); - bind(AddressBookPersistence.class).toInstance(addressBookPersistence); - var runtimeProperties = mock(RuntimeProperties.class); - when(runtimeProperties.get(eq("api.transactions.enable"), anyBoolean())).thenReturn(true); - bind(RuntimeProperties.class).toInstance(runtimeProperties); - } - } - ); - injector.injectMembers(this); - } + @Before + public void setup() { + var injector = + Guice.createInjector( + MempoolConfig.asModule(mempoolMaxSize, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + RERulesConfig.testingDefault() + .overrideFeeTable( + FeeTable.create( + Amount.ofSubunits(UInt256.ONE), + Map.of( + ValidatorRegisteredCopy.class, Amount.ofSubunits(UInt256.ONE))))), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 1), + new MockedGenesisModule(Set.of(TEST_KEY.getPublicKey()), totalTokenAmount, stakeAmount), + new AbstractModule() { + @Override + protected void configure() { + bind(BerkeleyRecoverableProcessedTxnStore.class).in(Scopes.SINGLETON); + Multibinder.newSetBinder(binder(), BerkeleyAdditionalStore.class) + .addBinding() + .to(BerkeleyRecoverableProcessedTxnStore.class); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + bindConstant().annotatedWith(NetworkId.class).to(99); + bind(P2PConfig.class).toInstance(mock(P2PConfig.class)); + bind(AddressBook.class).in(Scopes.SINGLETON); + var selfUri = + RadixNodeUri.fromPubKeyAndAddress( + 99, TEST_KEY.getPublicKey(), "localhost", 23456); + bind(RadixNodeUri.class).annotatedWith(Self.class).toInstance(selfUri); + var addressBookPersistence = mock(AddressBookPersistence.class); + when(addressBookPersistence.getAllEntries()).thenReturn(ImmutableList.of()); + bind(AddressBookPersistence.class).toInstance(addressBookPersistence); + var runtimeProperties = mock(RuntimeProperties.class); + when(runtimeProperties.get(eq("api.transactions.enable"), anyBoolean())) + .thenReturn(true); + bind(RuntimeProperties.class).toInstance(runtimeProperties); + } + }); + injector.injectMembers(this); + } - protected Amount getStakeAmount() { - return stakeAmount; - } + protected Amount getStakeAmount() { + return stakeAmount; + } - protected Amount getLiquidAmount() { - return liquidAmount; - } + protected Amount getLiquidAmount() { + return liquidAmount; + } - protected NetworkIdentifier networkIdentifier() { - return new NetworkIdentifier().network("localnet"); - } + protected NetworkIdentifier networkIdentifier() { + return new NetworkIdentifier().network("localnet"); + } - protected ECPublicKey selfKey() { - return TEST_KEY.getPublicKey(); - } + protected ECPublicKey selfKey() { + return TEST_KEY.getPublicKey(); + } - protected final void start() { - runner.start(); - } + protected final void start() { + runner.start(); + } - protected final void runUntilCommitted(AID txnId) { - runner.runNextEventsThrough( - LedgerUpdate.class, - u -> { - var output = u.getStateComputerOutput().getInstance(REOutput.class); - return output.getProcessedTxns().stream().anyMatch(txn -> txn.getTxn().getId().equals(txnId)); - } - ); - } + protected final void runUntilCommitted(AID txnId) { + runner.runNextEventsThrough( + LedgerUpdate.class, + u -> { + var output = u.getStateComputerOutput().getInstance(REOutput.class); + return output.getProcessedTxns().stream() + .anyMatch(txn -> txn.getTxn().getId().equals(txnId)); + }); + } - private HttpServerExchange exchange(Exception e, Sender sender) { - var httpServerExchange = mock(HttpServerExchange.class); - when(httpServerExchange.getAttachment(ExceptionHandler.THROWABLE)).thenReturn(e); - when(httpServerExchange.isInIoThread()).thenReturn(false); - when(httpServerExchange.getResponseHeaders()).thenReturn(new HeaderMap()); - when(httpServerExchange.getResponseSender()).thenReturn(sender); - return httpServerExchange; - } + private HttpServerExchange exchange(Exception e, Sender sender) { + var httpServerExchange = mock(HttpServerExchange.class); + when(httpServerExchange.getAttachment(ExceptionHandler.THROWABLE)).thenReturn(e); + when(httpServerExchange.isInIoThread()).thenReturn(false); + when(httpServerExchange.getResponseHeaders()).thenReturn(new HeaderMap()); + when(httpServerExchange.getResponseSender()).thenReturn(sender); + return httpServerExchange; + } - private HttpServerExchange exchange(byte[] request, Sender sender) { - var httpServerExchange = mock(HttpServerExchange.class); - when(httpServerExchange.getInputStream()).thenReturn(new ByteArrayInputStream(request)); - when(httpServerExchange.isInIoThread()).thenReturn(false); - when(httpServerExchange.getResponseHeaders()).thenReturn(new HeaderMap()); - when(httpServerExchange.getResponseSender()).thenReturn(sender); - return httpServerExchange; - } + private HttpServerExchange exchange(byte[] request, Sender sender) { + var httpServerExchange = mock(HttpServerExchange.class); + when(httpServerExchange.getInputStream()).thenReturn(new ByteArrayInputStream(request)); + when(httpServerExchange.isInIoThread()).thenReturn(false); + when(httpServerExchange.getResponseHeaders()).thenReturn(new HeaderMap()); + when(httpServerExchange.getResponseSender()).thenReturn(sender); + return httpServerExchange; + } - private HttpServerExchange exchange(Sender sender) { - return exchange(new byte[0], sender); - } + private HttpServerExchange exchange(Sender sender) { + return exchange(new byte[0], sender); + } - protected String handleRequest(HttpHandler handler) throws Exception { - var sender = mock(Sender.class); - var response = new AtomicReference(); - doAnswer(invocation -> { - response.set(invocation.getArgument(0)); - return null; - }).when(sender).send(anyString()); - handler.handleRequest(exchange(sender)); - return response.get(); - } + protected String handleRequest(HttpHandler handler) throws Exception { + var sender = mock(Sender.class); + var response = new AtomicReference(); + doAnswer( + invocation -> { + response.set(invocation.getArgument(0)); + return null; + }) + .when(sender) + .send(anyString()); + handler.handleRequest(exchange(sender)); + return response.get(); + } - protected T handleExceptionWithExpectedResponse(HttpHandler handler, Exception e, Class responseClass) throws Exception { - var objectMapper = JSON.getDefault().getMapper(); - var sender = mock(Sender.class); - var response = new AtomicReference(); - doAnswer(invocation -> { - response.set(invocation.getArgument(0)); - return null; - }).when(sender).send(anyString()); - handler.handleRequest(exchange(e, sender)); - return objectMapper.readValue(response.get(), responseClass); - } + protected T handleExceptionWithExpectedResponse( + HttpHandler handler, Exception e, Class responseClass) throws Exception { + var objectMapper = JSON.getDefault().getMapper(); + var sender = mock(Sender.class); + var response = new AtomicReference(); + doAnswer( + invocation -> { + response.set(invocation.getArgument(0)); + return null; + }) + .when(sender) + .send(anyString()); + handler.handleRequest(exchange(e, sender)); + return objectMapper.readValue(response.get(), responseClass); + } - protected T handleRequestWithExpectedResponse(HttpHandler handler, byte[] requestBytes, Class responseClass) throws Exception { - var objectMapper = JSON.getDefault().getMapper(); - var sender = mock(Sender.class); - var response = new AtomicReference(); - doAnswer(invocation -> { - response.set(invocation.getArgument(0)); - return null; - }).when(sender).send(anyString()); - handler.handleRequest(exchange(requestBytes, sender)); - var deserializedResponse = objectMapper.readValue(response.get(), responseClass); - if (deserializedResponse == null) { - throw new IllegalStateException("Unexpected response: " + response.get()); - } - return deserializedResponse; - } + protected T handleRequestWithExpectedResponse( + HttpHandler handler, byte[] requestBytes, Class responseClass) throws Exception { + var objectMapper = JSON.getDefault().getMapper(); + var sender = mock(Sender.class); + var response = new AtomicReference(); + doAnswer( + invocation -> { + response.set(invocation.getArgument(0)); + return null; + }) + .when(sender) + .send(anyString()); + handler.handleRequest(exchange(requestBytes, sender)); + var deserializedResponse = objectMapper.readValue(response.get(), responseClass); + if (deserializedResponse == null) { + throw new IllegalStateException("Unexpected response: " + response.get()); + } + return deserializedResponse; + } - protected T handleRequestWithExpectedResponse(HttpHandler handler, Object request, Class responseClass) throws Exception { - var objectMapper = JSON.getDefault().getMapper(); - var requestBytes = objectMapper.writeValueAsBytes(request); - return handleRequestWithExpectedResponse(handler, requestBytes, responseClass); - } + protected T handleRequestWithExpectedResponse( + HttpHandler handler, Object request, Class responseClass) throws Exception { + var objectMapper = JSON.getDefault().getMapper(); + var requestBytes = objectMapper.writeValueAsBytes(request); + return handleRequestWithExpectedResponse(handler, requestBytes, responseClass); + } - protected T handleRequestWithExpectedResponse(HttpHandler handler, Class responseClass) throws Exception { - var sender = mock(Sender.class); - var response = new AtomicReference(); - doAnswer(invocation -> { - response.set(invocation.getArgument(0)); - return null; - }).when(sender).send(anyString()); - handler.handleRequest(exchange(sender)); - return JSON.getDefault().getMapper().readValue(response.get(), responseClass); - } + protected T handleRequestWithExpectedResponse(HttpHandler handler, Class responseClass) + throws Exception { + var sender = mock(Sender.class); + var response = new AtomicReference(); + doAnswer( + invocation -> { + response.set(invocation.getArgument(0)); + return null; + }) + .when(sender) + .send(anyString()); + handler.handleRequest(exchange(sender)); + return JSON.getDefault().getMapper().readValue(response.get(), responseClass); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/UnhandledExceptionHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/UnhandledExceptionHandlerTest.java index 400eb30098..9bbddef01b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/UnhandledExceptionHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/UnhandledExceptionHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,25 +64,25 @@ package com.radixdlt.api; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.core.openapitools.model.InternalServerError; import com.radixdlt.api.core.openapitools.model.UnexpectedError; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class UnhandledExceptionHandlerTest extends ApiTest { - @Inject - private UnhandledExceptionHandler sut; + @Inject private UnhandledExceptionHandler sut; - @Test - public void unhandled_exception_should_return_error() throws Exception { - // Arrange - start(); + @Test + public void unhandled_exception_should_return_error() throws Exception { + // Arrange + start(); - // Act - var response = handleExceptionWithExpectedResponse(sut, new NullPointerException(), UnexpectedError.class); + // Act + var response = + handleExceptionWithExpectedResponse(sut, new NullPointerException(), UnexpectedError.class); - assertThat(response.getDetails()).isInstanceOf(InternalServerError.class); - } + assertThat(response.getDetails()).isInstanceOf(InternalServerError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeePayerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeePayerTest.java index ac64ed3523..283606f4ce 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeePayerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeePayerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -81,47 +84,41 @@ import com.radixdlt.utils.PrivateKeys; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class ConstructionBuildFeePayerTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; - private ConstructionBuildRequest buildRequestWithFeePayer(EntityIdentifier feePayer) { - var transferAmount = getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var otherAddress = REAddr.ofPubKeyAccount(otherKey); - return new ConstructionBuildRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(feePayer) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(coreModelMapper.entityIdentifier(accountAddress)) - .amount(coreModelMapper.nativeTokenAmount(false, transferAmount)) - ) - .addOperationsItem(new Operation() - .entityIdentifier(coreModelMapper.entityIdentifier(otherAddress)) - .amount(coreModelMapper.nativeTokenAmount(true, transferAmount)) - ) - ); - } + private ConstructionBuildRequest buildRequestWithFeePayer(EntityIdentifier feePayer) { + var transferAmount = getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var otherAddress = REAddr.ofPubKeyAccount(otherKey); + return new ConstructionBuildRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(feePayer) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(coreModelMapper.entityIdentifier(accountAddress)) + .amount(coreModelMapper.nativeTokenAmount(false, transferAmount))) + .addOperationsItem( + new Operation() + .entityIdentifier(coreModelMapper.entityIdentifier(otherAddress)) + .amount(coreModelMapper.nativeTokenAmount(true, transferAmount)))); + } - @Test - public void bad_fee_payer_should_throw_exception() throws Exception { - // Arrange - start(); + @Test + public void bad_fee_payer_should_throw_exception() throws Exception { + // Arrange + start(); - // Act - var request = buildRequestWithFeePayer(coreModelMapper.entityIdentifier(self)); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var request = buildRequestWithFeePayer(coreModelMapper.entityIdentifier(self)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidFeePayerEntityError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidFeePayerEntityError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeeTest.java index c9a3c4a0f4..06d04b1e79 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildFeeTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -82,93 +85,96 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.math.BigInteger; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class ConstructionBuildFeeTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; - private ConstructionBuildRequest buildTransfer( - ResourceIdentifier resourceIdentifier, - UInt256 amount, - EntityIdentifier from, - EntityIdentifier to - ) { - return new ConstructionBuildRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(from) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(from) - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value("-" + amount.toString()) - ) - ) - .addOperationsItem(new Operation() - .entityIdentifier(to) - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(amount.toString()) - ) - ) - ); - } + private ConstructionBuildRequest buildTransfer( + ResourceIdentifier resourceIdentifier, + UInt256 amount, + EntityIdentifier from, + EntityIdentifier to) { + return new ConstructionBuildRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(from) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(from) + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value("-" + amount.toString()))) + .addOperationsItem( + new Operation() + .entityIdentifier(to) + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value(amount.toString())))); + } - @Test - public void no_balance_should_cause_a_not_enough_for_fees_error() throws Exception { - // Arrange - start(); + @Test + public void no_balance_should_cause_a_not_enough_for_fees_error() throws Exception { + // Arrange + start(); - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var request = buildTransfer( - coreModelMapper.nativeToken(), - UInt256.ONE, - coreModelMapper.entityIdentifier(otherAddress), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + UInt256.ONE, + coreModelMapper.entityIdentifier(otherAddress), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self))); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOfSatisfying(NotEnoughNativeTokensForFeesError.class, e -> { - assertThat(e.getAvailable()).isEqualTo(coreModelMapper.nativeTokenAmount(UInt256.ZERO)); - }); - } + // Assert + assertThat(response.getDetails()) + .isInstanceOfSatisfying( + NotEnoughNativeTokensForFeesError.class, + e -> { + assertThat(e.getAvailable()) + .isEqualTo(coreModelMapper.nativeTokenAmount(UInt256.ZERO)); + }); + } - @Test - public void trying_to_send_whole_balance_should_fail() throws Exception { - // Arrange - start(); + @Test + public void trying_to_send_whole_balance_should_fail() throws Exception { + // Arrange + start(); - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var request = buildTransfer( - coreModelMapper.nativeToken(), - getLiquidAmount().toSubunits(), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifier(otherAddress) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + getLiquidAmount().toSubunits(), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifier(otherAddress)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOfSatisfying(NotEnoughResourcesError.class, err -> { - assertThat(err.getFee().getResourceIdentifier()).isEqualTo(coreModelMapper.nativeToken()); - assertThat(new BigInteger(err.getFee().getValue())).isGreaterThan(BigInteger.ZERO); - assertThat(err.getAvailable().getResourceIdentifier()).isEqualTo(coreModelMapper.nativeToken()); - assertThat(new BigInteger(err.getAvailable().getValue())).isGreaterThan(BigInteger.ZERO); - assertThat(err.getAttemptedToTake().getResourceIdentifier()).isEqualTo(coreModelMapper.nativeToken()); - assertThat(new BigInteger(err.getAttemptedToTake().getValue())) - .isEqualTo(new BigInteger(1, getLiquidAmount().toSubunits().toByteArray())); - }); - } + // Assert + assertThat(response.getDetails()) + .isInstanceOfSatisfying( + NotEnoughResourcesError.class, + err -> { + assertThat(err.getFee().getResourceIdentifier()) + .isEqualTo(coreModelMapper.nativeToken()); + assertThat(new BigInteger(err.getFee().getValue())).isGreaterThan(BigInteger.ZERO); + assertThat(err.getAvailable().getResourceIdentifier()) + .isEqualTo(coreModelMapper.nativeToken()); + assertThat(new BigInteger(err.getAvailable().getValue())) + .isGreaterThan(BigInteger.ZERO); + assertThat(err.getAttemptedToTake().getResourceIdentifier()) + .isEqualTo(coreModelMapper.nativeToken()); + assertThat(new BigInteger(err.getAttemptedToTake().getValue())) + .isEqualTo(new BigInteger(1, getLiquidAmount().toSubunits().toByteArray())); + }); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMessageTest.java index 8a4cd1eeae..82e5c66cfc 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMessageTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -82,72 +85,63 @@ import com.radixdlt.utils.UInt256; import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - public class ConstructionBuildMessageTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; - - private ConstructionBuildRequest buildRequestWithMessage(String message) { - var transferAmount = UInt256.ONE; - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var otherAddress = REAddr.ofPubKeyAccount(otherKey); - return new ConstructionBuildRequest() - .message(message) - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(coreModelMapper.entityIdentifier(accountAddress)) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(coreModelMapper.entityIdentifier(accountAddress)) - .amount(coreModelMapper.nativeTokenAmount(false, transferAmount)) - ) - .addOperationsItem(new Operation() - .entityIdentifier(coreModelMapper.entityIdentifier(otherAddress)) - .amount(coreModelMapper.nativeTokenAmount(true, transferAmount)) - ) - ); - } - - @Test - public void building_with_message_should_be_in_transaction() throws Exception { - // Arrange - start(); - + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; - // Act - var hex = "deadbeefdeadbeef"; - var messageBytes = Bytes.fromHexString(hex); - var request = buildRequestWithMessage(hex); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + private ConstructionBuildRequest buildRequestWithMessage(String message) { + var transferAmount = UInt256.ONE; + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var otherAddress = REAddr.ofPubKeyAccount(otherKey); + return new ConstructionBuildRequest() + .message(message) + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(coreModelMapper.entityIdentifier(accountAddress)) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(coreModelMapper.entityIdentifier(accountAddress)) + .amount(coreModelMapper.nativeTokenAmount(false, transferAmount))) + .addOperationsItem( + new Operation() + .entityIdentifier(coreModelMapper.entityIdentifier(otherAddress)) + .amount(coreModelMapper.nativeTokenAmount(true, transferAmount)))); + } - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - var unsignedTransactionBytes = Bytes.fromHexString(response.getUnsignedTransaction()); - assertThat(unsignedTransactionBytes).isNotNull(); - var indexOfMessageBytes = com.google.common.primitives.Bytes.indexOf(unsignedTransactionBytes, messageBytes); - assertThat(indexOfMessageBytes).isNotNegative(); - } + @Test + public void building_with_message_should_be_in_transaction() throws Exception { + // Arrange + start(); + // Act + var hex = "deadbeefdeadbeef"; + var messageBytes = Bytes.fromHexString(hex); + var request = buildRequestWithMessage(hex); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - @Test - public void building_with_message_too_large_should_fail() throws Exception { - // Arrange - start(); + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + var unsignedTransactionBytes = Bytes.fromHexString(response.getUnsignedTransaction()); + assertThat(unsignedTransactionBytes).isNotNull(); + var indexOfMessageBytes = + com.google.common.primitives.Bytes.indexOf(unsignedTransactionBytes, messageBytes); + assertThat(indexOfMessageBytes).isNotNegative(); + } + @Test + public void building_with_message_too_large_should_fail() throws Exception { + // Arrange + start(); - // Act - var hex = "aa".repeat(256); - var request = buildRequestWithMessage(hex); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var hex = "aa".repeat(256); + var request = buildRequestWithMessage(hex); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(MessageTooLongError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(MessageTooLongError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMintBurnTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMintBurnTest.java index 59d4cfa3f1..9102af868a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMintBurnTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildMintBurnTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -83,74 +86,64 @@ import com.radixdlt.utils.UInt256; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public final class ConstructionBuildMintBurnTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; - private ConstructionBuildRequest buildMintOrBurn( - ResourceIdentifier resourceIdentifier, - UInt256 amount, - boolean isMint, - EntityIdentifier to - ) { - var accountAddress = REAddr.ofPubKeyAccount(self); - return new ConstructionBuildRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(coreModelMapper.entityIdentifier(accountAddress)) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(to) - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(isMint ? amount.toString() : "-" + amount.toString()) - ) - ) - ); - } + private ConstructionBuildRequest buildMintOrBurn( + ResourceIdentifier resourceIdentifier, UInt256 amount, boolean isMint, EntityIdentifier to) { + var accountAddress = REAddr.ofPubKeyAccount(self); + return new ConstructionBuildRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(coreModelMapper.entityIdentifier(accountAddress)) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(to) + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value(isMint ? amount.toString() : "-" + amount.toString())))); + } - @Test - public void building_token_mints_should_work() throws Exception { - // Arrange - start(); + @Test + public void building_token_mints_should_work() throws Exception { + // Arrange + start(); - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var request = buildMintOrBurn( - coreModelMapper.nativeToken(), - UInt256.ONE, - true, - coreModelMapper.entityIdentifier(otherAddress) - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var request = + buildMintOrBurn( + coreModelMapper.nativeToken(), + UInt256.ONE, + true, + coreModelMapper.entityIdentifier(otherAddress)); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } - @Test - public void building_token_burns_should_work() throws Exception { - // Arrange - start(); + @Test + public void building_token_burns_should_work() throws Exception { + // Arrange + start(); - // Act - var request = buildMintOrBurn( - coreModelMapper.nativeToken(), - UInt256.ONE, - false, - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)) - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + // Act + var request = + buildMintOrBurn( + coreModelMapper.nativeToken(), + UInt256.ONE, + false, + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self))); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTokenDefinitionTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTokenDefinitionTest.java index 677da7fb4f..b73250deb7 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTokenDefinitionTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTokenDefinitionTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -85,176 +88,164 @@ import com.radixdlt.utils.Bytes; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public final class ConstructionBuildTokenDefinitionTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; - - private ConstructionBuildRequest buildTokenDefinition( - EntityIdentifier tokenEntityIdentifier, - String symbol, - REAddr owner, - int granularity, - boolean isMutable - ) { - var accountAddress = REAddr.ofPubKeyAccount(self); - return new ConstructionBuildRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(coreModelMapper.entityIdentifier(accountAddress)) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(tokenEntityIdentifier) - .data(new Data() - .action(Data.ActionEnum.CREATE) - .dataObject(new TokenData() - .owner(owner == null ? null : coreModelMapper.entityIdentifier(owner)) - .granularity(Integer.toString(granularity)) - .isMutable(isMutable) - .type("TokenData") - ) - ) - ) - .addOperationsItem(new Operation() - .entityIdentifier(tokenEntityIdentifier) - .data(new Data() - .action(Data.ActionEnum.CREATE) - .dataObject(new TokenMetadata() - .symbol(symbol) - .name("") - .url("") - .iconUrl("") - .description("") - .type("TokenMetadata") - ) - ) - ) - ); - } - - @Test - public void creating_a_new_token_definition_should_work() throws Exception { - // Arrange - start(); - var accountAddress = REAddr.ofPubKeyAccount(self); - - // Act - var request = buildTokenDefinition( - coreModelMapper.entityIdentifier(accountAddress, "test"), - "test", - accountAddress, - 1, - true - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void using_different_symbols_should_fail() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var request = buildTokenDefinition( - coreModelMapper.entityIdentifier(accountAddress, "test2"), - "test", - accountAddress, - 1, - true - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); - } - - @Test - public void using_different_granularity_than_1_should_fail() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var request = buildTokenDefinition( - coreModelMapper.entityIdentifier(accountAddress, "test"), - "test", - accountAddress, - 2, - true - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); - } - - @Test - public void creating_fixed_supply_token_with_owner_should_fail() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var request = buildTokenDefinition( - coreModelMapper.entityIdentifier(accountAddress, "test"), - "test", - accountAddress, - 1, - false - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); - } - - @Test - public void creating_mutable_supply_token_with_no_owner_should_fail() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var request = buildTokenDefinition( - coreModelMapper.entityIdentifier(accountAddress, "test"), - "test", - null, - 1, - true - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); - } - - @Test - public void creating_token_in_account_entity_should_fail() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var request = buildTokenDefinition( - coreModelMapper.entityIdentifier(accountAddress), - "test", - null, - 1, - true - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(DataObjectNotSupportedByEntityError.class); - } + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; + + private ConstructionBuildRequest buildTokenDefinition( + EntityIdentifier tokenEntityIdentifier, + String symbol, + REAddr owner, + int granularity, + boolean isMutable) { + var accountAddress = REAddr.ofPubKeyAccount(self); + return new ConstructionBuildRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(coreModelMapper.entityIdentifier(accountAddress)) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(tokenEntityIdentifier) + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new TokenData() + .owner( + owner == null + ? null + : coreModelMapper.entityIdentifier(owner)) + .granularity(Integer.toString(granularity)) + .isMutable(isMutable) + .type("TokenData")))) + .addOperationsItem( + new Operation() + .entityIdentifier(tokenEntityIdentifier) + .data( + new Data() + .action(Data.ActionEnum.CREATE) + .dataObject( + new TokenMetadata() + .symbol(symbol) + .name("") + .url("") + .iconUrl("") + .description("") + .type("TokenMetadata"))))); + } + + @Test + public void creating_a_new_token_definition_should_work() throws Exception { + // Arrange + start(); + var accountAddress = REAddr.ofPubKeyAccount(self); + + // Act + var request = + buildTokenDefinition( + coreModelMapper.entityIdentifier(accountAddress, "test"), + "test", + accountAddress, + 1, + true); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void using_different_symbols_should_fail() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTokenDefinition( + coreModelMapper.entityIdentifier(accountAddress, "test2"), + "test", + accountAddress, + 1, + true); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); + } + + @Test + public void using_different_granularity_than_1_should_fail() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTokenDefinition( + coreModelMapper.entityIdentifier(accountAddress, "test"), + "test", + accountAddress, + 2, + true); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); + } + + @Test + public void creating_fixed_supply_token_with_owner_should_fail() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTokenDefinition( + coreModelMapper.entityIdentifier(accountAddress, "test"), + "test", + accountAddress, + 1, + false); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); + } + + @Test + public void creating_mutable_supply_token_with_no_owner_should_fail() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTokenDefinition( + coreModelMapper.entityIdentifier(accountAddress, "test"), "test", null, 1, true); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidDataObjectError.class); + } + + @Test + public void creating_token_in_account_entity_should_fail() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTokenDefinition( + coreModelMapper.entityIdentifier(accountAddress), "test", null, 1, true); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(DataObjectNotSupportedByEntityError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTransferStakeUnstakeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTransferStakeUnstakeTest.java index b41959be5b..61d8eacbf6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTransferStakeUnstakeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildTransferStakeUnstakeTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -96,227 +99,218 @@ import com.radixdlt.utils.UInt256; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public final class ConstructionBuildTransferStakeUnstakeTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - private Forks forks; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - - private ConstructionBuildRequest buildTransfer( - ResourceIdentifier resourceIdentifier, - UInt256 amount, - EntityIdentifier from, - EntityIdentifier to - ) { - var accountAddress = REAddr.ofPubKeyAccount(self); - return new ConstructionBuildRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(coreModelMapper.entityIdentifier(accountAddress)) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(from) - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value("-" + amount.toString()) - ) - ) - .addOperationsItem(new Operation() - .entityIdentifier(to) - .amount(new ResourceAmount() - .resourceIdentifier(resourceIdentifier) - .value(amount.toString()) - ) - ) - ); - } - - @Test - public void transferring_tokens_should_work() throws Exception { - // Arrange - start(); - - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var request = buildTransfer( - coreModelMapper.nativeToken(), - UInt256.ONE, - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifier(otherAddress) - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void setting_disable_mint_and_burn_should_set_correct_header() throws Exception { - // Arrange - start(); - - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var request = buildTransfer( - coreModelMapper.nativeToken(), - UInt256.ONE, - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifier(otherAddress) - ); - request.setDisableResourceAllocateAndDestroy(true); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - var bytes = Bytes.fromHexString(response.getUnsignedTransaction()); - assertThat(bytes).isNotNull(); - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - var parsed = radixEngine.getParser().parse(Txn.create(bytes)); - assertThat(parsed.disableResourceAllocAndDestroy()).isTrue(); - } - - - @Test - public void staking_tokens_directly_to_validator_should_fail() throws Exception { - // Arrange - start(); - - // Act - var request = buildTransfer( - coreModelMapper.nativeToken(), - getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifier(self) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(ResourceDepositOperationNotSupportedByEntityError.class); - } - - @Test - public void transferring_too_many_tokens_should_fail() throws Exception { - // Arrange - start(); - - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var tooLargeAmount = getLiquidAmount().toSubunits().add(UInt256.ONE); - var request = buildTransfer( - coreModelMapper.nativeToken(), - tooLargeAmount, - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifier(otherAddress) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(NotEnoughResourcesError.class); - } - - @Test - public void staking_tokens_to_self_should_succeed() throws Exception { - // Arrange - start(); - - // Act - var selfAddress = REAddr.ofPubKeyAccount(self); - var request = buildTransfer( - coreModelMapper.nativeToken(), - getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifierPreparedStake(selfAddress, self) - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void staking_too_little_tokens_should_fail() throws Exception { - // Arrange - start(); - - // Act - var selfAddress = REAddr.ofPubKeyAccount(self); - var request = buildTransfer( - coreModelMapper.nativeToken(), - forks.get(1).getConfig().getMinimumStake().toSubunits().subtract(UInt128.ONE), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifierPreparedStake(selfAddress, self) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(BelowMinimumStakeError.class); - } - - @Test - public void staking_tokens_to_non_owned_validator_should_fail() throws Exception { - // Arrange - start(); - - // Act - var selfAddress = REAddr.ofPubKeyAccount(self); - var otherKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var request = buildTransfer( - coreModelMapper.nativeToken(), - getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifierPreparedStake(selfAddress, otherKey) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(NotValidatorOwnerError.class); - } - - @Test - public void withdrawing_staked_tokens_should_fail() throws Exception { - // Arrange - start(); - - // Act - var request = buildTransfer( - coreModelMapper.nativeToken(), - getStakeAmount().toSubunits(), - coreModelMapper.entityIdentifier(self).subEntity(new SubEntity().address("system")), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(ResourceWithdrawOperationNotSupportedByEntityError.class); - } - - - @Test - public void unstaking_tokens_should_work() throws Exception { - // Arrange - start(); - - // Act - var selfAddress = REAddr.ofPubKeyAccount(self); - var request = buildTransfer( - coreModelMapper.stakeUnit(self), - getStakeAmount().toSubunits(), - coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), - coreModelMapper.entityIdentifierPreparedUnstake(selfAddress) - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject private Forks forks; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + + private ConstructionBuildRequest buildTransfer( + ResourceIdentifier resourceIdentifier, + UInt256 amount, + EntityIdentifier from, + EntityIdentifier to) { + var accountAddress = REAddr.ofPubKeyAccount(self); + return new ConstructionBuildRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(coreModelMapper.entityIdentifier(accountAddress)) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(from) + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value("-" + amount.toString()))) + .addOperationsItem( + new Operation() + .entityIdentifier(to) + .amount( + new ResourceAmount() + .resourceIdentifier(resourceIdentifier) + .value(amount.toString())))); + } + + @Test + public void transferring_tokens_should_work() throws Exception { + // Arrange + start(); + + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + UInt256.ONE, + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifier(otherAddress)); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void setting_disable_mint_and_burn_should_set_correct_header() throws Exception { + // Arrange + start(); + + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + UInt256.ONE, + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifier(otherAddress)); + request.setDisableResourceAllocateAndDestroy(true); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + var bytes = Bytes.fromHexString(response.getUnsignedTransaction()); + assertThat(bytes).isNotNull(); + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + var parsed = radixEngine.getParser().parse(Txn.create(bytes)); + assertThat(parsed.disableResourceAllocAndDestroy()).isTrue(); + } + + @Test + public void staking_tokens_directly_to_validator_should_fail() throws Exception { + // Arrange + start(); + + // Act + var request = + buildTransfer( + coreModelMapper.nativeToken(), + getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifier(self)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()) + .isInstanceOf(ResourceDepositOperationNotSupportedByEntityError.class); + } + + @Test + public void transferring_too_many_tokens_should_fail() throws Exception { + // Arrange + start(); + + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var tooLargeAmount = getLiquidAmount().toSubunits().add(UInt256.ONE); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + tooLargeAmount, + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifier(otherAddress)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(NotEnoughResourcesError.class); + } + + @Test + public void staking_tokens_to_self_should_succeed() throws Exception { + // Arrange + start(); + + // Act + var selfAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifierPreparedStake(selfAddress, self)); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void staking_too_little_tokens_should_fail() throws Exception { + // Arrange + start(); + + // Act + var selfAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + forks.get(1).getConfig().getMinimumStake().toSubunits().subtract(UInt128.ONE), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifierPreparedStake(selfAddress, self)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(BelowMinimumStakeError.class); + } + + @Test + public void staking_tokens_to_non_owned_validator_should_fail() throws Exception { + // Arrange + start(); + + // Act + var selfAddress = REAddr.ofPubKeyAccount(self); + var otherKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var request = + buildTransfer( + coreModelMapper.nativeToken(), + getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifierPreparedStake(selfAddress, otherKey)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(NotValidatorOwnerError.class); + } + + @Test + public void withdrawing_staked_tokens_should_fail() throws Exception { + // Arrange + start(); + + // Act + var request = + buildTransfer( + coreModelMapper.nativeToken(), + getStakeAmount().toSubunits(), + coreModelMapper.entityIdentifier(self).subEntity(new SubEntity().address("system")), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self))); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()) + .isInstanceOf(ResourceWithdrawOperationNotSupportedByEntityError.class); + } + + @Test + public void unstaking_tokens_should_work() throws Exception { + // Arrange + start(); + + // Act + var selfAddress = REAddr.ofPubKeyAccount(self); + var request = + buildTransfer( + coreModelMapper.stakeUnit(self), + getStakeAmount().toSubunits(), + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(self)), + coreModelMapper.entityIdentifierPreparedUnstake(selfAddress)); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildValidatorTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildValidatorTest.java index 38e56e9d9c..29f9af2bf7 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildValidatorTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionBuildValidatorTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionBuildHandler; @@ -88,141 +91,121 @@ import com.radixdlt.utils.PrivateKeys; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class ConstructionBuildValidatorTest extends ApiTest { - @Inject - private ConstructionBuildHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; - - private ConstructionBuildRequest buildValidatorUpdate(DataObject dataObject) { - var accountAddress = REAddr.ofPubKeyAccount(self); - return new ConstructionBuildRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .feePayer(coreModelMapper.entityIdentifier(accountAddress)) - .addOperationGroupsItem(new OperationGroup() - .addOperationsItem(new Operation() - .entityIdentifier(coreModelMapper.entityIdentifier(self)) - .data(new Data() - .action(Data.ActionEnum.CREATE) - .dataObject(dataObject) - ) - ) - ); - } - - @Test - public void create_validator_register_transaction() throws Exception { - // Arrange - start(); - - // Act - var request = buildValidatorUpdate( - new PreparedValidatorRegistered() - .registered(true) - .type("PreparedValidatorRegistered") - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void create_validator_fee_update_transaction() throws Exception { - // Arrange - start(); - - - // Act - var request = buildValidatorUpdate( - new PreparedValidatorFee() - .fee(10) - .type("PreparedValidatorFee") - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void create_validator_too_large_fee_update_should_throw() throws Exception { - // Arrange - start(); - - // Act - var request = buildValidatorUpdate( - new PreparedValidatorFee() - .fee(1000) - .type("PreparedValidatorFee") - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOfSatisfying(AboveMaximumValidatorFeeIncreaseError.class, e -> { - assertThat(e.getAttemptedValidatorFeeIncrease()).isEqualTo(1000); - }); - } - - @Test - public void create_validator_owner_update_transaction() throws Exception { - // Arrange - start(); - - // Act - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var request = buildValidatorUpdate( - new PreparedValidatorOwner() - .owner(coreModelMapper.entityIdentifier(otherAddress)) - .type("PreparedValidatorOwner") - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void create_validator_allow_delegation_transaction() throws Exception { - // Arrange - start(); - - // Act - var request = buildValidatorUpdate( - new ValidatorAllowDelegation() - .allowDelegation(true) - .type("ValidatorAllowDelegation") - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } - - @Test - public void create_validator_metadata_update_transaction() throws Exception { - // Arrange - start(); - - // Act - var request = buildValidatorUpdate( - new ValidatorMetadata() - .name("hello") - .type("ValidatorMetadata") - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); - - // Assert - assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); - assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); - } + @Inject private ConstructionBuildHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; + + private ConstructionBuildRequest buildValidatorUpdate(DataObject dataObject) { + var accountAddress = REAddr.ofPubKeyAccount(self); + return new ConstructionBuildRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .feePayer(coreModelMapper.entityIdentifier(accountAddress)) + .addOperationGroupsItem( + new OperationGroup() + .addOperationsItem( + new Operation() + .entityIdentifier(coreModelMapper.entityIdentifier(self)) + .data(new Data().action(Data.ActionEnum.CREATE).dataObject(dataObject)))); + } + + @Test + public void create_validator_register_transaction() throws Exception { + // Arrange + start(); + + // Act + var request = + buildValidatorUpdate( + new PreparedValidatorRegistered().registered(true).type("PreparedValidatorRegistered")); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void create_validator_fee_update_transaction() throws Exception { + // Arrange + start(); + + // Act + var request = + buildValidatorUpdate(new PreparedValidatorFee().fee(10).type("PreparedValidatorFee")); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void create_validator_too_large_fee_update_should_throw() throws Exception { + // Arrange + start(); + + // Act + var request = + buildValidatorUpdate(new PreparedValidatorFee().fee(1000).type("PreparedValidatorFee")); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()) + .isInstanceOfSatisfying( + AboveMaximumValidatorFeeIncreaseError.class, + e -> { + assertThat(e.getAttemptedValidatorFeeIncrease()).isEqualTo(1000); + }); + } + + @Test + public void create_validator_owner_update_transaction() throws Exception { + // Arrange + start(); + + // Act + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var request = + buildValidatorUpdate( + new PreparedValidatorOwner() + .owner(coreModelMapper.entityIdentifier(otherAddress)) + .type("PreparedValidatorOwner")); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void create_validator_allow_delegation_transaction() throws Exception { + // Arrange + start(); + + // Act + var request = + buildValidatorUpdate( + new ValidatorAllowDelegation().allowDelegation(true).type("ValidatorAllowDelegation")); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } + + @Test + public void create_validator_metadata_update_transaction() throws Exception { + // Arrange + start(); + + // Act + var request = + buildValidatorUpdate(new ValidatorMetadata().name("hello").type("ValidatorMetadata")); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionBuildResponse.class); + + // Assert + assertThat(Bytes.fromHexString(response.getPayloadToSign())).isNotNull(); + assertThat(Bytes.fromHexString(response.getUnsignedTransaction())).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionDeriveHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionDeriveHandlerTest.java index 37b711e278..169d5a552b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionDeriveHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionDeriveHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,9 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionDeriveHandler; @@ -85,194 +89,208 @@ import com.radixdlt.utils.PrivateKeys; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - public class ConstructionDeriveHandlerTest extends ApiTest { - @Inject - private ConstructionDeriveHandler sut; - @Inject - private CoreModelMapper coreModelMapper; + @Inject private ConstructionDeriveHandler sut; + @Inject private CoreModelMapper coreModelMapper; - @Test - public void derive_account_request_should_return_account_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - start(); + @Test + public void derive_account_request_should_return_account_entity_identifier() + throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataAccount().type("Account")); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata(new ConstructionDeriveRequestMetadataAccount().type("Account")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(publicKey))); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo(coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(publicKey))); + } - @Test - public void derive_validator_request_should_return_validator_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - start(); + @Test + public void derive_validator_request_should_return_validator_entity_identifier() + throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataValidator().type("Validator")); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata(new ConstructionDeriveRequestMetadataValidator().type("Validator")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifier(publicKey)); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo(coreModelMapper.entityIdentifier(publicKey)); + } - @Test - public void derive_token_request_should_return_token_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - start(); + @Test + public void derive_token_request_should_return_token_entity_identifier() throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataToken().symbol("test").type("Token")); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata(new ConstructionDeriveRequestMetadataToken().symbol("test").type("Token")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifier( - REAddr.ofHashedKey(publicKey, "test"), "test" - )); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo(coreModelMapper.entityIdentifier(REAddr.ofHashedKey(publicKey, "test"), "test")); + } - @Test - public void derive_prepared_stakes_should_return_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var validatorKey = PrivateKeys.ofNumeric(3).getPublicKey(); - start(); + @Test + public void derive_prepared_stakes_should_return_entity_identifier() throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var validatorKey = PrivateKeys.ofNumeric(3).getPublicKey(); + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataPreparedStakes() - .validator(coreModelMapper.entityIdentifier(validatorKey)) - .type("PreparedStakes") - ); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata( + new ConstructionDeriveRequestMetadataPreparedStakes() + .validator(coreModelMapper.entityIdentifier(validatorKey)) + .type("PreparedStakes")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifierPreparedStake(REAddr.ofPubKeyAccount(publicKey), validatorKey)); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo( + coreModelMapper.entityIdentifierPreparedStake( + REAddr.ofPubKeyAccount(publicKey), validatorKey)); + } - @Test - public void derive_prepared_stakes_with_invalid_validator_should_return_error() { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var validatorKey = PrivateKeys.ofNumeric(3).getPublicKey(); - start(); + @Test + public void derive_prepared_stakes_with_invalid_validator_should_return_error() { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var validatorKey = PrivateKeys.ofNumeric(3).getPublicKey(); + start(); - // Act - // Assert - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataPreparedStakes() - .validator(coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(validatorKey))) - .type("PreparedStakes") - ); - assertThatThrownBy(() -> sut.handleRequest(request)) - .isInstanceOfSatisfying(CoreApiException.class, e -> { - assertThat(e.toError().getDetails()).isInstanceOf(NotValidatorEntityError.class); - }); - } + // Act + // Assert + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata( + new ConstructionDeriveRequestMetadataPreparedStakes() + .validator( + coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(validatorKey))) + .type("PreparedStakes")); + assertThatThrownBy(() -> sut.handleRequest(request)) + .isInstanceOfSatisfying( + CoreApiException.class, + e -> { + assertThat(e.toError().getDetails()).isInstanceOf(NotValidatorEntityError.class); + }); + } - @Test - public void derive_validator_system_should_return_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - start(); + @Test + public void derive_validator_system_should_return_entity_identifier() throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataValidatorSystem().type("ValidatorSystem")); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata( + new ConstructionDeriveRequestMetadataValidatorSystem().type("ValidatorSystem")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifierValidatorSystem(publicKey)); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo(coreModelMapper.entityIdentifierValidatorSystem(publicKey)); + } - @Test - public void derive_prepared_unstakes_should_return_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - start(); + @Test + public void derive_prepared_unstakes_should_return_entity_identifier() throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataPreparedUnstakes() - .type("PreparedUnstakes") - ); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata( + new ConstructionDeriveRequestMetadataPreparedUnstakes().type("PreparedUnstakes")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifierPreparedUnstake(REAddr.ofPubKeyAccount(publicKey))); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo( + coreModelMapper.entityIdentifierPreparedUnstake(REAddr.ofPubKeyAccount(publicKey))); + } - @Test - public void derive_exiting_unstakes_should_return_entity_identifier() throws CoreApiException { - // Arrange - var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var validatorKey = PrivateKeys.ofNumeric(3).getPublicKey(); - long epochUnlock = 236L; - start(); + @Test + public void derive_exiting_unstakes_should_return_entity_identifier() throws CoreApiException { + // Arrange + var publicKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var validatorKey = PrivateKeys.ofNumeric(3).getPublicKey(); + long epochUnlock = 236L; + start(); - // Act - var request = new ConstructionDeriveRequest() - .networkIdentifier(networkIdentifier()) - .publicKey(coreModelMapper.publicKey(publicKey)) - .metadata(new ConstructionDeriveRequestMetadataExitingUnstakes() - .validator(coreModelMapper.entityIdentifier(validatorKey)) - .epochUnlock(epochUnlock) - .type("ExitingUnstakes") - ); - var response = sut.handleRequest(request); + // Act + var request = + new ConstructionDeriveRequest() + .networkIdentifier(networkIdentifier()) + .publicKey(coreModelMapper.publicKey(publicKey)) + .metadata( + new ConstructionDeriveRequestMetadataExitingUnstakes() + .validator(coreModelMapper.entityIdentifier(validatorKey)) + .epochUnlock(epochUnlock) + .type("ExitingUnstakes")); + var response = sut.handleRequest(request); - // Assert - assertThat(response.getEntityIdentifier()) - .isEqualTo(coreModelMapper.entityIdentifierExitingStake(REAddr.ofPubKeyAccount(publicKey), validatorKey, epochUnlock)); - } + // Assert + assertThat(response.getEntityIdentifier()) + .isEqualTo( + coreModelMapper.entityIdentifierExitingStake( + REAddr.ofPubKeyAccount(publicKey), validatorKey, epochUnlock)); + } - @Test - public void invalid_public_key_should_throw_exception() { - // Arrange - start(); + @Test + public void invalid_public_key_should_throw_exception() { + // Arrange + start(); - // Act - // Assert - var request = new ConstructionDeriveRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .publicKey(new PublicKey().hex("deadbeaddeadbead")) - .metadata(new ConstructionDeriveRequestMetadataToken().symbol("test").type("Token")); - assertThatThrownBy(() -> sut.handleRequest(request)) - .isInstanceOfSatisfying(CoreApiException.class, e -> { - var error = e.toError(); - assertThat(error.getDetails()).isInstanceOf(InvalidPublicKeyError.class); - assertThat(error.getCode()).isEqualTo(CoreApiErrorCode.BAD_REQUEST.getErrorCode()); - }); - } + // Act + // Assert + var request = + new ConstructionDeriveRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .publicKey(new PublicKey().hex("deadbeaddeadbead")) + .metadata(new ConstructionDeriveRequestMetadataToken().symbol("test").type("Token")); + assertThatThrownBy(() -> sut.handleRequest(request)) + .isInstanceOfSatisfying( + CoreApiException.class, + e -> { + var error = e.toError(); + assertThat(error.getDetails()).isInstanceOf(InvalidPublicKeyError.class); + assertThat(error.getCode()).isEqualTo(CoreApiErrorCode.BAD_REQUEST.getErrorCode()); + }); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionFinalizeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionFinalizeTest.java index 0bfccbec13..5dbb8d0f6f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionFinalizeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionFinalizeTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionFinalizeHandler; @@ -92,113 +95,98 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; +import java.io.ByteArrayOutputStream; +import java.util.List; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1OutputStream; import org.bouncycastle.asn1.DLSequence; import org.junit.Test; -import java.io.ByteArrayOutputStream; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - public class ConstructionFinalizeTest extends ApiTest { - @Inject - private ConstructionFinalizeHandler sut; - @Inject - @LocalSigner - private HashSigner hashSigner; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - @Inject - private Forks forks; - - private UnsignedTxnData buildUnsignedTransferTxn(REAddr from, REAddr to) throws Exception { - final UInt256 toTransfer = getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(from), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ), - EntityOperation.from( - new AccountVaultEntity(to), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new - ); - return builder.buildForExternalSign(); - } + @Inject private ConstructionFinalizeHandler sut; + @Inject @LocalSigner private HashSigner hashSigner; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + @Inject private Forks forks; - @Test - public void finalizing_a_transaction_with_signature_should_return_back_signed_tranaction() throws Exception { - // Arrange - start(); + private UnsignedTxnData buildUnsignedTransferTxn(REAddr from, REAddr to) throws Exception { + final UInt256 toTransfer = + getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(from), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)), + EntityOperation.from( + new AccountVaultEntity(to), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)))); + var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new); + return builder.buildForExternalSign(); + } - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var unsignedTxn = buildUnsignedTransferTxn(accountAddress, otherAddress); - var sig = hashSigner.sign(unsignedTxn.hashToSign()); - var os = new ByteArrayOutputStream(); - var asn1OutputStream = ASN1OutputStream.create(os); - asn1OutputStream.writeObject(new DLSequence(new ASN1Encodable[] { - new ASN1Integer(sig.getR()), - new ASN1Integer(sig.getS()) - })); - var derSignature = os.toByteArray(); - var request = new ConstructionFinalizeRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .unsignedTransaction(Bytes.toHexString(unsignedTxn.blob())) - .signature(new Signature() - .bytes(Bytes.toHexString(derSignature)) - .publicKey(coreModelMapper.publicKey(self)) - ); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionFinalizeResponse.class); + @Test + public void finalizing_a_transaction_with_signature_should_return_back_signed_tranaction() + throws Exception { + // Arrange + start(); - // Assert - var bytes = Bytes.fromHexString(response.getSignedTransaction()); - assertThat(bytes).isNotNull(); - } + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var unsignedTxn = buildUnsignedTransferTxn(accountAddress, otherAddress); + var sig = hashSigner.sign(unsignedTxn.hashToSign()); + var os = new ByteArrayOutputStream(); + var asn1OutputStream = ASN1OutputStream.create(os); + asn1OutputStream.writeObject( + new DLSequence( + new ASN1Encodable[] {new ASN1Integer(sig.getR()), new ASN1Integer(sig.getS())})); + var derSignature = os.toByteArray(); + var request = + new ConstructionFinalizeRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .unsignedTransaction(Bytes.toHexString(unsignedTxn.blob())) + .signature( + new Signature() + .bytes(Bytes.toHexString(derSignature)) + .publicKey(coreModelMapper.publicKey(self))); + var response = + handleRequestWithExpectedResponse(sut, request, ConstructionFinalizeResponse.class); + // Assert + var bytes = Bytes.fromHexString(response.getSignedTransaction()); + assertThat(bytes).isNotNull(); + } - @Test - public void finalizing_a_transaction_with_invalid_signature_should_throw() throws Exception { - // Arrange - start(); + @Test + public void finalizing_a_transaction_with_invalid_signature_should_throw() throws Exception { + // Arrange + start(); - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var unsignedTxn = buildUnsignedTransferTxn(accountAddress, otherAddress); - var sig = hashSigner.sign(unsignedTxn.hashToSign()); - var invalidSignature = sig.toHexString(); // This is an invalid signature since it must be DER encoded - var request = new ConstructionFinalizeRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .unsignedTransaction(Bytes.toHexString(unsignedTxn.blob())) - .signature(new Signature() - .bytes(invalidSignature) - .publicKey(coreModelMapper.publicKey(self)) - ); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var unsignedTxn = buildUnsignedTransferTxn(accountAddress, otherAddress); + var sig = hashSigner.sign(unsignedTxn.hashToSign()); + var invalidSignature = + sig.toHexString(); // This is an invalid signature since it must be DER encoded + var request = + new ConstructionFinalizeRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .unsignedTransaction(Bytes.toHexString(unsignedTxn.blob())) + .signature( + new Signature().bytes(invalidSignature).publicKey(coreModelMapper.publicKey(self))); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidSignatureError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidSignatureError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionHashHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionHashHandlerTest.java index 58645c075f..caef16bbce 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionHashHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionHashHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionHashHandler; @@ -76,43 +79,41 @@ import com.radixdlt.utils.Bytes; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class ConstructionHashHandlerTest extends ApiTest { - @Inject - private ConstructionHashHandler sut; - @Inject - private CoreModelMapper coreModelMapper; + @Inject private ConstructionHashHandler sut; + @Inject private CoreModelMapper coreModelMapper; - @Test - public void handling_construction_hash_should_return_hash() throws Exception { - // Arrange - start(); + @Test + public void handling_construction_hash_should_return_hash() throws Exception { + // Arrange + start(); - // Act - var txn = Txn.create(Bytes.fromHexString("deadbeef")); - var request = new ConstructionHashRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .signedTransaction("deadbeef"); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionHashResponse.class); + // Act + var txn = Txn.create(Bytes.fromHexString("deadbeef")); + var request = + new ConstructionHashRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .signedTransaction("deadbeef"); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionHashResponse.class); - // Assert - assertThat(response.getTransactionIdentifier()) - .isEqualTo(coreModelMapper.transactionIdentifier(txn.getId())); - } + // Assert + assertThat(response.getTransactionIdentifier()) + .isEqualTo(coreModelMapper.transactionIdentifier(txn.getId())); + } - @Test - public void invalid_hex_should_throw() throws Exception { - // Arrange - start(); + @Test + public void invalid_hex_should_throw() throws Exception { + // Arrange + start(); - // Act - var request = new ConstructionHashRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .signedTransaction("this_is_not_hex"); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var request = + new ConstructionHashRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .signedTransaction("this_is_not_hex"); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidHexError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidHexError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseMessageTest.java index 54b278442c..a8b9ff0ccf 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseMessageTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionParseHandler; @@ -84,67 +87,54 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class ConstructionParseMessageTest extends ApiTest { - @Inject - private ConstructionParseHandler sut; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - @Inject - private Forks forks; + @Inject private ConstructionParseHandler sut; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + @Inject private Forks forks; - private byte[] buildUnsignedTxnWithMessage(String message) throws Exception { - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(accountAddress), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - UInt256.ONE - ) - ), - EntityOperation.from( - new AccountVaultEntity(otherAddress), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - UInt256.ONE - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(message, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, accountAddress, NotEnoughNativeTokensForFeesException::new - ); - var unsignedTransaction = builder.buildForExternalSign(); - return unsignedTransaction.blob(); - } + private byte[] buildUnsignedTxnWithMessage(String message) throws Exception { + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(accountAddress), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), UInt256.ONE)), + EntityOperation.from( + new AccountVaultEntity(otherAddress), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), UInt256.ONE)))); + var operationTxBuilder = new OperationTxBuilder(message, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, accountAddress, NotEnoughNativeTokensForFeesException::new); + var unsignedTransaction = builder.buildForExternalSign(); + return unsignedTransaction.blob(); + } - @Test - public void parsing_transaction_with_message_should_show_message() throws Exception { - // Arrange - start(); + @Test + public void parsing_transaction_with_message_should_show_message() throws Exception { + // Arrange + start(); - // Act - var hex = "deadbeefdeadbeef"; - var unsignedTxn = buildUnsignedTxnWithMessage(hex); - var request = new ConstructionParseRequest() - .signed(false) - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .transaction(Bytes.toHexString(unsignedTxn)); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionParseResponse.class); + // Act + var hex = "deadbeefdeadbeef"; + var unsignedTxn = buildUnsignedTxnWithMessage(hex); + var request = + new ConstructionParseRequest() + .signed(false) + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .transaction(Bytes.toHexString(unsignedTxn)); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionParseResponse.class); - // Assert - assertThat(response.getMetadata()).isNotNull(); - assertThat(response.getMetadata().getMessage()).isEqualTo(hex); - } + // Assert + assertThat(response.getMetadata()).isNotNull(); + assertThat(response.getMetadata().getMessage()).isEqualTo(hex); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseTransferTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseTransferTest.java index 55ec823f74..57da660381 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseTransferTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionParseTransferTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionParseHandler; @@ -87,101 +90,89 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.math.BigInteger; import java.util.List; import java.util.Map; import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class ConstructionParseTransferTest extends ApiTest { - @Inject - private ConstructionParseHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - @Inject - private Forks forks; + @Inject private ConstructionParseHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + @Inject private Forks forks; - private UInt256 transferAmount() { - return getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); - } + private UInt256 transferAmount() { + return getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); + } - private byte[] buildUnsignedTransferTxn(REAddr from, REAddr to) throws Exception { - final UInt256 toTransfer = transferAmount(); - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(from), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ), - EntityOperation.from( - new AccountVaultEntity(to), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new - ); - var unsignedTransaction = builder.buildForExternalSign(); - return unsignedTransaction.blob(); - } + private byte[] buildUnsignedTransferTxn(REAddr from, REAddr to) throws Exception { + final UInt256 toTransfer = transferAmount(); + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(from), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)), + EntityOperation.from( + new AccountVaultEntity(to), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)))); + var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new); + var unsignedTransaction = builder.buildForExternalSign(); + return unsignedTransaction.blob(); + } - @Test - public void parsing_transaction_with_transfer_should_have_proper_substates() throws Exception { - // Arrange - start(); + @Test + public void parsing_transaction_with_transfer_should_have_proper_substates() throws Exception { + // Arrange + start(); - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var unsignedTxn = buildUnsignedTransferTxn(accountAddress, otherAddress); - var request = new ConstructionParseRequest() - .signed(false) - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .transaction(Bytes.toHexString(unsignedTxn)); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionParseResponse.class); + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var unsignedTxn = buildUnsignedTransferTxn(accountAddress, otherAddress); + var request = + new ConstructionParseRequest() + .signed(false) + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .transaction(Bytes.toHexString(unsignedTxn)); + var response = handleRequestWithExpectedResponse(sut, request, ConstructionParseResponse.class); - // Assert - assertThat(response.getMetadata()).isNotNull(); - assertThat(response.getMetadata().getMessage()).isNull(); - var feeValue = new BigInteger(response.getMetadata().getFee().getValue()); - assertThat(feeValue).isGreaterThan(BigInteger.ZERO); - var entityHoldings = response.getOperationGroups().stream() - .flatMap(g -> g.getOperations().stream()) - .peek(op -> { - assertThat(op.getSubstate()).isNotNull(); - assertThat(op.getSubstate().getSubstateIdentifier()).isNotNull(); - assertThat(op.getAmount()).isNotNull(); - assertThat(op.getAmount().getResourceIdentifier()).isEqualTo(coreModelMapper.nativeToken()); - }) - .collect(Collectors.groupingBy( - Operation::getEntityIdentifier, - Collectors.mapping( - op -> new BigInteger(op.getAmount().getValue()), - Collectors.reducing(BigInteger.ZERO, BigInteger::add) - ) - )); + // Assert + assertThat(response.getMetadata()).isNotNull(); + assertThat(response.getMetadata().getMessage()).isNull(); + var feeValue = new BigInteger(response.getMetadata().getFee().getValue()); + assertThat(feeValue).isGreaterThan(BigInteger.ZERO); + var entityHoldings = + response.getOperationGroups().stream() + .flatMap(g -> g.getOperations().stream()) + .peek( + op -> { + assertThat(op.getSubstate()).isNotNull(); + assertThat(op.getSubstate().getSubstateIdentifier()).isNotNull(); + assertThat(op.getAmount()).isNotNull(); + assertThat(op.getAmount().getResourceIdentifier()) + .isEqualTo(coreModelMapper.nativeToken()); + }) + .collect( + Collectors.groupingBy( + Operation::getEntityIdentifier, + Collectors.mapping( + op -> new BigInteger(op.getAmount().getValue()), + Collectors.reducing(BigInteger.ZERO, BigInteger::add)))); - var accountEntityIdentifier = coreModelMapper.entityIdentifier(accountAddress); - var otherEntityIdentifier = coreModelMapper.entityIdentifier(otherAddress); - var transferAmount = new BigInteger(transferAmount().toString()); - var expectedChange = transferAmount.negate().subtract(feeValue); - assertThat(entityHoldings).containsExactlyInAnyOrderEntriesOf( - Map.of(accountEntityIdentifier, expectedChange, otherEntityIdentifier, transferAmount) - ); - } + var accountEntityIdentifier = coreModelMapper.entityIdentifier(accountAddress); + var otherEntityIdentifier = coreModelMapper.entityIdentifier(otherAddress); + var transferAmount = new BigInteger(transferAmount().toString()); + var expectedChange = transferAmount.negate().subtract(feeValue); + assertThat(entityHoldings) + .containsExactlyInAnyOrderEntriesOf( + Map.of(accountEntityIdentifier, expectedChange, otherEntityIdentifier, transferAmount)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionSubmitTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionSubmitTest.java index 79757d7493..8cccb78fed 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionSubmitTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/ConstructionSubmitTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.ConstructionSubmitHandler; @@ -91,139 +94,129 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.util.Arrays; import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class ConstructionSubmitTest extends ApiTest { - @Inject - private ConstructionSubmitHandler sut; - @Inject - @LocalSigner - private HashSigner hashSigner; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - @Inject - private Forks forks; - @Inject - private RadixEngineMempool mempool; - - public ConstructionSubmitTest() { - super(1); - } - - private Txn buildSignedTxn(REAddr from, REAddr to) throws Exception { - var toTransfer = UInt256.ONE; - - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(from), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ), - EntityOperation.from( - new AccountVaultEntity(to), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new - ); - return builder.signAndBuild(hashSigner::sign); - } - - @Test - public void submit_correct_txn_should_make_it_into_the_mempool() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var signedTxn = buildSignedTxn(accountAddress, otherAddress); - var request = new ConstructionSubmitRequest() - .networkIdentifier(networkIdentifier()) - .signedTransaction(Bytes.toHexString(signedTxn.getPayload())); - var response = handleRequestWithExpectedResponse(sut, request, ConstructionSubmitResponse.class); - - // Assert - assertThat(response.getTransactionIdentifier().getHash()).isEqualTo(signedTxn.getId().toString()); - assertThat(mempool.getData(m -> m.containsKey(signedTxn.getId())).booleanValue()).isTrue(); - } - - @Test - public void transaction_already_committed_should_return_error() throws Exception { - // Arrange - start(); - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var txn = buildSignedTxn(accountAddress, otherAddress); - mempool.add(txn); - runUntilCommitted(txn.getId()); - - // Act - var request = new ConstructionSubmitRequest() - .networkIdentifier(networkIdentifier()) - .signedTransaction(Bytes.toHexString(txn.getPayload())); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(SubstateDependencyNotFoundError.class); - assertThat(mempool.getData(m -> m.containsKey(txn.getId())).booleanValue()).isFalse(); - } - - @Test - public void mempool_full_should_return_error() throws Exception { - // Arrange - start(); - var accountAddress = REAddr.ofPubKeyAccount(self); - var firstOtherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var firstTxn = buildSignedTxn(accountAddress, firstOtherAddress); - mempool.add(firstTxn); - - // Act - var secondOtherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(3).getPublicKey()); - var signedTxn = buildSignedTxn(accountAddress, secondOtherAddress); - var request = new ConstructionSubmitRequest() - .networkIdentifier(networkIdentifier()) - .signedTransaction(Bytes.toHexString(signedTxn.getPayload())); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(MempoolFullError.class); - assertThat(mempool.getData(m -> m.containsKey(firstTxn.getId())).booleanValue()).isTrue(); - } - - @Test - public void submit_incorrect_txn_should_not_make_it_into_the_mempool() throws Exception { - // Arrange - start(); - - // Act - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var signedTxn = buildSignedTxn(accountAddress, otherAddress); - var malformedTxn = Txn.create(Arrays.copyOfRange(signedTxn.getPayload(), 1, signedTxn.getPayload().length)); - var request = new ConstructionSubmitRequest() - .networkIdentifier(networkIdentifier()) - .signedTransaction(Bytes.toHexString(malformedTxn.getPayload())); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidTransactionError.class); - assertThat(mempool.getData(m -> m.containsKey(malformedTxn.getId())).booleanValue()).isFalse(); - } + @Inject private ConstructionSubmitHandler sut; + @Inject @LocalSigner private HashSigner hashSigner; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + @Inject private Forks forks; + @Inject private RadixEngineMempool mempool; + + public ConstructionSubmitTest() { + super(1); + } + + private Txn buildSignedTxn(REAddr from, REAddr to) throws Exception { + var toTransfer = UInt256.ONE; + + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(from), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)), + EntityOperation.from( + new AccountVaultEntity(to), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)))); + var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new); + return builder.signAndBuild(hashSigner::sign); + } + + @Test + public void submit_correct_txn_should_make_it_into_the_mempool() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var signedTxn = buildSignedTxn(accountAddress, otherAddress); + var request = + new ConstructionSubmitRequest() + .networkIdentifier(networkIdentifier()) + .signedTransaction(Bytes.toHexString(signedTxn.getPayload())); + var response = + handleRequestWithExpectedResponse(sut, request, ConstructionSubmitResponse.class); + + // Assert + assertThat(response.getTransactionIdentifier().getHash()) + .isEqualTo(signedTxn.getId().toString()); + assertThat(mempool.getData(m -> m.containsKey(signedTxn.getId())).booleanValue()).isTrue(); + } + + @Test + public void transaction_already_committed_should_return_error() throws Exception { + // Arrange + start(); + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var txn = buildSignedTxn(accountAddress, otherAddress); + mempool.add(txn); + runUntilCommitted(txn.getId()); + + // Act + var request = + new ConstructionSubmitRequest() + .networkIdentifier(networkIdentifier()) + .signedTransaction(Bytes.toHexString(txn.getPayload())); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(SubstateDependencyNotFoundError.class); + assertThat(mempool.getData(m -> m.containsKey(txn.getId())).booleanValue()).isFalse(); + } + + @Test + public void mempool_full_should_return_error() throws Exception { + // Arrange + start(); + var accountAddress = REAddr.ofPubKeyAccount(self); + var firstOtherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var firstTxn = buildSignedTxn(accountAddress, firstOtherAddress); + mempool.add(firstTxn); + + // Act + var secondOtherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(3).getPublicKey()); + var signedTxn = buildSignedTxn(accountAddress, secondOtherAddress); + var request = + new ConstructionSubmitRequest() + .networkIdentifier(networkIdentifier()) + .signedTransaction(Bytes.toHexString(signedTxn.getPayload())); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(MempoolFullError.class); + assertThat(mempool.getData(m -> m.containsKey(firstTxn.getId())).booleanValue()).isTrue(); + } + + @Test + public void submit_incorrect_txn_should_not_make_it_into_the_mempool() throws Exception { + // Arrange + start(); + + // Act + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var signedTxn = buildSignedTxn(accountAddress, otherAddress); + var malformedTxn = + Txn.create(Arrays.copyOfRange(signedTxn.getPayload(), 1, signedTxn.getPayload().length)); + var request = + new ConstructionSubmitRequest() + .networkIdentifier(networkIdentifier()) + .signedTransaction(Bytes.toHexString(malformedTxn.getPayload())); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidTransactionError.class); + assertThat(mempool.getData(m -> m.containsKey(malformedTxn.getId())).booleanValue()).isFalse(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineConfigurationHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineConfigurationHandlerTest.java index df300eb3cd..561d4eab38 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineConfigurationHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineConfigurationHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.EngineConfigurationHandler; @@ -73,29 +76,25 @@ import com.radixdlt.utils.Bytes; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class EngineConfigurationHandlerTest extends ApiTest { - @Inject - private EngineConfigurationHandler sut; - @Inject - @Genesis - private VerifiedTxnsAndProof genesis; + @Inject private EngineConfigurationHandler sut; + @Inject @Genesis private VerifiedTxnsAndProof genesis; - @Test - public void engine_configuration_should_return_correct_data() throws Exception { - // Arrange - start(); + @Test + public void engine_configuration_should_return_correct_data() throws Exception { + // Arrange + start(); - // Act - var request = new EngineConfigurationRequest() - .networkIdentifier(networkIdentifier()); - var response = handleRequestWithExpectedResponse(sut, request, EngineConfigurationResponse.class); + // Act + var request = new EngineConfigurationRequest().networkIdentifier(networkIdentifier()); + var response = + handleRequestWithExpectedResponse(sut, request, EngineConfigurationResponse.class); - // Assert - assertThat(response.getCheckpoints()).hasSize(1); - assertThat(response.getCheckpoints().get(0).getCheckpointTransaction()) - .isEqualTo(Bytes.toHexString(genesis.getTxns().get(0).getPayload())); - assertThat(response.getForks()).allMatch(fork -> fork.getEngineConfiguration().getMaximumMessageLength() == 255); - } + // Assert + assertThat(response.getCheckpoints()).hasSize(1); + assertThat(response.getCheckpoints().get(0).getCheckpointTransaction()) + .isEqualTo(Bytes.toHexString(genesis.getTxns().get(0).getPayload())); + assertThat(response.getForks()) + .allMatch(fork -> fork.getEngineConfiguration().getMaximumMessageLength() == 255); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineStatusHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineStatusHandlerTest.java index ffe1e3df89..895b80d7db 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineStatusHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EngineStatusHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.EngineStatusHandler; @@ -73,29 +76,25 @@ import com.radixdlt.networks.Addressing; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public final class EngineStatusHandlerTest extends ApiTest { - @Inject - private EngineStatusHandler sut; - @Inject - private Addressing addressing; + @Inject private EngineStatusHandler sut; + @Inject private Addressing addressing; - @Test - public void engine_configuration_should_return_correct_data() throws Exception { - // Arrange - start(); + @Test + public void engine_configuration_should_return_correct_data() throws Exception { + // Arrange + start(); - // Act - var request = new EngineStatusRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")); - var response = handleRequestWithExpectedResponse(sut, request, EngineStatusResponse.class); + // Act + var request = + new EngineStatusRequest().networkIdentifier(new NetworkIdentifier().network("localnet")); + var response = handleRequestWithExpectedResponse(sut, request, EngineStatusResponse.class); - // Assert - assertThat(response.getValidatorSet()) - .containsExactly(new Validator() - .validatorAddress(addressing.forValidators().of(selfKey())) - .stake(getStakeAmount().toSubunits().toString()) - ); - } + // Assert + assertThat(response.getValidatorSet()) + .containsExactly( + new Validator() + .validatorAddress(addressing.forValidators().of(selfKey())) + .stake(getStakeAmount().toSubunits().toString())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EntityHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EntityHandlerTest.java index 91a7ef049f..b4d1312718 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EntityHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/EntityHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.EntityHandler; @@ -94,240 +97,254 @@ import com.radixdlt.utils.Bytes; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - // TODO: Refactor so that every entity is just a parameter in a parametrized test public class EntityHandlerTest extends ApiTest { - @Inject - private EntityHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @Genesis - private VerifiedTxnsAndProof genesis; - - @Test - public void retrieve_system_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(new EntityIdentifier().address("system")); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getBalances()).isEmpty(); - assertThat(response.getDataObjects()).isNotEmpty(); - } - - @Test - public void retrieve_native_token_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifier(REAddr.ofNativeToken(), "xrd")); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getBalances()).isEmpty(); - assertThat(response.getDataObjects()).hasAtLeastOneElementOfType(TokenData.class); - assertThat(response.getDataObjects()).hasAtLeastOneElementOfType(TokenMetadata.class); - } - - @Test - public void retrieve_non_existent_token_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var tokenAddress = REAddr.ofHashedKey(selfKey(), "test"); - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifier(tokenAddress, "test")); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getBalances()).isEmpty(); - assertThat(response.getDataObjects()) - .containsExactly(new UnclaimedRadixEngineAddress() - .type(SubstateTypeMapping.getName(SubstateTypeId.UNCLAIMED_READDR)) - ); - } - - @Test - public void retrieve_account_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var request = new EntityRequest() - .networkIdentifier(networkIdentifier()) - .entityIdentifier(coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(selfKey()))); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getBalances()) - .containsExactlyInAnyOrder( - coreModelMapper.nativeTokenAmount(getLiquidAmount().toSubunits()), - coreModelMapper.stakeUnitAmount(selfKey(), getStakeAmount().toSubunits()) - ); - } - - @Test - public void retrieve_validator_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifier(selfKey())); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getDataObjects()).hasOnlyElementsOfTypes( - ValidatorAllowDelegation.class, - ValidatorMetadata.class, - ValidatorSystemMetadata.class, - PreparedValidatorOwner.class, - PreparedValidatorFee.class, - PreparedValidatorRegistered.class - ); - assertThat(response.getBalances()).isEmpty(); - } - - @Test - public void retrieve_validator_system_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifierValidatorSystem(selfKey())); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getDataObjects()) - .hasOnlyElementsOfTypes(ValidatorBFTData.class, ValidatorData.class); - assertThat(response.getBalances()) - .containsExactly(coreModelMapper.nativeTokenAmount(getStakeAmount().toSubunits())); - } - - @Test - public void retrieve_prepared_stake_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var address = REAddr.ofPubKeyAccount(selfKey()); - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifierPreparedStake(address, selfKey())); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getDataObjects()).isEmpty(); - assertThat(response.getBalances()).isEmpty(); - } - - @Test - public void retrieve_prepared_unstake_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var address = REAddr.ofPubKeyAccount(selfKey()); - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifierPreparedUnstake(address)); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getDataObjects()).isEmpty(); - assertThat(response.getBalances()).isEmpty(); - } - - @Test - public void retrieve_exiting_stake_entity_on_genesis() throws Exception { - // Arrange - start(); - - // Act - var address = REAddr.ofPubKeyAccount(selfKey()); - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(coreModelMapper.entityIdentifierExitingStake(address, selfKey(), 1)); - var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); - - // Assert - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - assertThat(response.getDataObjects()).isEmpty(); - assertThat(response.getBalances()).isEmpty(); - } - - @Test - public void retrieve_invalid_entity_should_throw() throws Exception { - // Arrange - start(); - - // Act - // Assert - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(new EntityIdentifier().address("some_garbage_address")); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - assertThat(response.getDetails()).isInstanceOf(InvalidAddressError.class); - } - - @Test - public void retrieve_invalid_sub_entity_should_throw() throws Exception { - // Arrange - start(); - - // Act - var address = REAddr.ofPubKeyAccount(selfKey()); - var invalidSubEntity = coreModelMapper - .entityIdentifier(address) - .subEntity(new SubEntity().address("prepared_stakes")); - var request = new EntityRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .entityIdentifier(invalidSubEntity); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidSubEntityError.class); - } + @Inject private EntityHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @Genesis private VerifiedTxnsAndProof genesis; + + @Test + public void retrieve_system_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(new EntityIdentifier().address("system")); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getBalances()).isEmpty(); + assertThat(response.getDataObjects()).isNotEmpty(); + } + + @Test + public void retrieve_native_token_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifier(REAddr.ofNativeToken(), "xrd")); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getBalances()).isEmpty(); + assertThat(response.getDataObjects()).hasAtLeastOneElementOfType(TokenData.class); + assertThat(response.getDataObjects()).hasAtLeastOneElementOfType(TokenMetadata.class); + } + + @Test + public void retrieve_non_existent_token_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var tokenAddress = REAddr.ofHashedKey(selfKey(), "test"); + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifier(tokenAddress, "test")); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getBalances()).isEmpty(); + assertThat(response.getDataObjects()) + .containsExactly( + new UnclaimedRadixEngineAddress() + .type(SubstateTypeMapping.getName(SubstateTypeId.UNCLAIMED_READDR))); + } + + @Test + public void retrieve_account_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var request = + new EntityRequest() + .networkIdentifier(networkIdentifier()) + .entityIdentifier(coreModelMapper.entityIdentifier(REAddr.ofPubKeyAccount(selfKey()))); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getBalances()) + .containsExactlyInAnyOrder( + coreModelMapper.nativeTokenAmount(getLiquidAmount().toSubunits()), + coreModelMapper.stakeUnitAmount(selfKey(), getStakeAmount().toSubunits())); + } + + @Test + public void retrieve_validator_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifier(selfKey())); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getDataObjects()) + .hasOnlyElementsOfTypes( + ValidatorAllowDelegation.class, + ValidatorMetadata.class, + ValidatorSystemMetadata.class, + PreparedValidatorOwner.class, + PreparedValidatorFee.class, + PreparedValidatorRegistered.class); + assertThat(response.getBalances()).isEmpty(); + } + + @Test + public void retrieve_validator_system_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifierValidatorSystem(selfKey())); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getDataObjects()) + .hasOnlyElementsOfTypes(ValidatorBFTData.class, ValidatorData.class); + assertThat(response.getBalances()) + .containsExactly(coreModelMapper.nativeTokenAmount(getStakeAmount().toSubunits())); + } + + @Test + public void retrieve_prepared_stake_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var address = REAddr.ofPubKeyAccount(selfKey()); + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifierPreparedStake(address, selfKey())); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getDataObjects()).isEmpty(); + assertThat(response.getBalances()).isEmpty(); + } + + @Test + public void retrieve_prepared_unstake_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var address = REAddr.ofPubKeyAccount(selfKey()); + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifierPreparedUnstake(address)); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getDataObjects()).isEmpty(); + assertThat(response.getBalances()).isEmpty(); + } + + @Test + public void retrieve_exiting_stake_entity_on_genesis() throws Exception { + // Arrange + start(); + + // Act + var address = REAddr.ofPubKeyAccount(selfKey()); + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(coreModelMapper.entityIdentifierExitingStake(address, selfKey(), 1)); + var response = handleRequestWithExpectedResponse(sut, request, EntityResponse.class); + + // Assert + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + assertThat(response.getDataObjects()).isEmpty(); + assertThat(response.getBalances()).isEmpty(); + } + + @Test + public void retrieve_invalid_entity_should_throw() throws Exception { + // Arrange + start(); + + // Act + // Assert + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(new EntityIdentifier().address("some_garbage_address")); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + assertThat(response.getDetails()).isInstanceOf(InvalidAddressError.class); + } + + @Test + public void retrieve_invalid_sub_entity_should_throw() throws Exception { + // Arrange + start(); + + // Act + var address = REAddr.ofPubKeyAccount(selfKey()); + var invalidSubEntity = + coreModelMapper + .entityIdentifier(address) + .subEntity(new SubEntity().address("prepared_stakes")); + var request = + new EntityRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .entityIdentifier(invalidSubEntity); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidSubEntityError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeyListHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeyListHandlerTest.java index c48f10a914..61648c555d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeyListHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeyListHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.KeyListHandler; @@ -71,26 +74,22 @@ import com.radixdlt.api.core.openapitools.model.KeyListResponse; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class KeyListHandlerTest extends ApiTest { - @Inject - private KeyListHandler sut; + @Inject private KeyListHandler sut; - @Inject - private CoreModelMapper mapper; + @Inject private CoreModelMapper mapper; - @Test - public void can_retrieve_nodes_public_key() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_nodes_public_key() throws Exception { + // Arrange + start(); - // Act - var request = new KeyListRequest().networkIdentifier(networkIdentifier()); - var response = handleRequestWithExpectedResponse(sut, request, KeyListResponse.class); + // Act + var request = new KeyListRequest().networkIdentifier(networkIdentifier()); + var response = handleRequestWithExpectedResponse(sut, request, KeyListResponse.class); - // Assert - assertThat(response.getPublicKeys().get(0).getPublicKey()) - .isEqualTo(mapper.publicKey(selfKey())); - } + // Assert + assertThat(response.getPublicKeys().get(0).getPublicKey()) + .isEqualTo(mapper.publicKey(selfKey())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeySignHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeySignHandlerTest.java index 5c50483710..d38fd3ef17 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeySignHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/KeySignHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,10 +64,12 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; -import com.radixdlt.api.core.model.CoreModelMapper; import com.radixdlt.api.core.handlers.KeySignHandler; +import com.radixdlt.api.core.model.CoreModelMapper; import com.radixdlt.api.core.model.EntityOperation; import com.radixdlt.api.core.model.NotEnoughNativeTokensForFeesException; import com.radixdlt.api.core.model.OperationTxBuilder; @@ -74,9 +77,9 @@ import com.radixdlt.api.core.model.TokenResource; import com.radixdlt.api.core.model.entities.AccountVaultEntity; import com.radixdlt.api.core.openapitools.model.InvalidTransactionError; +import com.radixdlt.api.core.openapitools.model.KeySignRequest; import com.radixdlt.api.core.openapitools.model.NetworkIdentifier; import com.radixdlt.api.core.openapitools.model.PublicKeyNotSupportedError; -import com.radixdlt.api.core.openapitools.model.KeySignRequest; import com.radixdlt.api.core.openapitools.model.UnexpectedError; import com.radixdlt.engine.RadixEngine; import com.radixdlt.identifiers.REAddr; @@ -85,100 +88,90 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class KeySignHandlerTest extends ApiTest { - @Inject - private KeySignHandler sut; - @Inject - private Forks forks; - @Inject - private CoreModelMapper mapper; - @Inject - private RadixEngine radixEngine; + @Inject private KeySignHandler sut; + @Inject private Forks forks; + @Inject private CoreModelMapper mapper; + @Inject private RadixEngine radixEngine; - private byte[] buildUnsignedTxn(REAddr from, REAddr to) throws Exception { - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(from), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - UInt256.ONE - ) - ), - EntityOperation.from( - new AccountVaultEntity(to), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - UInt256.ONE - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new - ); - return builder.buildForExternalSign().blob(); - } + private byte[] buildUnsignedTxn(REAddr from, REAddr to) throws Exception { + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(from), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), UInt256.ONE)), + EntityOperation.from( + new AccountVaultEntity(to), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), UInt256.ONE)))); + var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new); + return builder.buildForExternalSign().blob(); + } - @Test - public void sign_should_work_on_correct_transaction() throws Exception { - // Arrange - start(); + @Test + public void sign_should_work_on_correct_transaction() throws Exception { + // Arrange + start(); - // Act - var from = REAddr.ofPubKeyAccount(selfKey()); - var other = PrivateKeys.ofNumeric(2); - var to = REAddr.ofPubKeyAccount(other.getPublicKey()); - var unsignedTxn = buildUnsignedTxn(from, to); - var request = new KeySignRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .publicKey(mapper.publicKey(selfKey())) - .unsignedTransaction(Bytes.toHexString(unsignedTxn)); - var response = sut.handleRequest(request); + // Act + var from = REAddr.ofPubKeyAccount(selfKey()); + var other = PrivateKeys.ofNumeric(2); + var to = REAddr.ofPubKeyAccount(other.getPublicKey()); + var unsignedTxn = buildUnsignedTxn(from, to); + var request = + new KeySignRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .publicKey(mapper.publicKey(selfKey())) + .unsignedTransaction(Bytes.toHexString(unsignedTxn)); + var response = sut.handleRequest(request); - // Assert - assertThat(Bytes.fromHexString(response.getSignedTransaction())).isNotNull(); - } + // Assert + assertThat(Bytes.fromHexString(response.getSignedTransaction())).isNotNull(); + } - @Test - public void sign_given_an_unsupported_public_key_should_fail() throws Exception { - // Arrange - start(); + @Test + public void sign_given_an_unsupported_public_key_should_fail() throws Exception { + // Arrange + start(); - // Act - var from = REAddr.ofPubKeyAccount(selfKey()); - var other = PrivateKeys.ofNumeric(2); - var to = REAddr.ofPubKeyAccount(other.getPublicKey()); - var unsignedTxn = buildUnsignedTxn(from, to); - var request = new KeySignRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .publicKey(mapper.publicKey(other.getPublicKey())) - .unsignedTransaction(Bytes.toHexString(unsignedTxn)); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var from = REAddr.ofPubKeyAccount(selfKey()); + var other = PrivateKeys.ofNumeric(2); + var to = REAddr.ofPubKeyAccount(other.getPublicKey()); + var unsignedTxn = buildUnsignedTxn(from, to); + var request = + new KeySignRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .publicKey(mapper.publicKey(other.getPublicKey())) + .unsignedTransaction(Bytes.toHexString(unsignedTxn)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(PublicKeyNotSupportedError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(PublicKeyNotSupportedError.class); + } - @Test - public void sign_should_fail_given_an_invalid_transaction() throws Exception { - // Arrange - start(); + @Test + public void sign_should_fail_given_an_invalid_transaction() throws Exception { + // Arrange + start(); - // Act - var request = new KeySignRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .publicKey(mapper.publicKey(selfKey())) - .unsignedTransaction("badbadbadbad"); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var request = + new KeySignRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .publicKey(mapper.publicKey(selfKey())) + .unsignedTransaction("badbadbadbad"); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidTransactionError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidTransactionError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolHandlerTest.java index 8ea5e7abcf..3d15f86e72 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.MempoolHandler; @@ -88,74 +91,55 @@ import com.radixdlt.statecomputer.forks.Forks; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public final class MempoolHandlerTest extends ApiTest { - @Inject - private MempoolHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @LocalSigner - private HashSigner hashSigner; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - @Inject - private Forks forks; - @Inject - private RadixEngineMempool mempool; + @Inject private MempoolHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @LocalSigner private HashSigner hashSigner; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + @Inject private Forks forks; + @Inject private RadixEngineMempool mempool; - private Txn buildSignedTxn(REAddr from, REAddr to) throws Exception { - final UInt256 toTransfer = getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); + private Txn buildSignedTxn(REAddr from, REAddr to) throws Exception { + final UInt256 toTransfer = + getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(from), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ), - EntityOperation.from( - new AccountVaultEntity(to), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new - ); - return builder.signAndBuild(hashSigner::sign); - } + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(from), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)), + EntityOperation.from( + new AccountVaultEntity(to), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)))); + var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new); + return builder.signAndBuild(hashSigner::sign); + } - @Test - public void transaction_in_mempool_should_be_seen() throws Exception { - // Arrange - start(); - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var signedTxn = buildSignedTxn(accountAddress, otherAddress); - mempool.add(signedTxn); + @Test + public void transaction_in_mempool_should_be_seen() throws Exception { + // Arrange + start(); + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var signedTxn = buildSignedTxn(accountAddress, otherAddress); + mempool.add(signedTxn); - // Act - var request = new MempoolRequest().networkIdentifier(networkIdentifier()); - var response = handleRequestWithExpectedResponse( - sut, request, MempoolResponse.class - ); + // Act + var request = new MempoolRequest().networkIdentifier(networkIdentifier()); + var response = handleRequestWithExpectedResponse(sut, request, MempoolResponse.class); - // Assert - assertThat(response.getTransactionIdentifiers()) - .containsExactly(coreModelMapper.transactionIdentifier(signedTxn.getId())); - } + // Assert + assertThat(response.getTransactionIdentifiers()) + .containsExactly(coreModelMapper.transactionIdentifier(signedTxn.getId())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolTransactionHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolTransactionHandlerTest.java index 6db7658d94..10836ce433 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolTransactionHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/MempoolTransactionHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.MempoolTransactionHandler; @@ -94,108 +97,95 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Test; - import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class MempoolTransactionHandlerTest extends ApiTest { - @Inject - private MempoolTransactionHandler sut; - @Inject - private CoreModelMapper coreModelMapper; - @Inject - @LocalSigner - private HashSigner hashSigner; - @Inject - @Self - private ECPublicKey self; - @Inject - private RadixEngine radixEngine; - @Inject - private Forks forks; - @Inject - private RadixEngineMempool mempool; + @Inject private MempoolTransactionHandler sut; + @Inject private CoreModelMapper coreModelMapper; + @Inject @LocalSigner private HashSigner hashSigner; + @Inject @Self private ECPublicKey self; + @Inject private RadixEngine radixEngine; + @Inject private Forks forks; + @Inject private RadixEngineMempool mempool; - private Txn buildSignedTxn(REAddr from, REAddr to) throws Exception { - final UInt256 toTransfer = getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); - var entityOperationGroups = - List.of(List.of( - EntityOperation.from( - new AccountVaultEntity(from), - ResourceOperation.withdraw( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ), - EntityOperation.from( - new AccountVaultEntity(to), - ResourceOperation.deposit( - new TokenResource("xrd", REAddr.ofNativeToken()), - toTransfer - ) - ) - )); - var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); - var builder = radixEngine.constructWithFees( - operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new - ); - return builder.signAndBuild(hashSigner::sign); - } + private Txn buildSignedTxn(REAddr from, REAddr to) throws Exception { + final UInt256 toTransfer = + getLiquidAmount().toSubunits().subtract(Amount.ofTokens(1).toSubunits()); + var entityOperationGroups = + List.of( + List.of( + EntityOperation.from( + new AccountVaultEntity(from), + ResourceOperation.withdraw( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)), + EntityOperation.from( + new AccountVaultEntity(to), + ResourceOperation.deposit( + new TokenResource("xrd", REAddr.ofNativeToken()), toTransfer)))); + var operationTxBuilder = new OperationTxBuilder(null, entityOperationGroups, forks); + var builder = + radixEngine.constructWithFees( + operationTxBuilder, false, from, NotEnoughNativeTokensForFeesException::new); + return builder.signAndBuild(hashSigner::sign); + } - @Test - public void transaction_in_mempool_should_be_retrievable() throws Exception { - // Arrange - start(); - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var signedTxn = buildSignedTxn(accountAddress, otherAddress); - mempool.add(signedTxn); + @Test + public void transaction_in_mempool_should_be_retrievable() throws Exception { + // Arrange + start(); + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var signedTxn = buildSignedTxn(accountAddress, otherAddress); + mempool.add(signedTxn); - // Act - var request = new MempoolTransactionRequest() - .networkIdentifier(networkIdentifier()) - .transactionIdentifier(coreModelMapper.transactionIdentifier(signedTxn.getId())); - var response = handleRequestWithExpectedResponse(sut, request, MempoolTransactionResponse.class); + // Act + var request = + new MempoolTransactionRequest() + .networkIdentifier(networkIdentifier()) + .transactionIdentifier(coreModelMapper.transactionIdentifier(signedTxn.getId())); + var response = + handleRequestWithExpectedResponse(sut, request, MempoolTransactionResponse.class); - // Assert - assertThat(response.getTransaction().getTransactionIdentifier()) - .isEqualTo(coreModelMapper.transactionIdentifier(signedTxn.getId())); - var metadata = response.getTransaction().getMetadata(); - assertThat(metadata.getHex()).isEqualTo(Bytes.toHexString(signedTxn.getPayload())); - } + // Assert + assertThat(response.getTransaction().getTransactionIdentifier()) + .isEqualTo(coreModelMapper.transactionIdentifier(signedTxn.getId())); + var metadata = response.getTransaction().getMetadata(); + assertThat(metadata.getHex()).isEqualTo(Bytes.toHexString(signedTxn.getPayload())); + } - @Test - public void retrieving_transaction_not_in_mempool_should_throw() throws Exception { - // Arrange - start(); - var accountAddress = REAddr.ofPubKeyAccount(self); - var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); - var signedTxn = buildSignedTxn(accountAddress, otherAddress); + @Test + public void retrieving_transaction_not_in_mempool_should_throw() throws Exception { + // Arrange + start(); + var accountAddress = REAddr.ofPubKeyAccount(self); + var otherAddress = REAddr.ofPubKeyAccount(PrivateKeys.ofNumeric(2).getPublicKey()); + var signedTxn = buildSignedTxn(accountAddress, otherAddress); - // Act - var request = new MempoolTransactionRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .transactionIdentifier(coreModelMapper.transactionIdentifier(signedTxn.getId())); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var request = + new MempoolTransactionRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .transactionIdentifier(coreModelMapper.transactionIdentifier(signedTxn.getId())); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(TransactionNotFoundError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(TransactionNotFoundError.class); + } - @Test - public void retrieving_invalid_hash_should_throw() throws Exception { - // Arrange - start(); + @Test + public void retrieving_invalid_hash_should_throw() throws Exception { + // Arrange + start(); - // Act - var request = new MempoolTransactionRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .transactionIdentifier(new TransactionIdentifier().hash("badbad")); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var request = + new MempoolTransactionRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .transactionIdentifier(new TransactionIdentifier().hash("badbad")); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidTransactionHashError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidTransactionHashError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkConfigurationHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkConfigurationHandlerTest.java index 9d9ee22f02..58bde4ca93 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkConfigurationHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkConfigurationHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,36 +64,34 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.NetworkConfigurationHandler; import com.radixdlt.api.core.openapitools.model.NetworkConfigurationResponse; import com.radixdlt.networks.Addressing; -import org.junit.Test; - import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class NetworkConfigurationHandlerTest extends ApiTest { - @Inject - private NetworkConfigurationHandler sut; - @Inject - private Addressing addressing; + @Inject private NetworkConfigurationHandler sut; + @Inject private Addressing addressing; - @Test - public void network_configuration_should_return_correct_data() throws Exception { - // Arrange - start(); + @Test + public void network_configuration_should_return_correct_data() throws Exception { + // Arrange + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, Map.of(), NetworkConfigurationResponse.class); + // Act + var response = + handleRequestWithExpectedResponse(sut, Map.of(), NetworkConfigurationResponse.class); - // Assert - var bech32 = response.getBech32HumanReadableParts(); - assertThat(bech32.getAccountHrp()).isEqualTo(addressing.forAccounts().getHrp()); - assertThat(bech32.getNodeHrp()).isEqualTo(addressing.forNodes().getHrp()); - assertThat(bech32.getValidatorHrp()).isEqualTo(addressing.forValidators().getHrp()); - assertThat(bech32.getResourceHrpSuffix()).isEqualTo(addressing.forResources().getHrpSuffix()); - } + // Assert + var bech32 = response.getBech32HumanReadableParts(); + assertThat(bech32.getAccountHrp()).isEqualTo(addressing.forAccounts().getHrp()); + assertThat(bech32.getNodeHrp()).isEqualTo(addressing.forNodes().getHrp()); + assertThat(bech32.getValidatorHrp()).isEqualTo(addressing.forValidators().getHrp()); + assertThat(bech32.getResourceHrpSuffix()).isEqualTo(addressing.forResources().getHrpSuffix()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkStatusHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkStatusHandlerTest.java index 369c96a653..a7fc098a33 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkStatusHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/NetworkStatusHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.NetworkStatusHandler; @@ -78,56 +81,51 @@ import com.radixdlt.utils.Bytes; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class NetworkStatusHandlerTest extends ApiTest { - @Inject - private NetworkStatusHandler sut; - @Inject - private CoreModelMapper mapper; - @Inject - @Genesis - private VerifiedTxnsAndProof genesis; + @Inject private NetworkStatusHandler sut; + @Inject private CoreModelMapper mapper; + @Inject @Genesis private VerifiedTxnsAndProof genesis; - @Test - public void network_status_should_return_correct_data() throws Exception { - // Arrange - start(); + @Test + public void network_status_should_return_correct_data() throws Exception { + // Arrange + start(); - // Act - var request = new NetworkStatusRequest().networkIdentifier(networkIdentifier()); - var response = handleRequestWithExpectedResponse(sut, request, NetworkStatusResponse.class); + // Act + var request = new NetworkStatusRequest().networkIdentifier(networkIdentifier()); + var response = handleRequestWithExpectedResponse(sut, request, NetworkStatusResponse.class); - // Assert - var genesisStateIdentifier = mapper.stateIdentifier(genesis.getProof().getAccumulatorState()); - assertThat(response.getCurrentStateIdentifier()).isEqualTo(genesisStateIdentifier); - assertThat(response.getGenesisStateIdentifier()).isEqualTo(genesisStateIdentifier); - } + // Assert + var genesisStateIdentifier = mapper.stateIdentifier(genesis.getProof().getAccumulatorState()); + assertThat(response.getCurrentStateIdentifier()).isEqualTo(genesisStateIdentifier); + assertThat(response.getGenesisStateIdentifier()).isEqualTo(genesisStateIdentifier); + } - @Test - public void invalid_json_should_return_json_error() throws Exception { - // Arrange - start(); + @Test + public void invalid_json_should_return_json_error() throws Exception { + // Arrange + start(); - // Act - var requestBytes = Bytes.fromHexString("deadbeef"); - var response = handleRequestWithExpectedResponse(sut, requestBytes, UnexpectedError.class); + // Act + var requestBytes = Bytes.fromHexString("deadbeef"); + var response = handleRequestWithExpectedResponse(sut, requestBytes, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidJsonError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidJsonError.class); + } - @Test - public void unknown_network_should_return_error() throws Exception { - // Arrange - start(); + @Test + public void unknown_network_should_return_error() throws Exception { + // Arrange + start(); - // Act - var request = new NetworkStatusRequest() - .networkIdentifier(new NetworkIdentifier().network("unknown_network")); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + // Act + var request = + new NetworkStatusRequest() + .networkIdentifier(new NetworkIdentifier().network("unknown_network")); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - // Assert - assertThat(response.getDetails()).isInstanceOf(NetworkNotSupportedError.class); - } + // Assert + assertThat(response.getDetails()).isInstanceOf(NetworkNotSupportedError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/TransactionsHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/TransactionsHandlerTest.java index 333cfacb06..a8789c1896 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/TransactionsHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/core/TransactionsHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.core; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.core.handlers.TransactionsHandler; @@ -78,82 +81,83 @@ import com.radixdlt.utils.Bytes; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class TransactionsHandlerTest extends ApiTest { - @Inject - private TransactionsHandler sut; - @Inject - @Genesis - private VerifiedTxnsAndProof genesis; - - @Test - public void retrieve_genesis_in_transaction_stream() throws Exception { - // Arrange - start(); - - // Act - var request = new CommittedTransactionsRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .limit(1L) - .stateIdentifier(new PartialStateIdentifier().stateVersion(0L)); - var response = handleRequestWithExpectedResponse(sut, request, CommittedTransactionsResponse.class); - - // Assert - assertThat(response.getTransactions()).hasSize(1); - var hex = response.getTransactions().get(0).getMetadata().getHex(); - assertThat(hex).isEqualTo(Bytes.toHexString(genesis.getTxns().get(0).getPayload())); - } - - - @Test - public void retrieve_last_state_version() throws Exception { - // Arrange - start(); - - // Act - var request = new CommittedTransactionsRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .limit(1L) - .stateIdentifier(new PartialStateIdentifier().stateVersion(1L)); - var response = handleRequestWithExpectedResponse(sut, request, CommittedTransactionsResponse.class); - - // Assert - assertThat(response.getTransactions()).isEmpty(); - var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); - var genesisAccumulator = genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); - assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); - } - - @Test - public void retrieving_past_last_state_version_should_throw_exception() throws Exception { - // Arrange - start(); - - // Act - var request = new CommittedTransactionsRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .limit(1L) - .stateIdentifier(new PartialStateIdentifier().stateVersion(2L)); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(StateIdentifierNotFoundError.class); - } - - @Test - public void retrieving_illegal_state_version_should_throw_exception() throws Exception { - // Arrange - start(); - - // Act - var request = new CommittedTransactionsRequest() - .networkIdentifier(new NetworkIdentifier().network("localnet")) - .limit(1L) - .stateIdentifier(new PartialStateIdentifier().stateVersion(-1L)); - var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); - - // Assert - assertThat(response.getDetails()).isInstanceOf(InvalidPartialStateIdentifierError.class); - } + @Inject private TransactionsHandler sut; + @Inject @Genesis private VerifiedTxnsAndProof genesis; + + @Test + public void retrieve_genesis_in_transaction_stream() throws Exception { + // Arrange + start(); + + // Act + var request = + new CommittedTransactionsRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .limit(1L) + .stateIdentifier(new PartialStateIdentifier().stateVersion(0L)); + var response = + handleRequestWithExpectedResponse(sut, request, CommittedTransactionsResponse.class); + + // Assert + assertThat(response.getTransactions()).hasSize(1); + var hex = response.getTransactions().get(0).getMetadata().getHex(); + assertThat(hex).isEqualTo(Bytes.toHexString(genesis.getTxns().get(0).getPayload())); + } + + @Test + public void retrieve_last_state_version() throws Exception { + // Arrange + start(); + + // Act + var request = + new CommittedTransactionsRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .limit(1L) + .stateIdentifier(new PartialStateIdentifier().stateVersion(1L)); + var response = + handleRequestWithExpectedResponse(sut, request, CommittedTransactionsResponse.class); + + // Assert + assertThat(response.getTransactions()).isEmpty(); + var stateAccumulator = response.getStateIdentifier().getTransactionAccumulator(); + var genesisAccumulator = + genesis.getProof().getAccumulatorState().getAccumulatorHash().asBytes(); + assertThat(stateAccumulator).isEqualTo(Bytes.toHexString(genesisAccumulator)); + } + + @Test + public void retrieving_past_last_state_version_should_throw_exception() throws Exception { + // Arrange + start(); + + // Act + var request = + new CommittedTransactionsRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .limit(1L) + .stateIdentifier(new PartialStateIdentifier().stateVersion(2L)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(StateIdentifierNotFoundError.class); + } + + @Test + public void retrieving_illegal_state_version_should_throw_exception() throws Exception { + // Arrange + start(); + + // Act + var request = + new CommittedTransactionsRequest() + .networkIdentifier(new NetworkIdentifier().network("localnet")) + .limit(1L) + .stateIdentifier(new PartialStateIdentifier().stateVersion(-1L)); + var response = handleRequestWithExpectedResponse(sut, request, UnexpectedError.class); + + // Assert + assertThat(response.getDetails()).isInstanceOf(InvalidPartialStateIdentifierError.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/AddressBookHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/AddressBookHandlerTest.java index b1522049ce..3690eaf7b0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/AddressBookHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/AddressBookHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,6 +64,8 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.openapitools.model.Address; @@ -73,45 +76,38 @@ import com.radixdlt.networks.Addressing; import com.radixdlt.networks.NetworkId; import com.radixdlt.utils.PrivateKeys; -import org.junit.Test; - import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class AddressBookHandlerTest extends ApiTest { - @Inject - @NetworkId - private int networkId; - @Inject - private AddressBookHandler sut; - @Inject - private AddressBook addressBook; - @Inject - private Addressing addressing; + @Inject @NetworkId private int networkId; + @Inject private AddressBookHandler sut; + @Inject private AddressBook addressBook; + @Inject private Addressing addressing; - @Test - public void can_retrieve_address_book() throws Exception { - // Arrange - var peerKey = PrivateKeys.ofNumeric(2).getPublicKey(); - var peerUri = RadixNodeUri.fromPubKeyAndAddress(networkId, peerKey, "localhost", 12345); - addressBook.addUncheckedPeers(Set.of(peerUri)); - start(); + @Test + public void can_retrieve_address_book() throws Exception { + // Arrange + var peerKey = PrivateKeys.ofNumeric(2).getPublicKey(); + var peerUri = RadixNodeUri.fromPubKeyAndAddress(networkId, peerKey, "localhost", 12345); + addressBook.addUncheckedPeers(Set.of(peerUri)); + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, SystemAddressBookResponse.class); + // Act + var response = handleRequestWithExpectedResponse(sut, SystemAddressBookResponse.class); - // Assert - var entries = response.getEntries(); - assertThat(entries).containsExactly(new AddressBookEntry() - .peerId(addressing.forNodes().of(peerKey)) - .banned(false) - .bannedUntil(null) - .addKnownAddressesItem(new Address() - .uri(peerUri.toString()) - .blacklisted(false) - .lastConnectionStatus(Address.LastConnectionStatusEnum.UNKNOWN) - ) - ); - } + // Assert + var entries = response.getEntries(); + assertThat(entries) + .containsExactly( + new AddressBookEntry() + .peerId(addressing.forNodes().of(peerKey)) + .banned(false) + .bannedUntil(null) + .addKnownAddressesItem( + new Address() + .uri(peerUri.toString()) + .blacklisted(false) + .lastConnectionStatus(Address.LastConnectionStatusEnum.UNKNOWN))); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/ConfigurationHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/ConfigurationHandlerTest.java index 285a1f8c6e..ca8e874b65 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/ConfigurationHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/ConfigurationHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,26 +64,25 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.openapitools.model.SystemConfigurationResponse; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class ConfigurationHandlerTest extends ApiTest { - @Inject - private ConfigurationHandler sut; + @Inject private ConfigurationHandler sut; - @Test - public void can_retrieve_configuration() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_configuration() throws Exception { + // Arrange + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, SystemConfigurationResponse.class); + // Act + var response = handleRequestWithExpectedResponse(sut, SystemConfigurationResponse.class); - // Assert - assertThat(response.getBft()).isNotNull(); - } + // Assert + assertThat(response.getBft()).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthHandlerTest.java index a99888d4c9..474d7a022d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,26 +64,25 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.openapitools.model.HealthResponse; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class HealthHandlerTest extends ApiTest { - @Inject - private HealthHandler sut; + @Inject private HealthHandler sut; - @Test - public void can_retrieve_peers() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_peers() throws Exception { + // Arrange + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, HealthResponse.class); + // Act + var response = handleRequestWithExpectedResponse(sut, HealthResponse.class); - // Assert - assertThat(response.getStatus()).isNotNull(); - } + // Assert + assertThat(response.getStatus()).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthInfoServiceTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthInfoServiceTest.java index 37f08eba89..6c1a1e7dd0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthInfoServiceTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/HealthInfoServiceTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -60,67 +61,72 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.api.system; +import static com.radixdlt.api.system.health.HealthInfoService.LEDGER_KEY; +import static com.radixdlt.api.system.health.HealthInfoService.TARGET_KEY; +import static com.radixdlt.api.system.health.NodeStatus.BOOTING; +import static com.radixdlt.api.system.health.NodeStatus.STALLED; +import static com.radixdlt.api.system.health.NodeStatus.SYNCING; +import static com.radixdlt.api.system.health.NodeStatus.UP; +import static com.radixdlt.counters.SystemCounters.CounterType; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + import com.radixdlt.api.system.health.HealthInfoService; import com.radixdlt.api.system.health.ScheduledStatsCollecting; -import org.junit.Test; - import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; import com.radixdlt.environment.ScheduledEventDispatcher; - import java.util.stream.IntStream; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -import static com.radixdlt.api.system.health.NodeStatus.BOOTING; -import static com.radixdlt.api.system.health.NodeStatus.STALLED; -import static com.radixdlt.api.system.health.NodeStatus.SYNCING; -import static com.radixdlt.api.system.health.NodeStatus.UP; -import static com.radixdlt.api.system.health.HealthInfoService.LEDGER_KEY; -import static com.radixdlt.api.system.health.HealthInfoService.TARGET_KEY; -import static com.radixdlt.counters.SystemCounters.CounterType; +import org.junit.Test; public class HealthInfoServiceTest { - @SuppressWarnings("unchecked") - private final ScheduledEventDispatcher dispatcher = mock(ScheduledEventDispatcher.class); - private final SystemCounters systemCounters = new SystemCountersImpl(); - private final HealthInfoService healthInfoService = new HealthInfoService(systemCounters, dispatcher); + @SuppressWarnings("unchecked") + private final ScheduledEventDispatcher dispatcher = + mock(ScheduledEventDispatcher.class); + + private final SystemCounters systemCounters = new SystemCountersImpl(); + private final HealthInfoService healthInfoService = + new HealthInfoService(systemCounters, dispatcher); - @Test - public void testNodeStatus() { - assertEquals(BOOTING, healthInfoService.nodeStatus()); + @Test + public void testNodeStatus() { + assertEquals(BOOTING, healthInfoService.nodeStatus()); - updateStatsSync(10, TARGET_KEY, 5, LEDGER_KEY); + updateStatsSync(10, TARGET_KEY, 5, LEDGER_KEY); - assertEquals(SYNCING, healthInfoService.nodeStatus()); + assertEquals(SYNCING, healthInfoService.nodeStatus()); - updateStatsSync(10, TARGET_KEY, 15, LEDGER_KEY); + updateStatsSync(10, TARGET_KEY, 15, LEDGER_KEY); - assertEquals(UP, healthInfoService.nodeStatus()); + assertEquals(UP, healthInfoService.nodeStatus()); - updateStatsSync(10, TARGET_KEY, 0, LEDGER_KEY); + updateStatsSync(10, TARGET_KEY, 0, LEDGER_KEY); - assertEquals(STALLED, healthInfoService.nodeStatus()); - } + assertEquals(STALLED, healthInfoService.nodeStatus()); + } - private void updateStatsSync(int count1, CounterType key1, int count2, CounterType key2) { - var count = Math.min(count1, count2); + private void updateStatsSync(int count1, CounterType key1, int count2, CounterType key2) { + var count = Math.min(count1, count2); - IntStream.range(0, count).forEach(__ -> { - systemCounters.increment(key1); - systemCounters.increment(key2); - healthInfoService.updateStats().process(ScheduledStatsCollecting.create()); - }); + IntStream.range(0, count) + .forEach( + __ -> { + systemCounters.increment(key1); + systemCounters.increment(key2); + healthInfoService.updateStats().process(ScheduledStatsCollecting.create()); + }); - var remaining = Math.max(count1, count2) - count; - var key = count1 > count2 ? key1 : key2; + var remaining = Math.max(count1, count2) - count; + var key = count1 > count2 ? key1 : key2; - IntStream.range(0, remaining).forEach(__ -> { - systemCounters.increment(key); - healthInfoService.updateStats().process(ScheduledStatsCollecting.create()); - }); - } + IntStream.range(0, remaining) + .forEach( + __ -> { + systemCounters.increment(key); + healthInfoService.updateStats().process(ScheduledStatsCollecting.create()); + }); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MetricsHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MetricsHandlerTest.java index 2c5ce955b8..1cdc9e8461 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MetricsHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MetricsHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,27 +64,25 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.openapitools.model.SystemMetricsResponse; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class MetricsHandlerTest extends ApiTest { - @Inject - private MetricsHandler sut; - + @Inject private MetricsHandler sut; - @Test - public void can_retrieve_metrics() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_metrics() throws Exception { + // Arrange + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, SystemMetricsResponse.class); + // Act + var response = handleRequestWithExpectedResponse(sut, SystemMetricsResponse.class); - // Assert - assertThat(response.getBft()).isNotNull(); - } + // Assert + assertThat(response.getBft()).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MovingAverageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MovingAverageTest.java index 90e7ff6a48..401b5e0cda 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MovingAverageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/MovingAverageTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -60,127 +61,128 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.api.system; +import static org.junit.Assert.assertEquals; + import com.radixdlt.api.system.health.MovingAverage; import org.junit.Test; -import static org.junit.Assert.assertEquals; - public class MovingAverageTest { - @Test - public void emptyAverageIsZero() { - assertEquals(0, MovingAverage.create(2).asLong()); - } + @Test + public void emptyAverageIsZero() { + assertEquals(0, MovingAverage.create(2).asLong()); + } - @Test - public void addingValuesUpdatesAverage() { - var avg = MovingAverage.create(5); + @Test + public void addingValuesUpdatesAverage() { + var avg = MovingAverage.create(5); - assertEquals(0, avg.asInteger()); - assertEquals(10, avg.update(10).asInteger()); - assertEquals(10, avg.update(10).asInteger()); - assertEquals(10, avg.update(10).asInteger()); - assertEquals(10, avg.update(10).asInteger()); - assertEquals(10, avg.update(10).asInteger()); - assertEquals(12, avg.update(20).asInteger()); - assertEquals(13, avg.update(20).asInteger()); - assertEquals(14, avg.update(20).asInteger()); - assertEquals(17, avg.update(30).asInteger()); - assertEquals(20, avg.update(30).asInteger()); - assertEquals(20, avg.update(20).asInteger()); - assertEquals(18, avg.update(10).asInteger()); - assertEquals(14, avg.update(0).asInteger()); - assertEquals(11, avg.update(0).asInteger()); - assertEquals(9, avg.update(0).asInteger()); - assertEquals(7, avg.update(0).asInteger()); - assertEquals(5, avg.update(0).asInteger()); - assertEquals(4, avg.update(0).asInteger()); - assertEquals(3, avg.update(0).asInteger()); - assertEquals(3, avg.update(0).asInteger()); - assertEquals(2, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(0, avg.update(0).asInteger()); - } + assertEquals(0, avg.asInteger()); + assertEquals(10, avg.update(10).asInteger()); + assertEquals(10, avg.update(10).asInteger()); + assertEquals(10, avg.update(10).asInteger()); + assertEquals(10, avg.update(10).asInteger()); + assertEquals(10, avg.update(10).asInteger()); + assertEquals(12, avg.update(20).asInteger()); + assertEquals(13, avg.update(20).asInteger()); + assertEquals(14, avg.update(20).asInteger()); + assertEquals(17, avg.update(30).asInteger()); + assertEquals(20, avg.update(30).asInteger()); + assertEquals(20, avg.update(20).asInteger()); + assertEquals(18, avg.update(10).asInteger()); + assertEquals(14, avg.update(0).asInteger()); + assertEquals(11, avg.update(0).asInteger()); + assertEquals(9, avg.update(0).asInteger()); + assertEquals(7, avg.update(0).asInteger()); + assertEquals(5, avg.update(0).asInteger()); + assertEquals(4, avg.update(0).asInteger()); + assertEquals(3, avg.update(0).asInteger()); + assertEquals(3, avg.update(0).asInteger()); + assertEquals(2, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(0, avg.update(0).asInteger()); + } - @Test - public void addingValuesUpdatesAverage1() { - var avg = MovingAverage.create(10); + @Test + public void addingValuesUpdatesAverage1() { + var avg = MovingAverage.create(10); - assertEquals(0, avg.asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(315, avg.update(315).asInteger()); - assertEquals(283, avg.update(0).asInteger()); - assertEquals(255, avg.update(0).asInteger()); - assertEquals(229, avg.update(0).asInteger()); - assertEquals(206, avg.update(0).asInteger()); - assertEquals(186, avg.update(0).asInteger()); - assertEquals(167, avg.update(0).asInteger()); - assertEquals(150, avg.update(0).asInteger()); - assertEquals(135, avg.update(0).asInteger()); - assertEquals(122, avg.update(0).asInteger()); - assertEquals(109, avg.update(0).asInteger()); - assertEquals(98, avg.update(0).asInteger()); - assertEquals(88, avg.update(0).asInteger()); - assertEquals(80, avg.update(0).asInteger()); - assertEquals(72, avg.update(0).asInteger()); - assertEquals(64, avg.update(0).asInteger()); - assertEquals(58, avg.update(0).asInteger()); - assertEquals(52, avg.update(0).asInteger()); - assertEquals(47, avg.update(0).asInteger()); - assertEquals(42, avg.update(0).asInteger()); - assertEquals(38, avg.update(0).asInteger()); - assertEquals(34, avg.update(0).asInteger()); - assertEquals(31, avg.update(0).asInteger()); - assertEquals(27, avg.update(0).asInteger()); - assertEquals(25, avg.update(0).asInteger()); - assertEquals(22, avg.update(0).asInteger()); - assertEquals(20, avg.update(0).asInteger()); - assertEquals(18, avg.update(0).asInteger()); - assertEquals(16, avg.update(0).asInteger()); - assertEquals(14, avg.update(0).asInteger()); - assertEquals(13, avg.update(0).asInteger()); - assertEquals(12, avg.update(0).asInteger()); - assertEquals(10, avg.update(0).asInteger()); - assertEquals(9, avg.update(0).asInteger()); - assertEquals(8, avg.update(0).asInteger()); - assertEquals(7, avg.update(0).asInteger()); - assertEquals(7, avg.update(0).asInteger()); - assertEquals(6, avg.update(0).asInteger()); - assertEquals(5, avg.update(0).asInteger()); - assertEquals(5, avg.update(0).asInteger()); - assertEquals(4, avg.update(0).asInteger()); - assertEquals(4, avg.update(0).asInteger()); - assertEquals(3, avg.update(0).asInteger()); - assertEquals(3, avg.update(0).asInteger()); - assertEquals(3, avg.update(0).asInteger()); - assertEquals(2, avg.update(0).asInteger()); - assertEquals(2, avg.update(0).asInteger()); - assertEquals(2, avg.update(0).asInteger()); - assertEquals(2, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(1, avg.update(0).asInteger()); - assertEquals(0, avg.update(0).asInteger()); - } + assertEquals(0, avg.asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(315, avg.update(315).asInteger()); + assertEquals(283, avg.update(0).asInteger()); + assertEquals(255, avg.update(0).asInteger()); + assertEquals(229, avg.update(0).asInteger()); + assertEquals(206, avg.update(0).asInteger()); + assertEquals(186, avg.update(0).asInteger()); + assertEquals(167, avg.update(0).asInteger()); + assertEquals(150, avg.update(0).asInteger()); + assertEquals(135, avg.update(0).asInteger()); + assertEquals(122, avg.update(0).asInteger()); + assertEquals(109, avg.update(0).asInteger()); + assertEquals(98, avg.update(0).asInteger()); + assertEquals(88, avg.update(0).asInteger()); + assertEquals(80, avg.update(0).asInteger()); + assertEquals(72, avg.update(0).asInteger()); + assertEquals(64, avg.update(0).asInteger()); + assertEquals(58, avg.update(0).asInteger()); + assertEquals(52, avg.update(0).asInteger()); + assertEquals(47, avg.update(0).asInteger()); + assertEquals(42, avg.update(0).asInteger()); + assertEquals(38, avg.update(0).asInteger()); + assertEquals(34, avg.update(0).asInteger()); + assertEquals(31, avg.update(0).asInteger()); + assertEquals(27, avg.update(0).asInteger()); + assertEquals(25, avg.update(0).asInteger()); + assertEquals(22, avg.update(0).asInteger()); + assertEquals(20, avg.update(0).asInteger()); + assertEquals(18, avg.update(0).asInteger()); + assertEquals(16, avg.update(0).asInteger()); + assertEquals(14, avg.update(0).asInteger()); + assertEquals(13, avg.update(0).asInteger()); + assertEquals(12, avg.update(0).asInteger()); + assertEquals(10, avg.update(0).asInteger()); + assertEquals(9, avg.update(0).asInteger()); + assertEquals(8, avg.update(0).asInteger()); + assertEquals(7, avg.update(0).asInteger()); + assertEquals(7, avg.update(0).asInteger()); + assertEquals(6, avg.update(0).asInteger()); + assertEquals(5, avg.update(0).asInteger()); + assertEquals(5, avg.update(0).asInteger()); + assertEquals(4, avg.update(0).asInteger()); + assertEquals(4, avg.update(0).asInteger()); + assertEquals(3, avg.update(0).asInteger()); + assertEquals(3, avg.update(0).asInteger()); + assertEquals(3, avg.update(0).asInteger()); + assertEquals(2, avg.update(0).asInteger()); + assertEquals(2, avg.update(0).asInteger()); + assertEquals(2, avg.update(0).asInteger()); + assertEquals(2, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(1, avg.update(0).asInteger()); + assertEquals(0, avg.update(0).asInteger()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PeersHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PeersHandlerTest.java index 7ee0875517..758f7b702e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PeersHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PeersHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,26 +64,25 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.openapitools.model.SystemPeersResponse; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class PeersHandlerTest extends ApiTest { - @Inject - private PeersHandler sut; + @Inject private PeersHandler sut; - @Test - public void can_retrieve_peers() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_peers() throws Exception { + // Arrange + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, SystemPeersResponse.class); + // Act + var response = handleRequestWithExpectedResponse(sut, SystemPeersResponse.class); - // Assert - assertThat(response.getPeers()).isNotNull(); - } + // Assert + assertThat(response.getPeers()).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PrometheusHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PrometheusHandlerTest.java index 48d25ec70c..bb6689e5ad 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PrometheusHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/PrometheusHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,26 +64,25 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.prometheus.PrometheusHandler; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class PrometheusHandlerTest extends ApiTest { - @Inject - private PrometheusHandler sut; + @Inject private PrometheusHandler sut; - @Test - public void can_retrieve_metrics() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_metrics() throws Exception { + // Arrange + start(); - // Act - var response = handleRequest(sut); + // Act + var response = handleRequest(sut); - // Assert - assertThat(response).isNotEmpty(); - } + // Assert + assertThat(response).isNotEmpty(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/VersionHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/VersionHandlerTest.java index 25d7c388ef..02cdd4cae5 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/VersionHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/api/system/VersionHandlerTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,26 +64,25 @@ package com.radixdlt.api.system; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.inject.Inject; import com.radixdlt.api.ApiTest; import com.radixdlt.api.system.openapitools.model.VersionResponse; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class VersionHandlerTest extends ApiTest { - @Inject - private VersionHandler sut; + @Inject private VersionHandler sut; - @Test - public void can_retrieve_version() throws Exception { - // Arrange - start(); + @Test + public void can_retrieve_version() throws Exception { + // Arrange + start(); - // Act - var response = handleRequestWithExpectedResponse(sut, VersionResponse.class); + // Act + var response = handleRequestWithExpectedResponse(sut, VersionResponse.class); - // Assert - assertThat(response.getVersion()).isNotNull(); - } + // Assert + assertThat(response.getVersion()).isNotNull(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTConfigurationTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTConfigurationTest.java index 35af49b116..aced32a7a4 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTConfigurationTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTConfigurationTest.java @@ -64,18 +64,17 @@ package com.radixdlt.consensus; -import nl.jqno.equalsverifier.EqualsVerifier; - import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; +import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; public class BFTConfigurationTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTConfiguration.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTConfiguration.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTHeaderTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTHeaderTest.java index 60bec0c672..0e6ae7e8db 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTHeaderTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/BFTHeaderTest.java @@ -64,6 +64,9 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.HashUtils; @@ -71,54 +74,51 @@ import org.junit.Before; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - public class BFTHeaderTest { - private BFTHeader testObject; - private HashCode id; - private LedgerHeader ledgerHeader; + private BFTHeader testObject; + private HashCode id; + private LedgerHeader ledgerHeader; - @Before - public void setUp() { - View view = View.of(1234567890L); - this.id = HashUtils.random256(); - this.ledgerHeader = mock(LedgerHeader.class); - this.testObject = new BFTHeader(view, id, ledgerHeader); - } + @Before + public void setUp() { + View view = View.of(1234567890L); + this.id = HashUtils.random256(); + this.ledgerHeader = mock(LedgerHeader.class); + this.testObject = new BFTHeader(view, id, ledgerHeader); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTHeader.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTHeader.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void testGetters() { - assertThat(View.of(1234567890L)).isEqualTo(this.testObject.getView()); + @Test + public void testGetters() { + assertThat(View.of(1234567890L)).isEqualTo(this.testObject.getView()); - assertThat(id).isEqualTo(this.testObject.getVertexId()); - assertThat(ledgerHeader).isEqualTo(this.testObject.getLedgerHeader()); - } + assertThat(id).isEqualTo(this.testObject.getVertexId()); + assertThat(ledgerHeader).isEqualTo(this.testObject.getLedgerHeader()); + } - @Test - public void testToString() { - assertThat(this.testObject.toString()).contains(BFTHeader.class.getSimpleName()); - } + @Test + public void testToString() { + assertThat(this.testObject.toString()).contains(BFTHeader.class.getSimpleName()); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - BFTHeader.create(1L, null, mock(LedgerHeader.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + BFTHeader.create(1L, null, mock(LedgerHeader.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - BFTHeader.create(1L, mock(HashCode.class), null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + BFTHeader.create(1L, mock(HashCode.class), null); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidViewThrowsException() { - BFTHeader.create(-1L, mock(HashCode.class), mock(LedgerHeader.class)); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidViewThrowsException() { + BFTHeader.create(-1L, mock(HashCode.class), mock(LedgerHeader.class)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/HighQCTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/HighQCTest.java index 8a29fb3c37..1950f2ad6f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/HighQCTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/HighQCTest.java @@ -1,148 +1,147 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus; - -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.crypto.HashUtils; -import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Test; -import org.radix.serialization.SerializeObject; - -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -public class HighQCTest extends SerializeObject { - public HighQCTest() { - super(HighQC.class, HighQCTest::get); - } - - private static HighQC get() { - View view = View.of(1234567891L); - HashCode id = HashUtils.random256(); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - BFTHeader header = new BFTHeader(view, id, ledgerHeader); - BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); - BFTHeader commit = new BFTHeader(View.of(1234567889L), HashUtils.random256(), ledgerHeader); - VoteData voteData = new VoteData(header, parent, commit); - QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - return HighQC.from(qc, qc, Optional.empty()); - } - - @Test - public void when_created_with_equal_qcs__highest_committed_is_elided() { - QuorumCertificate qc = mock(QuorumCertificate.class); - HighQC highQC = HighQC.from(qc, qc, Optional.empty()); - QuorumCertificate storedCommitQC = highQC.rawHighestCommittedQC(); - assertThat(storedCommitQC).isNull(); - assertThat(highQC.highestQC()).isEqualTo(qc); - assertThat(highQC.highestCommittedQC()).isEqualTo(qc); - } - - @Test - public void when_created_with_unequal_qcs__highest_committed_is_stored() { - QuorumCertificate qc = mock(QuorumCertificate.class); - QuorumCertificate cqc = mock(QuorumCertificate.class); - HighQC highQC = HighQC.from(qc, cqc, Optional.empty()); - QuorumCertificate storedCommitQC = highQC.rawHighestCommittedQC(); - assertThat(storedCommitQC).isEqualTo(cqc); - assertThat(highQC.highestQC()).isEqualTo(qc); - assertThat(highQC.highestCommittedQC()).isEqualTo(cqc); - } - - @Test - public void sensibleToString() { - QuorumCertificate qc = mock(QuorumCertificate.class); - QuorumCertificate cqc = mock(QuorumCertificate.class); - HighQC highQC1 = HighQC.from(qc, cqc, Optional.empty()); - - String s1 = highQC1.toString(); - assertThat(s1) - .contains(HighQC.class.getSimpleName()) - .contains(qc.toString()) - .contains(cqc.toString()); - - HighQC highQC2 = HighQC.from(qc, qc, Optional.empty()); - String s2 = highQC2.toString(); - assertThat(s2) - .contains(HighQC.class.getSimpleName()) - .contains(qc.toString()) - .contains(""); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(HighQC.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - HighQC.serializerCreate(null, mock(QuorumCertificate.class), mock(TimeoutCertificate.class)); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import com.google.common.hash.HashCode; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.crypto.HashUtils; +import java.util.Optional; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; +import org.radix.serialization.SerializeObject; + +public class HighQCTest extends SerializeObject { + public HighQCTest() { + super(HighQC.class, HighQCTest::get); + } + + private static HighQC get() { + View view = View.of(1234567891L); + HashCode id = HashUtils.random256(); + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + BFTHeader header = new BFTHeader(view, id, ledgerHeader); + BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); + BFTHeader commit = new BFTHeader(View.of(1234567889L), HashUtils.random256(), ledgerHeader); + VoteData voteData = new VoteData(header, parent, commit); + QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + return HighQC.from(qc, qc, Optional.empty()); + } + + @Test + public void when_created_with_equal_qcs__highest_committed_is_elided() { + QuorumCertificate qc = mock(QuorumCertificate.class); + HighQC highQC = HighQC.from(qc, qc, Optional.empty()); + QuorumCertificate storedCommitQC = highQC.rawHighestCommittedQC(); + assertThat(storedCommitQC).isNull(); + assertThat(highQC.highestQC()).isEqualTo(qc); + assertThat(highQC.highestCommittedQC()).isEqualTo(qc); + } + + @Test + public void when_created_with_unequal_qcs__highest_committed_is_stored() { + QuorumCertificate qc = mock(QuorumCertificate.class); + QuorumCertificate cqc = mock(QuorumCertificate.class); + HighQC highQC = HighQC.from(qc, cqc, Optional.empty()); + QuorumCertificate storedCommitQC = highQC.rawHighestCommittedQC(); + assertThat(storedCommitQC).isEqualTo(cqc); + assertThat(highQC.highestQC()).isEqualTo(qc); + assertThat(highQC.highestCommittedQC()).isEqualTo(cqc); + } + + @Test + public void sensibleToString() { + QuorumCertificate qc = mock(QuorumCertificate.class); + QuorumCertificate cqc = mock(QuorumCertificate.class); + HighQC highQC1 = HighQC.from(qc, cqc, Optional.empty()); + + String s1 = highQC1.toString(); + assertThat(s1) + .contains(HighQC.class.getSimpleName()) + .contains(qc.toString()) + .contains(cqc.toString()); + + HighQC highQC2 = HighQC.from(qc, qc, Optional.empty()); + String s2 = highQC2.toString(); + assertThat(s2) + .contains(HighQC.class.getSimpleName()) + .contains(qc.toString()) + .contains(""); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(HighQC.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } + + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + HighQC.serializerCreate(null, mock(QuorumCertificate.class), mock(TimeoutCertificate.class)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerHeaderTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerHeaderTest.java index 7a32608fa9..32c0bd1037 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerHeaderTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerHeaderTest.java @@ -64,65 +64,63 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import com.google.common.collect.ImmutableSet; import com.google.common.hash.HashCode; import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.HashUtils; import com.radixdlt.ledger.AccumulatorState; - import nl.jqno.equalsverifier.EqualsVerifier; - import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Before; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - public class LedgerHeaderTest { - private LedgerHeader ledgerHeader; - private long timestamp; - private AccumulatorState accumulatorState; + private LedgerHeader ledgerHeader; + private long timestamp; + private AccumulatorState accumulatorState; - @Before - public void setup() { - this.timestamp = 12345678L; - this.accumulatorState = mock(AccumulatorState.class); - this.ledgerHeader = LedgerHeader.create(0, View.genesis(), accumulatorState, timestamp); - } + @Before + public void setup() { + this.timestamp = 12345678L; + this.accumulatorState = mock(AccumulatorState.class); + this.ledgerHeader = LedgerHeader.create(0, View.genesis(), accumulatorState, timestamp); + } - @Test - public void testGetters() { - assertThat(ledgerHeader.getAccumulatorState()).isEqualTo(accumulatorState); - assertThat(ledgerHeader.timestamp()).isEqualTo(timestamp); - assertThat(ledgerHeader.isEndOfEpoch()).isFalse(); - } + @Test + public void testGetters() { + assertThat(ledgerHeader.getAccumulatorState()).isEqualTo(accumulatorState); + assertThat(ledgerHeader.timestamp()).isEqualTo(timestamp); + assertThat(ledgerHeader.isEndOfEpoch()).isFalse(); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(LedgerHeader.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(LedgerHeader.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void sensibleToString() { - String s = this.ledgerHeader.toString(); - AssertionsForClassTypes.assertThat(s).contains("12345"); - } + @Test + public void sensibleToString() { + String s = this.ledgerHeader.toString(); + AssertionsForClassTypes.assertThat(s).contains("12345"); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithWrongEpochThrowsException() { - new LedgerHeader(-1L, 1L, mock(AccumulatorState.class), 1L, ImmutableSet.of()); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithWrongEpochThrowsException() { + new LedgerHeader(-1L, 1L, mock(AccumulatorState.class), 1L, ImmutableSet.of()); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithWrongViewThrowsException() { - new LedgerHeader(1L, -1L, mock(AccumulatorState.class), 1L, ImmutableSet.of()); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithWrongViewThrowsException() { + new LedgerHeader(1L, -1L, mock(AccumulatorState.class), 1L, ImmutableSet.of()); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullAccumulatorStateThrowsException() { - new LedgerHeader(1L, 1L, null, 1L, ImmutableSet.of()); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullAccumulatorStateThrowsException() { + new LedgerHeader(1L, 1L, null, 1L, ImmutableSet.of()); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerProofTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerProofTest.java index b00777f81d..79efab7735 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerProofTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/LedgerProofTest.java @@ -64,6 +64,10 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.LedgerProof.OrderByEpochAndVersionComparator; import com.radixdlt.consensus.bft.View; @@ -73,164 +77,132 @@ import org.junit.Before; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class LedgerProofTest { - private OrderByEpochAndVersionComparator headerComparator; + private OrderByEpochAndVersionComparator headerComparator; - @Before - public void setup() { - this.headerComparator = new OrderByEpochAndVersionComparator(); - } + @Before + public void setup() { + this.headerComparator = new OrderByEpochAndVersionComparator(); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(LedgerProof.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(LedgerProof.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void testGetters() { - LedgerHeader l0 = mock(LedgerHeader.class); - HashCode accumulatorHash = mock(HashCode.class); - View view = mock(View.class); - when(l0.getEpoch()).thenReturn(3L); - AccumulatorState accumulatorState = mock(AccumulatorState.class); - when(accumulatorState.getAccumulatorHash()).thenReturn(accumulatorHash); - when(accumulatorState.getStateVersion()).thenReturn(12345L); - when(l0.getAccumulatorState()).thenReturn(accumulatorState); - when(l0.getView()).thenReturn(view); - when(l0.timestamp()).thenReturn(2468L); - when(l0.isEndOfEpoch()).thenReturn(true); - var ledgerHeaderAndProof = new LedgerProof( - HashUtils.random256(), - l0, - mock(TimestampedECDSASignatures.class) - ); - assertThat(ledgerHeaderAndProof.getEpoch()).isEqualTo(3L); - assertThat(ledgerHeaderAndProof.getStateVersion()).isEqualTo(12345L); - assertThat(ledgerHeaderAndProof.getView()).isEqualTo(view); - assertThat(ledgerHeaderAndProof.timestamp()).isEqualTo(2468L); - assertThat(ledgerHeaderAndProof.isEndOfEpoch()).isTrue(); - } + @Test + public void testGetters() { + LedgerHeader l0 = mock(LedgerHeader.class); + HashCode accumulatorHash = mock(HashCode.class); + View view = mock(View.class); + when(l0.getEpoch()).thenReturn(3L); + AccumulatorState accumulatorState = mock(AccumulatorState.class); + when(accumulatorState.getAccumulatorHash()).thenReturn(accumulatorHash); + when(accumulatorState.getStateVersion()).thenReturn(12345L); + when(l0.getAccumulatorState()).thenReturn(accumulatorState); + when(l0.getView()).thenReturn(view); + when(l0.timestamp()).thenReturn(2468L); + when(l0.isEndOfEpoch()).thenReturn(true); + var ledgerHeaderAndProof = + new LedgerProof(HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class)); + assertThat(ledgerHeaderAndProof.getEpoch()).isEqualTo(3L); + assertThat(ledgerHeaderAndProof.getStateVersion()).isEqualTo(12345L); + assertThat(ledgerHeaderAndProof.getView()).isEqualTo(view); + assertThat(ledgerHeaderAndProof.timestamp()).isEqualTo(2468L); + assertThat(ledgerHeaderAndProof.isEndOfEpoch()).isTrue(); + } - @Test - public void testComparsionBetweenDifferentEpochs() { - LedgerHeader l0 = mock(LedgerHeader.class); - when(l0.getEpoch()).thenReturn(1L); - var s0 = new LedgerProof( - HashUtils.random256(), - l0, - mock(TimestampedECDSASignatures.class) - ); - LedgerHeader l1 = mock(LedgerHeader.class); - when(l1.getEpoch()).thenReturn(2L); - LedgerProof s1 = new LedgerProof( - HashUtils.random256(), - l1, - mock(TimestampedECDSASignatures.class) - ); - assertThat(headerComparator.compare(s0, s1)).isNegative(); - assertThat(headerComparator.compare(s1, s0)).isPositive(); - } + @Test + public void testComparsionBetweenDifferentEpochs() { + LedgerHeader l0 = mock(LedgerHeader.class); + when(l0.getEpoch()).thenReturn(1L); + var s0 = new LedgerProof(HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class)); + LedgerHeader l1 = mock(LedgerHeader.class); + when(l1.getEpoch()).thenReturn(2L); + LedgerProof s1 = + new LedgerProof(HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class)); + assertThat(headerComparator.compare(s0, s1)).isNegative(); + assertThat(headerComparator.compare(s1, s0)).isPositive(); + } - @Test - public void testComparsionBetweenDifferentStateVersions() { - LedgerHeader l0 = mock(LedgerHeader.class); - when(l0.getEpoch()).thenReturn(2L); - AccumulatorState accumulatorState = mock(AccumulatorState.class); - when(accumulatorState.getStateVersion()).thenReturn(2L); - when(l0.getAccumulatorState()).thenReturn(accumulatorState); - LedgerProof s0 = new LedgerProof( - HashUtils.random256(), - l0, - mock(TimestampedECDSASignatures.class) - ); - LedgerHeader l1 = mock(LedgerHeader.class); - when(l1.getEpoch()).thenReturn(2L); - AccumulatorState accumulatorState1 = mock(AccumulatorState.class); - when(accumulatorState1.getStateVersion()).thenReturn(3L); - when(l1.getAccumulatorState()).thenReturn(accumulatorState1); - LedgerProof s1 = new LedgerProof( - HashUtils.random256(), - l1, - mock(TimestampedECDSASignatures.class) - ); - assertThat(headerComparator.compare(s0, s1)).isNegative(); - assertThat(headerComparator.compare(s1, s0)).isPositive(); - } + @Test + public void testComparsionBetweenDifferentStateVersions() { + LedgerHeader l0 = mock(LedgerHeader.class); + when(l0.getEpoch()).thenReturn(2L); + AccumulatorState accumulatorState = mock(AccumulatorState.class); + when(accumulatorState.getStateVersion()).thenReturn(2L); + when(l0.getAccumulatorState()).thenReturn(accumulatorState); + LedgerProof s0 = + new LedgerProof(HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class)); + LedgerHeader l1 = mock(LedgerHeader.class); + when(l1.getEpoch()).thenReturn(2L); + AccumulatorState accumulatorState1 = mock(AccumulatorState.class); + when(accumulatorState1.getStateVersion()).thenReturn(3L); + when(l1.getAccumulatorState()).thenReturn(accumulatorState1); + LedgerProof s1 = + new LedgerProof(HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class)); + assertThat(headerComparator.compare(s0, s1)).isNegative(); + assertThat(headerComparator.compare(s1, s0)).isPositive(); + } - @Test - public void testComparsionWithEndOfEpoch() { - LedgerHeader l0 = mock(LedgerHeader.class); - when(l0.getEpoch()).thenReturn(2L); - AccumulatorState accumulatorState = mock(AccumulatorState.class); - when(accumulatorState.getStateVersion()).thenReturn(2L); - when(l0.getAccumulatorState()).thenReturn(accumulatorState); - when(l0.isEndOfEpoch()).thenReturn(false); - LedgerProof s0 = new LedgerProof( - HashUtils.random256(), - l0, - mock(TimestampedECDSASignatures.class) - ); - LedgerHeader l1 = mock(LedgerHeader.class); - when(l1.getEpoch()).thenReturn(2L); - AccumulatorState accumulatorState1 = mock(AccumulatorState.class); - when(accumulatorState1.getStateVersion()).thenReturn(3L); - when(l1.getAccumulatorState()).thenReturn(accumulatorState1); - when(l1.isEndOfEpoch()).thenReturn(true); - LedgerProof s1 = new LedgerProof( - HashUtils.random256(), - l1, - mock(TimestampedECDSASignatures.class) - ); - assertThat(headerComparator.compare(s0, s1)).isNegative(); - assertThat(headerComparator.compare(s1, s0)).isPositive(); - } + @Test + public void testComparsionWithEndOfEpoch() { + LedgerHeader l0 = mock(LedgerHeader.class); + when(l0.getEpoch()).thenReturn(2L); + AccumulatorState accumulatorState = mock(AccumulatorState.class); + when(accumulatorState.getStateVersion()).thenReturn(2L); + when(l0.getAccumulatorState()).thenReturn(accumulatorState); + when(l0.isEndOfEpoch()).thenReturn(false); + LedgerProof s0 = + new LedgerProof(HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class)); + LedgerHeader l1 = mock(LedgerHeader.class); + when(l1.getEpoch()).thenReturn(2L); + AccumulatorState accumulatorState1 = mock(AccumulatorState.class); + when(accumulatorState1.getStateVersion()).thenReturn(3L); + when(l1.getAccumulatorState()).thenReturn(accumulatorState1); + when(l1.isEndOfEpoch()).thenReturn(true); + LedgerProof s1 = + new LedgerProof(HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class)); + assertThat(headerComparator.compare(s0, s1)).isNegative(); + assertThat(headerComparator.compare(s1, s0)).isPositive(); + } - @Test - public void testComparsionEqual() { - LedgerHeader l0 = mock(LedgerHeader.class); - when(l0.getEpoch()).thenReturn(2L); - AccumulatorState accumulatorState = mock(AccumulatorState.class); - when(accumulatorState.getStateVersion()).thenReturn(3L); - when(l0.getAccumulatorState()).thenReturn(accumulatorState); - when(l0.isEndOfEpoch()).thenReturn(true); - LedgerProof s0 = new LedgerProof( - HashUtils.random256(), - l0, - mock(TimestampedECDSASignatures.class) - ); - LedgerHeader l1 = mock(LedgerHeader.class); - when(l1.getEpoch()).thenReturn(2L); - AccumulatorState accumulatorState1 = mock(AccumulatorState.class); - when(accumulatorState1.getStateVersion()).thenReturn(3L); - when(l1.getAccumulatorState()).thenReturn(accumulatorState1); - when(l1.isEndOfEpoch()).thenReturn(true); - LedgerProof s1 = new LedgerProof( - HashUtils.random256(), - l1, - mock(TimestampedECDSASignatures.class) - ); - assertThat(headerComparator.compare(s0, s1)).isZero(); - assertThat(headerComparator.compare(s1, s0)).isZero(); - } + @Test + public void testComparsionEqual() { + LedgerHeader l0 = mock(LedgerHeader.class); + when(l0.getEpoch()).thenReturn(2L); + AccumulatorState accumulatorState = mock(AccumulatorState.class); + when(accumulatorState.getStateVersion()).thenReturn(3L); + when(l0.getAccumulatorState()).thenReturn(accumulatorState); + when(l0.isEndOfEpoch()).thenReturn(true); + LedgerProof s0 = + new LedgerProof(HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class)); + LedgerHeader l1 = mock(LedgerHeader.class); + when(l1.getEpoch()).thenReturn(2L); + AccumulatorState accumulatorState1 = mock(AccumulatorState.class); + when(accumulatorState1.getStateVersion()).thenReturn(3L); + when(l1.getAccumulatorState()).thenReturn(accumulatorState1); + when(l1.isEndOfEpoch()).thenReturn(true); + LedgerProof s1 = + new LedgerProof(HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class)); + assertThat(headerComparator.compare(s0, s1)).isZero(); + assertThat(headerComparator.compare(s1, s0)).isZero(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - new LedgerProof(null, mock(LedgerHeader.class), mock(TimestampedECDSASignatures.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + new LedgerProof(null, mock(LedgerHeader.class), mock(TimestampedECDSASignatures.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - new LedgerProof(mock(HashCode.class), null, mock(TimestampedECDSASignatures.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + new LedgerProof(mock(HashCode.class), null, mock(TimestampedECDSASignatures.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException3() { - new LedgerProof(mock(HashCode.class), mock(LedgerHeader.class), null); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException3() { + new LedgerProof(mock(HashCode.class), mock(LedgerHeader.class), null); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PacemakerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PacemakerTest.java index dc21ab26b8..81591e1d36 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PacemakerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PacemakerTest.java @@ -64,14 +64,7 @@ package com.radixdlt.consensus; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.utils.PrivateKeys; -import org.assertj.core.api.Condition; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.assertj.core.api.Assertions.assertThat; import com.google.inject.AbstractModule; import com.google.inject.Guice; @@ -79,6 +72,7 @@ import com.google.inject.Injector; import com.google.inject.TypeLiteral; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.bft.BFTInsertUpdate; import com.radixdlt.consensus.bft.ViewUpdate; import com.radixdlt.consensus.epoch.EpochViewUpdate; @@ -89,115 +83,143 @@ import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.mempool.MempoolConfig; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; - +import com.radixdlt.utils.PrivateKeys; import java.util.Set; +import org.assertj.core.api.Condition; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Verifies pacemaker functionality - */ +/** Verifies pacemaker functionality */ public class PacemakerTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Inject - private DeterministicProcessor processor; - - @Inject - private ViewUpdate initialViewUpdate; - - @Inject - private DeterministicNetwork network; - - private Injector createRunner() { - return Guice.createInjector( - MempoolConfig.asModule(10, 10), - new MainnetForkConfigsModule(), - new ForksModule(), - new RadixEngineForksLatestOnlyModule(), - new MockedGenesisModule( - Set.of(PrivateKeys.ofNumeric(1).getPublicKey()), - Amount.ofTokens(1000), - Amount.ofTokens(100) - ), - new SingleNodeAndPeersDeterministicNetworkModule(PrivateKeys.ofNumeric(1), 0), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } - - @Test - public void on_startup_pacemaker_should_schedule_timeouts() { - // Arrange - createRunner().injectMembers(this); - - // Act - processor.start(); - - // Assert - assertThat(network.allMessages()) - //.hasSize(3) // FIXME: Added hack to include a message regarding genesis committed so ignore this check - .haveExactly(1, - new Condition<>(msg -> Epoched.isInstance(msg.message(), ScheduledLocalTimeout.class), - "A single epoched scheduled timeout has been emitted")) - .haveExactly(1, - new Condition<>(msg -> msg.message() instanceof ScheduledLocalTimeout, - "A single scheduled timeout update has been emitted")) - .haveExactly(1, - new Condition<>(msg -> msg.message() instanceof Proposal, - "A proposal has been emitted")); - } - - @Test - public void on_timeout_pacemaker_should_send_vote_with_timeout() { - // Arrange - createRunner().injectMembers(this); - processor.start(); - - // Act - ControlledMessage timeoutMsg = network.nextMessage(e -> Epoched.isInstance(e.message(), ScheduledLocalTimeout.class)).value(); - processor.handleMessage(timeoutMsg.origin(), timeoutMsg.message(), new TypeLiteral>() { }); - ControlledMessage bftUpdateMsg = network.nextMessage(e -> e.message() instanceof BFTInsertUpdate).value(); - processor.handleMessage(bftUpdateMsg.origin(), bftUpdateMsg.message(), null); - - // Assert - assertThat(network.allMessages()) - .haveExactly(1, new Condition<>( - msg -> (msg.message() instanceof Vote) && ((Vote) msg.message()).isTimeout(), - "A remote timeout vote has been emitted")); - } - - @Test - public void on_view_timeout_quorum_pacemaker_should_move_to_next_view() { - // Arrange - createRunner().injectMembers(this); - processor.start(); - ControlledMessage timeoutMsg = network.nextMessage(e -> Epoched.isInstance(e.message(), ScheduledLocalTimeout.class)).value(); - processor.handleMessage(timeoutMsg.origin(), timeoutMsg.message(), new TypeLiteral>() { }); - ControlledMessage bftUpdateMsg = network.nextMessage(e -> e.message() instanceof BFTInsertUpdate).value(); - processor.handleMessage(bftUpdateMsg.origin(), bftUpdateMsg.message(), null); - - // Act - ControlledMessage viewTimeout = network.nextMessage(e -> - (e.message() instanceof Vote) && ((Vote) e.message()).isTimeout()).value(); - processor.handleMessage(viewTimeout.origin(), viewTimeout.message(), null); - - // Assert - assertThat(network.allMessages()) - .haveExactly(1, new Condition<>(msg -> msg.message() instanceof EpochViewUpdate, "A remote view timeout has been emitted")); - EpochViewUpdate nextEpochViewUpdate = network.allMessages().stream() - .filter(msg -> msg.message() instanceof EpochViewUpdate) - .map(ControlledMessage::message) - .map(EpochViewUpdate.class::cast) - .findAny() - .orElseThrow(); - assertThat(nextEpochViewUpdate.getViewUpdate().getCurrentView()).isEqualTo(initialViewUpdate.getCurrentView().next()); - } + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Inject private DeterministicProcessor processor; + + @Inject private ViewUpdate initialViewUpdate; + + @Inject private DeterministicNetwork network; + + private Injector createRunner() { + return Guice.createInjector( + MempoolConfig.asModule(10, 10), + new MainnetForkConfigsModule(), + new ForksModule(), + new RadixEngineForksLatestOnlyModule(), + new MockedGenesisModule( + Set.of(PrivateKeys.ofNumeric(1).getPublicKey()), + Amount.ofTokens(1000), + Amount.ofTokens(100)), + new SingleNodeAndPeersDeterministicNetworkModule(PrivateKeys.ofNumeric(1), 0), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } + + @Test + public void on_startup_pacemaker_should_schedule_timeouts() { + // Arrange + createRunner().injectMembers(this); + + // Act + processor.start(); + + // Assert + assertThat(network.allMessages()) + // .hasSize(3) // FIXME: Added hack to include a message regarding genesis committed so + // ignore this check + .haveExactly( + 1, + new Condition<>( + msg -> Epoched.isInstance(msg.message(), ScheduledLocalTimeout.class), + "A single epoched scheduled timeout has been emitted")) + .haveExactly( + 1, + new Condition<>( + msg -> msg.message() instanceof ScheduledLocalTimeout, + "A single scheduled timeout update has been emitted")) + .haveExactly( + 1, + new Condition<>( + msg -> msg.message() instanceof Proposal, "A proposal has been emitted")); + } + + @Test + public void on_timeout_pacemaker_should_send_vote_with_timeout() { + // Arrange + createRunner().injectMembers(this); + processor.start(); + + // Act + ControlledMessage timeoutMsg = + network + .nextMessage(e -> Epoched.isInstance(e.message(), ScheduledLocalTimeout.class)) + .value(); + processor.handleMessage( + timeoutMsg.origin(), + timeoutMsg.message(), + new TypeLiteral>() {}); + ControlledMessage bftUpdateMsg = + network.nextMessage(e -> e.message() instanceof BFTInsertUpdate).value(); + processor.handleMessage(bftUpdateMsg.origin(), bftUpdateMsg.message(), null); + + // Assert + assertThat(network.allMessages()) + .haveExactly( + 1, + new Condition<>( + msg -> (msg.message() instanceof Vote) && ((Vote) msg.message()).isTimeout(), + "A remote timeout vote has been emitted")); + } + + @Test + public void on_view_timeout_quorum_pacemaker_should_move_to_next_view() { + // Arrange + createRunner().injectMembers(this); + processor.start(); + ControlledMessage timeoutMsg = + network + .nextMessage(e -> Epoched.isInstance(e.message(), ScheduledLocalTimeout.class)) + .value(); + processor.handleMessage( + timeoutMsg.origin(), + timeoutMsg.message(), + new TypeLiteral>() {}); + ControlledMessage bftUpdateMsg = + network.nextMessage(e -> e.message() instanceof BFTInsertUpdate).value(); + processor.handleMessage(bftUpdateMsg.origin(), bftUpdateMsg.message(), null); + + // Act + ControlledMessage viewTimeout = + network + .nextMessage(e -> (e.message() instanceof Vote) && ((Vote) e.message()).isTimeout()) + .value(); + processor.handleMessage(viewTimeout.origin(), viewTimeout.message(), null); + + // Assert + assertThat(network.allMessages()) + .haveExactly( + 1, + new Condition<>( + msg -> msg.message() instanceof EpochViewUpdate, + "A remote view timeout has been emitted")); + EpochViewUpdate nextEpochViewUpdate = + network.allMessages().stream() + .filter(msg -> msg.message() instanceof EpochViewUpdate) + .map(ControlledMessage::message) + .map(EpochViewUpdate.class::cast) + .findAny() + .orElseThrow(); + assertThat(nextEpochViewUpdate.getViewUpdate().getCurrentView()) + .isEqualTo(initialViewUpdate.getCurrentView().next()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PendingVotesTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PendingVotesTest.java index 70dbb64889..5f590bc9c8 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PendingVotesTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/PendingVotesTest.java @@ -1,326 +1,311 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus; - -import org.junit.Before; -import org.junit.Test; - -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.BFTValidator; -import com.radixdlt.consensus.bft.BFTValidatorSet; -import com.radixdlt.consensus.bft.ValidationState; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.bft.ViewVotingResult; -import com.radixdlt.consensus.bft.VoteProcessingResult; -import com.radixdlt.consensus.bft.VoteProcessingResult.VoteRejected.VoteRejectedReason; -import com.radixdlt.crypto.ECDSASignature; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.crypto.Hasher; -import com.radixdlt.utils.RandomHasher; -import com.radixdlt.utils.UInt256; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Optional; - -import nl.jqno.equalsverifier.EqualsVerifier; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class PendingVotesTest { - private PendingVotes pendingVotes; - private Hasher hasher; - - @Before - public void setup() { - this.hasher = new RandomHasher(); - this.pendingVotes = new PendingVotes(hasher); - } - - @Test - public void equalsContractForPreviousVote() { - EqualsVerifier.forClass(PendingVotes.PreviousVote.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - - @Test - public void when_inserting_valid_but_unaccepted_votes__then_no_qc_is_returned() { - HashCode vertexId = HashUtils.random256(); - Vote vote1 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId); - Vote vote2 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId); - - BFTValidatorSet validatorSet = BFTValidatorSet.from( - Collections.singleton(BFTValidator.from(vote1.getAuthor(), UInt256.ONE)) - ); - VoteData voteData = mock(VoteData.class); - BFTHeader proposed = vote1.getVoteData().getProposed(); - when(voteData.getProposed()).thenReturn(proposed); - - assertEquals( - VoteProcessingResult.rejected(VoteRejectedReason.INVALID_AUTHOR), - this.pendingVotes.insertVote(vote2, validatorSet) - ); - } - - @Test - public void when_inserting_valid_and_accepted_votes__then_qc_is_formed() { - BFTNode author = mock(BFTNode.class); - Vote vote = makeSignedVoteFor(author, View.genesis(), HashUtils.random256()); - - BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); - ValidationState validationState = mock(ValidationState.class); - TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); - when(validationState.addSignature(any(), anyLong(), any())).thenReturn(true); - when(validationState.complete()).thenReturn(true); - when(validationState.signatures()).thenReturn(signatures); - when(validatorSet.newValidationState()).thenReturn(validationState); - when(validatorSet.containsNode(any())).thenReturn(true); - - VoteData voteData = mock(VoteData.class); - BFTHeader proposed = vote.getVoteData().getProposed(); - when(voteData.getProposed()).thenReturn(proposed); - - assertTrue( - this.pendingVotes.insertVote(vote, validatorSet) instanceof VoteProcessingResult.QuorumReached); - } - - @Test - public void when_inserting_valid_timeout_votes__then_tc_is_formed() { - HashCode vertexId1 = HashUtils.random256(); - HashCode vertexId2 = HashUtils.random256(); - Vote vote1 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId1); - when(vote1.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); - when(vote1.isTimeout()).thenReturn(true); - Vote vote2 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId2); - when(vote2.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); - when(vote2.isTimeout()).thenReturn(true); - - BFTValidatorSet validatorSet = BFTValidatorSet.from( - Arrays.asList( - BFTValidator.from(vote1.getAuthor(), UInt256.ONE), - BFTValidator.from(vote2.getAuthor(), UInt256.ONE) - ) - ); - - assertTrue( - this.pendingVotes.insertVote(vote1, validatorSet) instanceof VoteProcessingResult.VoteAccepted); - - VoteProcessingResult result2 = this.pendingVotes.insertVote(vote2, validatorSet); - - assertTrue(result2 instanceof VoteProcessingResult.QuorumReached); - - assertTrue(((VoteProcessingResult.QuorumReached) result2).getViewVotingResult() - instanceof ViewVotingResult.FormedTC); - } - - @Test - public void when_voting_again__previous_vote_is_removed() { - BFTNode author = mock(BFTNode.class); - Vote vote = makeSignedVoteFor(author, View.genesis(), HashUtils.random256()); - - BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); - ValidationState validationState = mock(ValidationState.class); - TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); - when(validationState.signatures()).thenReturn(signatures); - when(validationState.isEmpty()).thenReturn(true); - when(validatorSet.newValidationState()).thenReturn(validationState); - when(validatorSet.containsNode(any())).thenReturn(true); - - VoteData voteData = mock(VoteData.class); - BFTHeader proposed = vote.getVoteData().getProposed(); - when(voteData.getProposed()).thenReturn(proposed); - - // Preconditions - assertEquals( - VoteProcessingResult.accepted(), - this.pendingVotes.insertVote(vote, validatorSet) - ); - assertEquals(1, this.pendingVotes.voteStateSize()); - assertEquals(1, this.pendingVotes.previousVotesSize()); - - Vote vote2 = makeSignedVoteFor(author, View.of(1), HashUtils.random256()); - // Need a different hash for this (different) vote - assertEquals( - VoteProcessingResult.accepted(), - this.pendingVotes.insertVote(vote2, validatorSet) - ); - assertEquals(1, this.pendingVotes.voteStateSize()); - assertEquals(1, this.pendingVotes.previousVotesSize()); - } - - @Test - public void when_voting_again__previous_timeoutvote_is_removed() { - BFTNode author = mock(BFTNode.class); - Vote vote = makeSignedVoteFor(author, View.genesis(), HashUtils.random256()); - when(vote.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); - when(vote.isTimeout()).thenReturn(true); - - BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); - ValidationState validationState = mock(ValidationState.class); - TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); - when(validationState.signatures()).thenReturn(signatures); - when(validationState.isEmpty()).thenReturn(true); - when(validatorSet.newValidationState()).thenReturn(validationState); - when(validatorSet.containsNode(any())).thenReturn(true); - - VoteData voteData = mock(VoteData.class); - BFTHeader proposed = vote.getVoteData().getProposed(); - when(voteData.getProposed()).thenReturn(proposed); - - // Preconditions - assertEquals( - VoteProcessingResult.accepted(), - this.pendingVotes.insertVote(vote, validatorSet) - ); - assertEquals(1, this.pendingVotes.voteStateSize()); - assertEquals(1, this.pendingVotes.timeoutVoteStateSize()); - assertEquals(1, this.pendingVotes.previousVotesSize()); - - Vote vote2 = makeSignedVoteFor(author, View.of(1), HashUtils.random256()); - // Need a different hash for this (different) vote - assertEquals( - VoteProcessingResult.accepted(), - this.pendingVotes.insertVote(vote2, validatorSet) - ); - assertEquals(1, this.pendingVotes.voteStateSize()); - assertEquals(0, this.pendingVotes.timeoutVoteStateSize()); - assertEquals(1, this.pendingVotes.previousVotesSize()); - } - - @Test - public void when_submitting_a_duplicate_vote__then_can_be_replaced_if_has_timeout() { - final var vertexId1 = HashUtils.random256(); - final var vertexId2 = HashUtils.random256(); - final var vote1 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId1); - when(vote1.getTimeoutSignature()).thenReturn(Optional.empty()); - when(vote1.isTimeout()).thenReturn(false); - final var vote2 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId2); - when(vote2.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); - when(vote2.isTimeout()).thenReturn(true); - - BFTValidatorSet validatorSet = BFTValidatorSet.from( - Arrays.asList( - BFTValidator.from(vote1.getAuthor(), UInt256.ONE), - BFTValidator.from(vote2.getAuthor(), UInt256.ONE) - ) - ); - - assertTrue( - this.pendingVotes.insertVote(vote1, validatorSet) instanceof VoteProcessingResult.VoteAccepted); - - // submit duplicate vote, should fail - assertEquals( - VoteProcessingResult.rejected(VoteRejectedReason.DUPLICATE_VOTE), - this.pendingVotes.insertVote(vote1, validatorSet) - ); - - // submit again, but this time with a timeout - when(vote1.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); - when(vote1.isTimeout()).thenReturn(true); - - // should be accepted - assertEquals( - VoteProcessingResult.accepted(), - this.pendingVotes.insertVote(vote1, validatorSet) - ); - - // insert another timeout vote - final var result2 = this.pendingVotes.insertVote(vote2, validatorSet); - - // and form a TC - assertTrue(result2 instanceof VoteProcessingResult.QuorumReached); - - assertTrue(((VoteProcessingResult.QuorumReached) result2).getViewVotingResult() - instanceof ViewVotingResult.FormedTC); - } - - private Vote makeSignedVoteFor(BFTNode author, View parentView, HashCode vertexId) { - Vote vote = makeVoteWithoutSignatureFor(author, parentView, vertexId); - when(vote.getSignature()).thenReturn(ECDSASignature.zeroSignature()); - return vote; - } - - private Vote makeVoteWithoutSignatureFor(BFTNode author, View parentView, HashCode vertexId) { - Vote vote = mock(Vote.class); - BFTHeader proposed = new BFTHeader(parentView.next(), vertexId, mock(LedgerHeader.class)); - BFTHeader parent = new BFTHeader(parentView, HashUtils.random256(), mock(LedgerHeader.class)); - VoteData voteData = new VoteData(proposed, parent, null); - when(vote.getHashOfData(any())) - .thenReturn(Vote.getHashOfData(hasher, voteData, 123456L)); - when(vote.getVoteData()).thenReturn(voteData); - when(vote.getTimestamp()).thenReturn(123456L); - when(vote.getAuthor()).thenReturn(author); - when(vote.getView()).thenReturn(parentView); - when(vote.getEpoch()).thenReturn(0L); - return vote; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.hash.HashCode; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.BFTValidator; +import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.ValidationState; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.bft.ViewVotingResult; +import com.radixdlt.consensus.bft.VoteProcessingResult; +import com.radixdlt.consensus.bft.VoteProcessingResult.VoteRejected.VoteRejectedReason; +import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.crypto.Hasher; +import com.radixdlt.utils.RandomHasher; +import com.radixdlt.utils.UInt256; +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Before; +import org.junit.Test; + +public class PendingVotesTest { + private PendingVotes pendingVotes; + private Hasher hasher; + + @Before + public void setup() { + this.hasher = new RandomHasher(); + this.pendingVotes = new PendingVotes(hasher); + } + + @Test + public void equalsContractForPreviousVote() { + EqualsVerifier.forClass(PendingVotes.PreviousVote.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } + + @Test + public void when_inserting_valid_but_unaccepted_votes__then_no_qc_is_returned() { + HashCode vertexId = HashUtils.random256(); + Vote vote1 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId); + Vote vote2 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId); + + BFTValidatorSet validatorSet = + BFTValidatorSet.from( + Collections.singleton(BFTValidator.from(vote1.getAuthor(), UInt256.ONE))); + VoteData voteData = mock(VoteData.class); + BFTHeader proposed = vote1.getVoteData().getProposed(); + when(voteData.getProposed()).thenReturn(proposed); + + assertEquals( + VoteProcessingResult.rejected(VoteRejectedReason.INVALID_AUTHOR), + this.pendingVotes.insertVote(vote2, validatorSet)); + } + + @Test + public void when_inserting_valid_and_accepted_votes__then_qc_is_formed() { + BFTNode author = mock(BFTNode.class); + Vote vote = makeSignedVoteFor(author, View.genesis(), HashUtils.random256()); + + BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); + ValidationState validationState = mock(ValidationState.class); + TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); + when(validationState.addSignature(any(), anyLong(), any())).thenReturn(true); + when(validationState.complete()).thenReturn(true); + when(validationState.signatures()).thenReturn(signatures); + when(validatorSet.newValidationState()).thenReturn(validationState); + when(validatorSet.containsNode(any())).thenReturn(true); + + VoteData voteData = mock(VoteData.class); + BFTHeader proposed = vote.getVoteData().getProposed(); + when(voteData.getProposed()).thenReturn(proposed); + + assertTrue( + this.pendingVotes.insertVote(vote, validatorSet) + instanceof VoteProcessingResult.QuorumReached); + } + + @Test + public void when_inserting_valid_timeout_votes__then_tc_is_formed() { + HashCode vertexId1 = HashUtils.random256(); + HashCode vertexId2 = HashUtils.random256(); + Vote vote1 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId1); + when(vote1.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); + when(vote1.isTimeout()).thenReturn(true); + Vote vote2 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId2); + when(vote2.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); + when(vote2.isTimeout()).thenReturn(true); + + BFTValidatorSet validatorSet = + BFTValidatorSet.from( + Arrays.asList( + BFTValidator.from(vote1.getAuthor(), UInt256.ONE), + BFTValidator.from(vote2.getAuthor(), UInt256.ONE))); + + assertTrue( + this.pendingVotes.insertVote(vote1, validatorSet) + instanceof VoteProcessingResult.VoteAccepted); + + VoteProcessingResult result2 = this.pendingVotes.insertVote(vote2, validatorSet); + + assertTrue(result2 instanceof VoteProcessingResult.QuorumReached); + + assertTrue( + ((VoteProcessingResult.QuorumReached) result2).getViewVotingResult() + instanceof ViewVotingResult.FormedTC); + } + + @Test + public void when_voting_again__previous_vote_is_removed() { + BFTNode author = mock(BFTNode.class); + Vote vote = makeSignedVoteFor(author, View.genesis(), HashUtils.random256()); + + BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); + ValidationState validationState = mock(ValidationState.class); + TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); + when(validationState.signatures()).thenReturn(signatures); + when(validationState.isEmpty()).thenReturn(true); + when(validatorSet.newValidationState()).thenReturn(validationState); + when(validatorSet.containsNode(any())).thenReturn(true); + + VoteData voteData = mock(VoteData.class); + BFTHeader proposed = vote.getVoteData().getProposed(); + when(voteData.getProposed()).thenReturn(proposed); + + // Preconditions + assertEquals(VoteProcessingResult.accepted(), this.pendingVotes.insertVote(vote, validatorSet)); + assertEquals(1, this.pendingVotes.voteStateSize()); + assertEquals(1, this.pendingVotes.previousVotesSize()); + + Vote vote2 = makeSignedVoteFor(author, View.of(1), HashUtils.random256()); + // Need a different hash for this (different) vote + assertEquals( + VoteProcessingResult.accepted(), this.pendingVotes.insertVote(vote2, validatorSet)); + assertEquals(1, this.pendingVotes.voteStateSize()); + assertEquals(1, this.pendingVotes.previousVotesSize()); + } + + @Test + public void when_voting_again__previous_timeoutvote_is_removed() { + BFTNode author = mock(BFTNode.class); + Vote vote = makeSignedVoteFor(author, View.genesis(), HashUtils.random256()); + when(vote.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); + when(vote.isTimeout()).thenReturn(true); + + BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); + ValidationState validationState = mock(ValidationState.class); + TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); + when(validationState.signatures()).thenReturn(signatures); + when(validationState.isEmpty()).thenReturn(true); + when(validatorSet.newValidationState()).thenReturn(validationState); + when(validatorSet.containsNode(any())).thenReturn(true); + + VoteData voteData = mock(VoteData.class); + BFTHeader proposed = vote.getVoteData().getProposed(); + when(voteData.getProposed()).thenReturn(proposed); + + // Preconditions + assertEquals(VoteProcessingResult.accepted(), this.pendingVotes.insertVote(vote, validatorSet)); + assertEquals(1, this.pendingVotes.voteStateSize()); + assertEquals(1, this.pendingVotes.timeoutVoteStateSize()); + assertEquals(1, this.pendingVotes.previousVotesSize()); + + Vote vote2 = makeSignedVoteFor(author, View.of(1), HashUtils.random256()); + // Need a different hash for this (different) vote + assertEquals( + VoteProcessingResult.accepted(), this.pendingVotes.insertVote(vote2, validatorSet)); + assertEquals(1, this.pendingVotes.voteStateSize()); + assertEquals(0, this.pendingVotes.timeoutVoteStateSize()); + assertEquals(1, this.pendingVotes.previousVotesSize()); + } + + @Test + public void when_submitting_a_duplicate_vote__then_can_be_replaced_if_has_timeout() { + final var vertexId1 = HashUtils.random256(); + final var vertexId2 = HashUtils.random256(); + final var vote1 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId1); + when(vote1.getTimeoutSignature()).thenReturn(Optional.empty()); + when(vote1.isTimeout()).thenReturn(false); + final var vote2 = makeSignedVoteFor(mock(BFTNode.class), View.genesis(), vertexId2); + when(vote2.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); + when(vote2.isTimeout()).thenReturn(true); + + BFTValidatorSet validatorSet = + BFTValidatorSet.from( + Arrays.asList( + BFTValidator.from(vote1.getAuthor(), UInt256.ONE), + BFTValidator.from(vote2.getAuthor(), UInt256.ONE))); + + assertTrue( + this.pendingVotes.insertVote(vote1, validatorSet) + instanceof VoteProcessingResult.VoteAccepted); + + // submit duplicate vote, should fail + assertEquals( + VoteProcessingResult.rejected(VoteRejectedReason.DUPLICATE_VOTE), + this.pendingVotes.insertVote(vote1, validatorSet)); + + // submit again, but this time with a timeout + when(vote1.getTimeoutSignature()).thenReturn(Optional.of(mock(ECDSASignature.class))); + when(vote1.isTimeout()).thenReturn(true); + + // should be accepted + assertEquals( + VoteProcessingResult.accepted(), this.pendingVotes.insertVote(vote1, validatorSet)); + + // insert another timeout vote + final var result2 = this.pendingVotes.insertVote(vote2, validatorSet); + + // and form a TC + assertTrue(result2 instanceof VoteProcessingResult.QuorumReached); + + assertTrue( + ((VoteProcessingResult.QuorumReached) result2).getViewVotingResult() + instanceof ViewVotingResult.FormedTC); + } + + private Vote makeSignedVoteFor(BFTNode author, View parentView, HashCode vertexId) { + Vote vote = makeVoteWithoutSignatureFor(author, parentView, vertexId); + when(vote.getSignature()).thenReturn(ECDSASignature.zeroSignature()); + return vote; + } + + private Vote makeVoteWithoutSignatureFor(BFTNode author, View parentView, HashCode vertexId) { + Vote vote = mock(Vote.class); + BFTHeader proposed = new BFTHeader(parentView.next(), vertexId, mock(LedgerHeader.class)); + BFTHeader parent = new BFTHeader(parentView, HashUtils.random256(), mock(LedgerHeader.class)); + VoteData voteData = new VoteData(proposed, parent, null); + when(vote.getHashOfData(any())).thenReturn(Vote.getHashOfData(hasher, voteData, 123456L)); + when(vote.getVoteData()).thenReturn(voteData); + when(vote.getTimestamp()).thenReturn(123456L); + when(vote.getAuthor()).thenReturn(author); + when(vote.getView()).thenReturn(parentView); + when(vote.getEpoch()).thenReturn(0L); + return vote; + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ProposalTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ProposalTest.java index b7512fac1f..612bc39dfc 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ProposalTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ProposalTest.java @@ -64,74 +64,86 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.common.hash.HashCode; import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.HashUtils; +import java.util.Optional; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Before; import org.junit.Test; -import java.util.Optional; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class ProposalTest { - private Proposal proposal; - private UnverifiedVertex vertex; - private ECDSASignature signature; - private QuorumCertificate qc; - private QuorumCertificate commitQc; + private Proposal proposal; + private UnverifiedVertex vertex; + private ECDSASignature signature; + private QuorumCertificate qc; + private QuorumCertificate commitQc; - @Before - public void setUp() { - this.vertex = mock(UnverifiedVertex.class); - this.signature = mock(ECDSASignature.class); - this.commitQc = mock(QuorumCertificate.class); - this.qc = mock(QuorumCertificate.class); + @Before + public void setUp() { + this.vertex = mock(UnverifiedVertex.class); + this.signature = mock(ECDSASignature.class); + this.commitQc = mock(QuorumCertificate.class); + this.qc = mock(QuorumCertificate.class); - when(this.vertex.getQC()).thenReturn(qc); + when(this.vertex.getQC()).thenReturn(qc); - this.proposal = new Proposal(vertex, commitQc, signature, Optional.empty()); - } + this.proposal = new Proposal(vertex, commitQc, signature, Optional.empty()); + } - @Test - public void testGetters() { - assertThat(this.proposal.getVertex()).isEqualTo(vertex); - assertThat(this.proposal.highQC()).isEqualTo(HighQC.from(this.qc, this.commitQc, Optional.empty())); - } + @Test + public void testGetters() { + assertThat(this.proposal.getVertex()).isEqualTo(vertex); + assertThat(this.proposal.highQC()) + .isEqualTo(HighQC.from(this.qc, this.commitQc, Optional.empty())); + } - @Test - public void testToString() { - assertThat(this.proposal).isNotNull(); - } + @Test + public void testToString() { + assertThat(this.proposal).isNotNull(); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(Proposal.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(Proposal.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void sensibleToString() { - String s = this.proposal.toString(); - assertThat(s).contains(vertex.toString()); - } + @Test + public void sensibleToString() { + String s = this.proposal.toString(); + assertThat(s).contains(vertex.toString()); + } - @Test(expected = NullPointerException.class) - public void deserializeWithNullThrowsException1() { - new Proposal(null, mock(QuorumCertificate.class), mock(ECDSASignature.class), mock(TimeoutCertificate.class)); - } + @Test(expected = NullPointerException.class) + public void deserializeWithNullThrowsException1() { + new Proposal( + null, + mock(QuorumCertificate.class), + mock(ECDSASignature.class), + mock(TimeoutCertificate.class)); + } - @Test(expected = NullPointerException.class) - public void deserializeWithNullThrowsException2() { - new Proposal(mock(UnverifiedVertex.class), null, mock(ECDSASignature.class), mock(TimeoutCertificate.class)); - } + @Test(expected = NullPointerException.class) + public void deserializeWithNullThrowsException2() { + new Proposal( + mock(UnverifiedVertex.class), + null, + mock(ECDSASignature.class), + mock(TimeoutCertificate.class)); + } - @Test(expected = NullPointerException.class) - public void deserializeWithNullThrowsException3() { - new Proposal(mock(UnverifiedVertex.class), mock(QuorumCertificate.class), null, mock(TimeoutCertificate.class)); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializeWithNullThrowsException3() { + new Proposal( + mock(UnverifiedVertex.class), + mock(QuorumCertificate.class), + null, + mock(TimeoutCertificate.class)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/QuorumCertificateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/QuorumCertificateTest.java index 1ae63a184e..9d6d295bcd 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/QuorumCertificateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/QuorumCertificateTest.java @@ -64,6 +64,10 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.bft.VerifiedVertex; import com.radixdlt.consensus.bft.View; @@ -71,33 +75,29 @@ import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class QuorumCertificateTest { - @Test - public void when_create_genesis_qc_with_non_genesis_vertex__then_should_throw_exception() { - VerifiedVertex vertex = mock(VerifiedVertex.class); - when(vertex.getView()).thenReturn(View.of(1)); - assertThatThrownBy(() -> QuorumCertificate.ofGenesis(vertex, mock(LedgerHeader.class))) - .isInstanceOf(IllegalArgumentException.class); - } + @Test + public void when_create_genesis_qc_with_non_genesis_vertex__then_should_throw_exception() { + VerifiedVertex vertex = mock(VerifiedVertex.class); + when(vertex.getView()).thenReturn(View.of(1)); + assertThatThrownBy(() -> QuorumCertificate.ofGenesis(vertex, mock(LedgerHeader.class))) + .isInstanceOf(IllegalArgumentException.class); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(QuorumCertificate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(QuorumCertificate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - new QuorumCertificate(null, mock(TimestampedECDSASignatures.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + new QuorumCertificate(null, mock(TimestampedECDSASignatures.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - new QuorumCertificate(mock(VoteData.class), null); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + new QuorumCertificate(mock(VoteData.class), null); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimeoutCertificateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimeoutCertificateTest.java index 2f9ba6bbf1..db45c76d92 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimeoutCertificateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimeoutCertificateTest.java @@ -64,41 +64,40 @@ package com.radixdlt.consensus; +import static org.mockito.Mockito.mock; + import com.radixdlt.utils.SerializerTestDataGenerator; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; import org.radix.serialization.SerializeMessageObject; -import static org.mockito.Mockito.mock; - public class TimeoutCertificateTest extends SerializeMessageObject { - public TimeoutCertificateTest() { - super(TimeoutCertificate.class, TimeoutCertificateTest::get); - } + public TimeoutCertificateTest() { + super(TimeoutCertificate.class, TimeoutCertificateTest::get); + } - private static TimeoutCertificate get() { - return SerializerTestDataGenerator.randomTimeoutCertificate(); - } + private static TimeoutCertificate get() { + return SerializerTestDataGenerator.randomTimeoutCertificate(); + } - @Test - public void equalsTest() { - EqualsVerifier.forClass(TimeoutCertificate.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(TimeoutCertificate.class).verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - TimeoutCertificate.serializerCreate(1, 1, null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + TimeoutCertificate.serializerCreate(1, 1, null); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidEpochThrowsException() { - TimeoutCertificate.serializerCreate(-1, 1, mock(TimestampedECDSASignatures.class)); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidEpochThrowsException() { + TimeoutCertificate.serializerCreate(-1, 1, mock(TimestampedECDSASignatures.class)); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidViewThrowsException() { - TimeoutCertificate.serializerCreate(1, -1, mock(TimestampedECDSASignatures.class)); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidViewThrowsException() { + TimeoutCertificate.serializerCreate(1, -1, mock(TimestampedECDSASignatures.class)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignatureTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignatureTest.java index ce211edb91..cc0b944e6d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignatureTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignatureTest.java @@ -64,41 +64,40 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import com.radixdlt.crypto.ECDSASignature; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; import org.radix.serialization.SerializeObject; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - public class TimestampedECDSASignatureTest extends SerializeObject { - public TimestampedECDSASignatureTest() { - super(TimestampedECDSASignature.class, TimestampedECDSASignatureTest::create); - } + public TimestampedECDSASignatureTest() { + super(TimestampedECDSASignature.class, TimestampedECDSASignatureTest::create); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(TimestampedECDSASignature.class) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(TimestampedECDSASignature.class).verify(); + } - @Test - public void sensibleToString() { - assertThat(create().toString()).contains(TimestampedECDSASignature.class.getSimpleName()); - } + @Test + public void sensibleToString() { + assertThat(create().toString()).contains(TimestampedECDSASignature.class.getSimpleName()); + } - private static TimestampedECDSASignature create() { - return TimestampedECDSASignature.from(1L, ECDSASignature.zeroSignature()); - } + private static TimestampedECDSASignature create() { + return TimestampedECDSASignature.from(1L, ECDSASignature.zeroSignature()); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - TimestampedECDSASignature.from(1, null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + TimestampedECDSASignature.from(1, null); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidTimestampThrowsException() { - TimestampedECDSASignature.from(0, mock(ECDSASignature.class)); - } -} \ No newline at end of file + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidTimestampThrowsException() { + TimestampedECDSASignature.from(0, mock(ECDSASignature.class)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignaturesTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignaturesTest.java index bd13d32ef9..296c9c3374 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignaturesTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedECDSASignaturesTest.java @@ -64,26 +64,25 @@ package com.radixdlt.consensus; -import org.junit.Test; +import static org.mockito.Mockito.mock; import java.util.HashMap; - -import static org.mockito.Mockito.mock; +import org.junit.Test; public class TimestampedECDSASignaturesTest { - @Test(expected = NullPointerException.class) - public void deserializationWithInvalidMapThrowsException1() { - var map = new HashMap(); - map.put(null, mock(TimestampedECDSASignature.class)); + @Test(expected = NullPointerException.class) + public void deserializationWithInvalidMapThrowsException1() { + var map = new HashMap(); + map.put(null, mock(TimestampedECDSASignature.class)); - TimestampedECDSASignatures.from(map); - } + TimestampedECDSASignatures.from(map); + } - @Test(expected = NullPointerException.class) - public void deserializationWithInvalidMapThrowsException2() { - var map = new HashMap(); - map.put("not null string", null); + @Test(expected = NullPointerException.class) + public void deserializationWithInvalidMapThrowsException2() { + var map = new HashMap(); + map.put("not null string", null); - TimestampedECDSASignatures.from(map); - } -} \ No newline at end of file + TimestampedECDSASignatures.from(map); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/UnverifiedVertexTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/UnverifiedVertexTest.java index c4e52dda22..930eb4c6af 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/UnverifiedVertexTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/UnverifiedVertexTest.java @@ -64,84 +64,85 @@ package com.radixdlt.consensus; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.HashUtils; import com.radixdlt.crypto.exception.PublicKeyException; +import java.util.ArrayList; +import java.util.List; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - public class UnverifiedVertexTest { - private UnverifiedVertex testObject; - private QuorumCertificate qc; - - @Before - public void setUp() { - View baseView = View.of(1234567890L); - HashCode id = HashUtils.random256(); - - BFTHeader header = new BFTHeader(baseView.next(), id, mock(LedgerHeader.class)); - BFTHeader parent = new BFTHeader(baseView, HashUtils.random256(), mock(LedgerHeader.class)); - VoteData voteData = new VoteData(header, parent, parent); - - this.qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - this.testObject = UnverifiedVertex.create(this.qc, baseView.next().next(), List.of(), BFTNode.random()); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(UnverifiedVertex.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - - @Test - public void testGetters() { - assertEquals(this.qc, this.testObject.getQC()); - assertEquals(View.of(1234567892L), this.testObject.getView()); - } - - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() throws PublicKeyException { - var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); - UnverifiedVertex.create(null, 1, List.of(), proposer, false); - } - - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidViewThrowsException() throws PublicKeyException { - var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); - UnverifiedVertex.create(mock(QuorumCertificate.class), -1, List.of(), proposer, false); - } - - @Test(expected = NullPointerException.class) - public void deserializationWithInvalidTxnListThrowsException() throws PublicKeyException { - var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); - var list = new ArrayList(); - list.add(null); - UnverifiedVertex.create(mock(QuorumCertificate.class), 1, list, proposer, false); - } - - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidCombinationOfProposerTimeoutAndTxnListThrowsException() throws PublicKeyException { - var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); - var list = new ArrayList(); - list.add(new byte[0]); - UnverifiedVertex.create(mock(QuorumCertificate.class), 1, list, proposer, true); - } - - @Test(expected = PublicKeyException.class) - public void deserializationWithInvalidPublicKeyThrowsException() throws PublicKeyException { - var proposer = new byte[] {0x00}; - UnverifiedVertex.create(mock(QuorumCertificate.class), 1, List.of(), proposer, false); - } + private UnverifiedVertex testObject; + private QuorumCertificate qc; + + @Before + public void setUp() { + View baseView = View.of(1234567890L); + HashCode id = HashUtils.random256(); + + BFTHeader header = new BFTHeader(baseView.next(), id, mock(LedgerHeader.class)); + BFTHeader parent = new BFTHeader(baseView, HashUtils.random256(), mock(LedgerHeader.class)); + VoteData voteData = new VoteData(header, parent, parent); + + this.qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + this.testObject = + UnverifiedVertex.create(this.qc, baseView.next().next(), List.of(), BFTNode.random()); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(UnverifiedVertex.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } + + @Test + public void testGetters() { + assertEquals(this.qc, this.testObject.getQC()); + assertEquals(View.of(1234567892L), this.testObject.getView()); + } + + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() throws PublicKeyException { + var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); + UnverifiedVertex.create(null, 1, List.of(), proposer, false); + } + + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidViewThrowsException() throws PublicKeyException { + var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); + UnverifiedVertex.create(mock(QuorumCertificate.class), -1, List.of(), proposer, false); + } + + @Test(expected = NullPointerException.class) + public void deserializationWithInvalidTxnListThrowsException() throws PublicKeyException { + var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); + var list = new ArrayList(); + list.add(null); + UnverifiedVertex.create(mock(QuorumCertificate.class), 1, list, proposer, false); + } + + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidCombinationOfProposerTimeoutAndTxnListThrowsException() + throws PublicKeyException { + var proposer = ECKeyPair.generateNew().getPublicKey().getBytes(); + var list = new ArrayList(); + list.add(new byte[0]); + UnverifiedVertex.create(mock(QuorumCertificate.class), 1, list, proposer, true); + } + + @Test(expected = PublicKeyException.class) + public void deserializationWithInvalidPublicKeyThrowsException() throws PublicKeyException { + var proposer = new byte[] {0x00}; + UnverifiedVertex.create(mock(QuorumCertificate.class), 1, List.of(), proposer, false); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ViewTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ViewTest.java index 1e4bb4d0d8..225088548f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ViewTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/ViewTest.java @@ -1,33 +1,96 @@ -package com.radixdlt.consensus; - -import com.radixdlt.consensus.bft.View; -import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -public class ViewTest { - @Test - public void testBadArgument() { - assertThatThrownBy(() -> View.of(-1L)) - .isInstanceOf(IllegalArgumentException.class); - } - @Test - public void testPrevious() { - assertThat(View.of(2L).previous()).isEqualTo(View.of(1L)); - assertThatThrownBy(() -> View.of(0L).previous()); - } - - @Test - public void testNext() { - assertThat(View.of(1L).next()).isEqualTo(View.of(2L)); - assertThatThrownBy(() -> View.of(Long.MAX_VALUE).next()); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(View.class) - .verify(); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.radixdlt.consensus.bft.View; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class ViewTest { + @Test + public void testBadArgument() { + assertThatThrownBy(() -> View.of(-1L)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testPrevious() { + assertThat(View.of(2L).previous()).isEqualTo(View.of(1L)); + assertThatThrownBy(() -> View.of(0L).previous()); + } + + @Test + public void testNext() { + assertThat(View.of(1L).next()).isEqualTo(View.of(2L)); + assertThatThrownBy(() -> View.of(Long.MAX_VALUE).next()); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(View.class).verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteDataTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteDataTest.java index 4b2e56e047..814b6361c5 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteDataTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteDataTest.java @@ -64,28 +64,28 @@ package com.radixdlt.consensus; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; -import static org.mockito.Mockito.mock; - public class VoteDataTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(VoteData.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(VoteData.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - new VoteData(null, mock(BFTHeader.class), mock(BFTHeader.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + new VoteData(null, mock(BFTHeader.class), mock(BFTHeader.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - new VoteData(mock(BFTHeader.class), null, mock(BFTHeader.class)); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + new VoteData(mock(BFTHeader.class), null, mock(BFTHeader.class)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteTest.java index a7c77964ad..cd3ea60537 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/VoteTest.java @@ -64,6 +64,10 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.View; @@ -71,88 +75,123 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.HashUtils; import com.radixdlt.crypto.exception.PublicKeyException; +import java.util.Optional; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Before; import org.junit.Test; -import java.util.Optional; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - public class VoteTest { - private BFTNode author; - private Vote testObject; - private VoteData voteData; - private HighQC highQC; + private BFTNode author; + private Vote testObject; + private VoteData voteData; + private HighQC highQC; - @Before - public void setUp() { - BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), mock(LedgerHeader.class)); - this.voteData = new VoteData(BFTHeader.ofGenesisAncestor(mock(LedgerHeader.class)), parent, null); - this.author = mock(BFTNode.class); - this.highQC = mock(HighQC.class); - this.testObject = new Vote(author, this.voteData, 123456L, ECDSASignature.zeroSignature(), highQC, Optional.empty()); - } + @Before + public void setUp() { + BFTHeader parent = + new BFTHeader(View.of(1234567890L), HashUtils.random256(), mock(LedgerHeader.class)); + this.voteData = + new VoteData(BFTHeader.ofGenesisAncestor(mock(LedgerHeader.class)), parent, null); + this.author = mock(BFTNode.class); + this.highQC = mock(HighQC.class); + this.testObject = + new Vote( + author, + this.voteData, + 123456L, + ECDSASignature.zeroSignature(), + highQC, + Optional.empty()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(Vote.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(Vote.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void testGetters() { - assertEquals(this.testObject.getEpoch(), voteData.getProposed().getLedgerHeader().getEpoch()); - assertEquals(this.voteData, this.testObject.getVoteData()); - assertEquals(this.author, this.testObject.getAuthor()); - assertEquals(this.highQC, this.testObject.highQC()); - } + @Test + public void testGetters() { + assertEquals(this.testObject.getEpoch(), voteData.getProposed().getLedgerHeader().getEpoch()); + assertEquals(this.voteData, this.testObject.getVoteData()); + assertEquals(this.author, this.testObject.getAuthor()); + assertEquals(this.highQC, this.testObject.highQC()); + } - @Test - public void testToString() { - assertThat(this.testObject.toString()) - .isNotNull() - .contains(Vote.class.getSimpleName()) - .contains("epoch=0") - .contains("view=0") - .contains(this.author.toString()); - } + @Test + public void testToString() { + assertThat(this.testObject.toString()) + .isNotNull() + .contains(Vote.class.getSimpleName()) + .contains("epoch=0") + .contains("view=0") + .contains(this.author.toString()); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() throws PublicKeyException { - new Vote(null, mock(VoteData.class), 1L, mock(ECDSASignature.class), mock(HighQC.class), mock(ECDSASignature.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() throws PublicKeyException { + new Vote( + null, + mock(VoteData.class), + 1L, + mock(ECDSASignature.class), + mock(HighQC.class), + mock(ECDSASignature.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() throws PublicKeyException { - var author = ECKeyPair.generateNew().getPublicKey().getBytes(); - new Vote(author, null, 1L, mock(ECDSASignature.class), mock(HighQC.class), mock(ECDSASignature.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() throws PublicKeyException { + var author = ECKeyPair.generateNew().getPublicKey().getBytes(); + new Vote( + author, + null, + 1L, + mock(ECDSASignature.class), + mock(HighQC.class), + mock(ECDSASignature.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException3() throws PublicKeyException { - var author = ECKeyPair.generateNew().getPublicKey().getBytes(); - new Vote(author, mock(VoteData.class), 1L, null, mock(HighQC.class), mock(ECDSASignature.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException3() throws PublicKeyException { + var author = ECKeyPair.generateNew().getPublicKey().getBytes(); + new Vote( + author, mock(VoteData.class), 1L, null, mock(HighQC.class), mock(ECDSASignature.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException4() throws PublicKeyException { - var author = ECKeyPair.generateNew().getPublicKey().getBytes(); - new Vote(author, mock(VoteData.class), 1L, mock(ECDSASignature.class), null, mock(ECDSASignature.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException4() throws PublicKeyException { + var author = ECKeyPair.generateNew().getPublicKey().getBytes(); + new Vote( + author, + mock(VoteData.class), + 1L, + mock(ECDSASignature.class), + null, + mock(ECDSASignature.class)); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidEpochThrowsException4() throws PublicKeyException { - var author = ECKeyPair.generateNew().getPublicKey().getBytes(); - new Vote(author, mock(VoteData.class), -1L, mock(ECDSASignature.class), mock(HighQC.class), mock(ECDSASignature.class)); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidEpochThrowsException4() throws PublicKeyException { + var author = ECKeyPair.generateNew().getPublicKey().getBytes(); + new Vote( + author, + mock(VoteData.class), + -1L, + mock(ECDSASignature.class), + mock(HighQC.class), + mock(ECDSASignature.class)); + } - @Test(expected = PublicKeyException.class) - public void deserializationWithInvalidAuthorThrowsException4() throws PublicKeyException { - var author = new byte[0]; - new Vote(author, mock(VoteData.class), 1L, mock(ECDSASignature.class), mock(HighQC.class), mock(ECDSASignature.class)); - } + @Test(expected = PublicKeyException.class) + public void deserializationWithInvalidAuthorThrowsException4() throws PublicKeyException { + var author = new byte[0]; + new Vote( + author, + mock(VoteData.class), + 1L, + mock(ECDSASignature.class), + mock(HighQC.class), + mock(ECDSASignature.class)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventPreprocessorTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventPreprocessorTest.java index 777ee9b8ad..61f6640d6e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventPreprocessorTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventPreprocessorTest.java @@ -1,126 +1,125 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus.bft; - -import com.radixdlt.consensus.BFTEventProcessor; -import com.radixdlt.consensus.BFTHeader; -import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.LedgerProof; -import com.radixdlt.consensus.bft.BFTSyncer.SyncResult; -import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.QuorumCertificate; -import org.junit.Before; -import org.junit.Test; - -import java.util.Optional; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class BFTEventPreprocessorTest { - - private BFTEventPreprocessor bftEventPreprocessor; - - private final BFTEventProcessor forwardTo = mock(BFTEventProcessor.class); - private final BFTSyncer bftSyncer = mock(BFTSyncer.class); - private final ViewUpdate initialViewUpdate = mock(ViewUpdate.class); - - @Before - public void setUp() { - this.bftEventPreprocessor = new BFTEventPreprocessor(forwardTo, bftSyncer, initialViewUpdate); - } - - @Test - public void when_view_update__then_should_process_cached_events() { - final var proposal = mock(Proposal.class); - final var proposalHighQc = mock(HighQC.class); - final var proposalHighestCommittedQc = mock(QuorumCertificate.class); - final var proposalLedgerProof = mock(LedgerProof.class); - when(initialViewUpdate.getCurrentView()).thenReturn(View.of(2)); - when(proposal.getView()).thenReturn(View.of(4)); - when(proposal.highQC()).thenReturn(proposalHighQc); - when(proposalHighQc.highestCommittedQC()).thenReturn(proposalHighestCommittedQc); - var header = mock(BFTHeader.class); - when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); - when(proposalHighestCommittedQc.getCommitted()).thenReturn(Optional.of(header)); - when(proposalLedgerProof.isEndOfEpoch()).thenReturn(false); - when(bftSyncer.syncToQC(any(), any())).thenReturn(SyncResult.IN_PROGRESS); - - // we're at v2, proposal for v4 should get cached as sync returns IN_PROGRESS - this.bftEventPreprocessor.processProposal(proposal); - - final var newViewUpdate = mock(ViewUpdate.class); - when(newViewUpdate.getCurrentView()).thenReturn(View.of(4)); - when(bftSyncer.syncToQC(any(), any())).thenReturn(SyncResult.SYNCED); - - // we're going straight to v4, cached proposal should get processed - this.bftEventPreprocessor.processViewUpdate(newViewUpdate); - verify(forwardTo, times(1)).processProposal(proposal); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus.bft; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.radixdlt.consensus.BFTEventProcessor; +import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.HighQC; +import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.consensus.Proposal; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.bft.BFTSyncer.SyncResult; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; + +public class BFTEventPreprocessorTest { + + private BFTEventPreprocessor bftEventPreprocessor; + + private final BFTEventProcessor forwardTo = mock(BFTEventProcessor.class); + private final BFTSyncer bftSyncer = mock(BFTSyncer.class); + private final ViewUpdate initialViewUpdate = mock(ViewUpdate.class); + + @Before + public void setUp() { + this.bftEventPreprocessor = new BFTEventPreprocessor(forwardTo, bftSyncer, initialViewUpdate); + } + + @Test + public void when_view_update__then_should_process_cached_events() { + final var proposal = mock(Proposal.class); + final var proposalHighQc = mock(HighQC.class); + final var proposalHighestCommittedQc = mock(QuorumCertificate.class); + final var proposalLedgerProof = mock(LedgerProof.class); + when(initialViewUpdate.getCurrentView()).thenReturn(View.of(2)); + when(proposal.getView()).thenReturn(View.of(4)); + when(proposal.highQC()).thenReturn(proposalHighQc); + when(proposalHighQc.highestCommittedQC()).thenReturn(proposalHighestCommittedQc); + var header = mock(BFTHeader.class); + when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); + when(proposalHighestCommittedQc.getCommitted()).thenReturn(Optional.of(header)); + when(proposalLedgerProof.isEndOfEpoch()).thenReturn(false); + when(bftSyncer.syncToQC(any(), any())).thenReturn(SyncResult.IN_PROGRESS); + + // we're at v2, proposal for v4 should get cached as sync returns IN_PROGRESS + this.bftEventPreprocessor.processProposal(proposal); + + final var newViewUpdate = mock(ViewUpdate.class); + when(newViewUpdate.getCurrentView()).thenReturn(View.of(4)); + when(bftSyncer.syncToQC(any(), any())).thenReturn(SyncResult.SYNCED); + + // we're going straight to v4, cached proposal should get processed + this.bftEventPreprocessor.processViewUpdate(newViewUpdate); + verify(forwardTo, times(1)).processProposal(proposal); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventReducerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventReducerTest.java index 79dc9f5a14..1040662105 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventReducerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventReducerTest.java @@ -1,204 +1,209 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus.bft; - -import com.radixdlt.consensus.BFTHeader; -import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.PendingVotes; -import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.liveness.Pacemaker; -import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -import com.radixdlt.consensus.safety.SafetyRules; -import com.radixdlt.crypto.Hasher; -import com.radixdlt.environment.EventDispatcher; -import com.radixdlt.environment.RemoteEventDispatcher; -import org.junit.Before; -import org.junit.Test; - -import java.util.Optional; - -import static com.radixdlt.utils.TypedMocks.rmock; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -public class BFTEventReducerTest { - - private BFTNode self = mock(BFTNode.class); - private Hasher hasher = mock(Hasher.class); - private RemoteEventDispatcher voteDispatcher = rmock(RemoteEventDispatcher.class); - private PendingVotes pendingVotes = mock(PendingVotes.class); - private BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); - private VertexStore vertexStore = mock(VertexStore.class); - private SafetyRules safetyRules = mock(SafetyRules.class); - private Pacemaker pacemaker = mock(Pacemaker.class); - private EventDispatcher viewQuorumReachedEventDispatcher = rmock(EventDispatcher.class); - private EventDispatcher noVoteEventDispatcher = rmock(EventDispatcher.class); - - private BFTEventReducer bftEventReducer; - - @Before - public void setUp() { - this.bftEventReducer = new BFTEventReducer( - this.self, - this.pacemaker, - this.vertexStore, - this.viewQuorumReachedEventDispatcher, - this.noVoteEventDispatcher, - this.voteDispatcher, - this.hasher, - this.safetyRules, - this.validatorSet, - this.pendingVotes, - mock(ViewUpdate.class) - ); - } - - @Test - public void when_bft_update_for_previous_view__then_ignore() { - BFTInsertUpdate update = mock(BFTInsertUpdate.class); - BFTHeader header = mock(BFTHeader.class); - this.bftEventReducer.processViewUpdate(ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self)); - verify(this.pacemaker, times(1)).processViewUpdate(any()); - - when(update.getHeader()).thenReturn(header); - when(header.getView()).thenReturn(View.of(2)); - this.bftEventReducer.processBFTUpdate(update); - - verifyNoMoreInteractions(this.pacemaker); - } - - @Test - public void when_view_is_timed_out__then_dont_vote() { - BFTInsertUpdate bftUpdate = mock(BFTInsertUpdate.class); - BFTHeader header = mock(BFTHeader.class); - when(bftUpdate.getHeader()).thenReturn(header); - when(header.getView()).thenReturn(View.of(3)); - - ViewUpdate viewUpdate = ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self); - this.bftEventReducer.processViewUpdate(viewUpdate); - verify(this.pacemaker, times(1)).processViewUpdate(any()); - - this.bftEventReducer.processLocalTimeout(ScheduledLocalTimeout.create(viewUpdate, 1000)); - verify(this.pacemaker, times(1)).processLocalTimeout(any()); - - this.bftEventReducer.processBFTUpdate(bftUpdate); - - verifyNoMoreInteractions(this.voteDispatcher); - verifyNoMoreInteractions(this.noVoteEventDispatcher); - } - - @Test - public void when_previous_vote_exists_for_this_view__then_dont_vote() { - BFTInsertUpdate bftUpdate = mock(BFTInsertUpdate.class); - BFTHeader header = mock(BFTHeader.class); - when(bftUpdate.getHeader()).thenReturn(header); - when(header.getView()).thenReturn(View.of(3)); - - ViewUpdate viewUpdate = ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self); - this.bftEventReducer.processViewUpdate(viewUpdate); - verify(this.pacemaker, times(1)).processViewUpdate(any()); - - when(safetyRules.getLastVote(View.of(3))).thenReturn(Optional.of(mock(Vote.class))); - this.bftEventReducer.processBFTUpdate(bftUpdate); - - verifyNoMoreInteractions(this.voteDispatcher); - verifyNoMoreInteractions(this.noVoteEventDispatcher); - } - - @Test - public void when_process_vote_with_quorum_wrong_view__then_ignored() { - Vote vote = mock(Vote.class); - when(vote.getView()).thenReturn(View.of(1)); - this.bftEventReducer.processViewUpdate(ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self)); - this.bftEventReducer.processVote(vote); - verifyNoMoreInteractions(this.pendingVotes); - } - - @Test - public void when_process_vote_with_quorum__then_processed() { - BFTNode author = mock(BFTNode.class); - Vote vote = mock(Vote.class); - when(vote.getAuthor()).thenReturn(author); - - QuorumCertificate qc = mock(QuorumCertificate.class); - HighQC highQc = mock(HighQC.class); - QuorumCertificate highestCommittedQc = mock(QuorumCertificate.class); - when(highQc.highestCommittedQC()).thenReturn(highestCommittedQc); - when(vote.getView()).thenReturn(View.of(1)); - - when(this.pendingVotes.insertVote(any(), any())).thenReturn(VoteProcessingResult.qcQuorum(qc)); - when(this.vertexStore.highQC()).thenReturn(highQc); - - // Move to view 1 - this.bftEventReducer.processViewUpdate(ViewUpdate.create(View.of(1), highQc, mock(BFTNode.class), this.self)); - - this.bftEventReducer.processVote(vote); - - verify(this.viewQuorumReachedEventDispatcher, times(1)).dispatch(any()); - verify(this.pendingVotes, times(1)).insertVote(eq(vote), any()); - verifyNoMoreInteractions(this.pendingVotes); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus.bft; + +import static com.radixdlt.utils.TypedMocks.rmock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.HighQC; +import com.radixdlt.consensus.PendingVotes; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.liveness.Pacemaker; +import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; +import com.radixdlt.consensus.safety.SafetyRules; +import com.radixdlt.crypto.Hasher; +import com.radixdlt.environment.EventDispatcher; +import com.radixdlt.environment.RemoteEventDispatcher; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; + +public class BFTEventReducerTest { + + private BFTNode self = mock(BFTNode.class); + private Hasher hasher = mock(Hasher.class); + private RemoteEventDispatcher voteDispatcher = rmock(RemoteEventDispatcher.class); + private PendingVotes pendingVotes = mock(PendingVotes.class); + private BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); + private VertexStore vertexStore = mock(VertexStore.class); + private SafetyRules safetyRules = mock(SafetyRules.class); + private Pacemaker pacemaker = mock(Pacemaker.class); + private EventDispatcher viewQuorumReachedEventDispatcher = + rmock(EventDispatcher.class); + private EventDispatcher noVoteEventDispatcher = rmock(EventDispatcher.class); + + private BFTEventReducer bftEventReducer; + + @Before + public void setUp() { + this.bftEventReducer = + new BFTEventReducer( + this.self, + this.pacemaker, + this.vertexStore, + this.viewQuorumReachedEventDispatcher, + this.noVoteEventDispatcher, + this.voteDispatcher, + this.hasher, + this.safetyRules, + this.validatorSet, + this.pendingVotes, + mock(ViewUpdate.class)); + } + + @Test + public void when_bft_update_for_previous_view__then_ignore() { + BFTInsertUpdate update = mock(BFTInsertUpdate.class); + BFTHeader header = mock(BFTHeader.class); + this.bftEventReducer.processViewUpdate( + ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self)); + verify(this.pacemaker, times(1)).processViewUpdate(any()); + + when(update.getHeader()).thenReturn(header); + when(header.getView()).thenReturn(View.of(2)); + this.bftEventReducer.processBFTUpdate(update); + + verifyNoMoreInteractions(this.pacemaker); + } + + @Test + public void when_view_is_timed_out__then_dont_vote() { + BFTInsertUpdate bftUpdate = mock(BFTInsertUpdate.class); + BFTHeader header = mock(BFTHeader.class); + when(bftUpdate.getHeader()).thenReturn(header); + when(header.getView()).thenReturn(View.of(3)); + + ViewUpdate viewUpdate = + ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self); + this.bftEventReducer.processViewUpdate(viewUpdate); + verify(this.pacemaker, times(1)).processViewUpdate(any()); + + this.bftEventReducer.processLocalTimeout(ScheduledLocalTimeout.create(viewUpdate, 1000)); + verify(this.pacemaker, times(1)).processLocalTimeout(any()); + + this.bftEventReducer.processBFTUpdate(bftUpdate); + + verifyNoMoreInteractions(this.voteDispatcher); + verifyNoMoreInteractions(this.noVoteEventDispatcher); + } + + @Test + public void when_previous_vote_exists_for_this_view__then_dont_vote() { + BFTInsertUpdate bftUpdate = mock(BFTInsertUpdate.class); + BFTHeader header = mock(BFTHeader.class); + when(bftUpdate.getHeader()).thenReturn(header); + when(header.getView()).thenReturn(View.of(3)); + + ViewUpdate viewUpdate = + ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self); + this.bftEventReducer.processViewUpdate(viewUpdate); + verify(this.pacemaker, times(1)).processViewUpdate(any()); + + when(safetyRules.getLastVote(View.of(3))).thenReturn(Optional.of(mock(Vote.class))); + this.bftEventReducer.processBFTUpdate(bftUpdate); + + verifyNoMoreInteractions(this.voteDispatcher); + verifyNoMoreInteractions(this.noVoteEventDispatcher); + } + + @Test + public void when_process_vote_with_quorum_wrong_view__then_ignored() { + Vote vote = mock(Vote.class); + when(vote.getView()).thenReturn(View.of(1)); + this.bftEventReducer.processViewUpdate( + ViewUpdate.create(View.of(3), mock(HighQC.class), mock(BFTNode.class), this.self)); + this.bftEventReducer.processVote(vote); + verifyNoMoreInteractions(this.pendingVotes); + } + + @Test + public void when_process_vote_with_quorum__then_processed() { + BFTNode author = mock(BFTNode.class); + Vote vote = mock(Vote.class); + when(vote.getAuthor()).thenReturn(author); + + QuorumCertificate qc = mock(QuorumCertificate.class); + HighQC highQc = mock(HighQC.class); + QuorumCertificate highestCommittedQc = mock(QuorumCertificate.class); + when(highQc.highestCommittedQC()).thenReturn(highestCommittedQc); + when(vote.getView()).thenReturn(View.of(1)); + + when(this.pendingVotes.insertVote(any(), any())).thenReturn(VoteProcessingResult.qcQuorum(qc)); + when(this.vertexStore.highQC()).thenReturn(highQc); + + // Move to view 1 + this.bftEventReducer.processViewUpdate( + ViewUpdate.create(View.of(1), highQc, mock(BFTNode.class), this.self)); + + this.bftEventReducer.processVote(vote); + + verify(this.viewQuorumReachedEventDispatcher, times(1)).dispatch(any()); + verify(this.pendingVotes, times(1)).insertVote(eq(vote), any()); + verifyNoMoreInteractions(this.pendingVotes); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventVerifierTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventVerifierTest.java index c36990b740..3584f89a51 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventVerifierTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTEventVerifierTest.java @@ -74,146 +74,145 @@ import com.radixdlt.consensus.BFTEventProcessor; import com.radixdlt.consensus.HashVerifier; -import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; -import com.radixdlt.crypto.Hasher; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.crypto.Hasher; +import java.util.Optional; import org.junit.Before; import org.junit.Test; -import java.util.Optional; - public class BFTEventVerifierTest { - private BFTValidatorSet validatorSet; - private BFTEventProcessor forwardTo; - private Hasher hasher; - private HashVerifier verifier; - private BFTEventVerifier eventVerifier; - - @Before - public void setup() { - this.validatorSet = mock(BFTValidatorSet.class); - this.forwardTo = mock(BFTEventProcessor.class); - this.hasher = mock(Hasher.class); - this.verifier = mock(HashVerifier.class); - this.eventVerifier = new BFTEventVerifier(validatorSet, forwardTo, hasher, verifier); - } - - @Test - public void when_start__then_should_be_forwarded() { - eventVerifier.start(); - verify(forwardTo, times(1)).start(); - } - - @Test - public void when_process_local_timeout__then_should_be_forwarded() { - ScheduledLocalTimeout timeout = mock(ScheduledLocalTimeout.class); - eventVerifier.processLocalTimeout(timeout); - verify(forwardTo, times(1)).processLocalTimeout(eq(timeout)); - } - - @Test - public void when_process_local_sync__then_should_be_forwarded() { - BFTInsertUpdate update = mock(BFTInsertUpdate.class); - eventVerifier.processBFTUpdate(update); - verify(forwardTo, times(1)).processBFTUpdate(update); - } - - @Test - public void when_process_correct_proposal_then_should_be_forwarded() { - Proposal proposal = mock(Proposal.class); - BFTNode author = mock(BFTNode.class); - when(proposal.getAuthor()).thenReturn(author); - when(proposal.getSignature()).thenReturn(mock(ECDSASignature.class)); - when(validatorSet.containsNode(eq(author))).thenReturn(true); - when(verifier.verify(any(), any(), any())).thenReturn(true); - eventVerifier.processProposal(proposal); - verify(forwardTo, times(1)).processProposal(eq(proposal)); - } - - @Test - public void when_process_bad_author_proposal_then_should_not_be_forwarded() { - Proposal proposal = mock(Proposal.class); - BFTNode author = mock(BFTNode.class); - when(proposal.getAuthor()).thenReturn(author); - when(proposal.getSignature()).thenReturn(mock(ECDSASignature.class)); - when(validatorSet.containsNode(eq(author))).thenReturn(false); - when(verifier.verify(any(), any(), any())).thenReturn(true); - eventVerifier.processProposal(proposal); - verify(forwardTo, never()).processProposal(any()); - } - - @Test - public void when_process_bad_signature_proposal_then_should_not_be_forwarded() { - Proposal proposal = mock(Proposal.class); - BFTNode author = mock(BFTNode.class); - when(proposal.getAuthor()).thenReturn(author); - when(proposal.getSignature()).thenReturn(mock(ECDSASignature.class)); - when(validatorSet.containsNode(eq(author))).thenReturn(true); - when(verifier.verify(any(), any(), any())).thenReturn(false); - eventVerifier.processProposal(proposal); - verify(forwardTo, never()).processProposal(any()); - } - - @Test - public void when_process_correct_vote_then_should_be_forwarded() { - Vote vote = mock(Vote.class); - when(vote.getView()).thenReturn(View.of(1)); - when(vote.getEpoch()).thenReturn(0L); - BFTNode author = mock(BFTNode.class); - when(vote.getAuthor()).thenReturn(author); - ECDSASignature voteSignature = mock(ECDSASignature.class); - ECDSASignature timeoutSignature = mock(ECDSASignature.class); - when(vote.getSignature()).thenReturn(voteSignature); - when(vote.getTimeoutSignature()).thenReturn(Optional.of(timeoutSignature)); - when(validatorSet.containsNode(eq(author))).thenReturn(true); - when(verifier.verify(any(), any(), eq(voteSignature))).thenReturn(true); - when(verifier.verify(any(), any(), eq(timeoutSignature))).thenReturn(true); - eventVerifier.processVote(vote); - verify(forwardTo, times(1)).processVote(eq(vote)); - } - - @Test - public void when_process_bad_author_vote_then_should_not_be_forwarded() { - Vote vote = mock(Vote.class); - BFTNode author = mock(BFTNode.class); - when(vote.getAuthor()).thenReturn(author); - when(vote.getSignature()).thenReturn(mock(ECDSASignature.class)); - when(validatorSet.containsNode(eq(author))).thenReturn(false); - when(verifier.verify(any(), any(), any())).thenReturn(true); - eventVerifier.processVote(vote); - verify(forwardTo, never()).processVote(any()); - } - - @Test - public void when_process_bad_signature_vote_then_should_not_be_forwarded() { - Vote vote = mock(Vote.class); - BFTNode author = mock(BFTNode.class); - when(vote.getAuthor()).thenReturn(author); - when(vote.getSignature()).thenReturn(mock(ECDSASignature.class)); - when(validatorSet.containsNode(eq(author))).thenReturn(true); - when(verifier.verify(any(), any(), any())).thenReturn(false); - eventVerifier.processVote(vote); - verify(forwardTo, never()).processVote(any()); - } - - @Test - public void when_process_bad_timeout_signature_vote_then_should_not_be_forwarded() { - Vote vote = mock(Vote.class); - when(vote.getView()).thenReturn(View.of(1)); - when(vote.getEpoch()).thenReturn(0L); - BFTNode author = mock(BFTNode.class); - when(vote.getAuthor()).thenReturn(author); - ECDSASignature voteSignature = mock(ECDSASignature.class); - ECDSASignature timeoutSignature = mock(ECDSASignature.class); - when(vote.getSignature()).thenReturn(voteSignature); - when(vote.getTimeoutSignature()).thenReturn(Optional.of(timeoutSignature)); - when(validatorSet.containsNode(eq(author))).thenReturn(true); - when(verifier.verify(any(), any(), eq(voteSignature))).thenReturn(true); - when(verifier.verify(any(), any(), eq(timeoutSignature))).thenReturn(false); - eventVerifier.processVote(vote); - verify(forwardTo, never()).processVote(any()); - } + private BFTValidatorSet validatorSet; + private BFTEventProcessor forwardTo; + private Hasher hasher; + private HashVerifier verifier; + private BFTEventVerifier eventVerifier; + + @Before + public void setup() { + this.validatorSet = mock(BFTValidatorSet.class); + this.forwardTo = mock(BFTEventProcessor.class); + this.hasher = mock(Hasher.class); + this.verifier = mock(HashVerifier.class); + this.eventVerifier = new BFTEventVerifier(validatorSet, forwardTo, hasher, verifier); + } + + @Test + public void when_start__then_should_be_forwarded() { + eventVerifier.start(); + verify(forwardTo, times(1)).start(); + } + + @Test + public void when_process_local_timeout__then_should_be_forwarded() { + ScheduledLocalTimeout timeout = mock(ScheduledLocalTimeout.class); + eventVerifier.processLocalTimeout(timeout); + verify(forwardTo, times(1)).processLocalTimeout(eq(timeout)); + } + + @Test + public void when_process_local_sync__then_should_be_forwarded() { + BFTInsertUpdate update = mock(BFTInsertUpdate.class); + eventVerifier.processBFTUpdate(update); + verify(forwardTo, times(1)).processBFTUpdate(update); + } + + @Test + public void when_process_correct_proposal_then_should_be_forwarded() { + Proposal proposal = mock(Proposal.class); + BFTNode author = mock(BFTNode.class); + when(proposal.getAuthor()).thenReturn(author); + when(proposal.getSignature()).thenReturn(mock(ECDSASignature.class)); + when(validatorSet.containsNode(eq(author))).thenReturn(true); + when(verifier.verify(any(), any(), any())).thenReturn(true); + eventVerifier.processProposal(proposal); + verify(forwardTo, times(1)).processProposal(eq(proposal)); + } + + @Test + public void when_process_bad_author_proposal_then_should_not_be_forwarded() { + Proposal proposal = mock(Proposal.class); + BFTNode author = mock(BFTNode.class); + when(proposal.getAuthor()).thenReturn(author); + when(proposal.getSignature()).thenReturn(mock(ECDSASignature.class)); + when(validatorSet.containsNode(eq(author))).thenReturn(false); + when(verifier.verify(any(), any(), any())).thenReturn(true); + eventVerifier.processProposal(proposal); + verify(forwardTo, never()).processProposal(any()); + } + + @Test + public void when_process_bad_signature_proposal_then_should_not_be_forwarded() { + Proposal proposal = mock(Proposal.class); + BFTNode author = mock(BFTNode.class); + when(proposal.getAuthor()).thenReturn(author); + when(proposal.getSignature()).thenReturn(mock(ECDSASignature.class)); + when(validatorSet.containsNode(eq(author))).thenReturn(true); + when(verifier.verify(any(), any(), any())).thenReturn(false); + eventVerifier.processProposal(proposal); + verify(forwardTo, never()).processProposal(any()); + } + + @Test + public void when_process_correct_vote_then_should_be_forwarded() { + Vote vote = mock(Vote.class); + when(vote.getView()).thenReturn(View.of(1)); + when(vote.getEpoch()).thenReturn(0L); + BFTNode author = mock(BFTNode.class); + when(vote.getAuthor()).thenReturn(author); + ECDSASignature voteSignature = mock(ECDSASignature.class); + ECDSASignature timeoutSignature = mock(ECDSASignature.class); + when(vote.getSignature()).thenReturn(voteSignature); + when(vote.getTimeoutSignature()).thenReturn(Optional.of(timeoutSignature)); + when(validatorSet.containsNode(eq(author))).thenReturn(true); + when(verifier.verify(any(), any(), eq(voteSignature))).thenReturn(true); + when(verifier.verify(any(), any(), eq(timeoutSignature))).thenReturn(true); + eventVerifier.processVote(vote); + verify(forwardTo, times(1)).processVote(eq(vote)); + } + + @Test + public void when_process_bad_author_vote_then_should_not_be_forwarded() { + Vote vote = mock(Vote.class); + BFTNode author = mock(BFTNode.class); + when(vote.getAuthor()).thenReturn(author); + when(vote.getSignature()).thenReturn(mock(ECDSASignature.class)); + when(validatorSet.containsNode(eq(author))).thenReturn(false); + when(verifier.verify(any(), any(), any())).thenReturn(true); + eventVerifier.processVote(vote); + verify(forwardTo, never()).processVote(any()); + } + + @Test + public void when_process_bad_signature_vote_then_should_not_be_forwarded() { + Vote vote = mock(Vote.class); + BFTNode author = mock(BFTNode.class); + when(vote.getAuthor()).thenReturn(author); + when(vote.getSignature()).thenReturn(mock(ECDSASignature.class)); + when(validatorSet.containsNode(eq(author))).thenReturn(true); + when(verifier.verify(any(), any(), any())).thenReturn(false); + eventVerifier.processVote(vote); + verify(forwardTo, never()).processVote(any()); + } + + @Test + public void when_process_bad_timeout_signature_vote_then_should_not_be_forwarded() { + Vote vote = mock(Vote.class); + when(vote.getView()).thenReturn(View.of(1)); + when(vote.getEpoch()).thenReturn(0L); + BFTNode author = mock(BFTNode.class); + when(vote.getAuthor()).thenReturn(author); + ECDSASignature voteSignature = mock(ECDSASignature.class); + ECDSASignature timeoutSignature = mock(ECDSASignature.class); + when(vote.getSignature()).thenReturn(voteSignature); + when(vote.getTimeoutSignature()).thenReturn(Optional.of(timeoutSignature)); + when(validatorSet.containsNode(eq(author))).thenReturn(true); + when(verifier.verify(any(), any(), eq(voteSignature))).thenReturn(true); + when(verifier.verify(any(), any(), eq(timeoutSignature))).thenReturn(false); + eventVerifier.processVote(vote); + verify(forwardTo, never()).processVote(any()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTHighQCUpdateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTHighQCUpdateTest.java index 45790ee65e..66df67ddae 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTHighQCUpdateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTHighQCUpdateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class BFTHighQCUpdateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTHighQCUpdate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTHighQCUpdate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTInsertUpdateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTInsertUpdateTest.java index fea8b9c00e..4d6da84820 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTInsertUpdateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTInsertUpdateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class BFTInsertUpdateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTInsertUpdate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTInsertUpdate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTNodeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTNodeTest.java index 8f29f5f889..65aceddc2f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTNodeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTNodeTest.java @@ -69,11 +69,8 @@ public class BFTNodeTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTNode.class) - .withIgnoredFields("simpleName") - .verify(); - } - -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTNode.class).withIgnoredFields("simpleName").verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorSetTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorSetTest.java index ddd62633b6..3758ecda12 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorSetTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorSetTest.java @@ -64,158 +64,156 @@ package com.radixdlt.consensus.bft; -import com.google.common.hash.HashCode; -import com.radixdlt.utils.UInt256; - -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import com.google.common.hash.HashCode; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.HashUtils; +import com.radixdlt.utils.UInt256; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import nl.jqno.equalsverifier.EqualsVerifier; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; public class BFTValidatorSetTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTValidatorSet.class) - .verify(); - } - - @Test - public void sensibleToString() { - BFTNode node = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - String s = BFTValidatorSet.from(ImmutableList.of(BFTValidator.from(node, UInt256.ONE))).toString(); - assertThat(s) - .contains(BFTValidatorSet.class.getSimpleName()) - .contains(node.getSimpleName()); - } - - @Test - public void testStreamConstructor() { - BFTNode node = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - String s = BFTValidatorSet.from(ImmutableList.of(BFTValidator.from(node, UInt256.ONE)).stream()).toString(); - assertThat(s).contains(node.getSimpleName()); - } - - @Test - public void testValidate() { - ECKeyPair k1 = ECKeyPair.generateNew(); - ECKeyPair k2 = ECKeyPair.generateNew(); - ECKeyPair k3 = ECKeyPair.generateNew(); - ECKeyPair k4 = ECKeyPair.generateNew(); - ECKeyPair k5 = ECKeyPair.generateNew(); // Rogue signature - - BFTNode node1 = BFTNode.create(k1.getPublicKey()); - BFTNode node2 = BFTNode.create(k2.getPublicKey()); - BFTNode node3 = BFTNode.create(k3.getPublicKey()); - BFTNode node4 = BFTNode.create(k4.getPublicKey()); - BFTNode node5 = BFTNode.create(k5.getPublicKey()); - - BFTValidator v1 = BFTValidator.from(node1, UInt256.ONE); - BFTValidator v2 = BFTValidator.from(node2, UInt256.ONE); - BFTValidator v3 = BFTValidator.from(node3, UInt256.ONE); - BFTValidator v4 = BFTValidator.from(node4, UInt256.ONE); - - BFTValidatorSet vs = BFTValidatorSet.from(ImmutableSet.of(v1, v2, v3, v4)); - HashCode message = HashUtils.random256(); - - // 2 signatures for 4 validators -> fail - ValidationState vst1 = vs.newValidationState(); - assertTrue(vst1.addSignature(node1, 1L, k1.sign(message))); - assertFalse(vst1.complete()); - assertTrue(vst1.addSignature(node2, 1L, k2.sign(message))); - assertFalse(vst1.complete()); - assertEquals(2, vst1.signatures().count()); - - // 3 signatures for 4 validators -> pass - ValidationState vst2 = vs.newValidationState(); - assertTrue(vst2.addSignature(node1, 1L, k1.sign(message))); - assertFalse(vst1.complete()); - assertTrue(vst2.addSignature(node2, 1L, k2.sign(message))); - assertFalse(vst1.complete()); - assertTrue(vst2.addSignature(node3, 1L, k3.sign(message))); - assertTrue(vst2.complete()); - assertEquals(3, vst2.signatures().count()); - - // 2 signatures + 1 signature not from set for 4 validators -> fail - ValidationState vst3 = vs.newValidationState(); - assertTrue(vst3.addSignature(node1, 1L, k1.sign(message))); - assertFalse(vst3.complete()); - assertTrue(vst3.addSignature(node2, 1L, k2.sign(message))); - assertFalse(vst3.complete()); - assertFalse(vst3.addSignature(node5, 1L, k5.sign(message))); - assertFalse(vst3.complete()); - assertEquals(2, vst3.signatures().count()); - - // 3 signatures + 1 signature not from set for 4 validators -> pass - ValidationState vst4 = vs.newValidationState(); - assertTrue(vst4.addSignature(node1, 1L, k1.sign(message))); - assertFalse(vst3.complete()); - assertTrue(vst4.addSignature(node2, 1L, k2.sign(message))); - assertFalse(vst3.complete()); - assertFalse(vst4.addSignature(node5, 1L, k5.sign(message))); - assertFalse(vst3.complete()); - assertTrue(vst4.addSignature(node3, 1L, k3.sign(message))); - assertTrue(vst4.complete()); - assertEquals(3, vst4.signatures().count()); - } - - @Test - public void testValidateWithUnequalPower() { - ECKeyPair k1 = ECKeyPair.generateNew(); - ECKeyPair k2 = ECKeyPair.generateNew(); - - BFTNode node1 = BFTNode.create(k1.getPublicKey()); - BFTNode node2 = BFTNode.create(k2.getPublicKey()); - - BFTValidator v1 = BFTValidator.from(node1, UInt256.THREE); - BFTValidator v2 = BFTValidator.from(node2, UInt256.ONE); - - BFTValidatorSet vs = BFTValidatorSet.from(ImmutableSet.of(v1, v2)); - HashCode message = HashUtils.random256(); - ValidationState vst1 = vs.newValidationState(); - assertTrue(vst1.addSignature(node1, 1L, k1.sign(message))); - assertTrue(vst1.complete()); - assertEquals(1, vst1.signatures().count()); - } - - @Test - public void testRetainsOrder() { - for (var i = 0; i < 10; ++i) { - final var validators = IntStream.range(0, 100) - .mapToObj(n -> ECKeyPair.generateNew()) - .map(ECKeyPair::getPublicKey) - .map(BFTNode::create) - .map(node -> BFTValidator.from(node, UInt256.ONE)) - .collect(Collectors.toList()); - - final var validatorSet = BFTValidatorSet.from(validators); - final var setValidators = Lists.newArrayList(validatorSet.getValidators()); - checkIterableOrder(validators, setValidators); - } - } - - private void checkIterableOrder(Iterable iterable1, Iterable iterable2) { - final var i1 = iterable1.iterator(); - final var i2 = iterable2.iterator(); - - while (i1.hasNext() && i2.hasNext()) { - final var o1 = i1.next(); - final var o2 = i2.next(); - assertEquals("Objects not the same", o1, o2); - } - assertFalse("Iterable 1 larger than iterable 2", i1.hasNext()); - assertFalse("Iterable 2 larger than iterable 1", i2.hasNext()); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTValidatorSet.class).verify(); + } + + @Test + public void sensibleToString() { + BFTNode node = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + String s = + BFTValidatorSet.from(ImmutableList.of(BFTValidator.from(node, UInt256.ONE))).toString(); + assertThat(s).contains(BFTValidatorSet.class.getSimpleName()).contains(node.getSimpleName()); + } + + @Test + public void testStreamConstructor() { + BFTNode node = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + String s = + BFTValidatorSet.from(ImmutableList.of(BFTValidator.from(node, UInt256.ONE)).stream()) + .toString(); + assertThat(s).contains(node.getSimpleName()); + } + + @Test + public void testValidate() { + ECKeyPair k1 = ECKeyPair.generateNew(); + ECKeyPair k2 = ECKeyPair.generateNew(); + ECKeyPair k3 = ECKeyPair.generateNew(); + ECKeyPair k4 = ECKeyPair.generateNew(); + ECKeyPair k5 = ECKeyPair.generateNew(); // Rogue signature + + BFTNode node1 = BFTNode.create(k1.getPublicKey()); + BFTNode node2 = BFTNode.create(k2.getPublicKey()); + BFTNode node3 = BFTNode.create(k3.getPublicKey()); + BFTNode node4 = BFTNode.create(k4.getPublicKey()); + BFTNode node5 = BFTNode.create(k5.getPublicKey()); + + BFTValidator v1 = BFTValidator.from(node1, UInt256.ONE); + BFTValidator v2 = BFTValidator.from(node2, UInt256.ONE); + BFTValidator v3 = BFTValidator.from(node3, UInt256.ONE); + BFTValidator v4 = BFTValidator.from(node4, UInt256.ONE); + + BFTValidatorSet vs = BFTValidatorSet.from(ImmutableSet.of(v1, v2, v3, v4)); + HashCode message = HashUtils.random256(); + + // 2 signatures for 4 validators -> fail + ValidationState vst1 = vs.newValidationState(); + assertTrue(vst1.addSignature(node1, 1L, k1.sign(message))); + assertFalse(vst1.complete()); + assertTrue(vst1.addSignature(node2, 1L, k2.sign(message))); + assertFalse(vst1.complete()); + assertEquals(2, vst1.signatures().count()); + + // 3 signatures for 4 validators -> pass + ValidationState vst2 = vs.newValidationState(); + assertTrue(vst2.addSignature(node1, 1L, k1.sign(message))); + assertFalse(vst1.complete()); + assertTrue(vst2.addSignature(node2, 1L, k2.sign(message))); + assertFalse(vst1.complete()); + assertTrue(vst2.addSignature(node3, 1L, k3.sign(message))); + assertTrue(vst2.complete()); + assertEquals(3, vst2.signatures().count()); + + // 2 signatures + 1 signature not from set for 4 validators -> fail + ValidationState vst3 = vs.newValidationState(); + assertTrue(vst3.addSignature(node1, 1L, k1.sign(message))); + assertFalse(vst3.complete()); + assertTrue(vst3.addSignature(node2, 1L, k2.sign(message))); + assertFalse(vst3.complete()); + assertFalse(vst3.addSignature(node5, 1L, k5.sign(message))); + assertFalse(vst3.complete()); + assertEquals(2, vst3.signatures().count()); + + // 3 signatures + 1 signature not from set for 4 validators -> pass + ValidationState vst4 = vs.newValidationState(); + assertTrue(vst4.addSignature(node1, 1L, k1.sign(message))); + assertFalse(vst3.complete()); + assertTrue(vst4.addSignature(node2, 1L, k2.sign(message))); + assertFalse(vst3.complete()); + assertFalse(vst4.addSignature(node5, 1L, k5.sign(message))); + assertFalse(vst3.complete()); + assertTrue(vst4.addSignature(node3, 1L, k3.sign(message))); + assertTrue(vst4.complete()); + assertEquals(3, vst4.signatures().count()); + } + + @Test + public void testValidateWithUnequalPower() { + ECKeyPair k1 = ECKeyPair.generateNew(); + ECKeyPair k2 = ECKeyPair.generateNew(); + + BFTNode node1 = BFTNode.create(k1.getPublicKey()); + BFTNode node2 = BFTNode.create(k2.getPublicKey()); + + BFTValidator v1 = BFTValidator.from(node1, UInt256.THREE); + BFTValidator v2 = BFTValidator.from(node2, UInt256.ONE); + + BFTValidatorSet vs = BFTValidatorSet.from(ImmutableSet.of(v1, v2)); + HashCode message = HashUtils.random256(); + ValidationState vst1 = vs.newValidationState(); + assertTrue(vst1.addSignature(node1, 1L, k1.sign(message))); + assertTrue(vst1.complete()); + assertEquals(1, vst1.signatures().count()); + } + + @Test + public void testRetainsOrder() { + for (var i = 0; i < 10; ++i) { + final var validators = + IntStream.range(0, 100) + .mapToObj(n -> ECKeyPair.generateNew()) + .map(ECKeyPair::getPublicKey) + .map(BFTNode::create) + .map(node -> BFTValidator.from(node, UInt256.ONE)) + .collect(Collectors.toList()); + + final var validatorSet = BFTValidatorSet.from(validators); + final var setValidators = Lists.newArrayList(validatorSet.getValidators()); + checkIterableOrder(validators, setValidators); + } + } + + private void checkIterableOrder(Iterable iterable1, Iterable iterable2) { + final var i1 = iterable1.iterator(); + final var i2 = iterable2.iterator(); + + while (i1.hasNext() && i2.hasNext()) { + final var o1 = i1.next(); + final var o2 = i2.next(); + assertEquals("Objects not the same", o1, o2); + } + assertFalse("Iterable 1 larger than iterable 2", i1.hasNext()); + assertFalse("Iterable 2 larger than iterable 1", i2.hasNext()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorTest.java index 86d41164a3..d11387aa5b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/BFTValidatorTest.java @@ -64,34 +64,32 @@ package com.radixdlt.consensus.bft; -import com.radixdlt.utils.UInt256; -import org.junit.Test; - -import nl.jqno.equalsverifier.EqualsVerifier; - +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.mock; -import static org.assertj.core.api.Assertions.assertThat; + +import com.radixdlt.utils.UInt256; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; public class BFTValidatorTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(BFTValidator.class) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(BFTValidator.class).verify(); + } - @Test - public void sensibleToString() { - String s = create().toString(); - assertThat(s).contains(BFTValidator.class.getSimpleName()); - } + @Test + public void sensibleToString() { + String s = create().toString(); + assertThat(s).contains(BFTValidator.class.getSimpleName()); + } - @Test - public void testGetter() { - assertNotNull(create().getNode()); - } + @Test + public void testGetter() { + assertNotNull(create().getNode()); + } - private static BFTValidator create() { - return BFTValidator.from(mock(BFTNode.class), UInt256.ONE); - } + private static BFTValidator create() { + return BFTValidator.from(mock(BFTNode.class), UInt256.ONE); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/NoVoteTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/NoVoteTest.java index cdf23b6497..d046d6014c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/NoVoteTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/NoVoteTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class NoVoteTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(NoVote.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(NoVote.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ValidationStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ValidationStateTest.java index 16faa06b0f..904ed9bcfe 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ValidationStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ValidationStateTest.java @@ -64,50 +64,47 @@ package com.radixdlt.consensus.bft; -import org.junit.Test; - -import com.google.common.collect.ImmutableList; -import com.radixdlt.utils.UInt256; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import com.google.common.collect.ImmutableList; +import com.radixdlt.utils.UInt256; import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; public class ValidationStateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(ValidationState.class) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(ValidationState.class).verify(); + } - @Test - public void sensibleToString() { - String s = BFTValidatorSet.from(ImmutableList.of()).newValidationState().toString(); - assertThat(s).contains(ValidationState.class.getSimpleName()); - } + @Test + public void sensibleToString() { + String s = BFTValidatorSet.from(ImmutableList.of()).newValidationState().toString(); + assertThat(s).contains(ValidationState.class.getSimpleName()); + } - @Test - public void testAcceptableFaults() { - assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.ZERO)); - assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.ONE)); - assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.TWO)); - assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.THREE)); - assertEquals(UInt256.ONE, ValidationState.acceptableFaults(UInt256.FOUR)); - assertEquals(UInt256.THREE, ValidationState.acceptableFaults(UInt256.TEN)); - assertEquals(UInt256.from(33), ValidationState.acceptableFaults(UInt256.from(100))); - assertEquals(UInt256.from(333), ValidationState.acceptableFaults(UInt256.from(1000))); - } + @Test + public void testAcceptableFaults() { + assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.ZERO)); + assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.ONE)); + assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.TWO)); + assertEquals(UInt256.ZERO, ValidationState.acceptableFaults(UInt256.THREE)); + assertEquals(UInt256.ONE, ValidationState.acceptableFaults(UInt256.FOUR)); + assertEquals(UInt256.THREE, ValidationState.acceptableFaults(UInt256.TEN)); + assertEquals(UInt256.from(33), ValidationState.acceptableFaults(UInt256.from(100))); + assertEquals(UInt256.from(333), ValidationState.acceptableFaults(UInt256.from(1000))); + } - @Test - public void testThreshold() { - assertEquals(UInt256.ZERO, ValidationState.threshold(UInt256.ZERO)); - assertEquals(UInt256.ONE, ValidationState.threshold(UInt256.ONE)); - assertEquals(UInt256.TWO, ValidationState.threshold(UInt256.TWO)); - assertEquals(UInt256.THREE, ValidationState.threshold(UInt256.THREE)); - assertEquals(UInt256.THREE, ValidationState.threshold(UInt256.FOUR)); - assertEquals(UInt256.SEVEN, ValidationState.threshold(UInt256.from(10))); - assertEquals(UInt256.from(67), ValidationState.threshold(UInt256.from(100))); - assertEquals(UInt256.from(667), ValidationState.threshold(UInt256.from(1000))); - } + @Test + public void testThreshold() { + assertEquals(UInt256.ZERO, ValidationState.threshold(UInt256.ZERO)); + assertEquals(UInt256.ONE, ValidationState.threshold(UInt256.ONE)); + assertEquals(UInt256.TWO, ValidationState.threshold(UInt256.TWO)); + assertEquals(UInt256.THREE, ValidationState.threshold(UInt256.THREE)); + assertEquals(UInt256.THREE, ValidationState.threshold(UInt256.FOUR)); + assertEquals(UInt256.SEVEN, ValidationState.threshold(UInt256.from(10))); + assertEquals(UInt256.from(67), ValidationState.threshold(UInt256.from(100))); + assertEquals(UInt256.from(667), ValidationState.threshold(UInt256.from(1000))); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexChainTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexChainTest.java index 4a541174c7..fdf4940c1d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexChainTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexChainTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class VerifiedVertexChainTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(VerifiedVertexChain.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(VerifiedVertexChain.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateCreationTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateCreationTest.java index bc3bf92896..619ea65da5 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateCreationTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateCreationTest.java @@ -80,70 +80,61 @@ import com.radixdlt.crypto.HashUtils; import com.radixdlt.crypto.Hasher; import com.radixdlt.ledger.AccumulatorState; +import java.util.Optional; import org.junit.Before; import org.junit.Test; -import java.util.Optional; - public class VerifiedVertexStoreStateCreationTest { - private VerifiedVertex genesisVertex; - private HashCode genesisHash; - private Hasher hasher; - private static final LedgerHeader MOCKED_HEADER = LedgerHeader.create( - 0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0 - ); - - @Before - public void setup() { - this.genesisHash = HashUtils.zero256(); - this.genesisVertex = new VerifiedVertex(UnverifiedVertex.createGenesis(MOCKED_HEADER), genesisHash); - this.hasher = new Sha256Hasher(DefaultSerialization.getInstance()); - } + private VerifiedVertex genesisVertex; + private HashCode genesisHash; + private Hasher hasher; + private static final LedgerHeader MOCKED_HEADER = + LedgerHeader.create(0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0); - @Test - public void creating_vertex_store_with_root_not_committed_should_fail() { - BFTHeader genesisHeader = new BFTHeader(View.of(0), genesisHash, mock(LedgerHeader.class)); - VoteData voteData = new VoteData(genesisHeader, genesisHeader, null); - QuorumCertificate badRootQC = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - assertThatThrownBy(() -> - VerifiedVertexStoreState.create( - HighQC.from(badRootQC), - genesisVertex, - Optional.empty(), - hasher - ) - ).isInstanceOf(IllegalStateException.class); - } + @Before + public void setup() { + this.genesisHash = HashUtils.zero256(); + this.genesisVertex = + new VerifiedVertex(UnverifiedVertex.createGenesis(MOCKED_HEADER), genesisHash); + this.hasher = new Sha256Hasher(DefaultSerialization.getInstance()); + } - @Test - public void creating_vertex_store_with_committed_qc_not_matching_vertex_should_fail() { - BFTHeader genesisHeader = new BFTHeader(View.of(0), genesisHash, mock(LedgerHeader.class)); - BFTHeader otherHeader = new BFTHeader(View.of(0), HashUtils.random256(), mock(LedgerHeader.class)); - VoteData voteData = new VoteData(genesisHeader, genesisHeader, otherHeader); - QuorumCertificate badRootQC = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - assertThatThrownBy(() -> - VerifiedVertexStoreState.create( - HighQC.from(badRootQC), - genesisVertex, - Optional.empty(), - hasher - ) - ).isInstanceOf(IllegalStateException.class); - } + @Test + public void creating_vertex_store_with_root_not_committed_should_fail() { + BFTHeader genesisHeader = new BFTHeader(View.of(0), genesisHash, mock(LedgerHeader.class)); + VoteData voteData = new VoteData(genesisHeader, genesisHeader, null); + QuorumCertificate badRootQC = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + assertThatThrownBy( + () -> + VerifiedVertexStoreState.create( + HighQC.from(badRootQC), genesisVertex, Optional.empty(), hasher)) + .isInstanceOf(IllegalStateException.class); + } - @Test - public void creating_vertex_store_with_qc_not_matching_vertex_should_fail() { - BFTHeader genesisHeader = new BFTHeader(View.of(0), HashUtils.random256(), mock(LedgerHeader.class)); - VoteData voteData = new VoteData(genesisHeader, genesisHeader, genesisHeader); - QuorumCertificate badRootQC = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - assertThatThrownBy(() -> - VerifiedVertexStoreState.create( - HighQC.from(badRootQC), - genesisVertex, - Optional.empty(), - hasher - ) - ).isInstanceOf(IllegalStateException.class); - } + @Test + public void creating_vertex_store_with_committed_qc_not_matching_vertex_should_fail() { + BFTHeader genesisHeader = new BFTHeader(View.of(0), genesisHash, mock(LedgerHeader.class)); + BFTHeader otherHeader = + new BFTHeader(View.of(0), HashUtils.random256(), mock(LedgerHeader.class)); + VoteData voteData = new VoteData(genesisHeader, genesisHeader, otherHeader); + QuorumCertificate badRootQC = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + assertThatThrownBy( + () -> + VerifiedVertexStoreState.create( + HighQC.from(badRootQC), genesisVertex, Optional.empty(), hasher)) + .isInstanceOf(IllegalStateException.class); + } + @Test + public void creating_vertex_store_with_qc_not_matching_vertex_should_fail() { + BFTHeader genesisHeader = + new BFTHeader(View.of(0), HashUtils.random256(), mock(LedgerHeader.class)); + VoteData voteData = new VoteData(genesisHeader, genesisHeader, genesisHeader); + QuorumCertificate badRootQC = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + assertThatThrownBy( + () -> + VerifiedVertexStoreState.create( + HighQC.from(badRootQC), genesisVertex, Optional.empty(), hasher)) + .isInstanceOf(IllegalStateException.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateTest.java index 43d2c3f91c..75d8e2ee42 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexStoreStateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class VerifiedVertexStoreStateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(VerifiedVertexStoreState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(VerifiedVertexStoreState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexTest.java index 26d5b1cffb..4aadba290d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VerifiedVertexTest.java @@ -64,23 +64,18 @@ package com.radixdlt.consensus.bft; -import org.junit.Test; - import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; - import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; -/** - * Basic tests for {@link VerifiedVertex} - */ +/** Basic tests for {@link VerifiedVertex} */ public class VerifiedVertexTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(VerifiedVertex.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - + @Test + public void equalsContract() { + EqualsVerifier.forClass(VerifiedVertex.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java index 5c298fcd81..d5aa0b5b1a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java @@ -78,21 +78,20 @@ import com.google.common.collect.ImmutableMap; import com.google.common.hash.HashCode; import com.radixdlt.atom.Txn; +import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.Ledger; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.Sha256Hasher; import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.consensus.TimestampedECDSASignatures; -import com.radixdlt.consensus.VoteData; import com.radixdlt.consensus.UnverifiedVertex; -import com.radixdlt.consensus.Sha256Hasher; +import com.radixdlt.consensus.VoteData; import com.radixdlt.crypto.HashUtils; import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.ledger.AccumulatorState; - import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -104,176 +103,194 @@ import org.junit.Test; public class VertexStoreTest { - private VerifiedVertex genesisVertex; - private Supplier nextVertex; - private Function nextSkippableVertex; - private HashCode genesisHash; - private QuorumCertificate rootQC; - private VertexStore sut; - private Ledger ledger; - private EventDispatcher bftUpdateSender; - private EventDispatcher rebuildUpdateEventDispatcher; - private EventDispatcher bftHighQCUpdateEventDispatcher; - private EventDispatcher committedSender; - private Hasher hasher = Sha256Hasher.withDefaultSerialization(); + private VerifiedVertex genesisVertex; + private Supplier nextVertex; + private Function nextSkippableVertex; + private HashCode genesisHash; + private QuorumCertificate rootQC; + private VertexStore sut; + private Ledger ledger; + private EventDispatcher bftUpdateSender; + private EventDispatcher rebuildUpdateEventDispatcher; + private EventDispatcher bftHighQCUpdateEventDispatcher; + private EventDispatcher committedSender; + private Hasher hasher = Sha256Hasher.withDefaultSerialization(); - private static final LedgerHeader MOCKED_HEADER = LedgerHeader.create( - 0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0 - ); + private static final LedgerHeader MOCKED_HEADER = + LedgerHeader.create(0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0); - @Before - public void setUp() { - // No type check issues with mocking generic here - Ledger ssc = mock(Ledger.class); - this.ledger = ssc; - // TODO: replace mock with the real thing - doAnswer(invocation -> { - VerifiedVertex verifiedVertex = invocation.getArgument(1); - return Optional.of(new PreparedVertex(verifiedVertex, MOCKED_HEADER, ImmutableList.of(), ImmutableMap.of(), 1L)); - }).when(ledger).prepare(any(), any()); + @Before + public void setUp() { + // No type check issues with mocking generic here + Ledger ssc = mock(Ledger.class); + this.ledger = ssc; + // TODO: replace mock with the real thing + doAnswer( + invocation -> { + VerifiedVertex verifiedVertex = invocation.getArgument(1); + return Optional.of( + new PreparedVertex( + verifiedVertex, MOCKED_HEADER, ImmutableList.of(), ImmutableMap.of(), 1L)); + }) + .when(ledger) + .prepare(any(), any()); - this.bftUpdateSender = rmock(EventDispatcher.class); - this.rebuildUpdateEventDispatcher = rmock(EventDispatcher.class); - this.bftHighQCUpdateEventDispatcher = rmock(EventDispatcher.class); - this.committedSender = rmock(EventDispatcher.class); + this.bftUpdateSender = rmock(EventDispatcher.class); + this.rebuildUpdateEventDispatcher = rmock(EventDispatcher.class); + this.bftHighQCUpdateEventDispatcher = rmock(EventDispatcher.class); + this.committedSender = rmock(EventDispatcher.class); - this.genesisHash = HashUtils.zero256(); - this.genesisVertex = new VerifiedVertex(UnverifiedVertex.createGenesis(MOCKED_HEADER), genesisHash); - this.rootQC = QuorumCertificate.ofGenesis(genesisVertex, MOCKED_HEADER); - this.sut = VertexStore.create( - VerifiedVertexStoreState.create(HighQC.from(rootQC), genesisVertex, Optional.empty(), hasher), - ledger, - hasher, - bftUpdateSender, - rebuildUpdateEventDispatcher, - bftHighQCUpdateEventDispatcher, - committedSender - ); + this.genesisHash = HashUtils.zero256(); + this.genesisVertex = + new VerifiedVertex(UnverifiedVertex.createGenesis(MOCKED_HEADER), genesisHash); + this.rootQC = QuorumCertificate.ofGenesis(genesisVertex, MOCKED_HEADER); + this.sut = + VertexStore.create( + VerifiedVertexStoreState.create( + HighQC.from(rootQC), genesisVertex, Optional.empty(), hasher), + ledger, + hasher, + bftUpdateSender, + rebuildUpdateEventDispatcher, + bftHighQCUpdateEventDispatcher, + committedSender); - AtomicReference lastParentHeader - = new AtomicReference<>(new BFTHeader(View.genesis(), genesisHash, MOCKED_HEADER)); - AtomicReference lastGrandParentHeader - = new AtomicReference<>(new BFTHeader(View.genesis(), genesisHash, MOCKED_HEADER)); - AtomicReference lastGreatGrandParentHeader - = new AtomicReference<>(new BFTHeader(View.genesis(), genesisHash, MOCKED_HEADER)); + AtomicReference lastParentHeader = + new AtomicReference<>(new BFTHeader(View.genesis(), genesisHash, MOCKED_HEADER)); + AtomicReference lastGrandParentHeader = + new AtomicReference<>(new BFTHeader(View.genesis(), genesisHash, MOCKED_HEADER)); + AtomicReference lastGreatGrandParentHeader = + new AtomicReference<>(new BFTHeader(View.genesis(), genesisHash, MOCKED_HEADER)); - this.nextSkippableVertex = (skipOne) -> { - BFTHeader parentHeader = lastParentHeader.get(); - BFTHeader grandParentHeader = lastGrandParentHeader.get(); - BFTHeader greatGrandParentHeader = lastGreatGrandParentHeader.get(); - final QuorumCertificate qc; - if (!parentHeader.getView().equals(View.genesis())) { - VoteData data = new VoteData(parentHeader, grandParentHeader, skipOne ? null : greatGrandParentHeader); - qc = new QuorumCertificate(data, new TimestampedECDSASignatures()); - } else { - qc = rootQC; - } - View view = parentHeader.getView().next(); - if (skipOne) { - view = view.next(); - } + this.nextSkippableVertex = + (skipOne) -> { + BFTHeader parentHeader = lastParentHeader.get(); + BFTHeader grandParentHeader = lastGrandParentHeader.get(); + BFTHeader greatGrandParentHeader = lastGreatGrandParentHeader.get(); + final QuorumCertificate qc; + if (!parentHeader.getView().equals(View.genesis())) { + VoteData data = + new VoteData( + parentHeader, grandParentHeader, skipOne ? null : greatGrandParentHeader); + qc = new QuorumCertificate(data, new TimestampedECDSASignatures()); + } else { + qc = rootQC; + } + View view = parentHeader.getView().next(); + if (skipOne) { + view = view.next(); + } - var rawVertex = UnverifiedVertex.create(qc, view, List.of(Txn.create(new byte[0])), BFTNode.random()); - HashCode hash = hasher.hash(rawVertex); - VerifiedVertex vertex = new VerifiedVertex(rawVertex, hash); - lastParentHeader.set(new BFTHeader(view, hash, MOCKED_HEADER)); - lastGrandParentHeader.set(parentHeader); - lastGreatGrandParentHeader.set(grandParentHeader); + var rawVertex = + UnverifiedVertex.create(qc, view, List.of(Txn.create(new byte[0])), BFTNode.random()); + HashCode hash = hasher.hash(rawVertex); + VerifiedVertex vertex = new VerifiedVertex(rawVertex, hash); + lastParentHeader.set(new BFTHeader(view, hash, MOCKED_HEADER)); + lastGrandParentHeader.set(parentHeader); + lastGreatGrandParentHeader.set(grandParentHeader); - return vertex; - }; + return vertex; + }; - this.nextVertex = () -> nextSkippableVertex.apply(false); - } + this.nextVertex = () -> nextSkippableVertex.apply(false); + } - @Test - public void adding_a_qc_should_update_highest_qc() { - // Arrange - final List vertices = Stream.generate(this.nextVertex).limit(4).collect(Collectors.toList()); - sut.insertVertex(vertices.get(0)); + @Test + public void adding_a_qc_should_update_highest_qc() { + // Arrange + final List vertices = + Stream.generate(this.nextVertex).limit(4).collect(Collectors.toList()); + sut.insertVertex(vertices.get(0)); - // Act - QuorumCertificate qc = vertices.get(1).getQC(); - sut.addQC(qc); + // Act + QuorumCertificate qc = vertices.get(1).getQC(); + sut.addQC(qc); - // Assert - assertThat(sut.highQC().highestQC()).isEqualTo(qc); - assertThat(sut.highQC().highestCommittedQC()).isEqualTo(rootQC); - } + // Assert + assertThat(sut.highQC().highestQC()).isEqualTo(qc); + assertThat(sut.highQC().highestCommittedQC()).isEqualTo(rootQC); + } - @Test - public void adding_a_qc_with_commit_should_commit_vertices_to_ledger() { - // Arrange - final List vertices = Stream.generate(this.nextVertex).limit(4).collect(Collectors.toList()); - sut.insertVertex(vertices.get(0)); - sut.insertVertex(vertices.get(1)); - sut.insertVertex(vertices.get(2)); + @Test + public void adding_a_qc_with_commit_should_commit_vertices_to_ledger() { + // Arrange + final List vertices = + Stream.generate(this.nextVertex).limit(4).collect(Collectors.toList()); + sut.insertVertex(vertices.get(0)); + sut.insertVertex(vertices.get(1)); + sut.insertVertex(vertices.get(2)); - // Act - QuorumCertificate qc = vertices.get(3).getQC(); - boolean success = sut.addQC(qc); + // Act + QuorumCertificate qc = vertices.get(3).getQC(); + boolean success = sut.addQC(qc); - // Assert - assertThat(success).isTrue(); - assertThat(sut.highQC().highestQC()).isEqualTo(qc); - assertThat(sut.highQC().highestCommittedQC()).isEqualTo(qc); - assertThat(sut.getVertices(vertices.get(2).getId(), 3)).hasValue(ImmutableList.of( - vertices.get(2), vertices.get(1), vertices.get(0) - )); - verify(committedSender, times(1)).dispatch(argThat( - u -> u.getCommitted().size() == 1 && u.getCommitted().get(0).getVertex().equals(vertices.get(0)) - )); - } + // Assert + assertThat(success).isTrue(); + assertThat(sut.highQC().highestQC()).isEqualTo(qc); + assertThat(sut.highQC().highestCommittedQC()).isEqualTo(qc); + assertThat(sut.getVertices(vertices.get(2).getId(), 3)) + .hasValue(ImmutableList.of(vertices.get(2), vertices.get(1), vertices.get(0))); + verify(committedSender, times(1)) + .dispatch( + argThat( + u -> + u.getCommitted().size() == 1 + && u.getCommitted().get(0).getVertex().equals(vertices.get(0)))); + } - @Test - public void adding_a_qc_which_has_not_been_inserted_should_return_false() { - // Arrange - this.nextVertex.get(); + @Test + public void adding_a_qc_which_has_not_been_inserted_should_return_false() { + // Arrange + this.nextVertex.get(); - // Act - QuorumCertificate qc = this.nextVertex.get().getQC(); - boolean success = sut.addQC(qc); + // Act + QuorumCertificate qc = this.nextVertex.get().getQC(); + boolean success = sut.addQC(qc); - // Assert - assertThat(success).isFalse(); - } + // Assert + assertThat(success).isFalse(); + } - @Test - public void rebuilding_should_emit_updates() { - // Arrange - final List vertices = Stream.generate(this.nextVertex).limit(4).collect(Collectors.toList()); - VerifiedVertexStoreState vertexStoreState = VerifiedVertexStoreState.create( - HighQC.from(vertices.get(3).getQC()), - vertices.get(0), - vertices.stream().skip(1).collect(ImmutableList.toImmutableList()), - sut.getHighestTimeoutCertificate(), - hasher - ); + @Test + public void rebuilding_should_emit_updates() { + // Arrange + final List vertices = + Stream.generate(this.nextVertex).limit(4).collect(Collectors.toList()); + VerifiedVertexStoreState vertexStoreState = + VerifiedVertexStoreState.create( + HighQC.from(vertices.get(3).getQC()), + vertices.get(0), + vertices.stream().skip(1).collect(ImmutableList.toImmutableList()), + sut.getHighestTimeoutCertificate(), + hasher); - // Act - sut.tryRebuild(vertexStoreState); + // Act + sut.tryRebuild(vertexStoreState); - // Assert - verify(rebuildUpdateEventDispatcher, times(1)).dispatch( - argThat(u -> { - List sentVertices = u.getVertexStoreState().getVertices(); - return sentVertices.equals(vertices.subList(1, vertices.size())); - }) - ); - } + // Assert + verify(rebuildUpdateEventDispatcher, times(1)) + .dispatch( + argThat( + u -> { + List sentVertices = u.getVertexStoreState().getVertices(); + return sentVertices.equals(vertices.subList(1, vertices.size())); + })); + } - @Test - public void inserting_a_tc_should_only_replace_tcs_for_lower_views() { - TimeoutCertificate initialTC = new TimeoutCertificate(1, View.of(100), mock(TimestampedECDSASignatures.class)); - TimeoutCertificate higherTC = new TimeoutCertificate(1, View.of(101), mock(TimestampedECDSASignatures.class)); + @Test + public void inserting_a_tc_should_only_replace_tcs_for_lower_views() { + TimeoutCertificate initialTC = + new TimeoutCertificate(1, View.of(100), mock(TimestampedECDSASignatures.class)); + TimeoutCertificate higherTC = + new TimeoutCertificate(1, View.of(101), mock(TimestampedECDSASignatures.class)); - sut.insertTimeoutCertificate(initialTC); - assertEquals(initialTC, sut.getHighestTimeoutCertificate().orElse(null)); + sut.insertTimeoutCertificate(initialTC); + assertEquals(initialTC, sut.getHighestTimeoutCertificate().orElse(null)); - sut.insertTimeoutCertificate(higherTC); - assertEquals(higherTC, sut.getHighestTimeoutCertificate().orElse(null)); + sut.insertTimeoutCertificate(higherTC); + assertEquals(higherTC, sut.getHighestTimeoutCertificate().orElse(null)); - sut.insertTimeoutCertificate(initialTC); - assertEquals(higherTC, sut.getHighestTimeoutCertificate().orElse(null)); - } + sut.insertTimeoutCertificate(initialTC); + assertEquals(higherTC, sut.getHighestTimeoutCertificate().orElse(null)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewQuorumReachedTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewQuorumReachedTest.java index 96e8207bda..b2097e87a9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewQuorumReachedTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewQuorumReachedTest.java @@ -69,9 +69,8 @@ public class ViewQuorumReachedTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(ViewQuorumReached.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(ViewQuorumReached.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewUpdateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewUpdateTest.java index b42e866b84..8e2bcaa9fa 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewUpdateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewUpdateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class ViewUpdateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(ViewUpdate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(ViewUpdate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewVotingResultTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewVotingResultTest.java index 585f4853a9..2db022b2d5 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewVotingResultTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/ViewVotingResultTest.java @@ -71,16 +71,15 @@ public class ViewVotingResultTest { - @Test - public void equalsFormedTC() { - EqualsVerifier.forClass(ViewVotingResult.FormedTC.class) - .verify(); - } + @Test + public void equalsFormedTC() { + EqualsVerifier.forClass(ViewVotingResult.FormedTC.class).verify(); + } - @Test - public void equalsFormedQC() { - EqualsVerifier.forClass(ViewVotingResult.FormedQC.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsFormedQC() { + EqualsVerifier.forClass(ViewVotingResult.FormedQC.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VoteProcessingResultTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VoteProcessingResultTest.java index 521f4b1d2a..ef8e3bed6c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VoteProcessingResultTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/bft/VoteProcessingResultTest.java @@ -69,21 +69,18 @@ public class VoteProcessingResultTest { - @Test - public void equalsVoteAccepted() { - EqualsVerifier.forClass(VoteProcessingResult.VoteAccepted.class) - .verify(); - } + @Test + public void equalsVoteAccepted() { + EqualsVerifier.forClass(VoteProcessingResult.VoteAccepted.class).verify(); + } - @Test - public void equalsVoteRejected() { - EqualsVerifier.forClass(VoteProcessingResult.VoteRejected.class) - .verify(); - } + @Test + public void equalsVoteRejected() { + EqualsVerifier.forClass(VoteProcessingResult.VoteRejected.class).verify(); + } - @Test - public void equalsQuorumReached() { - EqualsVerifier.forClass(VoteProcessingResult.QuorumReached.class) - .verify(); - } + @Test + public void equalsQuorumReached() { + EqualsVerifier.forClass(VoteProcessingResult.QuorumReached.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochChangeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochChangeTest.java index 4babad0bda..aa23e17664 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochChangeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochChangeTest.java @@ -68,44 +68,42 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import nl.jqno.equalsverifier.EqualsVerifier; - import com.google.common.hash.HashCode; import com.radixdlt.consensus.BFTConfiguration; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.crypto.HashUtils; - +import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Before; import org.junit.Test; public class EpochChangeTest { - private LedgerProof proof; - private BFTConfiguration configuration; - private EpochChange epochChange; + private LedgerProof proof; + private BFTConfiguration configuration; + private EpochChange epochChange; - @Before - public void setup() { - this.proof = mock(LedgerProof.class); - when(proof.getEpoch()).thenReturn(323L); - this.configuration = mock(BFTConfiguration.class); + @Before + public void setup() { + this.proof = mock(LedgerProof.class); + when(proof.getEpoch()).thenReturn(323L); + this.configuration = mock(BFTConfiguration.class); - this.epochChange = new EpochChange(proof, configuration); - } + this.epochChange = new EpochChange(proof, configuration); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(EpochChange.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(EpochChange.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void when_get_next_epoch__then_should_be_epoch_after_proof() { - assertThat(epochChange.getEpoch()).isEqualTo(324L); - } + @Test + public void when_get_next_epoch__then_should_be_epoch_after_proof() { + assertThat(epochChange.getEpoch()).isEqualTo(324L); + } - @Test - public void when_get_configuration__then_should_return_configuration() { - assertThat(epochChange.getBFTConfiguration()).isEqualTo(configuration); - } -} \ No newline at end of file + @Test + public void when_get_configuration__then_should_return_configuration() { + assertThat(epochChange.getBFTConfiguration()).isEqualTo(configuration); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochManagerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochManagerTest.java index 8e6724a11d..c7160b2cab 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochManagerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochManagerTest.java @@ -89,36 +89,36 @@ import com.radixdlt.consensus.HashSigner; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.bft.BFTHighQCUpdate; -import com.radixdlt.consensus.bft.BFTRebuildUpdate; -import com.radixdlt.consensus.bft.NoVote; -import com.radixdlt.consensus.bft.VerifiedVertexStoreState; -import com.radixdlt.consensus.bft.ViewQuorumReached; -import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; -import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.UnverifiedVertex; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTCommittedUpdate; +import com.radixdlt.consensus.bft.BFTHighQCUpdate; import com.radixdlt.consensus.bft.BFTInsertUpdate; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.BFTRebuildUpdate; +import com.radixdlt.consensus.bft.BFTValidator; +import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.NoVote; import com.radixdlt.consensus.bft.PacemakerMaxExponent; import com.radixdlt.consensus.bft.PacemakerRate; import com.radixdlt.consensus.bft.PacemakerTimeout; import com.radixdlt.consensus.bft.PersistentVertexStore; import com.radixdlt.consensus.bft.Self; import com.radixdlt.consensus.bft.VerifiedVertex; +import com.radixdlt.consensus.bft.VerifiedVertexStoreState; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.bft.ViewQuorumReached; import com.radixdlt.consensus.bft.ViewUpdate; +import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.consensus.liveness.LocalTimeoutOccurrence; import com.radixdlt.consensus.liveness.NextTxnsGenerator; import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; import com.radixdlt.consensus.liveness.WeightedRotatingLeaders; import com.radixdlt.consensus.safety.PersistentSafetyStateStore; import com.radixdlt.consensus.sync.BFTSyncPatienceMillis; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.BFTValidator; -import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.sync.GetVerticesErrorResponse; import com.radixdlt.consensus.sync.GetVerticesRequest; import com.radixdlt.consensus.sync.GetVerticesResponse; @@ -140,204 +140,226 @@ import com.radixdlt.mempool.Mempool; import com.radixdlt.mempool.MempoolAdd; import com.radixdlt.middleware2.network.GetVerticesRequestRateLimit; -import com.radixdlt.utils.TimeSupplier; import com.radixdlt.store.LastEpochProof; import com.radixdlt.store.LastProof; import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.sync.messages.remote.LedgerStatusUpdate; +import com.radixdlt.utils.TimeSupplier; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.junit.Before; import org.junit.Test; -import javax.annotation.Nullable; - public class EpochManagerTest { - @Inject - private EpochManager epochManager; + @Inject private EpochManager epochManager; - @Inject - private Hasher hasher; + @Inject private Hasher hasher; - private ECKeyPair ecKeyPair = ECKeyPair.generateNew(); + private ECKeyPair ecKeyPair = ECKeyPair.generateNew(); - private NextTxnsGenerator nextTxnsGenerator = mock(NextTxnsGenerator.class); - private ScheduledEventDispatcher timeoutScheduler = rmock(ScheduledEventDispatcher.class); - private EventDispatcher syncLedgerRequestSender = rmock(EventDispatcher.class); - private RemoteEventDispatcher proposalDispatcher = rmock(RemoteEventDispatcher.class); - private RemoteEventDispatcher voteDispatcher = rmock(RemoteEventDispatcher.class); - private Mempool mempool = mock(Mempool.class); - private StateComputer stateComputer = new StateComputer() { - @Override - public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { - // No-op - } + private NextTxnsGenerator nextTxnsGenerator = mock(NextTxnsGenerator.class); + private ScheduledEventDispatcher timeoutScheduler = + rmock(ScheduledEventDispatcher.class); + private EventDispatcher syncLedgerRequestSender = rmock(EventDispatcher.class); + private RemoteEventDispatcher proposalDispatcher = rmock(RemoteEventDispatcher.class); + private RemoteEventDispatcher voteDispatcher = rmock(RemoteEventDispatcher.class); + private Mempool mempool = mock(Mempool.class); + private StateComputer stateComputer = + new StateComputer() { + @Override + public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { + // No-op + } - @Override - public List getNextTxnsFromMempool(List prepared) { - return List.of(); - } + @Override + public List getNextTxnsFromMempool(List prepared) { + return List.of(); + } - @Override - public StateComputerResult prepare(List previous, VerifiedVertex vertex, long timestamp) { - return new StateComputerResult(List.of(), Map.of()); - } + @Override + public StateComputerResult prepare( + List previous, VerifiedVertex vertex, long timestamp) { + return new StateComputerResult(List.of(), Map.of()); + } - @Override - public void commit(VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { - // No-op - } - }; + @Override + public void commit( + VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { + // No-op + } + }; - private Module getExternalModule() { - BFTNode self = BFTNode.create(ecKeyPair.getPublicKey()); - return new AbstractModule() { - @Override - protected void configure() { - bind(HashSigner.class).toInstance(ecKeyPair::sign); - bind(BFTNode.class).annotatedWith(Self.class).toInstance(self); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(syncLedgerRequestSender); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(timeoutScheduler); - bind(new TypeLiteral>() { }) - .toInstance(rmock(ScheduledEventDispatcher.class)); - bind(new TypeLiteral>>() { }) - .toInstance(rmock(ScheduledEventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(ScheduledEventDispatcher.class)); - bind(new TypeLiteral>() { }).toInstance(proposalDispatcher); - bind(new TypeLiteral>() { }).toInstance(voteDispatcher); - bind(new TypeLiteral>() { }) - .toInstance(rmock(RemoteEventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(RemoteEventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(RemoteEventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(rmock(RemoteEventDispatcher.class)); + private Module getExternalModule() { + BFTNode self = BFTNode.create(ecKeyPair.getPublicKey()); + return new AbstractModule() { + @Override + protected void configure() { + bind(HashSigner.class).toInstance(ecKeyPair::sign); + bind(BFTNode.class).annotatedWith(Self.class).toInstance(self); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(syncLedgerRequestSender); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(timeoutScheduler); + bind(new TypeLiteral>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + bind(new TypeLiteral>>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(ScheduledEventDispatcher.class)); + bind(new TypeLiteral>() {}).toInstance(proposalDispatcher); + bind(new TypeLiteral>() {}).toInstance(voteDispatcher); + bind(new TypeLiteral>() {}) + .toInstance(rmock(RemoteEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(RemoteEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(RemoteEventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(rmock(RemoteEventDispatcher.class)); - bind(PersistentSafetyStateStore.class).toInstance(mock(PersistentSafetyStateStore.class)); - bind(NextTxnsGenerator.class).toInstance(nextTxnsGenerator); - bind(SystemCounters.class).toInstance(new SystemCountersImpl()); - bind(Mempool.class).toInstance(mempool); - bind(StateComputer.class).toInstance(stateComputer); - bind(PersistentVertexStore.class).toInstance(mock(PersistentVertexStore.class)); - bind(RateLimiter.class).annotatedWith(GetVerticesRequestRateLimit.class) - .toInstance(RateLimiter.create(Double.MAX_VALUE)); - bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(50); - bindConstant().annotatedWith(PacemakerTimeout.class).to(10L); - bindConstant().annotatedWith(PacemakerRate.class).to(2.0); - bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); - bind(TimeSupplier.class).toInstance(System::currentTimeMillis); + bind(PersistentSafetyStateStore.class).toInstance(mock(PersistentSafetyStateStore.class)); + bind(NextTxnsGenerator.class).toInstance(nextTxnsGenerator); + bind(SystemCounters.class).toInstance(new SystemCountersImpl()); + bind(Mempool.class).toInstance(mempool); + bind(StateComputer.class).toInstance(stateComputer); + bind(PersistentVertexStore.class).toInstance(mock(PersistentVertexStore.class)); + bind(RateLimiter.class) + .annotatedWith(GetVerticesRequestRateLimit.class) + .toInstance(RateLimiter.create(Double.MAX_VALUE)); + bindConstant().annotatedWith(BFTSyncPatienceMillis.class).to(50); + bindConstant().annotatedWith(PacemakerTimeout.class).to(10L); + bindConstant().annotatedWith(PacemakerRate.class).to(2.0); + bindConstant().annotatedWith(PacemakerMaxExponent.class).to(0); + bind(TimeSupplier.class).toInstance(System::currentTimeMillis); - bind(new TypeLiteral>() { }).toInstance(rmock(Consumer.class)); - } + bind(new TypeLiteral>() {}).toInstance(rmock(Consumer.class)); + } - @Provides - private ViewUpdate view( - BFTConfiguration bftConfiguration - ) { - HighQC highQC = bftConfiguration.getVertexStoreState().getHighQC(); - View view = highQC.highestQC().getView().next(); - return ViewUpdate.create(view, highQC, self, self); - } + @Provides + private ViewUpdate view(BFTConfiguration bftConfiguration) { + HighQC highQC = bftConfiguration.getVertexStoreState().getHighQC(); + View view = highQC.highestQC().getView().next(); + return ViewUpdate.create(view, highQC, self, self); + } - @Provides - BFTValidatorSet validatorSet() { - return BFTValidatorSet.from(Stream.of(BFTValidator.from(self, UInt256.ONE))); - } + @Provides + BFTValidatorSet validatorSet() { + return BFTValidatorSet.from(Stream.of(BFTValidator.from(self, UInt256.ONE))); + } - @Provides - @LastProof - LedgerProof verifiedLedgerHeaderAndProof(BFTValidatorSet validatorSet) { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - return LedgerProof.genesis(accumulatorState, validatorSet, 0); - } + @Provides + @LastProof + LedgerProof verifiedLedgerHeaderAndProof(BFTValidatorSet validatorSet) { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + return LedgerProof.genesis(accumulatorState, validatorSet, 0); + } - @Provides - @LastEpochProof - LedgerProof lastEpochProof(BFTValidatorSet validatorSet) { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - return LedgerProof.genesis(accumulatorState, validatorSet, 0); - } + @Provides + @LastEpochProof + LedgerProof lastEpochProof(BFTValidatorSet validatorSet) { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + return LedgerProof.genesis(accumulatorState, validatorSet, 0); + } - @Provides - BFTConfiguration bftConfiguration(@Self BFTNode self, Hasher hasher, BFTValidatorSet validatorSet) { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - var unverifiedVertex = UnverifiedVertex.createGenesis( - LedgerHeader.genesis(accumulatorState, validatorSet, 0) - ); - var verifiedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); - var qc = QuorumCertificate.ofGenesis(verifiedVertex, LedgerHeader.genesis(accumulatorState, validatorSet, 0)); - var proposerElection = new WeightedRotatingLeaders(validatorSet); - return new BFTConfiguration( - proposerElection, - validatorSet, - VerifiedVertexStoreState.create(HighQC.from(qc), verifiedVertex, Optional.empty(), hasher) - ); - } - }; - } + @Provides + BFTConfiguration bftConfiguration( + @Self BFTNode self, Hasher hasher, BFTValidatorSet validatorSet) { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + var unverifiedVertex = + UnverifiedVertex.createGenesis(LedgerHeader.genesis(accumulatorState, validatorSet, 0)); + var verifiedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); + var qc = + QuorumCertificate.ofGenesis( + verifiedVertex, LedgerHeader.genesis(accumulatorState, validatorSet, 0)); + var proposerElection = new WeightedRotatingLeaders(validatorSet); + return new BFTConfiguration( + proposerElection, + validatorSet, + VerifiedVertexStoreState.create( + HighQC.from(qc), verifiedVertex, Optional.empty(), hasher)); + } + }; + } - @Before - public void setup() { - Guice.createInjector( - new CryptoModule(), - new ConsensusModule(), - new EpochsConsensusModule(), - new LedgerModule(), - getExternalModule() - ).injectMembers(this); - } + @Before + public void setup() { + Guice.createInjector( + new CryptoModule(), + new ConsensusModule(), + new EpochsConsensusModule(), + new LedgerModule(), + getExternalModule()) + .injectMembers(this); + } - @Test - public void should_not_send_consensus_messages_if_not_part_of_new_epoch() { - // Arrange - epochManager.start(); - BFTValidatorSet nextValidatorSet = BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))); - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - LedgerHeader header = LedgerHeader.genesis(accumulatorState, nextValidatorSet, 0); - UnverifiedVertex genesisVertex = UnverifiedVertex.createGenesis(header); - VerifiedVertex verifiedGenesisVertex = new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); - LedgerHeader nextLedgerHeader = LedgerHeader.create( - header.getEpoch() + 1, - View.genesis(), - header.getAccumulatorState(), - header.timestamp() - ); - var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); - var proposerElection = new WeightedRotatingLeaders(nextValidatorSet); - var bftConfiguration = new BFTConfiguration( - proposerElection, - nextValidatorSet, - VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher) - ); - LedgerProof proof = mock(LedgerProof.class); - when(proof.getEpoch()).thenReturn(header.getEpoch() + 1); - var epochChange = new EpochChange(proof, bftConfiguration); - var ledgerUpdate = new LedgerUpdate(mock(VerifiedTxnsAndProof.class), ImmutableClassToInstanceMap.of(EpochChange.class, epochChange)); + @Test + public void should_not_send_consensus_messages_if_not_part_of_new_epoch() { + // Arrange + epochManager.start(); + BFTValidatorSet nextValidatorSet = + BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))); + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + LedgerHeader header = LedgerHeader.genesis(accumulatorState, nextValidatorSet, 0); + UnverifiedVertex genesisVertex = UnverifiedVertex.createGenesis(header); + VerifiedVertex verifiedGenesisVertex = + new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); + LedgerHeader nextLedgerHeader = + LedgerHeader.create( + header.getEpoch() + 1, + View.genesis(), + header.getAccumulatorState(), + header.timestamp()); + var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); + var proposerElection = new WeightedRotatingLeaders(nextValidatorSet); + var bftConfiguration = + new BFTConfiguration( + proposerElection, + nextValidatorSet, + VerifiedVertexStoreState.create( + HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher)); + LedgerProof proof = mock(LedgerProof.class); + when(proof.getEpoch()).thenReturn(header.getEpoch() + 1); + var epochChange = new EpochChange(proof, bftConfiguration); + var ledgerUpdate = + new LedgerUpdate( + mock(VerifiedTxnsAndProof.class), + ImmutableClassToInstanceMap.of(EpochChange.class, epochChange)); - // Act - epochManager.epochsLedgerUpdateEventProcessor().process(ledgerUpdate); + // Act + epochManager.epochsLedgerUpdateEventProcessor().process(ledgerUpdate); - // Assert - verify(proposalDispatcher, never()).dispatch(any(Iterable.class), argThat(p -> p.getEpoch() == epochChange.getEpoch())); - verify(voteDispatcher, never()).dispatch(any(BFTNode.class), any()); - } + // Assert + verify(proposalDispatcher, never()) + .dispatch(any(Iterable.class), argThat(p -> p.getEpoch() == epochChange.getEpoch())); + verify(voteDispatcher, never()).dispatch(any(BFTNode.class), any()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewTest.java index d23426a0a5..fb1e92a527 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewTest.java @@ -74,30 +74,29 @@ public class EpochViewTest { - @Test - public void testBadArgument() { - assertThatThrownBy(() -> EpochView.of(-1, View.of(1))) - .isInstanceOf(IllegalArgumentException.class); - } + @Test + public void testBadArgument() { + assertThatThrownBy(() -> EpochView.of(-1, View.of(1))) + .isInstanceOf(IllegalArgumentException.class); + } - @Test - public void testGetters() { - View view = mock(View.class); - EpochView epochView = EpochView.of(12345L, view); - assertThat(epochView.getEpoch()).isEqualTo(12345L); - assertThat(epochView.getView()).isEqualTo(view); - } + @Test + public void testGetters() { + View view = mock(View.class); + EpochView epochView = EpochView.of(12345L, view); + assertThat(epochView.getEpoch()).isEqualTo(12345L); + assertThat(epochView.getView()).isEqualTo(view); + } - @Test - public void testToString() { - View view = mock(View.class); - EpochView epochView = EpochView.of(12345L, view); - assertThat(epochView.toString()).isNotNull(); - } + @Test + public void testToString() { + View view = mock(View.class); + EpochView epochView = EpochView.of(12345L, view); + assertThat(epochView.toString()).isNotNull(); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(EpochView.class) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(EpochView.class).verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewUpdateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewUpdateTest.java index f774822a86..117a1a5d69 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewUpdateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochViewUpdateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class EpochViewUpdateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(EpochViewUpdate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(EpochViewUpdate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochedTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochedTest.java index 0ef756db1e..14846a760b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochedTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/epoch/EpochedTest.java @@ -71,11 +71,10 @@ public class EpochedTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(Epoched.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(Epoched.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculatorTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculatorTest.java index ebb1131829..1200a712f2 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculatorTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ExponentialPacemakerTimeoutCalculatorTest.java @@ -64,46 +64,46 @@ package com.radixdlt.consensus.liveness; -import org.junit.Test; - -import java.util.Map; - import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; +import java.util.Map; +import org.junit.Test; + public class ExponentialPacemakerTimeoutCalculatorTest { - @Test - public void when_creating_timeout_calculator_with_invalid_timeout__then_exception_is_thrown() { - checkConstructionParams(0, 1.2, 1, "timeoutMilliseconds must be > 0"); - checkConstructionParams(-1, 1.2, 1, "timeoutMilliseconds must be > 0"); - checkConstructionParams(1, 1.0, 1, "rate must be > 1.0"); - checkConstructionParams(1, 1.2, -1, "maxExponent must be >= 0"); - checkConstructionParams(1, 100.0, 100, "Maximum timeout value"); - } + @Test + public void when_creating_timeout_calculator_with_invalid_timeout__then_exception_is_thrown() { + checkConstructionParams(0, 1.2, 1, "timeoutMilliseconds must be > 0"); + checkConstructionParams(-1, 1.2, 1, "timeoutMilliseconds must be > 0"); + checkConstructionParams(1, 1.0, 1, "rate must be > 1.0"); + checkConstructionParams(1, 1.2, -1, "maxExponent must be >= 0"); + checkConstructionParams(1, 100.0, 100, "Maximum timeout value"); + } - @Test - public void timeout_should_grow_exponentially() { - final ExponentialPacemakerTimeoutCalculator calculator = - new ExponentialPacemakerTimeoutCalculator(1000L, 2.0, 6); + @Test + public void timeout_should_grow_exponentially() { + final ExponentialPacemakerTimeoutCalculator calculator = + new ExponentialPacemakerTimeoutCalculator(1000L, 2.0, 6); - final Map expectedTimeouts = Map.of( + final Map expectedTimeouts = + Map.of( 0L, 1000L, 1L, 2000L, 2L, 4000L, 3L, 8000L, 4L, 16000L, - 5L, 32000L - ); + 5L, 32000L); - expectedTimeouts.forEach((uncommittedViews, expectedResult) -> - assertEquals(expectedResult.longValue(), calculator.timeout(uncommittedViews)) - ); - } + expectedTimeouts.forEach( + (uncommittedViews, expectedResult) -> + assertEquals(expectedResult.longValue(), calculator.timeout(uncommittedViews))); + } - private void checkConstructionParams(long timeout, double rate, int maxExponent, String exceptionMessage) { - assertThatThrownBy(() -> new ExponentialPacemakerTimeoutCalculator(timeout, rate, maxExponent)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith(exceptionMessage); - } + private void checkConstructionParams( + long timeout, double rate, int maxExponent, String exceptionMessage) { + assertThatThrownBy(() -> new ExponentialPacemakerTimeoutCalculator(timeout, rate, maxExponent)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith(exceptionMessage); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrenceTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrenceTest.java index 6e885fc8ad..cb12829ac0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrenceTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/LocalTimeoutOccurrenceTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class LocalTimeoutOccurrenceTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(LocalTimeoutOccurrence.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(LocalTimeoutOccurrence.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerStateTest.java index 53c62fd135..92fa960d71 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerStateTest.java @@ -64,6 +64,10 @@ package com.radixdlt.consensus.liveness; +import static com.radixdlt.utils.TypedMocks.rmock; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; + import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.bft.BFTNode; @@ -73,82 +77,80 @@ import org.junit.Before; import org.junit.Test; -import static com.radixdlt.utils.TypedMocks.rmock; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.times; - public class PacemakerStateTest { - private EventDispatcher viewUpdateSender = rmock(EventDispatcher.class); - private ProposerElection proposerElection = mock(ProposerElection.class); - - private PacemakerState pacemakerState; - - @Before - public void setUp() { - when(proposerElection.getProposer(any())).thenReturn(BFTNode.random()); - ViewUpdate viewUpdate = ViewUpdate.create(View.genesis(), mock(HighQC.class), BFTNode.random(), BFTNode.random()); - this.pacemakerState = new PacemakerState(viewUpdate, this.proposerElection, this.viewUpdateSender); - } - - @Test - public void when_process_qc_for_wrong_view__then_ignored() { - HighQC highQC = mock(HighQC.class); - when(highQC.getHighestView()).thenReturn(View.of(1)); - - // Move ahead for a bit so we can send in a QC for a lower view - this.pacemakerState.processQC(highQCFor(View.of(0))); - this.pacemakerState.processQC(highQCFor(View.of(1))); - this.pacemakerState.processQC(highQCFor(View.of(2))); - - verify(viewUpdateSender, times(1)) - .dispatch(argThat(v -> v.getCurrentView().equals(View.of(1)))); - verify(viewUpdateSender, times(1)) - .dispatch(argThat(v -> v.getCurrentView().equals(View.of(2)))); - verify(viewUpdateSender, times(1)) - .dispatch(argThat(v -> v.getCurrentView().equals(View.of(3)))); - - this.pacemakerState.processQC(highQC); - verifyNoMoreInteractions(viewUpdateSender); - } - - @Test - public void when_process_qc_for_current_view__then_processed() { - HighQC highQC = mock(HighQC.class); - when(highQC.getHighestView()).thenReturn(View.of(0)); - - this.pacemakerState.processQC(highQC); - verify(viewUpdateSender, times(1)) - .dispatch(argThat(v -> v.getCurrentView().equals(View.of(1)))); - - when(highQC.getHighestView()).thenReturn(View.of(1)); - this.pacemakerState.processQC(highQC); - verify(viewUpdateSender, times(1)) - .dispatch(argThat(v -> v.getCurrentView().equals(View.of(2)))); - } - - @Test - public void when_process_qc_with_a_high_tc__then_should_move_to_tc_view() { - HighQC highQC = mock(HighQC.class); - QuorumCertificate qc = mock(QuorumCertificate.class); - when(qc.getView()).thenReturn(View.of(3)); - when(highQC.getHighestView()).thenReturn(View.of(5)); - when(highQC.highestCommittedQC()).thenReturn(qc); - - this.pacemakerState.processQC(highQC); - verify(viewUpdateSender, times(1)) - .dispatch(argThat(v -> v.getCurrentView().equals(View.of(6)))); - } - - private HighQC highQCFor(View view) { - HighQC highQC = mock(HighQC.class); - QuorumCertificate hqc = mock(QuorumCertificate.class); - QuorumCertificate cqc = mock(QuorumCertificate.class); - when(hqc.getView()).thenReturn(view); - when(cqc.getView()).thenReturn(View.of(0)); - when(highQC.highestQC()).thenReturn(hqc); - when(highQC.highestCommittedQC()).thenReturn(cqc); - when(highQC.getHighestView()).thenReturn(view); - return highQC; - } + private EventDispatcher viewUpdateSender = rmock(EventDispatcher.class); + private ProposerElection proposerElection = mock(ProposerElection.class); + + private PacemakerState pacemakerState; + + @Before + public void setUp() { + when(proposerElection.getProposer(any())).thenReturn(BFTNode.random()); + ViewUpdate viewUpdate = + ViewUpdate.create(View.genesis(), mock(HighQC.class), BFTNode.random(), BFTNode.random()); + this.pacemakerState = + new PacemakerState(viewUpdate, this.proposerElection, this.viewUpdateSender); + } + + @Test + public void when_process_qc_for_wrong_view__then_ignored() { + HighQC highQC = mock(HighQC.class); + when(highQC.getHighestView()).thenReturn(View.of(1)); + + // Move ahead for a bit so we can send in a QC for a lower view + this.pacemakerState.processQC(highQCFor(View.of(0))); + this.pacemakerState.processQC(highQCFor(View.of(1))); + this.pacemakerState.processQC(highQCFor(View.of(2))); + + verify(viewUpdateSender, times(1)) + .dispatch(argThat(v -> v.getCurrentView().equals(View.of(1)))); + verify(viewUpdateSender, times(1)) + .dispatch(argThat(v -> v.getCurrentView().equals(View.of(2)))); + verify(viewUpdateSender, times(1)) + .dispatch(argThat(v -> v.getCurrentView().equals(View.of(3)))); + + this.pacemakerState.processQC(highQC); + verifyNoMoreInteractions(viewUpdateSender); + } + + @Test + public void when_process_qc_for_current_view__then_processed() { + HighQC highQC = mock(HighQC.class); + when(highQC.getHighestView()).thenReturn(View.of(0)); + + this.pacemakerState.processQC(highQC); + verify(viewUpdateSender, times(1)) + .dispatch(argThat(v -> v.getCurrentView().equals(View.of(1)))); + + when(highQC.getHighestView()).thenReturn(View.of(1)); + this.pacemakerState.processQC(highQC); + verify(viewUpdateSender, times(1)) + .dispatch(argThat(v -> v.getCurrentView().equals(View.of(2)))); + } + + @Test + public void when_process_qc_with_a_high_tc__then_should_move_to_tc_view() { + HighQC highQC = mock(HighQC.class); + QuorumCertificate qc = mock(QuorumCertificate.class); + when(qc.getView()).thenReturn(View.of(3)); + when(highQC.getHighestView()).thenReturn(View.of(5)); + when(highQC.highestCommittedQC()).thenReturn(qc); + + this.pacemakerState.processQC(highQC); + verify(viewUpdateSender, times(1)) + .dispatch(argThat(v -> v.getCurrentView().equals(View.of(6)))); + } + + private HighQC highQCFor(View view) { + HighQC highQC = mock(HighQC.class); + QuorumCertificate hqc = mock(QuorumCertificate.class); + QuorumCertificate cqc = mock(QuorumCertificate.class); + when(hqc.getView()).thenReturn(view); + when(cqc.getView()).thenReturn(View.of(0)); + when(highQC.highestQC()).thenReturn(hqc); + when(highQC.highestCommittedQC()).thenReturn(cqc); + when(highQC.getHighestView()).thenReturn(view); + return highQC; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerTest.java index ecd123d9fc..1cab777615 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/PacemakerTest.java @@ -64,6 +64,13 @@ package com.radixdlt.consensus.liveness; +import static com.radixdlt.utils.TypedMocks.rmock; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + import com.google.common.collect.ImmutableSet; import com.google.common.hash.HashCode; import com.radixdlt.consensus.BFTHeader; @@ -91,151 +98,152 @@ import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.utils.TimeSupplier; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import java.util.Optional; - -import static com.radixdlt.utils.TypedMocks.rmock; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - public class PacemakerTest { - private static final Hasher hasher = Sha256Hasher.withDefaultSerialization(); - - private BFTNode self = mock(BFTNode.class); - private SystemCounters counters = mock(SystemCounters.class); - private BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); - private VertexStore vertexStore = mock(VertexStore.class); - private SafetyRules safetyRules = mock(SafetyRules.class); - private PacemakerTimeoutCalculator timeoutCalculator = mock(PacemakerTimeoutCalculator.class); - private NextTxnsGenerator nextTxnsGenerator = mock(NextTxnsGenerator.class); - private RemoteEventDispatcher voteDispatcher = rmock(RemoteEventDispatcher.class); - private RemoteEventDispatcher proposalDispatcher = rmock(RemoteEventDispatcher.class); - private EventDispatcher timeoutDispatcher = rmock(EventDispatcher.class); - private ScheduledEventDispatcher timeoutSender = rmock(ScheduledEventDispatcher.class); - private TimeSupplier timeSupplier = mock(TimeSupplier.class); - - private Pacemaker pacemaker; - - @Before - public void setUp() { - HighQC highQC = mock(HighQC.class); - QuorumCertificate committedQc = mock(QuorumCertificate.class); - when(committedQc.getView()).thenReturn(View.of(0)); - when(highQC.highestCommittedQC()).thenReturn(committedQc); - - ViewUpdate initialViewUpdate = - ViewUpdate.create(View.of(0), highQC, mock(BFTNode.class), mock(BFTNode.class)); - - this.pacemaker = new Pacemaker( - this.self, - this.counters, - this.validatorSet, - this.vertexStore, - this.safetyRules, - this.timeoutDispatcher, - this.timeoutSender, - this.timeoutCalculator, - this.nextTxnsGenerator, - this.proposalDispatcher, - this.voteDispatcher, - hasher, - timeSupplier, - initialViewUpdate, - new SystemCountersImpl() - ); - } - - @Test - public void when_local_timeout__then_resend_previous_vote() { - View view = View.of(0); - Vote lastVote = mock(Vote.class); - Vote lastVoteWithTimeout = mock(Vote.class); - ImmutableSet validators = rmock(ImmutableSet.class); - - when(this.safetyRules.getLastVote(view)).thenReturn(Optional.of(lastVote)); - when(this.safetyRules.timeoutVote(lastVote)).thenReturn(lastVoteWithTimeout); - when(this.validatorSet.nodes()).thenReturn(validators); - - ViewUpdate viewUpdate = ViewUpdate.create(View.of(0), mock(HighQC.class), mock(BFTNode.class), mock(BFTNode.class)); - this.pacemaker.processLocalTimeout(ScheduledLocalTimeout.create(viewUpdate, 0L)); - - verify(this.voteDispatcher, times(1)).dispatch(eq(validators), eq(lastVoteWithTimeout)); - verifyNoMoreInteractions(this.vertexStore); - verify(this.safetyRules, times(1)).getLastVote(view); - verify(this.safetyRules, times(1)).timeoutVote(lastVote); - verifyNoMoreInteractions(this.safetyRules); - } - - @Test - public void when_local_timeout__then_send_empty_vote_if_no_previous() { - HighQC viewUpdateHighQc = mock(HighQC.class); - QuorumCertificate committedQc = mock(QuorumCertificate.class); - QuorumCertificate highestQc = mock(QuorumCertificate.class); - when(viewUpdateHighQc.highestCommittedQC()).thenReturn(committedQc); - when(viewUpdateHighQc.highestQC()).thenReturn(highestQc); - BFTHeader highestQcProposed = mock(BFTHeader.class); - HashCode highQcParentVertexId = mock(HashCode.class); - when(highestQcProposed.getVertexId()).thenReturn(highQcParentVertexId); - when(highestQc.getProposed()).thenReturn(highestQcProposed); - when(committedQc.getView()).thenReturn(View.of(0)); - ViewUpdate viewUpdate = ViewUpdate.create(View.of(1), viewUpdateHighQc, mock(BFTNode.class), mock(BFTNode.class)); - this.pacemaker.processViewUpdate(viewUpdate); - View view = View.of(1); - Vote emptyVote = mock(Vote.class); - Vote emptyVoteWithTimeout = mock(Vote.class); - ImmutableSet validators = rmock(ImmutableSet.class); - BFTHeader bftHeader = mock(BFTHeader.class); - HighQC highQC = mock(HighQC.class); - BFTInsertUpdate bftInsertUpdate = mock(BFTInsertUpdate.class); - when(bftInsertUpdate.getHeader()).thenReturn(bftHeader); - PreparedVertex preparedVertex = mock(PreparedVertex.class); - when(preparedVertex.getView()).thenReturn(view); - when(preparedVertex.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); - VerifiedVertexStoreState vertexStoreState = mock(VerifiedVertexStoreState.class); - when(vertexStoreState.getHighQC()).thenReturn(highQC); - when(bftInsertUpdate.getInserted()).thenReturn(preparedVertex); - when(bftInsertUpdate.getVertexStoreState()).thenReturn(vertexStoreState); - var node = BFTNode.random(); - when(preparedVertex.getId()).thenReturn(hasher.hash(UnverifiedVertex.createTimeout(highestQc, view, node))); - - when(this.safetyRules.getLastVote(view)).thenReturn(Optional.empty()); - when(this.safetyRules.createVote(any(), any(), anyLong(), any())).thenReturn(emptyVote); - when(this.safetyRules.timeoutVote(emptyVote)).thenReturn(emptyVoteWithTimeout); - when(this.validatorSet.nodes()).thenReturn(validators); - - when(this.vertexStore.getPreparedVertex(any())).thenReturn(Optional.empty()); - - this.pacemaker.processLocalTimeout(ScheduledLocalTimeout.create( - ViewUpdate.create(View.of(1), mock(HighQC.class), node, BFTNode.random()), 0L)); - - this.pacemaker.processBFTUpdate(bftInsertUpdate); - - verify(this.voteDispatcher, times(1)).dispatch(eq(validators), eq(emptyVoteWithTimeout)); - verify(this.safetyRules, times(1)).getLastVote(view); - verify(this.safetyRules, times(1)).createVote(any(), any(), anyLong(), any()); - verify(this.safetyRules, times(1)).timeoutVote(emptyVote); - verifyNoMoreInteractions(this.safetyRules); - - verify(this.vertexStore, times(1)).getPreparedVertex(any()); - - ArgumentCaptor insertVertexCaptor = ArgumentCaptor.forClass(VerifiedVertex.class); - verify(this.vertexStore, times(1)).insertVertex(insertVertexCaptor.capture()); - assertEquals(insertVertexCaptor.getValue().getParentId(), highQcParentVertexId); - - verifyNoMoreInteractions(this.vertexStore); - } - - @Test - public void when_local_timeout_for_non_current_view__then_ignored() { - this.pacemaker.processLocalTimeout(ScheduledLocalTimeout.create( - ViewUpdate.create(View.of(1), mock(HighQC.class), mock(BFTNode.class), mock(BFTNode.class)), 0L)); - verifyNoMoreInteractions(this.safetyRules); - } + private static final Hasher hasher = Sha256Hasher.withDefaultSerialization(); + + private BFTNode self = mock(BFTNode.class); + private SystemCounters counters = mock(SystemCounters.class); + private BFTValidatorSet validatorSet = mock(BFTValidatorSet.class); + private VertexStore vertexStore = mock(VertexStore.class); + private SafetyRules safetyRules = mock(SafetyRules.class); + private PacemakerTimeoutCalculator timeoutCalculator = mock(PacemakerTimeoutCalculator.class); + private NextTxnsGenerator nextTxnsGenerator = mock(NextTxnsGenerator.class); + private RemoteEventDispatcher voteDispatcher = rmock(RemoteEventDispatcher.class); + private RemoteEventDispatcher proposalDispatcher = rmock(RemoteEventDispatcher.class); + private EventDispatcher timeoutDispatcher = rmock(EventDispatcher.class); + private ScheduledEventDispatcher timeoutSender = + rmock(ScheduledEventDispatcher.class); + private TimeSupplier timeSupplier = mock(TimeSupplier.class); + + private Pacemaker pacemaker; + + @Before + public void setUp() { + HighQC highQC = mock(HighQC.class); + QuorumCertificate committedQc = mock(QuorumCertificate.class); + when(committedQc.getView()).thenReturn(View.of(0)); + when(highQC.highestCommittedQC()).thenReturn(committedQc); + + ViewUpdate initialViewUpdate = + ViewUpdate.create(View.of(0), highQC, mock(BFTNode.class), mock(BFTNode.class)); + + this.pacemaker = + new Pacemaker( + this.self, + this.counters, + this.validatorSet, + this.vertexStore, + this.safetyRules, + this.timeoutDispatcher, + this.timeoutSender, + this.timeoutCalculator, + this.nextTxnsGenerator, + this.proposalDispatcher, + this.voteDispatcher, + hasher, + timeSupplier, + initialViewUpdate, + new SystemCountersImpl()); + } + + @Test + public void when_local_timeout__then_resend_previous_vote() { + View view = View.of(0); + Vote lastVote = mock(Vote.class); + Vote lastVoteWithTimeout = mock(Vote.class); + ImmutableSet validators = rmock(ImmutableSet.class); + + when(this.safetyRules.getLastVote(view)).thenReturn(Optional.of(lastVote)); + when(this.safetyRules.timeoutVote(lastVote)).thenReturn(lastVoteWithTimeout); + when(this.validatorSet.nodes()).thenReturn(validators); + + ViewUpdate viewUpdate = + ViewUpdate.create(View.of(0), mock(HighQC.class), mock(BFTNode.class), mock(BFTNode.class)); + this.pacemaker.processLocalTimeout(ScheduledLocalTimeout.create(viewUpdate, 0L)); + + verify(this.voteDispatcher, times(1)).dispatch(eq(validators), eq(lastVoteWithTimeout)); + verifyNoMoreInteractions(this.vertexStore); + verify(this.safetyRules, times(1)).getLastVote(view); + verify(this.safetyRules, times(1)).timeoutVote(lastVote); + verifyNoMoreInteractions(this.safetyRules); + } + + @Test + public void when_local_timeout__then_send_empty_vote_if_no_previous() { + HighQC viewUpdateHighQc = mock(HighQC.class); + QuorumCertificate committedQc = mock(QuorumCertificate.class); + QuorumCertificate highestQc = mock(QuorumCertificate.class); + when(viewUpdateHighQc.highestCommittedQC()).thenReturn(committedQc); + when(viewUpdateHighQc.highestQC()).thenReturn(highestQc); + BFTHeader highestQcProposed = mock(BFTHeader.class); + HashCode highQcParentVertexId = mock(HashCode.class); + when(highestQcProposed.getVertexId()).thenReturn(highQcParentVertexId); + when(highestQc.getProposed()).thenReturn(highestQcProposed); + when(committedQc.getView()).thenReturn(View.of(0)); + ViewUpdate viewUpdate = + ViewUpdate.create(View.of(1), viewUpdateHighQc, mock(BFTNode.class), mock(BFTNode.class)); + this.pacemaker.processViewUpdate(viewUpdate); + View view = View.of(1); + Vote emptyVote = mock(Vote.class); + Vote emptyVoteWithTimeout = mock(Vote.class); + ImmutableSet validators = rmock(ImmutableSet.class); + BFTHeader bftHeader = mock(BFTHeader.class); + HighQC highQC = mock(HighQC.class); + BFTInsertUpdate bftInsertUpdate = mock(BFTInsertUpdate.class); + when(bftInsertUpdate.getHeader()).thenReturn(bftHeader); + PreparedVertex preparedVertex = mock(PreparedVertex.class); + when(preparedVertex.getView()).thenReturn(view); + when(preparedVertex.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); + VerifiedVertexStoreState vertexStoreState = mock(VerifiedVertexStoreState.class); + when(vertexStoreState.getHighQC()).thenReturn(highQC); + when(bftInsertUpdate.getInserted()).thenReturn(preparedVertex); + when(bftInsertUpdate.getVertexStoreState()).thenReturn(vertexStoreState); + var node = BFTNode.random(); + when(preparedVertex.getId()) + .thenReturn(hasher.hash(UnverifiedVertex.createTimeout(highestQc, view, node))); + + when(this.safetyRules.getLastVote(view)).thenReturn(Optional.empty()); + when(this.safetyRules.createVote(any(), any(), anyLong(), any())).thenReturn(emptyVote); + when(this.safetyRules.timeoutVote(emptyVote)).thenReturn(emptyVoteWithTimeout); + when(this.validatorSet.nodes()).thenReturn(validators); + + when(this.vertexStore.getPreparedVertex(any())).thenReturn(Optional.empty()); + + this.pacemaker.processLocalTimeout( + ScheduledLocalTimeout.create( + ViewUpdate.create(View.of(1), mock(HighQC.class), node, BFTNode.random()), 0L)); + + this.pacemaker.processBFTUpdate(bftInsertUpdate); + + verify(this.voteDispatcher, times(1)).dispatch(eq(validators), eq(emptyVoteWithTimeout)); + verify(this.safetyRules, times(1)).getLastVote(view); + verify(this.safetyRules, times(1)).createVote(any(), any(), anyLong(), any()); + verify(this.safetyRules, times(1)).timeoutVote(emptyVote); + verifyNoMoreInteractions(this.safetyRules); + + verify(this.vertexStore, times(1)).getPreparedVertex(any()); + + ArgumentCaptor insertVertexCaptor = + ArgumentCaptor.forClass(VerifiedVertex.class); + verify(this.vertexStore, times(1)).insertVertex(insertVertexCaptor.capture()); + assertEquals(insertVertexCaptor.getValue().getParentId(), highQcParentVertexId); + + verifyNoMoreInteractions(this.vertexStore); + } + + @Test + public void when_local_timeout_for_non_current_view__then_ignored() { + this.pacemaker.processLocalTimeout( + ScheduledLocalTimeout.create( + ViewUpdate.create( + View.of(1), mock(HighQC.class), mock(BFTNode.class), mock(BFTNode.class)), + 0L)); + verifyNoMoreInteractions(this.safetyRules); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeoutTest.java index c287543e83..dba299210d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/ScheduledLocalTimeoutTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class ScheduledLocalTimeoutTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(ScheduledLocalTimeout.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(ScheduledLocalTimeout.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/VoteTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/VoteTimeoutTest.java index 5742ad4534..58f25a2da7 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/VoteTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/VoteTimeoutTest.java @@ -71,17 +71,16 @@ public class VoteTimeoutTest extends SerializeMessageObject { - public VoteTimeoutTest() { - super(VoteTimeout.class, VoteTimeoutTest::get); - } + public VoteTimeoutTest() { + super(VoteTimeout.class, VoteTimeoutTest::get); + } - private static VoteTimeout get() { - return new VoteTimeout(View.of(1), 2); - } + private static VoteTimeout get() { + return new VoteTimeout(View.of(1), 2); + } - @Test - public void equalsTest() { - EqualsVerifier.forClass(VoteTimeout.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(VoteTimeout.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/WeightedRotatingLeadersTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/WeightedRotatingLeadersTest.java index 27afa2c0d3..9e9dd151e3 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/WeightedRotatingLeadersTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/liveness/WeightedRotatingLeadersTest.java @@ -71,14 +71,13 @@ import static org.assertj.core.api.Assertions.assertThat; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidator; import com.radixdlt.consensus.bft.BFTValidatorSet; +import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.utils.KeyComparator; import com.radixdlt.utils.UInt256; - import java.util.Comparator; import java.util.Map; import java.util.function.Supplier; @@ -87,106 +86,115 @@ import org.junit.Test; public class WeightedRotatingLeadersTest { - private WeightedRotatingLeaders weightedRotatingLeaders; - private WeightedRotatingLeaders weightedRotatingLeaders2; - private ImmutableList validatorsInOrder; - - private void setUp(int validatorSetSize, int sizeOfCache) { - this.validatorsInOrder = Stream.generate(() -> ECKeyPair.generateNew().getPublicKey()) - .limit(validatorSetSize) - .map(BFTNode::create) - .map(node -> BFTValidator.from(node, UInt256.ONE)) - .sorted(Comparator.comparing(v -> v.getNode().getKey(), KeyComparator.instance().reversed())) - .collect(ImmutableList.toImmutableList()); - - BFTValidatorSet validatorSet = BFTValidatorSet.from(validatorsInOrder); - this.weightedRotatingLeaders = - new WeightedRotatingLeaders(validatorSet, sizeOfCache); - this.weightedRotatingLeaders2 = - new WeightedRotatingLeaders(validatorSet, sizeOfCache); - } - - @Test - public void when_equivalent_leaders__then_leaders_are_round_robined_deterministically() { - for (int validatorSetSize = 1; validatorSetSize <= 128; validatorSetSize *= 2) { - for (int sizeOfCache = 1; sizeOfCache <= 128; sizeOfCache *= 2) { - setUp(validatorSetSize, sizeOfCache); - - // 2 round robins - final int viewsToTest = 2 * validatorSetSize; - - for (int view = 0; view < viewsToTest; view++) { - var expectedNodeForView = validatorsInOrder.get(validatorSetSize - (view % validatorSetSize) - 1).getNode(); - assertThat(weightedRotatingLeaders.getProposer(View.of(view))).isEqualTo(expectedNodeForView); - } - } - } - } - - - @Test - public void when_get_proposer_multiple_times__then_should_return_the_same_key() { - for (int validatorSetSize = 1; validatorSetSize <= 128; validatorSetSize *= 2) { - for (int sizeOfCache = 1; sizeOfCache <= 128; sizeOfCache *= 2) { - setUp(validatorSetSize, sizeOfCache); - - // 2 * sizeOfCache so cache eviction occurs - final int viewsToTest = 2 * sizeOfCache; - - BFTNode expectedNodeForView0 = weightedRotatingLeaders.getProposer(View.of(0)); - for (View view = View.of(1); view.compareTo(View.of(viewsToTest)) <= 0; view = view.next()) { - weightedRotatingLeaders.getProposer(view); - } - assertThat(weightedRotatingLeaders.getProposer(View.of(0))).isEqualTo(expectedNodeForView0); - } - } - } - - @Test - public void when_get_proposer_skipping_views__then_should_return_same_result_as_in_order() { - for (int validatorSetSize = 1; validatorSetSize <= 128; validatorSetSize *= 2) { - for (int sizeOfCache = 1; sizeOfCache <= 128; sizeOfCache *= 2) { - setUp(validatorSetSize, sizeOfCache); - - // 2 * sizeOfCache so cache eviction occurs - final int viewsToTest = 2 * sizeOfCache; - - for (int view = 0; view < viewsToTest; view++) { - weightedRotatingLeaders2.getProposer(View.of(view)); - } - BFTNode node1 = weightedRotatingLeaders.getProposer(View.of(viewsToTest - 1)); - BFTNode node2 = weightedRotatingLeaders2.getProposer(View.of(viewsToTest - 1)); - assertThat(node1).isEqualTo(node2); - } - } - } - - @Test - public void when_validators_distributed_by_fibonacci__then_leaders_also_distributed_in_fibonacci() { - // fibonacci sequence can quickly explode so keep sizes small - final int validatorSetSize = 8; - final int sizeOfCache = 4; - final Supplier fibonacci = () -> Stream.iterate(new int[]{1, 1}, t -> new int[]{t[1], t[0] + t[1]}) - .mapToInt(t -> t[0]) - .limit(validatorSetSize); - - final int sumOfPower = fibonacci.get().sum(); - this.validatorsInOrder = fibonacci.get() - .mapToObj(p -> BFTValidator.from(BFTNode.random(), UInt256.from(p))) - .collect(ImmutableList.toImmutableList()); - - BFTValidatorSet validatorSet = BFTValidatorSet.from(validatorsInOrder); - this.weightedRotatingLeaders = new WeightedRotatingLeaders(validatorSet, sizeOfCache); - - Map proposerCounts = Stream.iterate(View.of(0), View::next) - .limit(sumOfPower) - .map(this.weightedRotatingLeaders::getProposer) - .collect(groupingBy(p -> p, collectingAndThen(counting(), UInt256::from))); - - Map expected = validatorsInOrder.stream() - .collect(toMap(BFTValidator::getNode, BFTValidator::getPower)); - - assertThat(proposerCounts).isEqualTo(expected); - } - -} \ No newline at end of file + private WeightedRotatingLeaders weightedRotatingLeaders; + private WeightedRotatingLeaders weightedRotatingLeaders2; + private ImmutableList validatorsInOrder; + + private void setUp(int validatorSetSize, int sizeOfCache) { + this.validatorsInOrder = + Stream.generate(() -> ECKeyPair.generateNew().getPublicKey()) + .limit(validatorSetSize) + .map(BFTNode::create) + .map(node -> BFTValidator.from(node, UInt256.ONE)) + .sorted( + Comparator.comparing( + v -> v.getNode().getKey(), KeyComparator.instance().reversed())) + .collect(ImmutableList.toImmutableList()); + + BFTValidatorSet validatorSet = BFTValidatorSet.from(validatorsInOrder); + this.weightedRotatingLeaders = new WeightedRotatingLeaders(validatorSet, sizeOfCache); + this.weightedRotatingLeaders2 = new WeightedRotatingLeaders(validatorSet, sizeOfCache); + } + + @Test + public void when_equivalent_leaders__then_leaders_are_round_robined_deterministically() { + for (int validatorSetSize = 1; validatorSetSize <= 128; validatorSetSize *= 2) { + for (int sizeOfCache = 1; sizeOfCache <= 128; sizeOfCache *= 2) { + setUp(validatorSetSize, sizeOfCache); + + // 2 round robins + final int viewsToTest = 2 * validatorSetSize; + + for (int view = 0; view < viewsToTest; view++) { + var expectedNodeForView = + validatorsInOrder.get(validatorSetSize - (view % validatorSetSize) - 1).getNode(); + assertThat(weightedRotatingLeaders.getProposer(View.of(view))) + .isEqualTo(expectedNodeForView); + } + } + } + } + + @Test + public void when_get_proposer_multiple_times__then_should_return_the_same_key() { + for (int validatorSetSize = 1; validatorSetSize <= 128; validatorSetSize *= 2) { + for (int sizeOfCache = 1; sizeOfCache <= 128; sizeOfCache *= 2) { + setUp(validatorSetSize, sizeOfCache); + + // 2 * sizeOfCache so cache eviction occurs + final int viewsToTest = 2 * sizeOfCache; + + BFTNode expectedNodeForView0 = weightedRotatingLeaders.getProposer(View.of(0)); + for (View view = View.of(1); + view.compareTo(View.of(viewsToTest)) <= 0; + view = view.next()) { + weightedRotatingLeaders.getProposer(view); + } + assertThat(weightedRotatingLeaders.getProposer(View.of(0))).isEqualTo(expectedNodeForView0); + } + } + } + + @Test + public void when_get_proposer_skipping_views__then_should_return_same_result_as_in_order() { + for (int validatorSetSize = 1; validatorSetSize <= 128; validatorSetSize *= 2) { + for (int sizeOfCache = 1; sizeOfCache <= 128; sizeOfCache *= 2) { + setUp(validatorSetSize, sizeOfCache); + + // 2 * sizeOfCache so cache eviction occurs + final int viewsToTest = 2 * sizeOfCache; + + for (int view = 0; view < viewsToTest; view++) { + weightedRotatingLeaders2.getProposer(View.of(view)); + } + BFTNode node1 = weightedRotatingLeaders.getProposer(View.of(viewsToTest - 1)); + BFTNode node2 = weightedRotatingLeaders2.getProposer(View.of(viewsToTest - 1)); + assertThat(node1).isEqualTo(node2); + } + } + } + + @Test + public void + when_validators_distributed_by_fibonacci__then_leaders_also_distributed_in_fibonacci() { + // fibonacci sequence can quickly explode so keep sizes small + final int validatorSetSize = 8; + final int sizeOfCache = 4; + final Supplier fibonacci = + () -> + Stream.iterate(new int[] {1, 1}, t -> new int[] {t[1], t[0] + t[1]}) + .mapToInt(t -> t[0]) + .limit(validatorSetSize); + + final int sumOfPower = fibonacci.get().sum(); + this.validatorsInOrder = + fibonacci + .get() + .mapToObj(p -> BFTValidator.from(BFTNode.random(), UInt256.from(p))) + .collect(ImmutableList.toImmutableList()); + + BFTValidatorSet validatorSet = BFTValidatorSet.from(validatorsInOrder); + this.weightedRotatingLeaders = new WeightedRotatingLeaders(validatorSet, sizeOfCache); + + Map proposerCounts = + Stream.iterate(View.of(0), View::next) + .limit(sumOfPower) + .map(this.weightedRotatingLeaders::getProposer) + .collect(groupingBy(p -> p, collectingAndThen(counting(), UInt256::from))); + + Map expected = + validatorsInOrder.stream().collect(toMap(BFTValidator::getNode, BFTValidator::getPower)); + + assertThat(proposerCounts).isEqualTo(expected); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyRulesTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyRulesTest.java index c2cab09bd6..efe4f11787 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyRulesTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyRulesTest.java @@ -1,271 +1,279 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus.safety; - -import com.radixdlt.crypto.HashUtils; -import org.junit.Before; -import org.junit.Test; - -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.BFTHeader; -import com.radixdlt.consensus.HashSigner; -import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.VerifiedVertex; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.safety.SafetyState.Builder; -import com.radixdlt.crypto.ECDSASignature; -import com.radixdlt.crypto.Hasher; - -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * This tests that the {@link SafetyRules} implementation obeys HotStuff's safety and commit rules. - */ -public class SafetyRulesTest { - private SafetyState safetyState; - private SafetyRules safetyRules; - - @Before - public void setup() { - this.safetyState = mock(SafetyState.class); - Hasher hasher = mock(Hasher.class); - when(hasher.hash(any())).thenReturn(HashUtils.random256()); - when(hasher.hashBytes(any())).thenReturn(HashUtils.random256()); - HashSigner hashSigner = mock(HashSigner.class); - when(hashSigner.sign(any(HashCode.class))).thenReturn(ECDSASignature.zeroSignature()); - this.safetyRules = new SafetyRules(mock(BFTNode.class), safetyState, mock(PersistentSafetyStateStore.class), hasher, hashSigner); - } - - @Test - public void when_vote_on_same_view__then_exception_is_thrown() { - View view = mock(View.class); - when(view.lte(view)).thenReturn(true); - when(safetyState.getLastVotedView()).thenReturn(view); - VerifiedVertex vertex = mock(VerifiedVertex.class); - when(vertex.getView()).thenReturn(view); - - assertThat(this.safetyRules.voteFor(vertex, mock(BFTHeader.class), 0L, mock(HighQC.class))) - .isEmpty(); - } - - @Test - public void when_vote_with_qc_on_different_locked_view__then_exception_is_thrown() { - Hasher hasher = mock(Hasher.class); - when(hasher.hash(any())).thenReturn(mock(HashCode.class)); - HashSigner hashSigner = mock(HashSigner.class); - when(hashSigner.sign(any(HashCode.class))).thenReturn(ECDSASignature.zeroSignature()); - - Vote lastVote = mock(Vote.class); - when(lastVote.getView()).thenReturn(View.of(1)); - - SafetyRules safetyRules = new SafetyRules( - BFTNode.random(), - new SafetyState(View.of(2), Optional.of(lastVote)), - mock(PersistentSafetyStateStore.class), - hasher, - hashSigner - ); - - VerifiedVertex vertex = mock(VerifiedVertex.class); - when(vertex.getView()).thenReturn(View.of(3)); - BFTHeader parent = mock(BFTHeader.class); - when(parent.getView()).thenReturn(View.of(0)); - when(vertex.getParentHeader()).thenReturn(parent); - - assertThat(safetyRules.voteFor(vertex, mock(BFTHeader.class), 0L, mock(HighQC.class))) - .isEmpty(); - } - - @Test - public void when_vote_on_proposal_after_genesis__then_returned_vote_has_no_commit() { - when(safetyState.getLastVotedView()).thenReturn(View.of(0)); - when(safetyState.getLockedView()).thenReturn(View.of(0)); - when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); - VerifiedVertex vertex = mock(VerifiedVertex.class); - when(vertex.hasDirectParent()).thenReturn(true); - when(vertex.touchesGenesis()).thenReturn(true); - when(vertex.parentHasDirectParent()).thenReturn(true); - when(vertex.getView()).thenReturn(View.of(1)); - BFTHeader parent = mock(BFTHeader.class); - when(parent.getView()).thenReturn(View.of(0)); - when(vertex.getParentHeader()).thenReturn(parent); - BFTHeader grandParent = mock(BFTHeader.class); - when(grandParent.getView()).thenReturn(mock(View.class)); - when(vertex.getGrandParentHeader()).thenReturn(grandParent); - BFTHeader header = mock(BFTHeader.class); - Optional voteMaybe = safetyRules.voteFor(vertex, header, 1L, mock(HighQC.class)); - assertThat(voteMaybe).isNotEmpty(); - Vote vote = voteMaybe.get(); - assertThat(vote.getVoteData().getProposed()).isEqualTo(header); - assertThat(vote.getVoteData().getParent()).isEqualTo(parent); - assertThat(vote.getVoteData().getCommitted()).isEmpty(); - } - - @Test - public void when_vote_on_proposal_two_after_genesis__then_returned_vote_has_no_commit() { - when(safetyState.getLastVotedView()).thenReturn(View.of(1)); - when(safetyState.getLockedView()).thenReturn(View.of(0)); - when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); - VerifiedVertex proposal = mock(VerifiedVertex.class); - when(proposal.touchesGenesis()).thenReturn(true); - when(proposal.hasDirectParent()).thenReturn(true); - when(proposal.parentHasDirectParent()).thenReturn(true); - BFTHeader parent = mock(BFTHeader.class); - when(parent.getView()).thenReturn(View.of(1)); - when(proposal.getParentHeader()).thenReturn(parent); - when(proposal.getView()).thenReturn(View.of(2)); - BFTHeader grandParent = mock(BFTHeader.class); - when(grandParent.getView()).thenReturn(mock(View.class)); - when(proposal.getGrandParentHeader()).thenReturn(grandParent); - Optional voteMaybe = safetyRules.voteFor(proposal, mock(BFTHeader.class), 1L, mock(HighQC.class)); - assertThat(voteMaybe).isNotEmpty(); - Vote vote = voteMaybe.get(); - assertThat(vote.getVoteData().getCommitted()).isEmpty(); - } - - @Test - public void when_vote_on_proposal_three_after_genesis__then_returned_vote_has_commit() { - when(safetyState.getLastVotedView()).thenReturn(View.of(1)); - when(safetyState.getLockedView()).thenReturn(View.of(0)); - when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); - - VerifiedVertex proposal = mock(VerifiedVertex.class); - when(proposal.touchesGenesis()).thenReturn(false); - when(proposal.hasDirectParent()).thenReturn(true); - when(proposal.parentHasDirectParent()).thenReturn(true); - BFTHeader grandparentHeader = mock(BFTHeader.class); - when(grandparentHeader.getView()).thenReturn(mock(View.class)); - when(proposal.getGrandParentHeader()).thenReturn(grandparentHeader); - BFTHeader parent = mock(BFTHeader.class); - when(parent.getView()).thenReturn(View.of(2)); - when(proposal.getParentHeader()).thenReturn(parent); - when(proposal.getView()).thenReturn(View.of(3)); - - Optional voteMaybe = safetyRules.voteFor(proposal, mock(BFTHeader.class), 1L, mock(HighQC.class)); - assertThat(voteMaybe).isNotEmpty(); - Vote vote = voteMaybe.get(); - assertThat(vote.getVoteData().getCommitted()).hasValue(grandparentHeader); - } - - @Test - public void when_vote_on_proposal_three_after_genesis_with_skip__then_returned_vote_has_no_commit() { - when(safetyState.getLastVotedView()).thenReturn(View.of(1)); - when(safetyState.getLockedView()).thenReturn(View.of(0)); - when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); - - VerifiedVertex proposal = mock(VerifiedVertex.class); - when(proposal.touchesGenesis()).thenReturn(false); - when(proposal.hasDirectParent()).thenReturn(false); - when(proposal.parentHasDirectParent()).thenReturn(true); - BFTHeader parent = mock(BFTHeader.class); - when(parent.getView()).thenReturn(View.of(2)); - when(proposal.getParentHeader()).thenReturn(parent); - when(proposal.getView()).thenReturn(View.of(4)); - BFTHeader grandParent = mock(BFTHeader.class); - when(grandParent.getView()).thenReturn(mock(View.class)); - when(proposal.getGrandParentHeader()).thenReturn(grandParent); - - Optional voteMaybe = safetyRules.voteFor(proposal, mock(BFTHeader.class), 1L, mock(HighQC.class)); - assertThat(voteMaybe).isNotEmpty(); - Vote vote = voteMaybe.get(); - assertThat(vote.getVoteData().getCommitted()).isEmpty(); - } - - @Test - public void when_timeout_already_timed_out_vote_than_the_same_vote_is_returned() { - Vote vote = mock(Vote.class); - when(vote.isTimeout()).thenReturn(true); - assertEquals(vote, safetyRules.timeoutVote(vote)); - } - - @Test - public void when_timeout_a_vote_than_it_has_a_timeout_signature() { - Vote vote = mock(Vote.class); - Vote voteWithTimeout = mock(Vote.class); - when(vote.getView()).thenReturn(View.of(1)); - when(vote.getEpoch()).thenReturn(1L); - when(vote.withTimeoutSignature(any())).thenReturn(voteWithTimeout); - when(vote.isTimeout()).thenReturn(false); - - Builder builder = mock(Builder.class); - when(builder.lastVote(any())).thenReturn(builder); - when(builder.build()).thenReturn(this.safetyState); - when(safetyState.toBuilder()).thenReturn(builder); - - Vote resultVote = safetyRules.timeoutVote(vote); - verify(vote, times(1)).withTimeoutSignature(any()); - assertEquals(voteWithTimeout, resultVote); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus.safety; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.hash.HashCode; +import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.HashSigner; +import com.radixdlt.consensus.HighQC; +import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.VerifiedVertex; +import com.radixdlt.consensus.bft.View; +import com.radixdlt.consensus.safety.SafetyState.Builder; +import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.crypto.Hasher; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; + +/** + * This tests that the {@link SafetyRules} implementation obeys HotStuff's safety and commit rules. + */ +public class SafetyRulesTest { + private SafetyState safetyState; + private SafetyRules safetyRules; + + @Before + public void setup() { + this.safetyState = mock(SafetyState.class); + Hasher hasher = mock(Hasher.class); + when(hasher.hash(any())).thenReturn(HashUtils.random256()); + when(hasher.hashBytes(any())).thenReturn(HashUtils.random256()); + HashSigner hashSigner = mock(HashSigner.class); + when(hashSigner.sign(any(HashCode.class))).thenReturn(ECDSASignature.zeroSignature()); + this.safetyRules = + new SafetyRules( + mock(BFTNode.class), + safetyState, + mock(PersistentSafetyStateStore.class), + hasher, + hashSigner); + } + + @Test + public void when_vote_on_same_view__then_exception_is_thrown() { + View view = mock(View.class); + when(view.lte(view)).thenReturn(true); + when(safetyState.getLastVotedView()).thenReturn(view); + VerifiedVertex vertex = mock(VerifiedVertex.class); + when(vertex.getView()).thenReturn(view); + + assertThat(this.safetyRules.voteFor(vertex, mock(BFTHeader.class), 0L, mock(HighQC.class))) + .isEmpty(); + } + + @Test + public void when_vote_with_qc_on_different_locked_view__then_exception_is_thrown() { + Hasher hasher = mock(Hasher.class); + when(hasher.hash(any())).thenReturn(mock(HashCode.class)); + HashSigner hashSigner = mock(HashSigner.class); + when(hashSigner.sign(any(HashCode.class))).thenReturn(ECDSASignature.zeroSignature()); + + Vote lastVote = mock(Vote.class); + when(lastVote.getView()).thenReturn(View.of(1)); + + SafetyRules safetyRules = + new SafetyRules( + BFTNode.random(), + new SafetyState(View.of(2), Optional.of(lastVote)), + mock(PersistentSafetyStateStore.class), + hasher, + hashSigner); + + VerifiedVertex vertex = mock(VerifiedVertex.class); + when(vertex.getView()).thenReturn(View.of(3)); + BFTHeader parent = mock(BFTHeader.class); + when(parent.getView()).thenReturn(View.of(0)); + when(vertex.getParentHeader()).thenReturn(parent); + + assertThat(safetyRules.voteFor(vertex, mock(BFTHeader.class), 0L, mock(HighQC.class))) + .isEmpty(); + } + + @Test + public void when_vote_on_proposal_after_genesis__then_returned_vote_has_no_commit() { + when(safetyState.getLastVotedView()).thenReturn(View.of(0)); + when(safetyState.getLockedView()).thenReturn(View.of(0)); + when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); + VerifiedVertex vertex = mock(VerifiedVertex.class); + when(vertex.hasDirectParent()).thenReturn(true); + when(vertex.touchesGenesis()).thenReturn(true); + when(vertex.parentHasDirectParent()).thenReturn(true); + when(vertex.getView()).thenReturn(View.of(1)); + BFTHeader parent = mock(BFTHeader.class); + when(parent.getView()).thenReturn(View.of(0)); + when(vertex.getParentHeader()).thenReturn(parent); + BFTHeader grandParent = mock(BFTHeader.class); + when(grandParent.getView()).thenReturn(mock(View.class)); + when(vertex.getGrandParentHeader()).thenReturn(grandParent); + BFTHeader header = mock(BFTHeader.class); + Optional voteMaybe = safetyRules.voteFor(vertex, header, 1L, mock(HighQC.class)); + assertThat(voteMaybe).isNotEmpty(); + Vote vote = voteMaybe.get(); + assertThat(vote.getVoteData().getProposed()).isEqualTo(header); + assertThat(vote.getVoteData().getParent()).isEqualTo(parent); + assertThat(vote.getVoteData().getCommitted()).isEmpty(); + } + + @Test + public void when_vote_on_proposal_two_after_genesis__then_returned_vote_has_no_commit() { + when(safetyState.getLastVotedView()).thenReturn(View.of(1)); + when(safetyState.getLockedView()).thenReturn(View.of(0)); + when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); + VerifiedVertex proposal = mock(VerifiedVertex.class); + when(proposal.touchesGenesis()).thenReturn(true); + when(proposal.hasDirectParent()).thenReturn(true); + when(proposal.parentHasDirectParent()).thenReturn(true); + BFTHeader parent = mock(BFTHeader.class); + when(parent.getView()).thenReturn(View.of(1)); + when(proposal.getParentHeader()).thenReturn(parent); + when(proposal.getView()).thenReturn(View.of(2)); + BFTHeader grandParent = mock(BFTHeader.class); + when(grandParent.getView()).thenReturn(mock(View.class)); + when(proposal.getGrandParentHeader()).thenReturn(grandParent); + Optional voteMaybe = + safetyRules.voteFor(proposal, mock(BFTHeader.class), 1L, mock(HighQC.class)); + assertThat(voteMaybe).isNotEmpty(); + Vote vote = voteMaybe.get(); + assertThat(vote.getVoteData().getCommitted()).isEmpty(); + } + + @Test + public void when_vote_on_proposal_three_after_genesis__then_returned_vote_has_commit() { + when(safetyState.getLastVotedView()).thenReturn(View.of(1)); + when(safetyState.getLockedView()).thenReturn(View.of(0)); + when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); + + VerifiedVertex proposal = mock(VerifiedVertex.class); + when(proposal.touchesGenesis()).thenReturn(false); + when(proposal.hasDirectParent()).thenReturn(true); + when(proposal.parentHasDirectParent()).thenReturn(true); + BFTHeader grandparentHeader = mock(BFTHeader.class); + when(grandparentHeader.getView()).thenReturn(mock(View.class)); + when(proposal.getGrandParentHeader()).thenReturn(grandparentHeader); + BFTHeader parent = mock(BFTHeader.class); + when(parent.getView()).thenReturn(View.of(2)); + when(proposal.getParentHeader()).thenReturn(parent); + when(proposal.getView()).thenReturn(View.of(3)); + + Optional voteMaybe = + safetyRules.voteFor(proposal, mock(BFTHeader.class), 1L, mock(HighQC.class)); + assertThat(voteMaybe).isNotEmpty(); + Vote vote = voteMaybe.get(); + assertThat(vote.getVoteData().getCommitted()).hasValue(grandparentHeader); + } + + @Test + public void + when_vote_on_proposal_three_after_genesis_with_skip__then_returned_vote_has_no_commit() { + when(safetyState.getLastVotedView()).thenReturn(View.of(1)); + when(safetyState.getLockedView()).thenReturn(View.of(0)); + when(safetyState.toBuilder()).thenReturn(mock(Builder.class)); + + VerifiedVertex proposal = mock(VerifiedVertex.class); + when(proposal.touchesGenesis()).thenReturn(false); + when(proposal.hasDirectParent()).thenReturn(false); + when(proposal.parentHasDirectParent()).thenReturn(true); + BFTHeader parent = mock(BFTHeader.class); + when(parent.getView()).thenReturn(View.of(2)); + when(proposal.getParentHeader()).thenReturn(parent); + when(proposal.getView()).thenReturn(View.of(4)); + BFTHeader grandParent = mock(BFTHeader.class); + when(grandParent.getView()).thenReturn(mock(View.class)); + when(proposal.getGrandParentHeader()).thenReturn(grandParent); + + Optional voteMaybe = + safetyRules.voteFor(proposal, mock(BFTHeader.class), 1L, mock(HighQC.class)); + assertThat(voteMaybe).isNotEmpty(); + Vote vote = voteMaybe.get(); + assertThat(vote.getVoteData().getCommitted()).isEmpty(); + } + + @Test + public void when_timeout_already_timed_out_vote_than_the_same_vote_is_returned() { + Vote vote = mock(Vote.class); + when(vote.isTimeout()).thenReturn(true); + assertEquals(vote, safetyRules.timeoutVote(vote)); + } + + @Test + public void when_timeout_a_vote_than_it_has_a_timeout_signature() { + Vote vote = mock(Vote.class); + Vote voteWithTimeout = mock(Vote.class); + when(vote.getView()).thenReturn(View.of(1)); + when(vote.getEpoch()).thenReturn(1L); + when(vote.withTimeoutSignature(any())).thenReturn(voteWithTimeout); + when(vote.isTimeout()).thenReturn(false); + + Builder builder = mock(Builder.class); + when(builder.lastVote(any())).thenReturn(builder); + when(builder.build()).thenReturn(this.safetyState); + when(safetyState.toBuilder()).thenReturn(builder); + + Vote resultVote = safetyRules.timeoutVote(vote); + verify(vote, times(1)).withTimeoutSignature(any()); + assertEquals(voteWithTimeout, resultVote); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyStateTest.java index 36f8650382..a67a564cfd 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/safety/SafetyStateTest.java @@ -1,106 +1,104 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.consensus.safety; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.mockito.Mockito.mock; - -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.safety.SafetyState.Builder; -import com.radixdlt.crypto.HashUtils; -import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Test; - -import java.util.Optional; - -public class SafetyStateTest { - - @Test - public void when_build_with_no_new_views__then_should_return_the_same_object() { - SafetyState safetyState = SafetyState.initialState(); - Builder builder = safetyState.toBuilder(); - assertThat(builder.build()).isSameAs(safetyState); - } - - @Test - public void when_build_with_new_last_vote__then_should_build_with_new_last_vote() { - SafetyState safetyState = SafetyState.initialState(); - Builder builder = safetyState.toBuilder(); - Vote vote = mock(Vote.class); - builder.lastVote(vote); - SafetyState nextSafetyState = builder.build(); - assertThat(nextSafetyState.getLastVote()).isEqualTo(Optional.of(vote)); - assertThat(nextSafetyState.getLockedView()).isEqualTo(safetyState.getLockedView()); - } - - @Test - public void equalsContract() { - EqualsVerifier - .forClass(SafetyState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.consensus.safety; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; + +import com.google.common.hash.HashCode; +import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.safety.SafetyState.Builder; +import com.radixdlt.crypto.HashUtils; +import java.util.Optional; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class SafetyStateTest { + + @Test + public void when_build_with_no_new_views__then_should_return_the_same_object() { + SafetyState safetyState = SafetyState.initialState(); + Builder builder = safetyState.toBuilder(); + assertThat(builder.build()).isSameAs(safetyState); + } + + @Test + public void when_build_with_new_last_vote__then_should_build_with_new_last_vote() { + SafetyState safetyState = SafetyState.initialState(); + Builder builder = safetyState.toBuilder(); + Vote vote = mock(Vote.class); + builder.lastVote(vote); + SafetyState nextSafetyState = builder.build(); + assertThat(nextSafetyState.getLastVote()).isEqualTo(Optional.of(vote)); + assertThat(nextSafetyState.getLockedView()).isEqualTo(safetyState.getLockedView()); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(SafetyState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/GetVerticesRequestTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/GetVerticesRequestTest.java index e4226f8dad..8d68cc593a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/GetVerticesRequestTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/GetVerticesRequestTest.java @@ -71,10 +71,10 @@ public class GetVerticesRequestTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(GetVerticesRequest.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(GetVerticesRequest.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/VertexRequestTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/VertexRequestTimeoutTest.java index 71a5b7bd9d..30b85e6456 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/VertexRequestTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/sync/VertexRequestTimeoutTest.java @@ -70,11 +70,10 @@ import org.junit.Test; public class VertexRequestTimeoutTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(VertexRequestTimeout.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(VertexRequestTimeout.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/counters/SystemCountersImplTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/counters/SystemCountersImplTest.java index d2d629b9ef..f90629dd97 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/counters/SystemCountersImplTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/counters/SystemCountersImplTest.java @@ -64,110 +64,108 @@ package com.radixdlt.counters; -import java.util.Map; -import java.util.TreeMap; - -import org.junit.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.Assert.assertNotNull; import com.google.common.collect.ImmutableMap; import com.radixdlt.counters.SystemCounters.CounterType; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.Assert.assertNotNull; +import java.util.Map; +import java.util.TreeMap; +import org.junit.Test; public class SystemCountersImplTest { - @Test - public void when_get_count__then_count_should_be_0() { - SystemCounters counters = new SystemCountersImpl(); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(0L); - } + @Test + public void when_get_count__then_count_should_be_0() { + SystemCounters counters = new SystemCountersImpl(); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(0L); + } - @Test - public void when_increment__then_count_should_be_1() { - SystemCounters counters = new SystemCountersImpl(); - counters.increment(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1L); - counters.increment(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(2L); - } + @Test + public void when_increment__then_count_should_be_1() { + SystemCounters counters = new SystemCountersImpl(); + counters.increment(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1L); + counters.increment(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(2L); + } - @Test - public void when_add__then_count_should_be_added_value() { - SystemCounters counters = new SystemCountersImpl(); - counters.add(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L); - counters.add(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 4321); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L + 4321L); - } + @Test + public void when_add__then_count_should_be_added_value() { + SystemCounters counters = new SystemCountersImpl(); + counters.add(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L); + counters.add(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 4321); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L + 4321L); + } - @Test - public void when_set__then_count_should_be_1() { - SystemCounters counters = new SystemCountersImpl(); - counters.set(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L); - counters.set(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 4321); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(4321L); - } + @Test + public void when_set__then_count_should_be_1() { + SystemCounters counters = new SystemCountersImpl(); + counters.set(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L); + counters.set(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 4321); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(4321L); + } - @Test - public void when_set_all__then_count_should_be_correct() { - SystemCounters counters = new SystemCountersImpl(); - counters.setAll(ImmutableMap.of( - CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234L, - CounterType.BFT_PACEMAKER_PROPOSALS_SENT, 4567L - )); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L); - assertThat(counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)).isEqualTo(4567L); - counters.setAll(ImmutableMap.of( - CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 2345L, - CounterType.BFT_PACEMAKER_PROPOSALS_SENT, 5678L - )); - assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(2345L); - assertThat(counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)).isEqualTo(5678L); - } + @Test + public void when_set_all__then_count_should_be_correct() { + SystemCounters counters = new SystemCountersImpl(); + counters.setAll( + ImmutableMap.of( + CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234L, + CounterType.BFT_PACEMAKER_PROPOSALS_SENT, 4567L)); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(1234L); + assertThat(counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)).isEqualTo(4567L); + counters.setAll( + ImmutableMap.of( + CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 2345L, + CounterType.BFT_PACEMAKER_PROPOSALS_SENT, 5678L)); + assertThat(counters.get(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(2345L); + assertThat(counters.get(CounterType.BFT_PACEMAKER_PROPOSALS_SENT)).isEqualTo(5678L); + } - @Test - public void when_tomap__then_values_correct() { - SystemCounters counters = new SystemCountersImpl(); - for (CounterType value : CounterType.values()) { - counters.set(value, ordinal(value)); - } - // Ensure writeable - Map m = new TreeMap<>(counters.toMap()); - assertNotNull(m.remove("time")); - testMap("", m); - } + @Test + public void when_tomap__then_values_correct() { + SystemCounters counters = new SystemCountersImpl(); + for (CounterType value : CounterType.values()) { + counters.set(value, ordinal(value)); + } + // Ensure writeable + Map m = new TreeMap<>(counters.toMap()); + assertNotNull(m.remove("time")); + testMap("", m); + } - @Test - public void sensible_tostring() { - SystemCounters counters = new SystemCountersImpl(); - counters.set(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234); - String s = counters.toString(); - assertThat(s).contains(SystemCountersImpl.class.getSimpleName()); - assertThat(s).contains("1234"); - } + @Test + public void sensible_tostring() { + SystemCounters counters = new SystemCountersImpl(); + counters.set(CounterType.BFT_PACEMAKER_TIMEOUTS_SENT, 1234); + String s = counters.toString(); + assertThat(s).contains(SystemCountersImpl.class.getSimpleName()); + assertThat(s).contains("1234"); + } - private int ordinal(CounterType value) { - // Add one so that none are zero. - // Zero is the default value, and this lets us catch the "not set" case. - return value.ordinal() + 1; - } + private int ordinal(CounterType value) { + // Add one so that none are zero. + // Zero is the default value, and this lets us catch the "not set" case. + return value.ordinal() + 1; + } - private void testMap(String path, Map m) { - for (Map.Entry entry : m.entrySet()) { - String p = entry.getKey().toUpperCase(); - String newPath = path.isEmpty() ? p : path + "_" + p; - Object o = entry.getValue(); - if (o instanceof Map) { - @SuppressWarnings("unchecked") - Map newm = (Map) o; - testMap(newPath, newm); - } else { - String s = o.toString(); - CounterType ct = CounterType.valueOf(newPath); - // Check that values in the map match the values we set above - assertThat(Long.parseLong(s)).isEqualTo(ordinal(ct)); - } - } - } -} \ No newline at end of file + private void testMap(String path, Map m) { + for (Map.Entry entry : m.entrySet()) { + String p = entry.getKey().toUpperCase(); + String newPath = path.isEmpty() ? p : path + "_" + p; + Object o = entry.getValue(); + if (o instanceof Map) { + @SuppressWarnings("unchecked") + Map newm = (Map) o; + testMap(newPath, newm); + } else { + String s = o.toString(); + CounterType ct = CounterType.valueOf(newPath); + // Check that values in the map match the values we set above + assertThat(Long.parseLong(s)).isEqualTo(ordinal(ct)); + } + } + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsConsensusModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsConsensusModule.java index e877d00898..2c622bce39 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsConsensusModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsConsensusModule.java @@ -83,151 +83,108 @@ import com.radixdlt.consensus.sync.VertexStoreBFTSyncRequestProcessor; import com.radixdlt.ledger.LedgerUpdate; -/** - * Sets up processors for consensus which doesn't support epochs - */ +/** Sets up processors for consensus which doesn't support epochs */ public class NoEpochsConsensusModule extends AbstractModule { - @Override - public void configure() { - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(ScheduledLocalTimeout.class); - eventBinder.addBinding().toInstance(VertexRequestTimeout.class); - eventBinder.addBinding().toInstance(ViewUpdate.class); - eventBinder.addBinding().toInstance(LedgerUpdate.class); - } - - @ProvidesIntoSet - private StartProcessorOnRunner startProcessor(BFTEventProcessor processor) { - return new StartProcessorOnRunner( - Runners.CONSENSUS, - processor::start - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner proposalProcessor(BFTEventProcessor processor) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - Proposal.class, - processor::processProposal - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner remoteProposalProcessor(BFTEventProcessor processor) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - Proposal.class, - (node, proposal) -> processor.processProposal(proposal) - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner voteProcessor(BFTEventProcessor processor) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - Vote.class, - processor::processVote - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner remoteVoteProcessor(BFTEventProcessor processor) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - Vote.class, - (node, vote) -> processor.processVote(vote) - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner timeoutProcessor(BFTEventProcessor processor) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - ScheduledLocalTimeout.class, - processor::processLocalTimeout - ); - } - - @ProvidesIntoSet - public EventProcessorOnRunner bftSyncTimeoutProcessor(BFTSync bftSync) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - VertexRequestTimeout.class, - bftSync.vertexRequestTimeoutEventProcessor() - ); - } - - @ProvidesIntoSet - private EventProcessorOnRunner viewUpdateProcessor(BFTEventProcessor processor) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - ViewUpdate.class, - processor::processViewUpdate - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner bftSyncResponseProcessor(BFTSync bftSync) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - GetVerticesResponse.class, - bftSync.responseProcessor() - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner bftSyncErrorResponseProcessor(BFTSync bftSync) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - GetVerticesErrorResponse.class, - bftSync.errorResponseProcessor() - ); - } - - @ProvidesIntoSet - private RemoteEventProcessorOnRunner bftSyncRequestProcessor(VertexStoreBFTSyncRequestProcessor processor) { - return new RemoteEventProcessorOnRunner<>( - Runners.CONSENSUS, - GetVerticesRequest.class, - processor - ); - } - - @ProvidesIntoSet - public EventProcessorOnRunner bftRebuildUpdateEventProcessor(BFTEventProcessor eventProcessor) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - BFTRebuildUpdate.class, - eventProcessor::processBFTRebuildUpdate - ); - } - - @ProvidesIntoSet - public EventProcessorOnRunner bftUpdateEventProcessor(BFTEventProcessor eventProcessor) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - BFTInsertUpdate.class, - eventProcessor::processBFTUpdate - ); - } - - @ProvidesIntoSet - public EventProcessorOnRunner bftSync(BFTSync bftSync) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - BFTInsertUpdate.class, - bftSync::processBFTUpdate - ); - } - - @ProvidesIntoSet - public EventProcessorOnRunner baseLedgerUpdateEventProcessor(BFTSync bftSync) { - return new EventProcessorOnRunner<>( - Runners.CONSENSUS, - LedgerUpdate.class, - bftSync.baseLedgerUpdateEventProcessor() - ); - } + @Override + public void configure() { + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(ScheduledLocalTimeout.class); + eventBinder.addBinding().toInstance(VertexRequestTimeout.class); + eventBinder.addBinding().toInstance(ViewUpdate.class); + eventBinder.addBinding().toInstance(LedgerUpdate.class); + } + + @ProvidesIntoSet + private StartProcessorOnRunner startProcessor(BFTEventProcessor processor) { + return new StartProcessorOnRunner(Runners.CONSENSUS, processor::start); + } + + @ProvidesIntoSet + private EventProcessorOnRunner proposalProcessor(BFTEventProcessor processor) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, Proposal.class, processor::processProposal); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner remoteProposalProcessor(BFTEventProcessor processor) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, Proposal.class, (node, proposal) -> processor.processProposal(proposal)); + } + + @ProvidesIntoSet + private EventProcessorOnRunner voteProcessor(BFTEventProcessor processor) { + return new EventProcessorOnRunner<>(Runners.CONSENSUS, Vote.class, processor::processVote); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner remoteVoteProcessor(BFTEventProcessor processor) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, Vote.class, (node, vote) -> processor.processVote(vote)); + } + + @ProvidesIntoSet + private EventProcessorOnRunner timeoutProcessor(BFTEventProcessor processor) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, ScheduledLocalTimeout.class, processor::processLocalTimeout); + } + + @ProvidesIntoSet + public EventProcessorOnRunner bftSyncTimeoutProcessor(BFTSync bftSync) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, + VertexRequestTimeout.class, + bftSync.vertexRequestTimeoutEventProcessor()); + } + + @ProvidesIntoSet + private EventProcessorOnRunner viewUpdateProcessor(BFTEventProcessor processor) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, ViewUpdate.class, processor::processViewUpdate); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner bftSyncResponseProcessor(BFTSync bftSync) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, GetVerticesResponse.class, bftSync.responseProcessor()); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner bftSyncErrorResponseProcessor(BFTSync bftSync) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, GetVerticesErrorResponse.class, bftSync.errorResponseProcessor()); + } + + @ProvidesIntoSet + private RemoteEventProcessorOnRunner bftSyncRequestProcessor( + VertexStoreBFTSyncRequestProcessor processor) { + return new RemoteEventProcessorOnRunner<>( + Runners.CONSENSUS, GetVerticesRequest.class, processor); + } + + @ProvidesIntoSet + public EventProcessorOnRunner bftRebuildUpdateEventProcessor( + BFTEventProcessor eventProcessor) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, BFTRebuildUpdate.class, eventProcessor::processBFTRebuildUpdate); + } + + @ProvidesIntoSet + public EventProcessorOnRunner bftUpdateEventProcessor(BFTEventProcessor eventProcessor) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, BFTInsertUpdate.class, eventProcessor::processBFTUpdate); + } + + @ProvidesIntoSet + public EventProcessorOnRunner bftSync(BFTSync bftSync) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, BFTInsertUpdate.class, bftSync::processBFTUpdate); + } + + @ProvidesIntoSet + public EventProcessorOnRunner baseLedgerUpdateEventProcessor(BFTSync bftSync) { + return new EventProcessorOnRunner<>( + Runners.CONSENSUS, LedgerUpdate.class, bftSync.baseLedgerUpdateEventProcessor()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsSyncModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsSyncModule.java index 4b50ef92b6..a5f1fb4bb3 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsSyncModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/NoEpochsSyncModule.java @@ -69,8 +69,8 @@ import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.ProvidesIntoSet; import com.radixdlt.ledger.LedgerUpdate; -import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.sync.LocalSyncService; +import com.radixdlt.sync.messages.local.LocalSyncRequest; import com.radixdlt.sync.messages.local.SyncCheckReceiveStatusTimeout; import com.radixdlt.sync.messages.local.SyncCheckTrigger; import com.radixdlt.sync.messages.local.SyncLedgerUpdateTimeout; @@ -79,118 +79,88 @@ import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncResponse; -/** - * A sync module which doesn't involve epochs - */ +/** A sync module which doesn't involve epochs */ public class NoEpochsSyncModule extends AbstractModule { - @Override - public void configure() { - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(SyncCheckTrigger.class); - eventBinder.addBinding().toInstance(SyncCheckReceiveStatusTimeout.class); - eventBinder.addBinding().toInstance(SyncRequestTimeout.class); - eventBinder.addBinding().toInstance(LocalSyncRequest.class); - eventBinder.addBinding().toInstance(SyncLedgerUpdateTimeout.class); - } + @Override + public void configure() { + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(SyncCheckTrigger.class); + eventBinder.addBinding().toInstance(SyncCheckReceiveStatusTimeout.class); + eventBinder.addBinding().toInstance(SyncRequestTimeout.class); + eventBinder.addBinding().toInstance(LocalSyncRequest.class); + eventBinder.addBinding().toInstance(SyncLedgerUpdateTimeout.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner ledgerUpdateEventProcessor( - LocalSyncService localSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - LedgerUpdate.class, - localSyncService.ledgerUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner ledgerUpdateEventProcessor(LocalSyncService localSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, LedgerUpdate.class, localSyncService.ledgerUpdateEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner localSyncRequestEventProcessor( - LocalSyncService localSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - LocalSyncRequest.class, - localSyncService.localSyncRequestEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner localSyncRequestEventProcessor( + LocalSyncService localSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, LocalSyncRequest.class, localSyncService.localSyncRequestEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncCheckTriggerEventProcessor( - LocalSyncService localSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncCheckTrigger.class, - localSyncService.syncCheckTriggerEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncCheckTriggerEventProcessor( + LocalSyncService localSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, SyncCheckTrigger.class, localSyncService.syncCheckTriggerEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncRequestTimeoutEventProcessor( - LocalSyncService localSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncRequestTimeout.class, - localSyncService.syncRequestTimeoutEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncRequestTimeoutEventProcessor( + LocalSyncService localSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncRequestTimeout.class, + localSyncService.syncRequestTimeoutEventProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncLedgerUpdateTimeoutEventProcessor( - LocalSyncService localSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncLedgerUpdateTimeout.class, - localSyncService.syncLedgerUpdateTimeoutProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncLedgerUpdateTimeoutEventProcessor( + LocalSyncService localSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncLedgerUpdateTimeout.class, + localSyncService.syncLedgerUpdateTimeoutProcessor()); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncCheckReceiveStatusTimeoutEventProcessor( - LocalSyncService localSyncService - ) { - return new EventProcessorOnRunner<>( - Runners.SYNC, - SyncCheckReceiveStatusTimeout.class, - localSyncService.syncCheckReceiveStatusTimeoutEventProcessor() - ); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncCheckReceiveStatusTimeoutEventProcessor( + LocalSyncService localSyncService) { + return new EventProcessorOnRunner<>( + Runners.SYNC, + SyncCheckReceiveStatusTimeout.class, + localSyncService.syncCheckReceiveStatusTimeoutEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner statusResponseEventProcessor( - LocalSyncService localSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - StatusResponse.class, - localSyncService.statusResponseEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner statusResponseEventProcessor( + LocalSyncService localSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, StatusResponse.class, localSyncService.statusResponseEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner syncResponseEventProcessor( - LocalSyncService localSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - SyncResponse.class, - localSyncService.syncResponseEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner syncResponseEventProcessor( + LocalSyncService localSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, SyncResponse.class, localSyncService.syncResponseEventProcessor()); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner ledgerStatusUpdateEventProcessor( - LocalSyncService localSyncService - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - LedgerStatusUpdate.class, - localSyncService.ledgerStatusUpdateEventProcessor() - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner ledgerStatusUpdateEventProcessor( + LocalSyncService localSyncService) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, + LedgerStatusUpdate.class, + localSyncService.ledgerStatusUpdateEventProcessor()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/DeterministicProcessor.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/DeterministicProcessor.java index ae5c3db587..d35d6460cc 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/DeterministicProcessor.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/DeterministicProcessor.java @@ -70,71 +70,71 @@ import com.radixdlt.environment.EventProcessorOnRunner; import com.radixdlt.environment.RemoteEventProcessorOnRunner; import com.radixdlt.environment.StartProcessorOnRunner; - -import javax.inject.Inject; import java.util.Objects; import java.util.Set; +import javax.inject.Inject; public final class DeterministicProcessor { - private final BFTNode self; - private final Set startProcessors; - private final Set> processorOnRunners; - private final Set> remoteProcessorOnRunners; + private final BFTNode self; + private final Set startProcessors; + private final Set> processorOnRunners; + private final Set> remoteProcessorOnRunners; - @Inject - public DeterministicProcessor( - @Self BFTNode self, - Set startProcessors, - Set> processorOnRunners, - Set> remoteProcessorOnRunners - ) { - this.self = Objects.requireNonNull(self); - this.startProcessors = Objects.requireNonNull(startProcessors); - this.processorOnRunners = Objects.requireNonNull(processorOnRunners); - this.remoteProcessorOnRunners = Objects.requireNonNull(remoteProcessorOnRunners); - } + @Inject + public DeterministicProcessor( + @Self BFTNode self, + Set startProcessors, + Set> processorOnRunners, + Set> remoteProcessorOnRunners) { + this.self = Objects.requireNonNull(self); + this.startProcessors = Objects.requireNonNull(startProcessors); + this.processorOnRunners = Objects.requireNonNull(processorOnRunners); + this.remoteProcessorOnRunners = Objects.requireNonNull(remoteProcessorOnRunners); + } - public BFTNode self() { - return self; - } + public BFTNode self() { + return self; + } - public void start() { - startProcessors.forEach(p -> p.getProcessor().start()); - } + public void start() { + startProcessors.forEach(p -> p.getProcessor().start()); + } - @SuppressWarnings("unchecked") - private static boolean tryExecute(T event, TypeLiteral msgType, EventProcessorOnRunner processor) { - if (msgType != null) { - var typeLiteral = (TypeLiteral) msgType; - final var maybeProcessor = processor.getProcessor(typeLiteral); - maybeProcessor.ifPresent(p -> p.process(event)); - return maybeProcessor.isPresent(); - } else { - final var eventClass = (Class) event.getClass(); - final var maybeProcessor = processor.getProcessor(eventClass); - maybeProcessor.ifPresent(p -> p.process(event)); - return maybeProcessor.isPresent(); - } - } + @SuppressWarnings("unchecked") + private static boolean tryExecute( + T event, TypeLiteral msgType, EventProcessorOnRunner processor) { + if (msgType != null) { + var typeLiteral = (TypeLiteral) msgType; + final var maybeProcessor = processor.getProcessor(typeLiteral); + maybeProcessor.ifPresent(p -> p.process(event)); + return maybeProcessor.isPresent(); + } else { + final var eventClass = (Class) event.getClass(); + final var maybeProcessor = processor.getProcessor(eventClass); + maybeProcessor.ifPresent(p -> p.process(event)); + return maybeProcessor.isPresent(); + } + } - @SuppressWarnings("unchecked") - private static boolean tryExecute(BFTNode origin, T event, RemoteEventProcessorOnRunner processor) { - final var eventClass = (Class) event.getClass(); - final var maybeProcessor = processor.getProcessor(eventClass); - maybeProcessor.ifPresent(p -> p.process(origin, event)); - return maybeProcessor.isPresent(); - } + @SuppressWarnings("unchecked") + private static boolean tryExecute( + BFTNode origin, T event, RemoteEventProcessorOnRunner processor) { + final var eventClass = (Class) event.getClass(); + final var maybeProcessor = processor.getProcessor(eventClass); + maybeProcessor.ifPresent(p -> p.process(origin, event)); + return maybeProcessor.isPresent(); + } - public void handleMessage(BFTNode origin, Object message, TypeLiteral msgType) { - boolean messageHandled = false; - if (Objects.equals(self, origin)) { - for (EventProcessorOnRunner p : processorOnRunners) { - messageHandled = tryExecute(message, msgType, p) || messageHandled; - } - } else { - for (RemoteEventProcessorOnRunner p : remoteProcessorOnRunners) { - messageHandled = tryExecute(origin, message, p) || messageHandled; - } - } - } + public void handleMessage(BFTNode origin, Object message, TypeLiteral msgType) { + boolean messageHandled = false; + if (Objects.equals(self, origin)) { + for (EventProcessorOnRunner p : processorOnRunners) { + messageHandled = tryExecute(message, msgType, p) || messageHandled; + } + } else { + for (RemoteEventProcessorOnRunner p : remoteProcessorOnRunners) { + messageHandled = tryExecute(origin, message, p) || messageHandled; + } + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/LastEventsModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/LastEventsModule.java index 988861320a..29988cc9ae 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/LastEventsModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/LastEventsModule.java @@ -72,22 +72,21 @@ import com.radixdlt.environment.EventProcessorOnDispatch; public final class LastEventsModule extends AbstractModule { - private final Class[] messageClasses; + private final Class[] messageClasses; - public LastEventsModule(Class... messageClasses) { - this.messageClasses = messageClasses; - } + public LastEventsModule(Class... messageClasses) { + this.messageClasses = messageClasses; + } - @Override - protected void configure() { - var map = MutableClassToInstanceMap.create(); - bind(new TypeLiteral>() { }) - .toInstance(map); + @Override + protected void configure() { + var map = MutableClassToInstanceMap.create(); + bind(new TypeLiteral>() {}).toInstance(map); - for (var c : messageClasses) { - Multibinder.newSetBinder(binder(), new TypeLiteral>() { }) - .addBinding() - .toInstance(new EventProcessorOnDispatch<>(c, e -> map.put(c, e))); - } - } + for (var c : messageClasses) { + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}) + .addBinding() + .toInstance(new EventProcessorOnDispatch<>(c, e -> map.put(c, e))); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/MultiNodeDeterministicRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/MultiNodeDeterministicRunner.java index 9b4ead0bf3..13aca9ea84 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/MultiNodeDeterministicRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/MultiNodeDeterministicRunner.java @@ -72,87 +72,86 @@ import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import io.reactivex.rxjava3.schedulers.Timed; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.ThreadContext; - import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.ThreadContext; public final class MultiNodeDeterministicRunner { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private final List> nodeCreators; - private final DeterministicNetwork network; - private final List nodes = new ArrayList<>(); - private final Consumer teardown; + private final List> nodeCreators; + private final DeterministicNetwork network; + private final List nodes = new ArrayList<>(); + private final Consumer teardown; - @Inject - public MultiNodeDeterministicRunner( - List> nodeCreators, - Consumer teardown, - DeterministicNetwork network - ) { - this.nodeCreators = nodeCreators; - this.teardown = teardown; - this.network = network; - } + @Inject + public MultiNodeDeterministicRunner( + List> nodeCreators, + Consumer teardown, + DeterministicNetwork network) { + this.nodeCreators = nodeCreators; + this.teardown = teardown; + this.network = network; + } - public int getSize() { - return nodes.size(); - } + public int getSize() { + return nodes.size(); + } - public Injector getNode(int index) { - return nodes.get(index); - } + public Injector getNode(int index) { + return nodes.get(index); + } - public void dispatchToAll(Key> dispatcherKey, T t) { - this.nodes.forEach(n -> n.getInstance(dispatcherKey).dispatch(t)); - } + public void dispatchToAll(Key> dispatcherKey, T t) { + this.nodes.forEach(n -> n.getInstance(dispatcherKey).dispatch(t)); + } - public void start() { - for (Supplier nodeCreator : nodeCreators) { - this.nodes.add(nodeCreator.get()); - } - this.nodes.forEach(i -> i.getInstance(DeterministicProcessor.class).start()); - } + public void start() { + for (Supplier nodeCreator : nodeCreators) { + this.nodes.add(nodeCreator.get()); + } + this.nodes.forEach(i -> i.getInstance(DeterministicProcessor.class).start()); + } - public void tearDown() { - this.nodes.forEach(teardown); - } + public void tearDown() { + this.nodes.forEach(teardown); + } - public void restartNode(int index) { - this.network.dropMessages(m -> m.channelId().receiverIndex() == index); - var injector = nodeCreators.get(index).get(); - teardown.accept(this.nodes.set(index, injector)); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - injector.getInstance(DeterministicProcessor.class).start(); - } finally { - ThreadContext.remove("self"); - } - } + public void restartNode(int index) { + this.network.dropMessages(m -> m.channelId().receiverIndex() == index); + var injector = nodeCreators.get(index).get(); + teardown.accept(this.nodes.set(index, injector)); + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + injector.getInstance(DeterministicProcessor.class).start(); + } finally { + ThreadContext.remove("self"); + } + } - public void processNext() { - Timed msg = this.network.nextMessage(); - logger.debug("Processing message {}", msg); + public void processNext() { + Timed msg = this.network.nextMessage(); + logger.debug("Processing message {}", msg); - int nodeIndex = msg.value().channelId().receiverIndex(); - Injector injector = this.nodes.get(nodeIndex); - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - injector.getInstance(DeterministicProcessor.class) - .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); - } finally { - ThreadContext.remove("self"); - } - } + int nodeIndex = msg.value().channelId().receiverIndex(); + Injector injector = this.nodes.get(nodeIndex); + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + injector + .getInstance(DeterministicProcessor.class) + .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); + } finally { + ThreadContext.remove("self"); + } + } - public void processForCount(int messageCount) { - for (int i = 0; i < messageCount; i++) { - processNext(); - } - } + public void processForCount(int messageCount) { + for (int i = 0; i < messageCount; i++) { + processNext(); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/SingleNodeDeterministicRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/SingleNodeDeterministicRunner.java index b869f83a00..3f0f706bd4 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/SingleNodeDeterministicRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/SingleNodeDeterministicRunner.java @@ -67,53 +67,51 @@ import com.google.inject.Inject; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; - import java.util.function.Predicate; public final class SingleNodeDeterministicRunner { - private static final int MAX_EVENTS_DEFAULT = 10000; + private static final int MAX_EVENTS_DEFAULT = 10000; - private final DeterministicProcessor processor; - private final DeterministicNetwork network; + private final DeterministicProcessor processor; + private final DeterministicNetwork network; - @Inject - public SingleNodeDeterministicRunner( - DeterministicProcessor processor, - DeterministicNetwork network - ) { - this.processor = processor; - this.network = network; - } + @Inject + public SingleNodeDeterministicRunner( + DeterministicProcessor processor, DeterministicNetwork network) { + this.processor = processor; + this.network = network; + } - public void start() { - processor.start(); - } + public void start() { + processor.start(); + } - public void processNext(int count) { - for (int i = 0; i < count; i++) { - processNext(); - } - } + public void processNext(int count) { + for (int i = 0; i < count; i++) { + processNext(); + } + } - public ControlledMessage processNext() { - var msg = network.nextMessage().value(); - processor.handleMessage(msg.origin(), msg.message(), msg.typeLiteral()); - return msg; - } + public ControlledMessage processNext() { + var msg = network.nextMessage().value(); + processor.handleMessage(msg.origin(), msg.message(), msg.typeLiteral()); + return msg; + } - public T runNextEventsThrough(Class eventClass) { - return runNextEventsThrough(eventClass, t -> true); - } + public T runNextEventsThrough(Class eventClass) { + return runNextEventsThrough(eventClass, t -> true); + } - @SuppressWarnings("unchecked") - public T runNextEventsThrough(Class eventClass, Predicate eventPredicate) { - for (int i = 0; i < MAX_EVENTS_DEFAULT; i++) { - var msg = processNext(); - if (eventClass.isInstance(msg.message()) && eventPredicate.test((T) msg.message())) { - return (T) msg.message(); - } - } + @SuppressWarnings("unchecked") + public T runNextEventsThrough(Class eventClass, Predicate eventPredicate) { + for (int i = 0; i < MAX_EVENTS_DEFAULT; i++) { + var msg = processNext(); + if (eventClass.isInstance(msg.message()) && eventPredicate.test((T) msg.message())) { + return (T) msg.message(); + } + } - throw new RuntimeException("Reached " + MAX_EVENTS_DEFAULT + " events without finding " + eventClass); - } + throw new RuntimeException( + "Reached " + MAX_EVENTS_DEFAULT + " events without finding " + eventClass); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ChannelId.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ChannelId.java index d8caec7f98..d8a0e77867 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ChannelId.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ChannelId.java @@ -66,48 +66,45 @@ import java.util.Objects; -/** - * ID for a channel between two nodes. - */ +/** ID for a channel between two nodes. */ public final class ChannelId { - private final int senderIndex; - private final int receiverIndex; + private final int senderIndex; + private final int receiverIndex; - private ChannelId(int senderIndex, int receiverIndex) { - this.senderIndex = senderIndex; - this.receiverIndex = receiverIndex; - } + private ChannelId(int senderIndex, int receiverIndex) { + this.senderIndex = senderIndex; + this.receiverIndex = receiverIndex; + } - public static ChannelId of(int senderIndex, int receiverIndex) { - return new ChannelId(senderIndex, receiverIndex); - } + public static ChannelId of(int senderIndex, int receiverIndex) { + return new ChannelId(senderIndex, receiverIndex); + } - public int receiverIndex() { - return this.receiverIndex; - } + public int receiverIndex() { + return this.receiverIndex; + } - public int senderIndex() { - return this.senderIndex; - } + public int senderIndex() { + return this.senderIndex; + } - @Override - public int hashCode() { - return Objects.hash(this.senderIndex, this.receiverIndex); - } + @Override + public int hashCode() { + return Objects.hash(this.senderIndex, this.receiverIndex); + } - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ChannelId)) { - return false; - } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ChannelId)) { + return false; + } - ChannelId other = (ChannelId) obj; - return this.senderIndex == other.senderIndex && this.receiverIndex == other.receiverIndex; - } + ChannelId other = (ChannelId) obj; + return this.senderIndex == other.senderIndex && this.receiverIndex == other.receiverIndex; + } - @Override - public String toString() { - return this.senderIndex + "->" + this.receiverIndex; - } + @Override + public String toString() { + return this.senderIndex + "->" + this.receiverIndex; + } } - diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledMessage.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledMessage.java index 3b3b02d928..810c145ca9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledMessage.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledMessage.java @@ -68,71 +68,80 @@ import com.radixdlt.consensus.bft.BFTNode; import java.util.Objects; -/** - * A message sent over a channel. - */ +/** A message sent over a channel. */ public final class ControlledMessage { - private final BFTNode origin; - private final ChannelId channelId; - private final Object message; - private final TypeLiteral typeLiteral; - private final long arrivalTime; + private final BFTNode origin; + private final ChannelId channelId; + private final Object message; + private final TypeLiteral typeLiteral; + private final long arrivalTime; - public ControlledMessage(BFTNode origin, ChannelId channelId, Object message, TypeLiteral typeLiteral, long arrivalTime) { - this.origin = origin; - this.channelId = channelId; - this.message = message; - this.typeLiteral = typeLiteral; - this.arrivalTime = arrivalTime; - } + public ControlledMessage( + BFTNode origin, + ChannelId channelId, + Object message, + TypeLiteral typeLiteral, + long arrivalTime) { + this.origin = origin; + this.channelId = channelId; + this.message = message; + this.typeLiteral = typeLiteral; + this.arrivalTime = arrivalTime; + } - public ControlledMessage withAdditionalDelay(long additionalDelay) { - return new ControlledMessage(this.origin, this.channelId, this.message, this.typeLiteral, this.arrivalTime + additionalDelay); - } + public ControlledMessage withAdditionalDelay(long additionalDelay) { + return new ControlledMessage( + this.origin, + this.channelId, + this.message, + this.typeLiteral, + this.arrivalTime + additionalDelay); + } - public ControlledMessage withArrivalTime(long arrivalTime) { - return new ControlledMessage(this.origin, this.channelId, this.message, this.typeLiteral, arrivalTime); - } + public ControlledMessage withArrivalTime(long arrivalTime) { + return new ControlledMessage( + this.origin, this.channelId, this.message, this.typeLiteral, arrivalTime); + } - public BFTNode origin() { - return origin; - } + public BFTNode origin() { + return origin; + } - public ChannelId channelId() { - return this.channelId; - } + public ChannelId channelId() { + return this.channelId; + } - public Object message() { - return this.message; - } + public Object message() { + return this.message; + } - public TypeLiteral typeLiteral() { - return typeLiteral; - } + public TypeLiteral typeLiteral() { + return typeLiteral; + } - public long arrivalTime() { - return this.arrivalTime; - } + public long arrivalTime() { + return this.arrivalTime; + } - @Override - public int hashCode() { - return Objects.hash(this.origin, this.channelId, this.message, this.arrivalTime); - } + @Override + public int hashCode() { + return Objects.hash(this.origin, this.channelId, this.message, this.arrivalTime); + } - @Override - public boolean equals(Object o) { - if (o instanceof ControlledMessage) { - ControlledMessage that = (ControlledMessage) o; - return this.arrivalTime == that.arrivalTime - && Objects.equals(this.origin, that.origin) - && Objects.equals(this.channelId, that.channelId) - && Objects.equals(this.message, that.message); - } - return false; - } + @Override + public boolean equals(Object o) { + if (o instanceof ControlledMessage) { + ControlledMessage that = (ControlledMessage) o; + return this.arrivalTime == that.arrivalTime + && Objects.equals(this.origin, that.origin) + && Objects.equals(this.channelId, that.channelId) + && Objects.equals(this.message, that.message); + } + return false; + } - @Override - public String toString() { - return String.format("%s(%s) %s", channelId, arrivalTime, message); - } + @Override + public String toString() { + return String.format("%s(%s) %s", channelId, arrivalTime, message); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledSender.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledSender.java index 78f9b2498c..0744133250 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledSender.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/ControlledSender.java @@ -65,76 +65,72 @@ package com.radixdlt.environment.deterministic.network; import com.google.inject.TypeLiteral; +import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.environment.Environment; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.environment.ScheduledEventDispatcher; -import com.radixdlt.consensus.bft.BFTNode; - -/** - * A sender within a deterministic network. - */ +/** A sender within a deterministic network. */ public final class ControlledSender implements Environment { - private final DeterministicNetwork network; - private final BFTNode self; - private final int senderIndex; - private final ChannelId localChannel; + private final DeterministicNetwork network; + private final BFTNode self; + private final int senderIndex; + private final ChannelId localChannel; - ControlledSender(DeterministicNetwork network, BFTNode self, int senderIndex) { - this.network = network; - this.self = self; - this.senderIndex = senderIndex; - this.localChannel = ChannelId.of(this.senderIndex, this.senderIndex); - } + ControlledSender(DeterministicNetwork network, BFTNode self, int senderIndex) { + this.network = network; + this.self = self; + this.senderIndex = senderIndex; + this.localChannel = ChannelId.of(this.senderIndex, this.senderIndex); + } - @Override - public EventDispatcher getDispatcher(Class eventClass) { - return e -> handleMessage(new ControlledMessage(self, this.localChannel, e, null, arrivalTime(this.localChannel))); - } + @Override + public EventDispatcher getDispatcher(Class eventClass) { + return e -> + handleMessage( + new ControlledMessage( + self, this.localChannel, e, null, arrivalTime(this.localChannel))); + } - @Override - public ScheduledEventDispatcher getScheduledDispatcher(Class eventClass) { - return (t, milliseconds) -> { - var msg = new ControlledMessage( - self, - this.localChannel, - t, - null, - arrivalTime(this.localChannel) + milliseconds - ); - handleMessage(msg); - }; - } + @Override + public ScheduledEventDispatcher getScheduledDispatcher(Class eventClass) { + return (t, milliseconds) -> { + var msg = + new ControlledMessage( + self, this.localChannel, t, null, arrivalTime(this.localChannel) + milliseconds); + handleMessage(msg); + }; + } - @Override - public ScheduledEventDispatcher getScheduledDispatcher(TypeLiteral typeLiteral) { - return (t, milliseconds) -> { - var msg = new ControlledMessage( - self, - this.localChannel, - t, - typeLiteral, - arrivalTime(this.localChannel) + milliseconds - ); - handleMessage(msg); - }; - } + @Override + public ScheduledEventDispatcher getScheduledDispatcher(TypeLiteral typeLiteral) { + return (t, milliseconds) -> { + var msg = + new ControlledMessage( + self, + this.localChannel, + t, + typeLiteral, + arrivalTime(this.localChannel) + milliseconds); + handleMessage(msg); + }; + } - @Override - public RemoteEventDispatcher getRemoteDispatcher(Class eventClass) { - return (node, e) -> { - ChannelId channelId = ChannelId.of(this.senderIndex, this.network.lookup(node)); - handleMessage(new ControlledMessage(self, channelId, e, null, arrivalTime(channelId))); - }; - } + @Override + public RemoteEventDispatcher getRemoteDispatcher(Class eventClass) { + return (node, e) -> { + ChannelId channelId = ChannelId.of(this.senderIndex, this.network.lookup(node)); + handleMessage(new ControlledMessage(self, channelId, e, null, arrivalTime(channelId))); + }; + } - private void handleMessage(ControlledMessage controlledMessage) { - this.network.handleMessage(controlledMessage); - } + private void handleMessage(ControlledMessage controlledMessage) { + this.network.handleMessage(controlledMessage); + } - private long arrivalTime(ChannelId channelId) { - long delay = this.network.delayForChannel(channelId); - return this.network.currentTime() + delay; - } + private long arrivalTime(ChannelId channelId) { + long delay = this.network.delayForChannel(channelId); + return this.network.currentTime() + delay; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/DeterministicNetwork.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/DeterministicNetwork.java index 8426664794..7ccf01e9ce 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/DeterministicNetwork.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/DeterministicNetwork.java @@ -68,123 +68,124 @@ import com.google.common.collect.Streams; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.utils.Pair; - import io.reactivex.rxjava3.schedulers.Timed; import java.io.PrintStream; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; - import java.util.function.Predicate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** - * A BFT network supporting the EventCoordinatorNetworkSender interface which - * stores each message in a queue until they are synchronously popped. + * A BFT network supporting the EventCoordinatorNetworkSender interface which stores each message in + * a queue until they are synchronously popped. */ public final class DeterministicNetwork { - private static final Logger log = LogManager.getLogger(); - private static final long DEFAULT_LATENCY = 50L; // virtual milliseconds - private final MessageQueue messageQueue = new MessageQueue(); - private final MessageSelector messageSelector; - private final MessageMutator messageMutator; - - private final ImmutableBiMap nodeLookup; - - private long currentTime = 0L; - - /** - * Create a BFT test network for deterministic tests. - * @param nodes The nodes on the network - * @param messageSelector A {@link MessageSelector} for choosing messages to process next - * @param messageMutator A {@link MessageMutator} for mutating and queueing messages - */ - public DeterministicNetwork( - List nodes, - MessageSelector messageSelector, - MessageMutator messageMutator - ) { - this.messageSelector = Objects.requireNonNull(messageSelector); - this.messageMutator = Objects.requireNonNull(messageMutator); - this.nodeLookup = Streams.mapWithIndex( - nodes.stream(), - (node, index) -> Pair.of(node, (int) index) - ).collect(ImmutableBiMap.toImmutableBiMap(Pair::getFirst, Pair::getSecond)); - - log.debug("Nodes {}", this.nodeLookup); - } - - public ControlledSender createSender(BFTNode node) { - return new ControlledSender(this, node, this.lookup(node)); - } - - public ControlledSender createSender(int nodeIndex) { - return new ControlledSender(this, this.lookup(nodeIndex), nodeIndex); - } - - // TODO: use better method than Timed to store time - public Timed nextMessage() { - List controlledMessages = this.messageQueue.lowestTimeMessages(); - if (controlledMessages == null || controlledMessages.isEmpty()) { - throw new IllegalStateException("No messages available (Lost Responsiveness)"); - } - ControlledMessage controlledMessage = this.messageSelector.select(controlledMessages); - - this.messageQueue.remove(controlledMessage); - this.currentTime = Math.max(this.currentTime, controlledMessage.arrivalTime()); - - return new Timed<>(controlledMessage, this.currentTime, TimeUnit.MILLISECONDS); - } - - public Timed nextMessage(Predicate predicate) { - Set allMessages = this.messageQueue.allMessages(); - ControlledMessage controlledMessage = allMessages.stream().filter(predicate).findAny().orElseThrow( - () -> new IllegalStateException(String.format("Could not find message. Messages present: %s", allMessages)) - ); - this.messageQueue.remove(controlledMessage); - this.currentTime = Math.max(this.currentTime, controlledMessage.arrivalTime()); - - return new Timed<>(controlledMessage, this.currentTime, TimeUnit.MILLISECONDS); - } - - public Set allMessages() { - return this.messageQueue.allMessages(); - } - - public void dropMessages(Predicate controlledMessagePredicate) { - this.messageQueue.remove(controlledMessagePredicate); - } - - public long currentTime() { - return this.currentTime; - } - - public void dumpMessages(PrintStream out) { - this.messageQueue.dump(out); - } - - public int lookup(BFTNode node) { - return this.nodeLookup.get(node); - } - - public BFTNode lookup(int nodeIndex) { - return this.nodeLookup.inverse().get(nodeIndex); - } - - long delayForChannel(ChannelId channelId) { - if (channelId.receiverIndex() == channelId.senderIndex()) { - return 0L; - } - return DEFAULT_LATENCY; - } - - void handleMessage(ControlledMessage controlledMessage) { - log.debug("Sent message {}", controlledMessage); - if (!this.messageMutator.mutate(controlledMessage, this.messageQueue)) { - // If nothing processes this message, we just add it to the queue - this.messageQueue.add(controlledMessage); - } - } + private static final Logger log = LogManager.getLogger(); + private static final long DEFAULT_LATENCY = 50L; // virtual milliseconds + private final MessageQueue messageQueue = new MessageQueue(); + private final MessageSelector messageSelector; + private final MessageMutator messageMutator; + + private final ImmutableBiMap nodeLookup; + + private long currentTime = 0L; + + /** + * Create a BFT test network for deterministic tests. + * + * @param nodes The nodes on the network + * @param messageSelector A {@link MessageSelector} for choosing messages to process next + * @param messageMutator A {@link MessageMutator} for mutating and queueing messages + */ + public DeterministicNetwork( + List nodes, MessageSelector messageSelector, MessageMutator messageMutator) { + this.messageSelector = Objects.requireNonNull(messageSelector); + this.messageMutator = Objects.requireNonNull(messageMutator); + this.nodeLookup = + Streams.mapWithIndex(nodes.stream(), (node, index) -> Pair.of(node, (int) index)) + .collect(ImmutableBiMap.toImmutableBiMap(Pair::getFirst, Pair::getSecond)); + + log.debug("Nodes {}", this.nodeLookup); + } + + public ControlledSender createSender(BFTNode node) { + return new ControlledSender(this, node, this.lookup(node)); + } + + public ControlledSender createSender(int nodeIndex) { + return new ControlledSender(this, this.lookup(nodeIndex), nodeIndex); + } + + // TODO: use better method than Timed to store time + public Timed nextMessage() { + List controlledMessages = this.messageQueue.lowestTimeMessages(); + if (controlledMessages == null || controlledMessages.isEmpty()) { + throw new IllegalStateException("No messages available (Lost Responsiveness)"); + } + ControlledMessage controlledMessage = this.messageSelector.select(controlledMessages); + + this.messageQueue.remove(controlledMessage); + this.currentTime = Math.max(this.currentTime, controlledMessage.arrivalTime()); + + return new Timed<>(controlledMessage, this.currentTime, TimeUnit.MILLISECONDS); + } + + public Timed nextMessage(Predicate predicate) { + Set allMessages = this.messageQueue.allMessages(); + ControlledMessage controlledMessage = + allMessages.stream() + .filter(predicate) + .findAny() + .orElseThrow( + () -> + new IllegalStateException( + String.format( + "Could not find message. Messages present: %s", allMessages))); + this.messageQueue.remove(controlledMessage); + this.currentTime = Math.max(this.currentTime, controlledMessage.arrivalTime()); + + return new Timed<>(controlledMessage, this.currentTime, TimeUnit.MILLISECONDS); + } + + public Set allMessages() { + return this.messageQueue.allMessages(); + } + + public void dropMessages(Predicate controlledMessagePredicate) { + this.messageQueue.remove(controlledMessagePredicate); + } + + public long currentTime() { + return this.currentTime; + } + + public void dumpMessages(PrintStream out) { + this.messageQueue.dump(out); + } + + public int lookup(BFTNode node) { + return this.nodeLookup.get(node); + } + + public BFTNode lookup(int nodeIndex) { + return this.nodeLookup.inverse().get(nodeIndex); + } + + long delayForChannel(ChannelId channelId) { + if (channelId.receiverIndex() == channelId.senderIndex()) { + return 0L; + } + return DEFAULT_LATENCY; + } + + void handleMessage(ControlledMessage controlledMessage) { + log.debug("Sent message {}", controlledMessage); + if (!this.messageMutator.mutate(controlledMessage, this.messageQueue)) { + // If nothing processes this message, we just add it to the queue + this.messageQueue.add(controlledMessage); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageMutator.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageMutator.java index 213c0ce665..e11e71a665 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageMutator.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageMutator.java @@ -68,56 +68,53 @@ import com.radixdlt.consensus.liveness.ScheduledLocalTimeout; /** - * A mutator called on new messages that can mutate the message rank, - * the message itself, or the message queue. + * A mutator called on new messages that can mutate the message rank, the message itself, or the + * message queue. */ @FunctionalInterface public interface MessageMutator { - /** - * Mutates the message queue. The simplest form of mutation is - * to add the supplied message to the queue at the specified rank, but - * other mutations are possible; the rank can be changed, and the message - * can be substituted for a different message. - * - * @param message the message - * @param queue the queue to me mutated - * @return {@code true} if the message was processed, {@code false} otherwise. - */ - boolean mutate(ControlledMessage message, MessageQueue queue); + /** + * Mutates the message queue. The simplest form of mutation is to add the supplied message to the + * queue at the specified rank, but other mutations are possible; the rank can be changed, and the + * message can be substituted for a different message. + * + * @param message the message + * @param queue the queue to me mutated + * @return {@code true} if the message was processed, {@code false} otherwise. + */ + boolean mutate(ControlledMessage message, MessageQueue queue); - /** - * Chains this mutator with another. If this mutator does not - * handle the message, then the next mutator is called. - * - * @param next the next mutator in the chain to call if this - * mutator does not handle the message - * @return This mutator chained with the specified mutator - */ - default MessageMutator andThen(MessageMutator next) { - return (message, queue) -> mutate(message, queue) || next.mutate(message, queue); - } + /** + * Chains this mutator with another. If this mutator does not handle the message, then the next + * mutator is called. + * + * @param next the next mutator in the chain to call if this mutator does not handle the message + * @return This mutator chained with the specified mutator + */ + default MessageMutator andThen(MessageMutator next) { + return (message, queue) -> mutate(message, queue) || next.mutate(message, queue); + } - /** - * Returns default mutator that does not handle messages. - * By default, the underlying network code will add the message to the - * message queue. - * - * @return A {@code MessageMutator} that does nothing. - */ - static MessageMutator nothing() { - return (message, queue) -> false; - } - - /** - * Returns a mutator that drops timeout messages. - * @return A {@code MessageMutator} that drops {@code LocalTimeout} messages. - */ - static MessageMutator dropTimeouts() { - return (message, queue) -> { - Object msg = message.message(); - return msg instanceof ScheduledLocalTimeout - || Epoched.isInstance(msg, ScheduledLocalTimeout.class); - }; - } + /** + * Returns default mutator that does not handle messages. By default, the underlying network code + * will add the message to the message queue. + * + * @return A {@code MessageMutator} that does nothing. + */ + static MessageMutator nothing() { + return (message, queue) -> false; + } + /** + * Returns a mutator that drops timeout messages. + * + * @return A {@code MessageMutator} that drops {@code LocalTimeout} messages. + */ + static MessageMutator dropTimeouts() { + return (message, queue) -> { + Object msg = message.message(); + return msg instanceof ScheduledLocalTimeout + || Epoched.isInstance(msg, ScheduledLocalTimeout.class); + }; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageQueue.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageQueue.java index d1d24a474f..c526dcec39 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageQueue.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageQueue.java @@ -64,6 +64,8 @@ package com.radixdlt.environment.deterministic.network; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import java.io.PrintStream; import java.util.Collections; import java.util.Comparator; @@ -74,143 +76,141 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.function.Predicate; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import java.util.stream.Collectors; -/** - * Queue for messages by view. - */ +/** Queue for messages by view. */ public final class MessageQueue { - private final HashMap> messagesByTime = Maps.newHashMap(); - private long minimumMessageTime = Long.MAX_VALUE; // Cached minimum time - - MessageQueue() { - // Nothing here for now - } - - public boolean add(ControlledMessage item) { - long messageTime = item.arrivalTime(); - this.messagesByTime.computeIfAbsent(messageTime, k -> Lists.newLinkedList()).add(item); - if (messageTime < this.minimumMessageTime) { - this.minimumMessageTime = messageTime; - } - return true; - } - - public boolean addFirst(ControlledMessage item) { - long messageTime = item.arrivalTime(); - this.messagesByTime.computeIfAbsent(messageTime, k -> Lists.newLinkedList()).addFirst(item); - if (messageTime < this.minimumMessageTime) { - this.minimumMessageTime = messageTime; - } - return true; - } - - public boolean addBefore(ControlledMessage item, Predicate test) { - var messageTime = item.arrivalTime(); - var i = this.messagesByTime.computeIfAbsent(messageTime, k -> Lists.newLinkedList()).listIterator(); - var inserted = false; - while (i.hasNext()) { - if (test.test(i.next())) { - // Backup and insert - i.previous(); - i.add(item); - inserted = true; - break; - } - } - if (!inserted) { - i.add(item); - } - if (messageTime < this.minimumMessageTime) { - this.minimumMessageTime = messageTime; - } - return true; - } - - void remove(Predicate filter) { - messagesByTime.values().forEach(l -> l.removeIf(filter)); - messagesByTime.values().removeIf(List::isEmpty); - this.minimumMessageTime = minimumKey(this.messagesByTime.keySet()); - } - - void remove(ControlledMessage message) { - LinkedList msgs = this.messagesByTime.get(this.minimumMessageTime); - if (msgs == null) { - painfulRemove(message); - return; - } - if (!msgs.remove(message)) { - painfulRemove(message); - return; - } - if (msgs.isEmpty()) { - this.messagesByTime.remove(this.minimumMessageTime); - this.minimumMessageTime = minimumKey(this.messagesByTime.keySet()); - } - } - - public void dump(PrintStream out) { - Comparator channelIdComparator = Comparator - .comparingInt(ChannelId::senderIndex) - .thenComparing(ChannelId::receiverIndex); - out.println("{"); - this.messagesByTime.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .forEachOrdered(e1 -> { - out.format(" %s {%n", e1.getKey()); - e1.getValue().stream() - .sorted(Comparator.comparing(ControlledMessage::channelId, channelIdComparator)) - .forEach(cm -> out.format(" %s%n", cm)); - out.println(" }"); - }); - out.println("}"); - } - - Set allMessages() { - return this.messagesByTime.values().stream() - .flatMap(LinkedList::stream) - .collect(Collectors.toSet()); - } - - List lowestTimeMessages() { - if (this.messagesByTime.isEmpty()) { - return Collections.emptyList(); - } - return this.messagesByTime.get(this.minimumMessageTime); - } - - @Override - public String toString() { - return this.messagesByTime.toString(); - } - - // If not removing message of the lowest rank, then we do it the painful way - private void painfulRemove(ControlledMessage message) { - List>> entries = Lists.newArrayList(this.messagesByTime.entrySet()); - Collections.sort(entries, Map.Entry.comparingByKey()); - for (Map.Entry> entry : entries) { - LinkedList msgs = entry.getValue(); - if (msgs != null && msgs.remove(message)) { - if (msgs.isEmpty()) { - this.messagesByTime.remove(entry.getKey()); - // Can't affect minimumView if we are here - } - return; - } - } - throw new NoSuchElementException(); - } - - // Believe it or not, this is faster, when coupled with minimumView - // caching, than using a TreeMap for nodes == 100. - private static long minimumKey(Set eavs) { - if (eavs.isEmpty()) { - return Long.MAX_VALUE; - } - return Collections.min(eavs); - } + private final HashMap> messagesByTime = Maps.newHashMap(); + private long minimumMessageTime = Long.MAX_VALUE; // Cached minimum time + + MessageQueue() { + // Nothing here for now + } + + public boolean add(ControlledMessage item) { + long messageTime = item.arrivalTime(); + this.messagesByTime.computeIfAbsent(messageTime, k -> Lists.newLinkedList()).add(item); + if (messageTime < this.minimumMessageTime) { + this.minimumMessageTime = messageTime; + } + return true; + } + + public boolean addFirst(ControlledMessage item) { + long messageTime = item.arrivalTime(); + this.messagesByTime.computeIfAbsent(messageTime, k -> Lists.newLinkedList()).addFirst(item); + if (messageTime < this.minimumMessageTime) { + this.minimumMessageTime = messageTime; + } + return true; + } + + public boolean addBefore(ControlledMessage item, Predicate test) { + var messageTime = item.arrivalTime(); + var i = + this.messagesByTime.computeIfAbsent(messageTime, k -> Lists.newLinkedList()).listIterator(); + var inserted = false; + while (i.hasNext()) { + if (test.test(i.next())) { + // Backup and insert + i.previous(); + i.add(item); + inserted = true; + break; + } + } + if (!inserted) { + i.add(item); + } + if (messageTime < this.minimumMessageTime) { + this.minimumMessageTime = messageTime; + } + return true; + } + + void remove(Predicate filter) { + messagesByTime.values().forEach(l -> l.removeIf(filter)); + messagesByTime.values().removeIf(List::isEmpty); + this.minimumMessageTime = minimumKey(this.messagesByTime.keySet()); + } + + void remove(ControlledMessage message) { + LinkedList msgs = this.messagesByTime.get(this.minimumMessageTime); + if (msgs == null) { + painfulRemove(message); + return; + } + if (!msgs.remove(message)) { + painfulRemove(message); + return; + } + if (msgs.isEmpty()) { + this.messagesByTime.remove(this.minimumMessageTime); + this.minimumMessageTime = minimumKey(this.messagesByTime.keySet()); + } + } + + public void dump(PrintStream out) { + Comparator channelIdComparator = + Comparator.comparingInt(ChannelId::senderIndex) + .thenComparing(ChannelId::receiverIndex); + out.println("{"); + this.messagesByTime.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEachOrdered( + e1 -> { + out.format(" %s {%n", e1.getKey()); + e1.getValue().stream() + .sorted(Comparator.comparing(ControlledMessage::channelId, channelIdComparator)) + .forEach(cm -> out.format(" %s%n", cm)); + out.println(" }"); + }); + out.println("}"); + } + + Set allMessages() { + return this.messagesByTime.values().stream() + .flatMap(LinkedList::stream) + .collect(Collectors.toSet()); + } + + List lowestTimeMessages() { + if (this.messagesByTime.isEmpty()) { + return Collections.emptyList(); + } + return this.messagesByTime.get(this.minimumMessageTime); + } + + @Override + public String toString() { + return this.messagesByTime.toString(); + } + + // If not removing message of the lowest rank, then we do it the painful way + private void painfulRemove(ControlledMessage message) { + List>> entries = + Lists.newArrayList(this.messagesByTime.entrySet()); + Collections.sort(entries, Map.Entry.comparingByKey()); + for (Map.Entry> entry : entries) { + LinkedList msgs = entry.getValue(); + if (msgs != null && msgs.remove(message)) { + if (msgs.isEmpty()) { + this.messagesByTime.remove(entry.getKey()); + // Can't affect minimumView if we are here + } + return; + } + } + throw new NoSuchElementException(); + } + + // Believe it or not, this is faster, when coupled with minimumView + // caching, than using a TreeMap for nodes == 100. + private static long minimumKey(Set eavs) { + if (eavs.isEmpty()) { + return Long.MAX_VALUE; + } + return Collections.min(eavs); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageRank.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageRank.java index e4666af05f..046263e518 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageRank.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageRank.java @@ -67,58 +67,57 @@ import java.util.Comparator; /** - * Message rank. Used to implement a sense of time in a deterministic model. - * Messages with a particular rank are processed in arrival order, but - * timeouts in particular will be put into the next rank to ensure - * that they are processed after the current rank. + * Message rank. Used to implement a sense of time in a deterministic model. Messages with a + * particular rank are processed in arrival order, but timeouts in particular will be put into the + * next rank to ensure that they are processed after the current rank. */ public final class MessageRank implements Comparable { - static final MessageRank EARLIEST_POSSIBLE = new MessageRank(0L, 0L); + static final MessageRank EARLIEST_POSSIBLE = new MessageRank(0L, 0L); - private static final Comparator COMPARATOR = - Comparator.comparingLong((MessageRank mr) -> mr.major).thenComparingLong(mr -> mr.minor); + private static final Comparator COMPARATOR = + Comparator.comparingLong((MessageRank mr) -> mr.major).thenComparingLong(mr -> mr.minor); - private final long major; - private final long minor; + private final long major; + private final long minor; - private MessageRank(long major, long minor) { - this.major = major; - this.minor = minor; - } + private MessageRank(long major, long minor) { + this.major = major; + this.minor = minor; + } - public static MessageRank of(long major, long minor) { - return new MessageRank(major, minor); - } + public static MessageRank of(long major, long minor) { + return new MessageRank(major, minor); + } - public long major() { - return this.major; - } + public long major() { + return this.major; + } - public long minor() { - return this.minor; - } + public long minor() { + return this.minor; + } - @Override - public int hashCode() { - return Long.hashCode(this.major) * 31 + Long.hashCode(this.minor); - } + @Override + public int hashCode() { + return Long.hashCode(this.major) * 31 + Long.hashCode(this.minor); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof MessageRank)) { - return false; - } - MessageRank that = (MessageRank) o; - return this.major == that.major && this.minor == that.minor; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof MessageRank)) { + return false; + } + MessageRank that = (MessageRank) o; + return this.major == that.major && this.minor == that.minor; + } - @Override - public int compareTo(MessageRank that) { - return COMPARATOR.compare(this, that); - } + @Override + public int compareTo(MessageRank that) { + return COMPARATOR.compare(this, that); + } - @Override - public String toString() { - return String.format("[%s:%s]", this.major, this.minor); - } + @Override + public String toString() { + return String.format("[%s:%s]", this.major, this.minor); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageSelector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageSelector.java index a4def9bcd2..96dd075d1e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageSelector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/environment/deterministic/network/MessageSelector.java @@ -67,39 +67,35 @@ import java.util.List; import java.util.Random; -/** - * Select a message from a list of possible messages. - */ +/** Select a message from a list of possible messages. */ @FunctionalInterface public interface MessageSelector { - /** - * Selects a message for processing from the supplied non-empty list. - * The supplied list is from a single rank, and is in arrival order. - * - * @param messages the messages to select from - * @return The selected message, or {@code null} if processing should stop - */ - ControlledMessage select(List messages); + /** + * Selects a message for processing from the supplied non-empty list. The supplied list is from a + * single rank, and is in arrival order. + * + * @param messages the messages to select from + * @return The selected message, or {@code null} if processing should stop + */ + ControlledMessage select(List messages); - /** - * Returns a selector that always returns the first message from the - * supplied list. - * - * @return a selector that always returns the first message from the - * supplied list - */ - static MessageSelector firstSelector() { - return messages -> messages.get(0); - } + /** + * Returns a selector that always returns the first message from the supplied list. + * + * @return a selector that always returns the first message from the supplied list + */ + static MessageSelector firstSelector() { + return messages -> messages.get(0); + } - /** - * Returns a selector that returns a random message from the supplied list. - * - * @param random the random generator to use to select messages - * @return a selector that returns a random message from the supplied list - */ - static MessageSelector randomSelector(Random random) { - return messages -> messages.get(random.nextInt(messages.size())); - } + /** + * Returns a selector that returns a random message from the supplied list. + * + * @param random the random generator to use to select messages + * @return a selector that returns a random message from the supplied list + */ + static MessageSelector randomSelector(Random random) { + return messages -> messages.get(random.nextInt(messages.size())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/AccumulatorStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/AccumulatorStateTest.java index 2453993747..b85dcc823a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/AccumulatorStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/AccumulatorStateTest.java @@ -64,28 +64,28 @@ package com.radixdlt.ledger; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; -import static org.mockito.Mockito.mock; - public class AccumulatorStateTest { - @Test - public void testEquals() { - EqualsVerifier.forClass(AccumulatorState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void testEquals() { + EqualsVerifier.forClass(AccumulatorState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithWrongStateVersionThrowsException() { - new AccumulatorState(-1, mock(HashCode.class)); - } + @Test(expected = IllegalArgumentException.class) + public void deserializationWithWrongStateVersionThrowsException() { + new AccumulatorState(-1, mock(HashCode.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullTrowsException() { - new AccumulatorState(1, null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullTrowsException() { + new AccumulatorState(1, null); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoLedgerProofTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoLedgerProofTest.java index ace0b96c76..9d962ded69 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoLedgerProofTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoLedgerProofTest.java @@ -64,6 +64,8 @@ package com.radixdlt.ledger; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.TimestampedECDSASignatures; @@ -71,28 +73,26 @@ import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; -import static org.mockito.Mockito.mock; - public class DtoLedgerProofTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(DtoLedgerProof.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(DtoLedgerProof.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - new DtoLedgerProof(null, mock(LedgerHeader.class), mock(TimestampedECDSASignatures.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + new DtoLedgerProof(null, mock(LedgerHeader.class), mock(TimestampedECDSASignatures.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - new DtoLedgerProof(mock(HashCode.class), null, mock(TimestampedECDSASignatures.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + new DtoLedgerProof(mock(HashCode.class), null, mock(TimestampedECDSASignatures.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException3() { - new DtoLedgerProof(mock(HashCode.class), mock(LedgerHeader.class), null); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException3() { + new DtoLedgerProof(mock(HashCode.class), mock(LedgerHeader.class), null); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoTxnsAndProofTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoTxnsAndProofTest.java index 813ed44eec..b09850a815 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoTxnsAndProofTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/DtoTxnsAndProofTest.java @@ -64,40 +64,37 @@ package com.radixdlt.ledger; -import org.junit.Test; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; - import java.util.List; - import nl.jqno.equalsverifier.EqualsVerifier; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; +import org.junit.Test; public class DtoTxnsAndProofTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(DtoTxnsAndProof.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(DtoTxnsAndProof.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullHeadThrowsException() { - new DtoTxnsAndProof(List.of(), null, mock(DtoLedgerProof.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullHeadThrowsException() { + new DtoTxnsAndProof(List.of(), null, mock(DtoLedgerProof.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullTailThrowsException() { - new DtoTxnsAndProof(List.of(), mock(DtoLedgerProof.class), null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullTailThrowsException() { + new DtoTxnsAndProof(List.of(), mock(DtoLedgerProof.class), null); + } - @Test - public void deserializationWithNullTxnListIsSafe() { - var dto = new DtoTxnsAndProof(null, mock(DtoLedgerProof.class), mock(DtoLedgerProof.class)); + @Test + public void deserializationWithNullTxnListIsSafe() { + var dto = new DtoTxnsAndProof(null, mock(DtoLedgerProof.class), mock(DtoLedgerProof.class)); - assertNotNull(dto.getTxns()); - } + assertNotNull(dto.getTxns()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/IncorrectAlwaysAcceptingAccumulatorVerifierModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/IncorrectAlwaysAcceptingAccumulatorVerifierModule.java index bfcd5dfa8a..bcde88fc8f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/IncorrectAlwaysAcceptingAccumulatorVerifierModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/IncorrectAlwaysAcceptingAccumulatorVerifierModule.java @@ -68,44 +68,43 @@ import com.google.common.hash.HashCode; import com.google.inject.AbstractModule; import com.google.inject.Provides; - import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Function; -/** - * Accumulator verifier which incorrectly always gives false positives. - */ +/** Accumulator verifier which incorrectly always gives false positives. */ public class IncorrectAlwaysAcceptingAccumulatorVerifierModule extends AbstractModule { - @Provides - private LedgerAccumulatorVerifier badVerifier() { - return new LedgerAccumulatorVerifier() { - @Override - public boolean verify(AccumulatorState head, ImmutableList commands, AccumulatorState tail) { - return true; - } + @Provides + private LedgerAccumulatorVerifier badVerifier() { + return new LedgerAccumulatorVerifier() { + @Override + public boolean verify( + AccumulatorState head, ImmutableList commands, AccumulatorState tail) { + return true; + } - @Override - public Optional> verifyAndGetExtension( - AccumulatorState current, - List commands, - Function hashCodeMapper, - AccumulatorState tail - ) { - final long firstVersion = tail.getStateVersion() - commands.size() + 1; - if (current.getStateVersion() + 1 < firstVersion) { - // Missing versions - return Optional.empty(); - } + @Override + public Optional> verifyAndGetExtension( + AccumulatorState current, + List commands, + Function hashCodeMapper, + AccumulatorState tail) { + final long firstVersion = tail.getStateVersion() - commands.size() + 1; + if (current.getStateVersion() + 1 < firstVersion) { + // Missing versions + return Optional.empty(); + } - if (commands.isEmpty()) { - return (Objects.equals(current, tail)) ? Optional.of(ImmutableList.of()) : Optional.empty(); - } + if (commands.isEmpty()) { + return (Objects.equals(current, tail)) + ? Optional.of(ImmutableList.of()) + : Optional.empty(); + } - final int startIndex = (int) (current.getStateVersion() + 1 - firstVersion); - return Optional.of(commands.subList(startIndex, commands.size())); - } - }; - } + final int startIndex = (int) (current.getStateVersion() + 1 - firstVersion); + return Optional.of(commands.subList(startIndex, commands.size())); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/LedgerUpdateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/LedgerUpdateTest.java index db6b1d4f87..352e34ff0e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/LedgerUpdateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/LedgerUpdateTest.java @@ -66,16 +66,14 @@ import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; - import nl.jqno.equalsverifier.EqualsVerifier; - import org.junit.Test; public class LedgerUpdateTest { - @Test - public void testEquals() { - EqualsVerifier.forClass(LedgerUpdate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void testEquals() { + EqualsVerifier.forClass(LedgerUpdate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockPrepared.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockPrepared.java index 5f025e919b..7a55087d9a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockPrepared.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockPrepared.java @@ -69,14 +69,14 @@ public class MockPrepared implements PreparedTxn { - private final Txn txn; + private final Txn txn; - public MockPrepared(Txn txn) { - this.txn = txn; - } + public MockPrepared(Txn txn) { + this.txn = txn; + } - @Override - public Txn txn() { - return txn; - } + @Override + public Txn txn() { + return txn; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedCommandGeneratorModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedCommandGeneratorModule.java index 6f20e4f674..c2c0bd799d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedCommandGeneratorModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedCommandGeneratorModule.java @@ -68,12 +68,10 @@ import com.radixdlt.consensus.liveness.NextTxnsGenerator; import com.radixdlt.statecomputer.RandomHashTxnsGenerator; -/** - * Module which provides a random hash command generator - */ +/** Module which provides a random hash command generator */ public class MockedCommandGeneratorModule extends AbstractModule { - @Override - protected void configure() { - bind(NextTxnsGenerator.class).to(RandomHashTxnsGenerator.class); - } + @Override + protected void configure() { + bind(NextTxnsGenerator.class).to(RandomHashTxnsGenerator.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedLedgerModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedLedgerModule.java index c3c463b2bb..2584eeba8a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedLedgerModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/MockedLedgerModule.java @@ -64,16 +64,15 @@ package com.radixdlt.ledger; -import com.radixdlt.consensus.bft.PreparedVertex; -import com.radixdlt.consensus.bft.VerifiedVertex; -import com.radixdlt.ledger.StateComputerLedger.PreparedTxn; - import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; import com.radixdlt.consensus.Ledger; -import com.radixdlt.consensus.liveness.NextTxnsGenerator; import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.bft.PreparedVertex; +import com.radixdlt.consensus.bft.VerifiedVertex; +import com.radixdlt.consensus.liveness.NextTxnsGenerator; +import com.radixdlt.ledger.StateComputerLedger.PreparedTxn; import com.radixdlt.utils.TimeSupplier; import java.util.LinkedList; import java.util.List; @@ -82,31 +81,34 @@ import java.util.stream.Collectors; public class MockedLedgerModule extends AbstractModule { - @Override - public void configure() { - bind(NextTxnsGenerator.class).toInstance((view, aids) -> List.of()); - } + @Override + public void configure() { + bind(NextTxnsGenerator.class).toInstance((view, aids) -> List.of()); + } - @Provides - @Singleton - Ledger syncedLedger(TimeSupplier timeSupplier) { - return new Ledger() { - @Override - public Optional prepare(LinkedList previous, VerifiedVertex vertex) { - final long timestamp = vertex.getQC().getTimestampedSignatures().weightedTimestamp(); - final LedgerHeader ledgerHeader = vertex.getParentHeader().getLedgerHeader() - .updateViewAndTimestamp(vertex.getView(), timestamp); + @Provides + @Singleton + Ledger syncedLedger(TimeSupplier timeSupplier) { + return new Ledger() { + @Override + public Optional prepare( + LinkedList previous, VerifiedVertex vertex) { + final long timestamp = vertex.getQC().getTimestampedSignatures().weightedTimestamp(); + final LedgerHeader ledgerHeader = + vertex + .getParentHeader() + .getLedgerHeader() + .updateViewAndTimestamp(vertex.getView(), timestamp); - return Optional.of(vertex - .withHeader(ledgerHeader, timeSupplier.currentTime()) - .andTxns( - vertex.getTxns().stream() - .map(MockPrepared::new) - .collect(Collectors.toList()), - Map.of() - ) - ); - } - }; - } + return Optional.of( + vertex + .withHeader(ledgerHeader, timeSupplier.currentTime()) + .andTxns( + vertex.getTxns().stream() + .map(MockPrepared::new) + .collect(Collectors.toList()), + Map.of())); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifierTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifierTest.java index 77a9ce5ad9..56b498adc4 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifierTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/SimpleLedgerAccumulatorAndVerifierTest.java @@ -71,56 +71,66 @@ import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; import com.radixdlt.atom.Txn; -import com.radixdlt.crypto.Hasher; import com.radixdlt.consensus.Sha256Hasher; import com.radixdlt.crypto.HashUtils; +import com.radixdlt.crypto.Hasher; import org.junit.Before; import org.junit.Test; public class SimpleLedgerAccumulatorAndVerifierTest { - private SimpleLedgerAccumulatorAndVerifier accumulatorAndVerifier; - private Hasher hasher; + private SimpleLedgerAccumulatorAndVerifier accumulatorAndVerifier; + private Hasher hasher; - @Before - public void setup() { - hasher = Sha256Hasher.withDefaultSerialization(); - accumulatorAndVerifier = new SimpleLedgerAccumulatorAndVerifier(hasher); - } + @Before + public void setup() { + hasher = Sha256Hasher.withDefaultSerialization(); + accumulatorAndVerifier = new SimpleLedgerAccumulatorAndVerifier(hasher); + } - @Test - public void when_accumulate__then_should_verify() { - AccumulatorState headState = new AccumulatorState(345, HashUtils.zero256()); - AccumulatorState nextState = accumulatorAndVerifier.accumulate(headState, HashUtils.zero256()); - assertThat(accumulatorAndVerifier.verify(headState, ImmutableList.of(HashUtils.zero256()), nextState)).isTrue(); - } + @Test + public void when_accumulate__then_should_verify() { + AccumulatorState headState = new AccumulatorState(345, HashUtils.zero256()); + AccumulatorState nextState = accumulatorAndVerifier.accumulate(headState, HashUtils.zero256()); + assertThat( + accumulatorAndVerifier.verify( + headState, ImmutableList.of(HashUtils.zero256()), nextState)) + .isTrue(); + } - @Test - public void when_empty_command_truncate_from_bad_version__then_should_throw_exception() { - AccumulatorState curState = mock(AccumulatorState.class); - when(curState.getStateVersion()).thenReturn(1234L); - AccumulatorState nextState = mock(AccumulatorState.class); - when(nextState.getStateVersion()).thenReturn(1235L); + @Test + public void when_empty_command_truncate_from_bad_version__then_should_throw_exception() { + AccumulatorState curState = mock(AccumulatorState.class); + when(curState.getStateVersion()).thenReturn(1234L); + AccumulatorState nextState = mock(AccumulatorState.class); + when(nextState.getStateVersion()).thenReturn(1235L); - assertThat(accumulatorAndVerifier.verifyAndGetExtension(curState, ImmutableList.of(), i -> null, nextState)) - .isEmpty(); - } + assertThat( + accumulatorAndVerifier.verifyAndGetExtension( + curState, ImmutableList.of(), i -> null, nextState)) + .isEmpty(); + } - @Test - public void when_empty_command_truncate_from_perfect_version__then_should_return_empty_list() { - AccumulatorState state = mock(AccumulatorState.class); - when(state.getStateVersion()).thenReturn(1234L); - when(state.getAccumulatorHash()).thenReturn(mock(HashCode.class)); + @Test + public void when_empty_command_truncate_from_perfect_version__then_should_return_empty_list() { + AccumulatorState state = mock(AccumulatorState.class); + when(state.getStateVersion()).thenReturn(1234L); + when(state.getAccumulatorHash()).thenReturn(mock(HashCode.class)); - assertThat(accumulatorAndVerifier.verifyAndGetExtension(state, ImmutableList.of(), i -> null, state)) - .hasValue(ImmutableList.of()); - } + assertThat( + accumulatorAndVerifier.verifyAndGetExtension( + state, ImmutableList.of(), i -> null, state)) + .hasValue(ImmutableList.of()); + } - @Test - public void when_single_command_truncate_from_perfect_version__then_should_return_equivalent() { - var txn = Txn.create(new byte[]{0}); - AccumulatorState headState = new AccumulatorState(345, HashUtils.zero256()); - AccumulatorState nextState = accumulatorAndVerifier.accumulate(headState, txn.getId().asHashCode()); - assertThat(accumulatorAndVerifier.verifyAndGetExtension(headState, ImmutableList.of(txn), t -> t.getId().asHashCode(), nextState)) - .hasValue(ImmutableList.of(txn)); - } -} \ No newline at end of file + @Test + public void when_single_command_truncate_from_perfect_version__then_should_return_equivalent() { + var txn = Txn.create(new byte[] {0}); + AccumulatorState headState = new AccumulatorState(345, HashUtils.zero256()); + AccumulatorState nextState = + accumulatorAndVerifier.accumulate(headState, txn.getId().asHashCode()); + assertThat( + accumulatorAndVerifier.verifyAndGetExtension( + headState, ImmutableList.of(txn), t -> t.getId().asHashCode(), nextState)) + .hasValue(ImmutableList.of(txn)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/StateComputerLedgerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/StateComputerLedgerTest.java index 40fa9fddc4..3cfaf4b385 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/StateComputerLedgerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/StateComputerLedgerTest.java @@ -64,6 +64,14 @@ package com.radixdlt.ledger; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.radixdlt.atom.Txn; @@ -90,193 +98,195 @@ import com.radixdlt.utils.TimeSupplier; import com.radixdlt.utils.TypedMocks; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; - import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import org.junit.Before; +import org.junit.Test; public class StateComputerLedgerTest { - private Mempool mempool; - private StateComputer stateComputer; - private StateComputerLedger sut; - private LedgerProof currentLedgerHeader; - private SystemCounters counters; - private Comparator headerComparator; - private LedgerAccumulator accumulator; - private LedgerAccumulatorVerifier accumulatorVerifier; + private Mempool mempool; + private StateComputer stateComputer; + private StateComputerLedger sut; + private LedgerProof currentLedgerHeader; + private SystemCounters counters; + private Comparator headerComparator; + private LedgerAccumulator accumulator; + private LedgerAccumulatorVerifier accumulatorVerifier; - private LedgerHeader ledgerHeader; - private UnverifiedVertex genesis; - private VerifiedVertex genesisVertex; - private QuorumCertificate genesisQC; + private LedgerHeader ledgerHeader; + private UnverifiedVertex genesis; + private VerifiedVertex genesisVertex; + private QuorumCertificate genesisQC; - private final Txn nextTxn = Txn.create(new byte[] {0}); - private final Hasher hasher = Sha256Hasher.withDefaultSerialization(); - private final PreparedTxn successfulNextCommand = new PreparedTxn() { - @Override - public Txn txn() { - return nextTxn; - } - }; + private final Txn nextTxn = Txn.create(new byte[] {0}); + private final Hasher hasher = Sha256Hasher.withDefaultSerialization(); + private final PreparedTxn successfulNextCommand = + new PreparedTxn() { + @Override + public Txn txn() { + return nextTxn; + } + }; - private final long genesisEpoch = 3L; - private final long genesisStateVersion = 123L; + private final long genesisEpoch = 3L; + private final long genesisStateVersion = 123L; - @Before - public void setup() { - this.mempool = TypedMocks.rmock(Mempool.class); - // No type check issues with mocking generic here - this.stateComputer = mock(StateComputer.class); - this.counters = mock(SystemCounters.class); - this.headerComparator = TypedMocks.rmock(Comparator.class); + @Before + public void setup() { + this.mempool = TypedMocks.rmock(Mempool.class); + // No type check issues with mocking generic here + this.stateComputer = mock(StateComputer.class); + this.counters = mock(SystemCounters.class); + this.headerComparator = TypedMocks.rmock(Comparator.class); - this.accumulator = new SimpleLedgerAccumulatorAndVerifier(hasher); - this.accumulatorVerifier = new SimpleLedgerAccumulatorAndVerifier(hasher); + this.accumulator = new SimpleLedgerAccumulatorAndVerifier(hasher); + this.accumulatorVerifier = new SimpleLedgerAccumulatorAndVerifier(hasher); - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - this.ledgerHeader = LedgerHeader.genesis(accumulatorState, null, 0); - this.genesis = UnverifiedVertex.createGenesis(ledgerHeader); - this.genesisVertex = new VerifiedVertex(genesis, hasher.hash(genesis)); - this.genesisQC = QuorumCertificate.ofGenesis(genesisVertex, ledgerHeader); - this.currentLedgerHeader = this.genesisQC.getCommittedAndLedgerStateProof(hasher) - .map(Pair::getSecond).orElseThrow(); + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + this.ledgerHeader = LedgerHeader.genesis(accumulatorState, null, 0); + this.genesis = UnverifiedVertex.createGenesis(ledgerHeader); + this.genesisVertex = new VerifiedVertex(genesis, hasher.hash(genesis)); + this.genesisQC = QuorumCertificate.ofGenesis(genesisVertex, ledgerHeader); + this.currentLedgerHeader = + this.genesisQC.getCommittedAndLedgerStateProof(hasher).map(Pair::getSecond).orElseThrow(); - this.sut = new StateComputerLedger( - mock(TimeSupplier.class), - currentLedgerHeader, - headerComparator, - stateComputer, - accumulator, - accumulatorVerifier, - counters - ); - } + this.sut = + new StateComputerLedger( + mock(TimeSupplier.class), + currentLedgerHeader, + headerComparator, + stateComputer, + accumulator, + accumulatorVerifier, + counters); + } - public void genesisIsEndOfEpoch(boolean endOfEpoch) { - this.ledgerHeader = LedgerHeader.create( - genesisEpoch, - View.of(5), - new AccumulatorState(genesisStateVersion, HashUtils.zero256()), - 12345, - endOfEpoch ? BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))) : null - ); - this.genesis = UnverifiedVertex.createGenesis(ledgerHeader); - this.genesisVertex = new VerifiedVertex(genesis, hasher.hash(genesis)); - this.genesisQC = QuorumCertificate.ofGenesis(genesisVertex, ledgerHeader); - this.currentLedgerHeader = this.genesisQC.getCommittedAndLedgerStateProof(hasher) - .map(Pair::getSecond).orElseThrow(); + public void genesisIsEndOfEpoch(boolean endOfEpoch) { + this.ledgerHeader = + LedgerHeader.create( + genesisEpoch, + View.of(5), + new AccumulatorState(genesisStateVersion, HashUtils.zero256()), + 12345, + endOfEpoch + ? BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))) + : null); + this.genesis = UnverifiedVertex.createGenesis(ledgerHeader); + this.genesisVertex = new VerifiedVertex(genesis, hasher.hash(genesis)); + this.genesisQC = QuorumCertificate.ofGenesis(genesisVertex, ledgerHeader); + this.currentLedgerHeader = + this.genesisQC.getCommittedAndLedgerStateProof(hasher).map(Pair::getSecond).orElseThrow(); - this.sut = new StateComputerLedger( - mock(TimeSupplier.class), - currentLedgerHeader, - headerComparator, - stateComputer, - accumulator, - accumulatorVerifier, - counters - ); - } + this.sut = + new StateComputerLedger( + mock(TimeSupplier.class), + currentLedgerHeader, + headerComparator, + stateComputer, + accumulator, + accumulatorVerifier, + counters); + } - @Test - public void should_not_change_accumulator_when_there_is_no_command() { - // Arrange - genesisIsEndOfEpoch(false); - when(stateComputer.prepare(any(), any(), anyLong())) - .thenReturn(new StateComputerResult(ImmutableList.of(), ImmutableMap.of())); - var unverifiedVertex = UnverifiedVertex.create(genesisQC, View.of(1), List.of(), BFTNode.random()); - var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); + @Test + public void should_not_change_accumulator_when_there_is_no_command() { + // Arrange + genesisIsEndOfEpoch(false); + when(stateComputer.prepare(any(), any(), anyLong())) + .thenReturn(new StateComputerResult(ImmutableList.of(), ImmutableMap.of())); + var unverifiedVertex = + UnverifiedVertex.create(genesisQC, View.of(1), List.of(), BFTNode.random()); + var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); - // Act - Optional nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex); + // Act + Optional nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex); - // Assert - assertThat(nextPrepared) - .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isFalse()); - assertThat(nextPrepared) - .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().getAccumulatorState()).isEqualTo(ledgerHeader.getAccumulatorState())); - } + // Assert + assertThat(nextPrepared) + .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isFalse()); + assertThat(nextPrepared) + .hasValueSatisfying( + x -> + assertThat(x.getLedgerHeader().getAccumulatorState()) + .isEqualTo(ledgerHeader.getAccumulatorState())); + } - @Test - public void should_not_change_header_when_past_end_of_epoch_even_with_command() { - // Arrange - genesisIsEndOfEpoch(true); - when(stateComputer.prepare(any(), any(), anyLong())) - .thenReturn(new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of())); - var unverifiedVertex = UnverifiedVertex.create(genesisQC, View.of(1), List.of(nextTxn), BFTNode.random()); - var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); + @Test + public void should_not_change_header_when_past_end_of_epoch_even_with_command() { + // Arrange + genesisIsEndOfEpoch(true); + when(stateComputer.prepare(any(), any(), anyLong())) + .thenReturn( + new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of())); + var unverifiedVertex = + UnverifiedVertex.create(genesisQC, View.of(1), List.of(nextTxn), BFTNode.random()); + var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); - // Act - Optional nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex); + // Act + Optional nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex); - // Assert - assertThat(nextPrepared) - .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isTrue()); - assertThat(nextPrepared) - .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().getAccumulatorState()).isEqualTo(ledgerHeader.getAccumulatorState())); - } + // Assert + assertThat(nextPrepared) + .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isTrue()); + assertThat(nextPrepared) + .hasValueSatisfying( + x -> + assertThat(x.getLedgerHeader().getAccumulatorState()) + .isEqualTo(ledgerHeader.getAccumulatorState())); + } - @Test - public void should_accumulate_when_next_command_valid() { - // Arrange - genesisIsEndOfEpoch(false); - when(stateComputer.prepare(any(), any(), anyLong())) - .thenReturn(new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of())); + @Test + public void should_accumulate_when_next_command_valid() { + // Arrange + genesisIsEndOfEpoch(false); + when(stateComputer.prepare(any(), any(), anyLong())) + .thenReturn( + new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of())); - // Act - var unverifiedVertex = UnverifiedVertex.create(genesisQC, View.of(1), List.of(nextTxn), BFTNode.random()); - var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); - Optional nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex); + // Act + var unverifiedVertex = + UnverifiedVertex.create(genesisQC, View.of(1), List.of(nextTxn), BFTNode.random()); + var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex)); + Optional nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex); - // Assert - assertThat(nextPrepared).hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isFalse()); - assertThat(nextPrepared.flatMap(x -> - accumulatorVerifier.verifyAndGetExtension( - ledgerHeader.getAccumulatorState(), - List.of(nextTxn), - txn -> txn.getId().asHashCode(), - x.getLedgerHeader().getAccumulatorState() - )) - ).contains(List.of(nextTxn)); - } + // Assert + assertThat(nextPrepared) + .hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isFalse()); + assertThat( + nextPrepared.flatMap( + x -> + accumulatorVerifier.verifyAndGetExtension( + ledgerHeader.getAccumulatorState(), + List.of(nextTxn), + txn -> txn.getId().asHashCode(), + x.getLedgerHeader().getAccumulatorState()))) + .contains(List.of(nextTxn)); + } - @Test - public void should_do_nothing_if_committing_lower_state_version() { - // Arrange - genesisIsEndOfEpoch(false); - when(stateComputer.prepare(any(), any(), anyLong())) - .thenReturn(new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of())); - final AccumulatorState accumulatorState = new AccumulatorState(genesisStateVersion - 1, HashUtils.zero256()); - final LedgerHeader ledgerHeader = LedgerHeader.create( - genesisEpoch, - View.of(2), - accumulatorState, - 1234 - ); - final LedgerProof header = new LedgerProof( - HashUtils.random256(), - ledgerHeader, - new TimestampedECDSASignatures() - ); - var verified = VerifiedTxnsAndProof.create(List.of(nextTxn), header); + @Test + public void should_do_nothing_if_committing_lower_state_version() { + // Arrange + genesisIsEndOfEpoch(false); + when(stateComputer.prepare(any(), any(), anyLong())) + .thenReturn( + new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of())); + final AccumulatorState accumulatorState = + new AccumulatorState(genesisStateVersion - 1, HashUtils.zero256()); + final LedgerHeader ledgerHeader = + LedgerHeader.create(genesisEpoch, View.of(2), accumulatorState, 1234); + final LedgerProof header = + new LedgerProof(HashUtils.random256(), ledgerHeader, new TimestampedECDSASignatures()); + var verified = VerifiedTxnsAndProof.create(List.of(nextTxn), header); - // Act - sut.syncEventProcessor().process(verified); + // Act + sut.syncEventProcessor().process(verified); - // Assert - verify(stateComputer, never()).commit(any(), any()); - verify(mempool, never()).committed(any()); - } + // Assert + verify(stateComputer, never()).commit(any(), any()); + verify(mempool, never()).committed(any()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/VerifiedTxnsAndProofTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/VerifiedTxnsAndProofTest.java index 1f93fb8515..c901292a95 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/VerifiedTxnsAndProofTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ledger/VerifiedTxnsAndProofTest.java @@ -71,35 +71,33 @@ import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; import com.radixdlt.consensus.LedgerProof; - import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Before; import org.junit.Test; public class VerifiedTxnsAndProofTest { - private LedgerProof stateAndProof; - private VerifiedTxnsAndProof emptyCommandsAndProof; - private final long stateVersion = 232L; - - @Before - public void setUp() { - this.stateAndProof = mock(LedgerProof.class); - when(stateAndProof.getStateVersion()).thenReturn(stateVersion); + private LedgerProof stateAndProof; + private VerifiedTxnsAndProof emptyCommandsAndProof; + private final long stateVersion = 232L; - this.emptyCommandsAndProof = VerifiedTxnsAndProof.create(ImmutableList.of(), stateAndProof); - } + @Before + public void setUp() { + this.stateAndProof = mock(LedgerProof.class); + when(stateAndProof.getStateVersion()).thenReturn(stateVersion); - @Test - public void testGetters() { - assertThat(this.emptyCommandsAndProof.getProof()).isEqualTo(stateAndProof); - } + this.emptyCommandsAndProof = VerifiedTxnsAndProof.create(ImmutableList.of(), stateAndProof); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(VerifiedTxnsAndProof.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void testGetters() { + assertThat(this.emptyCommandsAndProof.getProof()).isEqualTo(stateAndProof); + } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(VerifiedTxnsAndProof.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolAddSuccessTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolAddSuccessTest.java index 036b3a6280..e4f365f94e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolAddSuccessTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolAddSuccessTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class MempoolAddSuccessTest { - @Test - public void equalsVerifier() { - EqualsVerifier.forClass(MempoolAddSuccess.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsVerifier() { + EqualsVerifier.forClass(MempoolAddSuccess.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolMetadataTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolMetadataTest.java index 3f962a025c..24a3b70684 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolMetadataTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolMetadataTest.java @@ -70,10 +70,11 @@ import org.junit.Test; public class MempoolMetadataTest { - @Test - public void equalsVerifier() { - EqualsVerifier.simple().forClass(MempoolMetadata.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsVerifier() { + EqualsVerifier.simple() + .forClass(MempoolMetadata.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRelayTriggerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRelayTriggerTest.java index 06d3cb2c49..472839b5e2 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRelayTriggerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRelayTriggerTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class MempoolRelayTriggerTest { - @Test - public void equalsVerifier() { - EqualsVerifier.forClass(MempoolRelayTrigger.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsVerifier() { + EqualsVerifier.forClass(MempoolRelayTrigger.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRunnerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRunnerTest.java index 2ad177f88f..c80e5d5a48 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRunnerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolRunnerTest.java @@ -64,7 +64,11 @@ package com.radixdlt.mempool; -import org.junit.Test; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; import com.google.inject.AbstractModule; import com.google.inject.Guice; @@ -96,68 +100,60 @@ import com.radixdlt.networks.Network; import com.radixdlt.store.LastProof; import com.radixdlt.utils.TimeSupplier; - +import io.reactivex.rxjava3.core.Flowable; import java.util.Comparator; import java.util.Map; - -import io.reactivex.rxjava3.core.Flowable; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; +import org.junit.Test; public final class MempoolRunnerTest { - @Inject - private Map moduleRunners; - @Inject - private EventDispatcher mempoolAddEventDispatcher; + @Inject private Map moduleRunners; + @Inject private EventDispatcher mempoolAddEventDispatcher; - private StateComputer stateComputer = mock(StateComputer.class); + private StateComputer stateComputer = mock(StateComputer.class); - @SuppressWarnings("unchecked") // The mock method doesn't support type-safe generics due to type erasure - public Module createModule() { - return new AbstractModule() { - @Override - public void configure() { - bind(BFTNode.class).annotatedWith(Self.class).toInstance(BFTNode.random()); - bind(LedgerProof.class).annotatedWith(LastProof.class) - .toInstance(mock(LedgerProof.class)); - bind(StateComputer.class).toInstance(stateComputer); - bind(SystemCounters.class).toInstance(new SystemCountersImpl()); - bind(RxRemoteEnvironment.class).toInstance(new RxRemoteEnvironment() { - @Override - public Flowable> remoteEvents(Class remoteEventClass) { - return Flowable.never(); - } - }); - bind(LedgerAccumulator.class).toInstance(mock(LedgerAccumulator.class)); - bind(LedgerAccumulatorVerifier.class).toInstance(mock(LedgerAccumulatorVerifier.class)); - bind(new TypeLiteral>() { }).toInstance(mock(Comparator.class)); - bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); - bind(TimeSupplier.class).toInstance(System::currentTimeMillis); - Multibinder.newSetBinder(binder(), StartProcessorOnRunner.class); - install(MempoolConfig.asModule(100, 10)); - install(new MockedKeyModule()); - install(new MockedCryptoModule()); - install(new RxEnvironmentModule()); - install(new DispatcherModule()); - install(new MempoolReceiverModule()); - install(new EventLoggerModule()); - } - }; - } + @SuppressWarnings( + "unchecked") // The mock method doesn't support type-safe generics due to type erasure + public Module createModule() { + return new AbstractModule() { + @Override + public void configure() { + bind(BFTNode.class).annotatedWith(Self.class).toInstance(BFTNode.random()); + bind(LedgerProof.class).annotatedWith(LastProof.class).toInstance(mock(LedgerProof.class)); + bind(StateComputer.class).toInstance(stateComputer); + bind(SystemCounters.class).toInstance(new SystemCountersImpl()); + bind(RxRemoteEnvironment.class) + .toInstance( + new RxRemoteEnvironment() { + @Override + public Flowable> remoteEvents(Class remoteEventClass) { + return Flowable.never(); + } + }); + bind(LedgerAccumulator.class).toInstance(mock(LedgerAccumulator.class)); + bind(LedgerAccumulatorVerifier.class).toInstance(mock(LedgerAccumulatorVerifier.class)); + bind(new TypeLiteral>() {}).toInstance(mock(Comparator.class)); + bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); + bind(TimeSupplier.class).toInstance(System::currentTimeMillis); + Multibinder.newSetBinder(binder(), StartProcessorOnRunner.class); + install(MempoolConfig.asModule(100, 10)); + install(new MockedKeyModule()); + install(new MockedCryptoModule()); + install(new RxEnvironmentModule()); + install(new DispatcherModule()); + install(new MempoolReceiverModule()); + install(new EventLoggerModule()); + } + }; + } - @Test - public void dispatched_mempool_add_arrives_at_state_computer() { - Guice.createInjector(createModule()).injectMembers(this); - moduleRunners.get(Runners.MEMPOOL).start(); + @Test + public void dispatched_mempool_add_arrives_at_state_computer() { + Guice.createInjector(createModule()).injectMembers(this); + moduleRunners.get(Runners.MEMPOOL).start(); - MempoolAdd mempoolAdd = MempoolAdd.create(Txn.create(new byte[0])); - mempoolAddEventDispatcher.dispatch(mempoolAdd); + MempoolAdd mempoolAdd = MempoolAdd.create(Txn.create(new byte[0])); + mempoolAddEventDispatcher.dispatch(mempoolAdd); - verify(stateComputer, timeout(1000).times(1)) - .addToMempool(eq(mempoolAdd), isNull()); - } + verify(stateComputer, timeout(1000).times(1)).addToMempool(eq(mempoolAdd), isNull()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolTest.java index b941e4f995..253b9f092b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/MempoolTest.java @@ -64,30 +64,24 @@ package com.radixdlt.mempool; -import com.radixdlt.application.system.scrypt.Syscall; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.atom.SubstateId; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERules; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.utils.PrivateKeys; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.application.tokens.Amount; +import com.radixdlt.atom.SubstateId; import com.radixdlt.atom.TxLowLevelBuilder; import com.radixdlt.atom.Txn; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; +import com.radixdlt.consensus.bft.View; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.ECKeyPair; @@ -102,291 +96,300 @@ import com.radixdlt.statecomputer.RadixEngineStateComputer; import com.radixdlt.statecomputer.checkpoint.Genesis; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERules; +import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; - +import com.radixdlt.utils.PrivateKeys; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class MempoolTest { - private static final ECKeyPair VALIDATOR_KEY = PrivateKeys.ofNumeric(1); - private static final int NUM_PEERS = 2; - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Inject @Self private BFTNode self; - @Inject @Genesis private VerifiedTxnsAndProof genesisTxns; - @Inject private DeterministicProcessor processor; - @Inject private DeterministicNetwork network; - @Inject private RadixEngineStateComputer stateComputer; - @Inject private SystemCounters systemCounters; - @Inject private PeersView peersView; - @Inject private RERules rules; - @Inject @MempoolRelayInitialDelay private long initialDelay; - @Inject @MempoolRelayRepeatDelay private long repeatDelay; - - private Injector getInjector() { - return Guice.createInjector( - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault().removeSigsPerRoundLimit()), - MempoolConfig.asModule(10, 10, 200, 500, 10), - new MainnetForkConfigsModule(), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(VALIDATOR_KEY, NUM_PEERS), - new MockedGenesisModule( - Set.of(VALIDATOR_KEY.getPublicKey()), - Amount.ofTokens(1000), - Amount.ofTokens(100) - ), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } - - private BFTNode getFirstPeer() { - return peersView.peers().findFirst().get().bftNode(); - } - - private Txn createTxn(ECKeyPair keyPair, int numMutexes) throws Exception { - TxLowLevelBuilder atomBuilder = TxLowLevelBuilder.newBuilder(rules.getSerialization()); - for (int i = 0; i < numMutexes; i++) { - var symbol = "test" + (char) ('c' + i); - var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), symbol); - atomBuilder - .syscall(Syscall.READDR_CLAIM, symbol.getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesisTxns.getTxns().get(0).getId(), 0), addr.getBytes()) - .end(); - } - var signature = keyPair.sign(atomBuilder.hashToSign()); - return atomBuilder.sig(signature).build(); - } - - private Txn createTxn(ECKeyPair keyPair) throws Exception { - return createTxn(keyPair, 1); - } - - @Test - public void add_local_command_to_mempool() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair); - - // Act - processor.handleMessage(self, MempoolAdd.create(txn), null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); - // FIXME: Added hack which requires genesis to be sent as message so ignore this check for now - //assertThat(network.allMessages()) - //.hasOnlyOneElementSatisfying(m -> assertThat(m.message()).isInstanceOf(MempoolAddSuccess.class)); - } - - @Test - public void add_remote_command_to_mempool() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair); - - // Act - processor.handleMessage(getFirstPeer(), MempoolAdd.create(txn), null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); - // FIXME: Added hack which requires genesis to be sent as message so ignore this check for now - //assertThat(network.allMessages()) - //.hasOnlyOneElementSatisfying(m -> assertThat(m.message()).isInstanceOf(MempoolAddSuccess.class)); - } - - @Test - public void relay_successful_local_add() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair); - - // Act - processor.handleMessage(self, MempoolAddSuccess.create(txn, null, null), null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_RELAYS_SENT)).isEqualTo(NUM_PEERS); - } - - @Test - public void relay_successful_remote_add() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair); - - // Act - processor.handleMessage(self, MempoolAddSuccess.create(txn, null, getFirstPeer()), null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_RELAYS_SENT)).isEqualTo(NUM_PEERS - 1); - } - - @Test - public void add_same_command_to_mempool() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair); - MempoolAdd mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - - // Act - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); - } - - @Test - public void add_conflicting_commands_to_mempool() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair, 2); - MempoolAdd mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - - // Act - var txn2 = createTxn(keyPair, 1); - MempoolAdd mempoolAddSuccess2 = MempoolAdd.create(txn2); - processor.handleMessage(getFirstPeer(), mempoolAddSuccess2, null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(2); - } - - @Test - public void add_bad_command_to_mempool() { - // Arrange - getInjector().injectMembers(this); - final var txn = Txn.create(new byte[0]); - - // Act - MempoolAdd mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); - } - - @Test - public void replay_command_to_mempool() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair); - var proof = mock(LedgerProof.class); - when(proof.getAccumulatorState()).thenReturn(new AccumulatorState(genesisTxns.getTxns().size() + 1, HashUtils.random256())); - when(proof.getStateVersion()).thenReturn((long) genesisTxns.getTxns().size() + 1); - when(proof.getView()).thenReturn(View.of(1)); - var commandsAndProof = VerifiedTxnsAndProof.create(List.of(txn), proof); - stateComputer.commit(commandsAndProof, null); - - // Act - MempoolAdd mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); - } - - @Test - public void mempool_removes_conflicts_on_commit() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair, 2); - MempoolAdd mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - - // Act - var txn2 = createTxn(keyPair, 1); - var proof = mock(LedgerProof.class); - when(proof.getAccumulatorState()).thenReturn(new AccumulatorState(genesisTxns.getTxns().size() + 1, HashUtils.random256())); - when(proof.getStateVersion()).thenReturn((long) genesisTxns.getTxns().size() + 1); - when(proof.getView()).thenReturn(View.of(1)); - var commandsAndProof = VerifiedTxnsAndProof.create(List.of(txn2), proof); - stateComputer.commit(commandsAndProof, null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); - } - - @Test - public void mempool_removes_multiple_conflicts_on_commit() throws Exception { - // Arrange - getInjector().injectMembers(this); - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = createTxn(keyPair, 2); - MempoolAdd mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(getFirstPeer(), mempoolAdd, null); - var txn2 = createTxn(keyPair, 3); - processor.handleMessage(getFirstPeer(), MempoolAdd.create(txn2), null); - - // Act - var txn3 = createTxn(keyPair, 1); - var proof = mock(LedgerProof.class); - when(proof.getAccumulatorState()).thenReturn(new AccumulatorState(genesisTxns.getTxns().size() + 1, HashUtils.random256())); - when(proof.getStateVersion()).thenReturn((long) genesisTxns.getTxns().size() + 1); - when(proof.getView()).thenReturn(View.of(1)); - var commandsAndProof = VerifiedTxnsAndProof.create(List.of(txn3), proof); - stateComputer.commit(commandsAndProof, null); - - // Assert - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); - } - - @Test - @Ignore("Added hack which requires genesis to be sent as message. Reenable when fixed.") - public void mempool_should_relay_commands_respecting_delay_config_params() throws Exception { - // Arrange - getInjector().injectMembers(this); - final var keyPair = ECKeyPair.generateNew(); - final var txn = createTxn(keyPair); - final var mempoolAdd = MempoolAdd.create(txn); - processor.handleMessage(self, mempoolAdd, null); - assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); - - assertThat(network.allMessages()) - .hasOnlyOneElementSatisfying(m -> assertThat(m.message()).isInstanceOf(MempoolAddSuccess.class)); - network.dropMessages(msg -> msg.message() instanceof MempoolAddSuccess); - - // should not relay immediately - processor.handleMessage(self, MempoolRelayTrigger.create(), null); - assertThat(network.allMessages()).isEmpty(); - - // should relay after initial delay - Thread.sleep(initialDelay); - processor.handleMessage(self, MempoolRelayTrigger.create(), null); - assertThat(network.allMessages()) - .extracting(ControlledMessage::message) - .hasOnlyElementsOfType(MempoolAdd.class); - network.dropMessages(msg -> msg.message() instanceof MempoolAdd); - - // should not relay again immediately - processor.handleMessage(self, MempoolRelayTrigger.create(), null); - assertThat(network.allMessages()).isEmpty(); - - // should relay after repeat delay - Thread.sleep(repeatDelay); - processor.handleMessage(self, MempoolRelayTrigger.create(), null); - assertThat(network.allMessages()) - .extracting(ControlledMessage::message) - .hasOnlyElementsOfType(MempoolAdd.class); - } + private static final ECKeyPair VALIDATOR_KEY = PrivateKeys.ofNumeric(1); + private static final int NUM_PEERS = 2; + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Inject @Self private BFTNode self; + @Inject @Genesis private VerifiedTxnsAndProof genesisTxns; + @Inject private DeterministicProcessor processor; + @Inject private DeterministicNetwork network; + @Inject private RadixEngineStateComputer stateComputer; + @Inject private SystemCounters systemCounters; + @Inject private PeersView peersView; + @Inject private RERules rules; + @Inject @MempoolRelayInitialDelay private long initialDelay; + @Inject @MempoolRelayRepeatDelay private long repeatDelay; + + private Injector getInjector() { + return Guice.createInjector( + new RadixEngineForksLatestOnlyModule( + RERulesConfig.testingDefault().removeSigsPerRoundLimit()), + MempoolConfig.asModule(10, 10, 200, 500, 10), + new MainnetForkConfigsModule(), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(VALIDATOR_KEY, NUM_PEERS), + new MockedGenesisModule( + Set.of(VALIDATOR_KEY.getPublicKey()), Amount.ofTokens(1000), Amount.ofTokens(100)), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } + + private BFTNode getFirstPeer() { + return peersView.peers().findFirst().get().bftNode(); + } + + private Txn createTxn(ECKeyPair keyPair, int numMutexes) throws Exception { + TxLowLevelBuilder atomBuilder = TxLowLevelBuilder.newBuilder(rules.getSerialization()); + for (int i = 0; i < numMutexes; i++) { + var symbol = "test" + (char) ('c' + i); + var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), symbol); + atomBuilder + .syscall(Syscall.READDR_CLAIM, symbol.getBytes(StandardCharsets.UTF_8)) + .virtualDown( + SubstateId.ofSubstate(genesisTxns.getTxns().get(0).getId(), 0), addr.getBytes()) + .end(); + } + var signature = keyPair.sign(atomBuilder.hashToSign()); + return atomBuilder.sig(signature).build(); + } + + private Txn createTxn(ECKeyPair keyPair) throws Exception { + return createTxn(keyPair, 1); + } + + @Test + public void add_local_command_to_mempool() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair); + + // Act + processor.handleMessage(self, MempoolAdd.create(txn), null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); + // FIXME: Added hack which requires genesis to be sent as message so ignore this check for now + // assertThat(network.allMessages()) + // .hasOnlyOneElementSatisfying(m -> + // assertThat(m.message()).isInstanceOf(MempoolAddSuccess.class)); + } + + @Test + public void add_remote_command_to_mempool() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair); + + // Act + processor.handleMessage(getFirstPeer(), MempoolAdd.create(txn), null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); + // FIXME: Added hack which requires genesis to be sent as message so ignore this check for now + // assertThat(network.allMessages()) + // .hasOnlyOneElementSatisfying(m -> + // assertThat(m.message()).isInstanceOf(MempoolAddSuccess.class)); + } + + @Test + public void relay_successful_local_add() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair); + + // Act + processor.handleMessage(self, MempoolAddSuccess.create(txn, null, null), null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_RELAYS_SENT)).isEqualTo(NUM_PEERS); + } + + @Test + public void relay_successful_remote_add() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair); + + // Act + processor.handleMessage(self, MempoolAddSuccess.create(txn, null, getFirstPeer()), null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_RELAYS_SENT)).isEqualTo(NUM_PEERS - 1); + } + + @Test + public void add_same_command_to_mempool() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair); + MempoolAdd mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + + // Act + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); + } + + @Test + public void add_conflicting_commands_to_mempool() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair, 2); + MempoolAdd mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + + // Act + var txn2 = createTxn(keyPair, 1); + MempoolAdd mempoolAddSuccess2 = MempoolAdd.create(txn2); + processor.handleMessage(getFirstPeer(), mempoolAddSuccess2, null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(2); + } + + @Test + public void add_bad_command_to_mempool() { + // Arrange + getInjector().injectMembers(this); + final var txn = Txn.create(new byte[0]); + + // Act + MempoolAdd mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); + } + + @Test + public void replay_command_to_mempool() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair); + var proof = mock(LedgerProof.class); + when(proof.getAccumulatorState()) + .thenReturn(new AccumulatorState(genesisTxns.getTxns().size() + 1, HashUtils.random256())); + when(proof.getStateVersion()).thenReturn((long) genesisTxns.getTxns().size() + 1); + when(proof.getView()).thenReturn(View.of(1)); + var commandsAndProof = VerifiedTxnsAndProof.create(List.of(txn), proof); + stateComputer.commit(commandsAndProof, null); + + // Act + MempoolAdd mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); + } + + @Test + public void mempool_removes_conflicts_on_commit() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair, 2); + MempoolAdd mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + + // Act + var txn2 = createTxn(keyPair, 1); + var proof = mock(LedgerProof.class); + when(proof.getAccumulatorState()) + .thenReturn(new AccumulatorState(genesisTxns.getTxns().size() + 1, HashUtils.random256())); + when(proof.getStateVersion()).thenReturn((long) genesisTxns.getTxns().size() + 1); + when(proof.getView()).thenReturn(View.of(1)); + var commandsAndProof = VerifiedTxnsAndProof.create(List.of(txn2), proof); + stateComputer.commit(commandsAndProof, null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); + } + + @Test + public void mempool_removes_multiple_conflicts_on_commit() throws Exception { + // Arrange + getInjector().injectMembers(this); + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = createTxn(keyPair, 2); + MempoolAdd mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(getFirstPeer(), mempoolAdd, null); + var txn2 = createTxn(keyPair, 3); + processor.handleMessage(getFirstPeer(), MempoolAdd.create(txn2), null); + + // Act + var txn3 = createTxn(keyPair, 1); + var proof = mock(LedgerProof.class); + when(proof.getAccumulatorState()) + .thenReturn(new AccumulatorState(genesisTxns.getTxns().size() + 1, HashUtils.random256())); + when(proof.getStateVersion()).thenReturn((long) genesisTxns.getTxns().size() + 1); + when(proof.getView()).thenReturn(View.of(1)); + var commandsAndProof = VerifiedTxnsAndProof.create(List.of(txn3), proof); + stateComputer.commit(commandsAndProof, null); + + // Assert + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isZero(); + } + + @Test + @Ignore("Added hack which requires genesis to be sent as message. Reenable when fixed.") + public void mempool_should_relay_commands_respecting_delay_config_params() throws Exception { + // Arrange + getInjector().injectMembers(this); + final var keyPair = ECKeyPair.generateNew(); + final var txn = createTxn(keyPair); + final var mempoolAdd = MempoolAdd.create(txn); + processor.handleMessage(self, mempoolAdd, null); + assertThat(systemCounters.get(CounterType.MEMPOOL_CURRENT_SIZE)).isEqualTo(1); + + assertThat(network.allMessages()) + .hasOnlyOneElementSatisfying( + m -> assertThat(m.message()).isInstanceOf(MempoolAddSuccess.class)); + network.dropMessages(msg -> msg.message() instanceof MempoolAddSuccess); + + // should not relay immediately + processor.handleMessage(self, MempoolRelayTrigger.create(), null); + assertThat(network.allMessages()).isEmpty(); + + // should relay after initial delay + Thread.sleep(initialDelay); + processor.handleMessage(self, MempoolRelayTrigger.create(), null); + assertThat(network.allMessages()) + .extracting(ControlledMessage::message) + .hasOnlyElementsOfType(MempoolAdd.class); + network.dropMessages(msg -> msg.message() instanceof MempoolAdd); + + // should not relay again immediately + processor.handleMessage(self, MempoolRelayTrigger.create(), null); + assertThat(network.allMessages()).isEmpty(); + + // should relay after repeat delay + Thread.sleep(repeatDelay); + processor.handleMessage(self, MempoolRelayTrigger.create(), null); + assertThat(network.allMessages()) + .extracting(ControlledMessage::message) + .hasOnlyElementsOfType(MempoolAdd.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/SimpleMempool.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/SimpleMempool.java index 411c7db74d..09a14a2dac 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/SimpleMempool.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/SimpleMempool.java @@ -61,104 +61,100 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.mempool; import com.google.common.collect.Lists; import com.radixdlt.atom.Txn; import com.radixdlt.counters.SystemCounters; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.Iterator; import java.util.Random; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -/** - * Simple mempool which performs no validation and removes on commit. - */ +/** Simple mempool which performs no validation and removes on commit. */ public final class SimpleMempool implements Mempool { - private final Set data = new HashSet<>(); - private final SystemCounters counters; - private final Random random; - private final int maxSize; + private final Set data = new HashSet<>(); + private final SystemCounters counters; + private final Random random; + private final int maxSize; - public SimpleMempool( - SystemCounters counters, - int maxSize, - Random random - ) { - if (maxSize <= 0) { - throw new IllegalArgumentException("mempool.maxSize must be positive: " + maxSize); - } - this.counters = Objects.requireNonNull(counters); - this.maxSize = maxSize; - this.random = Objects.requireNonNull(random); - } + public SimpleMempool(SystemCounters counters, int maxSize, Random random) { + if (maxSize <= 0) { + throw new IllegalArgumentException("mempool.maxSize must be positive: " + maxSize); + } + this.counters = Objects.requireNonNull(counters); + this.maxSize = maxSize; + this.random = Objects.requireNonNull(random); + } - @Override - public Txn add(Txn txn) throws MempoolFullException, MempoolDuplicateException { - if (this.data.size() >= maxSize) { - throw new MempoolFullException(this.data.size(), maxSize); - } - if (!this.data.add(txn)) { - throw new MempoolDuplicateException(String.format("Mempool already has command %s", txn)); - } + @Override + public Txn add(Txn txn) throws MempoolFullException, MempoolDuplicateException { + if (this.data.size() >= maxSize) { + throw new MempoolFullException(this.data.size(), maxSize); + } + if (!this.data.add(txn)) { + throw new MempoolDuplicateException(String.format("Mempool already has command %s", txn)); + } - updateCounts(); + updateCounts(); - return txn; - } + return txn; + } - @Override - public List committed(List commands) { - commands.forEach(this.data::remove); - updateCounts(); - return List.of(); - } + @Override + public List committed(List commands) { + commands.forEach(this.data::remove); + updateCounts(); + return List.of(); + } - @Override - public int getCount() { - return data.size(); - } + @Override + public int getCount() { + return data.size(); + } - @Override - public List getTxns(int count, List seen) { - int size = Math.min(count, this.data.size()); - if (size > 0) { - List commands = Lists.newArrayList(); - var values = new ArrayList<>(this.data); - Collections.shuffle(values, random); + @Override + public List getTxns(int count, List seen) { + int size = Math.min(count, this.data.size()); + if (size > 0) { + List commands = Lists.newArrayList(); + var values = new ArrayList<>(this.data); + Collections.shuffle(values, random); - Iterator i = values.iterator(); - while (commands.size() < size && i.hasNext()) { - var a = i.next(); - if (!seen.contains(a)) { - commands.add(a); - } - } - return commands; - } else { - return Collections.emptyList(); - } - } + Iterator i = values.iterator(); + while (commands.size() < size && i.hasNext()) { + var a = i.next(); + if (!seen.contains(a)) { + commands.add(a); + } + } + return commands; + } else { + return Collections.emptyList(); + } + } - @Override - public List scanUpdateAndGet(Predicate predicate, Consumer operator) { - return List.of(); - } + @Override + public List scanUpdateAndGet( + Predicate predicate, Consumer operator) { + return List.of(); + } - private void updateCounts() { - this.counters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, this.data.size()); - } + private void updateCounts() { + this.counters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, this.data.size()); + } - @Override - public String toString() { - return String.format("%s[%x:%s/%s]", - getClass().getSimpleName(), System.identityHashCode(this), this.data.size(), maxSize); - } + @Override + public String toString() { + return String.format( + "%s[%x:%s/%s]", + getClass().getSimpleName(), System.identityHashCode(this), this.data.size(), maxSize); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/messages/MempoolAddMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/messages/MempoolAddMessageTest.java index 01a81d8ef9..af6d9da8e9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/messages/MempoolAddMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempool/messages/MempoolAddMessageTest.java @@ -64,51 +64,50 @@ package com.radixdlt.mempool.messages; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.hash.HashCode; import com.radixdlt.atom.Txn; import com.radixdlt.crypto.HashUtils; import com.radixdlt.middleware2.network.MempoolAddMessage; +import java.util.ArrayList; +import java.util.List; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - public class MempoolAddMessageTest { - private MempoolAddMessage message; + private MempoolAddMessage message; - @Before - public void setUp() { - this.message = MempoolAddMessage.from(List.of(Txn.create(new byte[0]))); - } + @Before + public void setUp() { + this.message = MempoolAddMessage.from(List.of(Txn.create(new byte[0]))); + } - @Test - public void sensibleToString() { - assertThat(message.toString()).contains(MempoolAddMessage.class.getSimpleName()); - } + @Test + public void sensibleToString() { + assertThat(message.toString()).contains(MempoolAddMessage.class.getSimpleName()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(MempoolAddMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(MempoolAddMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new MempoolAddMessage(null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new MempoolAddMessage(null); + } - @Test(expected = NullPointerException.class) - public void deserializationWithListOfNullsThrowsException() { - var list = new ArrayList(); - list.add(null); - new MempoolAddMessage(list); - } + @Test(expected = NullPointerException.class) + public void deserializationWithListOfNullsThrowsException() { + var list = new ArrayList(); + list.add(null); + new MempoolAddMessage(list); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempoolfiller/MempoolFillerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempoolfiller/MempoolFillerTest.java index 6f883187ef..b1c777ae5c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempoolfiller/MempoolFillerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/mempoolfiller/MempoolFillerTest.java @@ -64,24 +64,18 @@ package com.radixdlt.mempoolfiller; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.utils.PrivateKeys; -import org.assertj.core.api.Condition; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.assertj.core.api.Assertions.assertThat; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; import com.radixdlt.counters.SystemCounters; +import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.deterministic.DeterministicProcessor; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; @@ -90,62 +84,61 @@ import com.radixdlt.network.p2p.PeersView; import com.radixdlt.statecomputer.RadixEngineStateComputer; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; - +import com.radixdlt.utils.PrivateKeys; import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; +import org.assertj.core.api.Condition; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class MempoolFillerTest { - private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); + private static final ECKeyPair TEST_KEY = PrivateKeys.ofNumeric(1); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Inject - @Self - private BFTNode self; - @Inject private Hasher hasher; - @Inject private DeterministicProcessor processor; - @Inject private DeterministicNetwork network; - @Inject private RadixEngineStateComputer stateComputer; - @Inject private SystemCounters systemCounters; - @Inject private PeersView peersView; + @Inject @Self private BFTNode self; + @Inject private Hasher hasher; + @Inject private DeterministicProcessor processor; + @Inject private DeterministicNetwork network; + @Inject private RadixEngineStateComputer stateComputer; + @Inject private SystemCounters systemCounters; + @Inject private PeersView peersView; - private Injector getInjector() { - return Guice.createInjector( - new RadixEngineForksLatestOnlyModule(), - MempoolConfig.asModule(10, 10), - new MainnetForkConfigsModule(), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), - new MockedGenesisModule( - Set.of(TEST_KEY.getPublicKey()), - Amount.ofTokens(10000000000L), - Amount.ofTokens(100) - ), - new AbstractModule() { - @Override - protected void configure() { - install(new MempoolFillerModule()); - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } + private Injector getInjector() { + return Guice.createInjector( + new RadixEngineForksLatestOnlyModule(), + MempoolConfig.asModule(10, 10), + new MainnetForkConfigsModule(), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(TEST_KEY, 0), + new MockedGenesisModule( + Set.of(TEST_KEY.getPublicKey()), Amount.ofTokens(10000000000L), Amount.ofTokens(100)), + new AbstractModule() { + @Override + protected void configure() { + install(new MempoolFillerModule()); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } - @Test - public void mempool_fill_starts_filling_mempool() { - // Arrange - getInjector().injectMembers(this); + @Test + public void mempool_fill_starts_filling_mempool() { + // Arrange + getInjector().injectMembers(this); - // Act - processor.handleMessage(self, MempoolFillerUpdate.enable(15, true), null); - processor.handleMessage(self, ScheduledMempoolFill.create(), null); + // Act + processor.handleMessage(self, MempoolFillerUpdate.enable(15, true), null); + processor.handleMessage(self, ScheduledMempoolFill.create(), null); - // Assert - assertThat(network.allMessages()) - .areAtLeast(1, new Condition<>(m -> m.message() instanceof MempoolAdd, "Has mempool add")); - } + // Assert + assertThat(network.allMessages()) + .areAtLeast(1, new Condition<>(m -> m.message() instanceof MempoolAdd, "Has mempool add")); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/ConsensusEventMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/ConsensusEventMessageTest.java index 74b4ea4e58..5b2672fcb0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/ConsensusEventMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/ConsensusEventMessageTest.java @@ -64,6 +64,11 @@ package com.radixdlt.middleware2.network; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; @@ -72,55 +77,46 @@ import nl.jqno.equalsverifier.Warning; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - public class ConsensusEventMessageTest { - @Test - public void sensibleToStringProposal() { - Proposal m = mock(Proposal.class); - ConsensusEventMessage msg1 = new ConsensusEventMessage(m); - String s1 = msg1.toString(); + @Test + public void sensibleToStringProposal() { + Proposal m = mock(Proposal.class); + ConsensusEventMessage msg1 = new ConsensusEventMessage(m); + String s1 = msg1.toString(); - assertThat(s1) - .contains(ConsensusEventMessage.class.getSimpleName()) - .contains(m.toString()); + assertThat(s1).contains(ConsensusEventMessage.class.getSimpleName()).contains(m.toString()); - assertTrue(msg1.getConsensusMessage() instanceof Proposal); - } + assertTrue(msg1.getConsensusMessage() instanceof Proposal); + } - @Test - public void sensibleToStringVote() { - Vote m = mock(Vote.class); - ConsensusEventMessage msg1 = new ConsensusEventMessage(m); - String s1 = msg1.toString(); - assertThat(s1) - .contains(ConsensusEventMessage.class.getSimpleName()) - .contains(m.toString()); + @Test + public void sensibleToStringVote() { + Vote m = mock(Vote.class); + ConsensusEventMessage msg1 = new ConsensusEventMessage(m); + String s1 = msg1.toString(); + assertThat(s1).contains(ConsensusEventMessage.class.getSimpleName()).contains(m.toString()); - assertTrue(msg1.getConsensusMessage() instanceof Vote); - } + assertTrue(msg1.getConsensusMessage() instanceof Vote); + } - @Test(expected = IllegalStateException.class) - public void failedConsensusMessage() { - ConsensusEventMessage msg1 = new ConsensusEventMessage((Proposal) null); - assertNotNull(msg1.getConsensusMessage()); - } + @Test(expected = IllegalStateException.class) + public void failedConsensusMessage() { + ConsensusEventMessage msg1 = new ConsensusEventMessage((Proposal) null); + assertNotNull(msg1.getConsensusMessage()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(ConsensusEventMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(ConsensusEventMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = IllegalStateException.class) - public void deserializationWithBothNullParametersThrowsException() { - new ConsensusEventMessage(null, null); - } -} \ No newline at end of file + @Test(expected = IllegalStateException.class) + public void deserializationWithBothNullParametersThrowsException() { + new ConsensusEventMessage(null, null); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java index 92f8cc740e..00e63dc4e7 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java @@ -64,29 +64,30 @@ package com.radixdlt.middleware2.network; +import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.UnverifiedVertex; import com.radixdlt.consensus.bft.VerifiedVertex; import com.radixdlt.crypto.HashUtils; import com.radixdlt.ledger.AccumulatorState; -import org.radix.serialization.SerializeMessageObject; - import java.util.Optional; +import org.radix.serialization.SerializeMessageObject; -public class GetVerticesErrorResponseMessageSerializeTest extends SerializeMessageObject { - public GetVerticesErrorResponseMessageSerializeTest() { - super(GetVerticesErrorResponseMessage.class, GetVerticesErrorResponseMessageSerializeTest::get); - } +public class GetVerticesErrorResponseMessageSerializeTest + extends SerializeMessageObject { + public GetVerticesErrorResponseMessageSerializeTest() { + super(GetVerticesErrorResponseMessage.class, GetVerticesErrorResponseMessageSerializeTest::get); + } - private static GetVerticesErrorResponseMessage get() { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - VerifiedVertex verifiedVertex = new VerifiedVertex(UnverifiedVertex.createGenesis(ledgerHeader), HashUtils.zero256()); - QuorumCertificate qc = QuorumCertificate.ofGenesis(verifiedVertex, ledgerHeader); - HighQC highQC = HighQC.from(qc, qc, Optional.empty()); - final var request = new GetVerticesRequestMessage(HashUtils.random256(), 3); - return new GetVerticesErrorResponseMessage(highQC, request); - } + private static GetVerticesErrorResponseMessage get() { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + VerifiedVertex verifiedVertex = + new VerifiedVertex(UnverifiedVertex.createGenesis(ledgerHeader), HashUtils.zero256()); + QuorumCertificate qc = QuorumCertificate.ofGenesis(verifiedVertex, ledgerHeader); + HighQC highQC = HighQC.from(qc, qc, Optional.empty()); + final var request = new GetVerticesRequestMessage(HashUtils.random256(), 3); + return new GetVerticesErrorResponseMessage(highQC, request); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java index f857f01829..06bf76bf5d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java @@ -64,6 +64,10 @@ package com.radixdlt.middleware2.network; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.common.hash.HashCode; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; @@ -71,46 +75,41 @@ import com.radixdlt.consensus.bft.VerifiedVertex; import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.HashUtils; +import java.util.Optional; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.Test; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class GetVerticesErrorResponseMessageTest { - @Test - public void sensibleToString() { - VerifiedVertex verifiedVertex = mock(VerifiedVertex.class); - when(verifiedVertex.getView()).thenReturn(View.genesis()); - when(verifiedVertex.getId()).thenReturn(HashCode.fromInt(1)); - QuorumCertificate qc = QuorumCertificate.ofGenesis(verifiedVertex, mock(LedgerHeader.class)); - HighQC highQC = HighQC.from(qc, qc, Optional.empty()); - final var request = mock(GetVerticesRequestMessage.class); - GetVerticesErrorResponseMessage msg1 = new GetVerticesErrorResponseMessage(highQC, request); - String s1 = msg1.toString(); - assertThat(s1).contains(GetVerticesErrorResponseMessage.class.getSimpleName()); - } + @Test + public void sensibleToString() { + VerifiedVertex verifiedVertex = mock(VerifiedVertex.class); + when(verifiedVertex.getView()).thenReturn(View.genesis()); + when(verifiedVertex.getId()).thenReturn(HashCode.fromInt(1)); + QuorumCertificate qc = QuorumCertificate.ofGenesis(verifiedVertex, mock(LedgerHeader.class)); + HighQC highQC = HighQC.from(qc, qc, Optional.empty()); + final var request = mock(GetVerticesRequestMessage.class); + GetVerticesErrorResponseMessage msg1 = new GetVerticesErrorResponseMessage(highQC, request); + String s1 = msg1.toString(); + assertThat(s1).contains(GetVerticesErrorResponseMessage.class.getSimpleName()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(GetVerticesErrorResponseMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(GetVerticesErrorResponseMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - new GetVerticesErrorResponseMessage(null, mock(GetVerticesRequestMessage.class)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + new GetVerticesErrorResponseMessage(null, mock(GetVerticesRequestMessage.class)); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - new GetVerticesErrorResponseMessage(mock(HighQC.class), null); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + new GetVerticesErrorResponseMessage(mock(HighQC.class), null); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageSerializeTest.java index 9573b4cff7..ec11b7a50f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageSerializeTest.java @@ -67,12 +67,13 @@ import com.radixdlt.crypto.HashUtils; import org.radix.serialization.SerializeMessageObject; -public class GetVerticesRequestMessageSerializeTest extends SerializeMessageObject { - public GetVerticesRequestMessageSerializeTest() { - super(GetVerticesRequestMessage.class, GetVerticesRequestMessageSerializeTest::get); - } +public class GetVerticesRequestMessageSerializeTest + extends SerializeMessageObject { + public GetVerticesRequestMessageSerializeTest() { + super(GetVerticesRequestMessage.class, GetVerticesRequestMessageSerializeTest::get); + } - private static GetVerticesRequestMessage get() { - return new GetVerticesRequestMessage(HashUtils.random256(), 1); - } + private static GetVerticesRequestMessage get() { + return new GetVerticesRequestMessage(HashUtils.random256(), 1); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageTest.java index 95d37156c6..9d0fc43e89 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesRequestMessageTest.java @@ -64,42 +64,42 @@ package com.radixdlt.middleware2.network; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - public class GetVerticesRequestMessageTest { - @Test - public void sensibleToString() { - HashCode vertexId = HashUtils.random256(); - GetVerticesRequestMessage msg1 = new GetVerticesRequestMessage(vertexId, 1); - String s1 = msg1.toString(); - assertThat(s1) - .contains(GetVerticesRequestMessage.class.getSimpleName()) - .contains(vertexId.toString()); - } + @Test + public void sensibleToString() { + HashCode vertexId = HashUtils.random256(); + GetVerticesRequestMessage msg1 = new GetVerticesRequestMessage(vertexId, 1); + String s1 = msg1.toString(); + assertThat(s1) + .contains(GetVerticesRequestMessage.class.getSimpleName()) + .contains(vertexId.toString()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(GetVerticesRequestMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(GetVerticesRequestMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new GetVerticesRequestMessage(null, 1); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new GetVerticesRequestMessage(null, 1); + } - @Test(expected = IllegalArgumentException.class) - public void deserializationWithInvalidCountThrowsException() { - new GetVerticesRequestMessage(mock(HashCode.class), 0); - } -} \ No newline at end of file + @Test(expected = IllegalArgumentException.class) + public void deserializationWithInvalidCountThrowsException() { + new GetVerticesRequestMessage(mock(HashCode.class), 0); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageSerializeTest.java index ac91abd54a..6282e83640 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageSerializeTest.java @@ -69,15 +69,15 @@ import com.radixdlt.consensus.UnverifiedVertex; import org.radix.serialization.SerializeMessageObject; -public class GetVerticesResponseMessageSerializeTest extends SerializeMessageObject { - public GetVerticesResponseMessageSerializeTest() { - super(GetVerticesResponseMessage.class, GetVerticesResponseMessageSerializeTest::get); - } - - private static GetVerticesResponseMessage get() { - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - UnverifiedVertex genesisVertex = UnverifiedVertex.createGenesis(ledgerHeader); - return new GetVerticesResponseMessage(ImmutableList.of(genesisVertex)); - } +public class GetVerticesResponseMessageSerializeTest + extends SerializeMessageObject { + public GetVerticesResponseMessageSerializeTest() { + super(GetVerticesResponseMessage.class, GetVerticesResponseMessageSerializeTest::get); + } + private static GetVerticesResponseMessage get() { + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + UnverifiedVertex genesisVertex = UnverifiedVertex.createGenesis(ledgerHeader); + return new GetVerticesResponseMessage(ImmutableList.of(genesisVertex)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageTest.java index 9294c6adce..77c07fac0b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesResponseMessageTest.java @@ -64,48 +64,48 @@ package com.radixdlt.middleware2.network; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; import com.radixdlt.consensus.UnverifiedVertex; import com.radixdlt.crypto.HashUtils; +import java.util.ArrayList; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.Test; -import java.util.ArrayList; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - public class GetVerticesResponseMessageTest { - @Test - public void sensibleToString() { - UnverifiedVertex genesisVertex = mock(UnverifiedVertex.class); - GetVerticesResponseMessage msg1 = new GetVerticesResponseMessage(ImmutableList.of(genesisVertex)); - String s1 = msg1.toString(); - assertThat(s1) - .contains(GetVerticesResponseMessage.class.getSimpleName()) - .contains(genesisVertex.toString()); - } + @Test + public void sensibleToString() { + UnverifiedVertex genesisVertex = mock(UnverifiedVertex.class); + GetVerticesResponseMessage msg1 = + new GetVerticesResponseMessage(ImmutableList.of(genesisVertex)); + String s1 = msg1.toString(); + assertThat(s1) + .contains(GetVerticesResponseMessage.class.getSimpleName()) + .contains(genesisVertex.toString()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(GetVerticesResponseMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(GetVerticesResponseMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new GetVerticesResponseMessage(null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new GetVerticesResponseMessage(null); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullListThrowsException() { - var list = new ArrayList(); - list.add(null); - new GetVerticesResponseMessage(list); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullListThrowsException() { + var list = new ArrayList(); + list.add(null); + new GetVerticesResponseMessage(list); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageSerializeTest.java index b865258f6e..fdc7f57d37 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageSerializeTest.java @@ -69,14 +69,14 @@ import com.radixdlt.ledger.AccumulatorState; import org.radix.serialization.SerializeMessageObject; -public class LedgerStatusUpdateMessageSerializeTest extends SerializeMessageObject { - public LedgerStatusUpdateMessageSerializeTest() { - super(LedgerStatusUpdateMessage.class, LedgerStatusUpdateMessageSerializeTest::get); - } - - private static LedgerStatusUpdateMessage get() { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - return new LedgerStatusUpdateMessage(LedgerProof.genesis(accumulatorState, null, 0)); - } +public class LedgerStatusUpdateMessageSerializeTest + extends SerializeMessageObject { + public LedgerStatusUpdateMessageSerializeTest() { + super(LedgerStatusUpdateMessage.class, LedgerStatusUpdateMessageSerializeTest::get); + } + private static LedgerStatusUpdateMessage get() { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + return new LedgerStatusUpdateMessage(LedgerProof.genesis(accumulatorState, null, 0)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageTest.java index bb4628fdb1..05751684ec 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/LedgerStatusUpdateMessageTest.java @@ -71,17 +71,17 @@ import org.junit.Test; public class LedgerStatusUpdateMessageTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(LedgerStatusUpdateMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(LedgerStatusUpdateMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new LedgerStatusUpdateMessage(null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new LedgerStatusUpdateMessage(null); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralBFTNetworkTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralBFTNetworkTest.java index 7659b5d5f7..0a4eb56bfc 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralBFTNetworkTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralBFTNetworkTest.java @@ -68,35 +68,35 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; +import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.network.messaging.MessageCentral; -import com.radixdlt.consensus.Vote; import com.radixdlt.network.messaging.MessageCentralMockProvider; - import com.radixdlt.network.p2p.NodeId; import org.junit.Before; import org.junit.Test; public class MessageCentralBFTNetworkTest { - private MessageCentral messageCentral; - private MessageCentralBFTNetwork network; + private MessageCentral messageCentral; + private MessageCentralBFTNetwork network; - @Before - public void setUp() { - this.messageCentral = MessageCentralMockProvider.get(); - this.network = new MessageCentralBFTNetwork(messageCentral); - } + @Before + public void setUp() { + this.messageCentral = MessageCentralMockProvider.get(); + this.network = new MessageCentralBFTNetwork(messageCentral); + } - @Test - public void when_send_vote__then_message_central_should_be_sent_vote_message() { - Vote vote = mock(Vote.class); - ECPublicKey leaderPk = ECKeyPair.generateNew().getPublicKey(); - BFTNode leader = mock(BFTNode.class); - when(leader.getKey()).thenReturn(leaderPk); + @Test + public void when_send_vote__then_message_central_should_be_sent_vote_message() { + Vote vote = mock(Vote.class); + ECPublicKey leaderPk = ECKeyPair.generateNew().getPublicKey(); + BFTNode leader = mock(BFTNode.class); + when(leader.getKey()).thenReturn(leaderPk); - network.voteDispatcher().dispatch(leader, vote); - verify(messageCentral, times(1)).send(eq(NodeId.fromPublicKey(leaderPk)), any(ConsensusEventMessage.class)); - } -} \ No newline at end of file + network.voteDispatcher().dispatch(leader, vote); + verify(messageCentral, times(1)) + .send(eq(NodeId.fromPublicKey(leaderPk)), any(ConsensusEventMessage.class)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralLedgerSyncTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralLedgerSyncTest.java index 7c4c8d8765..ad9b33f594 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralLedgerSyncTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralLedgerSyncTest.java @@ -71,98 +71,98 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.environment.rx.RemoteEvent; import com.radixdlt.ledger.DtoLedgerProof; -import com.radixdlt.network.messaging.MessageCentral; import com.radixdlt.ledger.DtoTxnsAndProof; +import com.radixdlt.network.messaging.MessageCentral; import com.radixdlt.network.messaging.MessageCentralMockProvider; import com.radixdlt.network.p2p.NodeId; import com.radixdlt.sync.messages.remote.StatusRequest; import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; - import io.reactivex.rxjava3.subscribers.TestSubscriber; import org.junit.Before; import org.junit.Test; public class MessageCentralLedgerSyncTest { - private MessageCentralLedgerSync messageCentralLedgerSync; - private MessageCentral messageCentral; + private MessageCentralLedgerSync messageCentralLedgerSync; + private MessageCentral messageCentral; - @Before - public void setup() { - this.messageCentral = MessageCentralMockProvider.get(); - this.messageCentralLedgerSync = new MessageCentralLedgerSync(messageCentral); - } + @Before + public void setup() { + this.messageCentral = MessageCentralMockProvider.get(); + this.messageCentralLedgerSync = new MessageCentralLedgerSync(messageCentral); + } - @Test - public void when_receive_sync_request__then_should_receive_it() { - TestSubscriber> testObserver = - this.messageCentralLedgerSync.syncRequests().test(); - final var peer = createPeer(); - SyncRequestMessage syncRequestMessage = mock(SyncRequestMessage.class); - DtoLedgerProof header = mock(DtoLedgerProof.class); - when(syncRequestMessage.getCurrentHeader()).thenReturn(header); - messageCentral.send(peer, syncRequestMessage); - testObserver.awaitCount(1); - testObserver.assertValue(syncRequest -> - syncRequest.getEvent().getHeader().equals(header) - && syncRequest.getOrigin().getKey().equals(peer.getPublicKey()) - ); - } + @Test + public void when_receive_sync_request__then_should_receive_it() { + TestSubscriber> testObserver = + this.messageCentralLedgerSync.syncRequests().test(); + final var peer = createPeer(); + SyncRequestMessage syncRequestMessage = mock(SyncRequestMessage.class); + DtoLedgerProof header = mock(DtoLedgerProof.class); + when(syncRequestMessage.getCurrentHeader()).thenReturn(header); + messageCentral.send(peer, syncRequestMessage); + testObserver.awaitCount(1); + testObserver.assertValue( + syncRequest -> + syncRequest.getEvent().getHeader().equals(header) + && syncRequest.getOrigin().getKey().equals(peer.getPublicKey())); + } - @Test - public void when_receive_sync_response__then_should_receive_it() { - TestSubscriber> testObserver = this.messageCentralLedgerSync.syncResponses().test(); - final var peer = createPeer(); - SyncResponseMessage syncResponseMessage = mock(SyncResponseMessage.class); - DtoTxnsAndProof commands = mock(DtoTxnsAndProof.class); - when(syncResponseMessage.getCommands()).thenReturn(commands); - messageCentral.send(peer, syncResponseMessage); - testObserver.awaitCount(1); - testObserver.assertValue(resp -> resp.getEvent().getTxnsAndProof().equals(commands)); - } + @Test + public void when_receive_sync_response__then_should_receive_it() { + TestSubscriber> testObserver = + this.messageCentralLedgerSync.syncResponses().test(); + final var peer = createPeer(); + SyncResponseMessage syncResponseMessage = mock(SyncResponseMessage.class); + DtoTxnsAndProof commands = mock(DtoTxnsAndProof.class); + when(syncResponseMessage.getCommands()).thenReturn(commands); + messageCentral.send(peer, syncResponseMessage); + testObserver.awaitCount(1); + testObserver.assertValue(resp -> resp.getEvent().getTxnsAndProof().equals(commands)); + } - @Test - public void when_receive_status_request__then_should_receive_it() { - TestSubscriber> testObserver = - this.messageCentralLedgerSync.statusRequests().test(); - final var peer = createPeer(); - StatusRequestMessage statusRequestMessage = mock(StatusRequestMessage.class); - messageCentral.send(peer, statusRequestMessage); - testObserver.awaitCount(1); - testObserver.assertValue(statusResponse -> - statusResponse.getOrigin().getKey().equals(peer.getPublicKey())); - } + @Test + public void when_receive_status_request__then_should_receive_it() { + TestSubscriber> testObserver = + this.messageCentralLedgerSync.statusRequests().test(); + final var peer = createPeer(); + StatusRequestMessage statusRequestMessage = mock(StatusRequestMessage.class); + messageCentral.send(peer, statusRequestMessage); + testObserver.awaitCount(1); + testObserver.assertValue( + statusResponse -> statusResponse.getOrigin().getKey().equals(peer.getPublicKey())); + } - @Test - public void when_receive_status_response__then_should_receive_it() { - TestSubscriber> testObserver = - this.messageCentralLedgerSync.statusResponses().test(); - final var peer = createPeer(); - final var header = mock(LedgerProof.class); - StatusResponseMessage statusResponseMessage = mock(StatusResponseMessage.class); - when(statusResponseMessage.getHeader()).thenReturn(header); - messageCentral.send(peer, statusResponseMessage); - testObserver.awaitCount(1); - testObserver.assertValue(statusResponse -> - statusResponse.getEvent().getHeader().equals(header) - && statusResponse.getOrigin().getKey().equals(peer.getPublicKey()) - ); - } + @Test + public void when_receive_status_response__then_should_receive_it() { + TestSubscriber> testObserver = + this.messageCentralLedgerSync.statusResponses().test(); + final var peer = createPeer(); + final var header = mock(LedgerProof.class); + StatusResponseMessage statusResponseMessage = mock(StatusResponseMessage.class); + when(statusResponseMessage.getHeader()).thenReturn(header); + messageCentral.send(peer, statusResponseMessage); + testObserver.awaitCount(1); + testObserver.assertValue( + statusResponse -> + statusResponse.getEvent().getHeader().equals(header) + && statusResponse.getOrigin().getKey().equals(peer.getPublicKey())); + } - @Test - public void when_receive_ledger_update__then_should_receive_it() { - final var testObserver = - this.messageCentralLedgerSync.ledgerStatusUpdates().test(); - final var peer = createPeer(); - final var updateMsg = mock(LedgerStatusUpdateMessage.class); - messageCentral.send(peer, updateMsg); - testObserver.awaitCount(1); - testObserver.assertValue(receivedMsg -> receivedMsg.getOrigin().getKey().equals(peer.getPublicKey())); - } + @Test + public void when_receive_ledger_update__then_should_receive_it() { + final var testObserver = this.messageCentralLedgerSync.ledgerStatusUpdates().test(); + final var peer = createPeer(); + final var updateMsg = mock(LedgerStatusUpdateMessage.class); + messageCentral.send(peer, updateMsg); + testObserver.awaitCount(1); + testObserver.assertValue( + receivedMsg -> receivedMsg.getOrigin().getKey().equals(peer.getPublicKey())); + } - private NodeId createPeer() { - final var key = ECKeyPair.generateNew().getPublicKey(); - return NodeId.fromPublicKey(key); - } + private NodeId createPeer() { + final var key = ECKeyPair.generateNew().getPublicKey(); + return NodeId.fromPublicKey(key); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralValidatorSyncTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralValidatorSyncTest.java index 90133dd657..b07e8631fb 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralValidatorSyncTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralValidatorSyncTest.java @@ -73,22 +73,21 @@ import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; -import com.radixdlt.consensus.sync.GetVerticesErrorResponse; -import com.radixdlt.consensus.sync.GetVerticesResponse; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.crypto.Hasher; -import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.UnverifiedVertex; +import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.VerifiedVertex; +import com.radixdlt.consensus.sync.GetVerticesErrorResponse; import com.radixdlt.consensus.sync.GetVerticesRequest; +import com.radixdlt.consensus.sync.GetVerticesResponse; +import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.HashUtils; +import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.rx.RemoteEvent; import com.radixdlt.network.messaging.MessageCentral; import com.radixdlt.network.messaging.MessageCentralMockProvider; - import com.radixdlt.network.p2p.NodeId; import com.radixdlt.utils.RandomHasher; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -96,64 +95,66 @@ import org.junit.Test; public class MessageCentralValidatorSyncTest { - private BFTNode self; - private MessageCentral messageCentral; - private MessageCentralValidatorSync sync; - private Hasher hasher; - - @Before - public void setUp() { - this.self = mock(BFTNode.class); - ECPublicKey pubKey = mock(ECPublicKey.class); - when(self.getKey()).thenReturn(pubKey); - this.messageCentral = MessageCentralMockProvider.get(); - this.hasher = new RandomHasher(); - this.sync = new MessageCentralValidatorSync(messageCentral, hasher); - } - - @Test - public void when_send_response__then_message_central_will_send_response() { - VerifiedVertex vertex = mock(VerifiedVertex.class); - when(vertex.toSerializable()).thenReturn(mock(UnverifiedVertex.class)); - ImmutableList vertices = ImmutableList.of(vertex); - - BFTNode node = mock(BFTNode.class); - ECPublicKey ecPublicKey = mock(ECPublicKey.class); - when(node.getKey()).thenReturn(ecPublicKey); - - sync.verticesResponseDispatcher().dispatch(node, new GetVerticesResponse(vertices)); - verify(messageCentral, times(1)).send(any(), any(GetVerticesResponseMessage.class)); - } - - @Test - public void when_send_error_response__then_message_central_will_send_error_response() { - QuorumCertificate qc = mock(QuorumCertificate.class); - HighQC highQC = mock(HighQC.class); - when(highQC.highestQC()).thenReturn(qc); - when(highQC.highestCommittedQC()).thenReturn(qc); - BFTNode node = mock(BFTNode.class); - ECPublicKey ecPublicKey = mock(ECPublicKey.class); - when(node.getKey()).thenReturn(ecPublicKey); - final var request = new GetVerticesRequest(HashUtils.random256(), 3); - - sync.verticesErrorResponseDispatcher().dispatch(node, new GetVerticesErrorResponse(highQC, request)); - - verify(messageCentral, times(1)).send(eq(NodeId.fromPublicKey(ecPublicKey)), any(GetVerticesErrorResponseMessage.class)); - } - - @Test - public void when_subscribed_to_rpc_requests__then_should_receive_requests() { - HashCode vertexId0 = mock(HashCode.class); - HashCode vertexId1 = mock(HashCode.class); - - final var peer = NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); - TestSubscriber testObserver = sync.requests().map(RemoteEvent::getEvent).test(); - messageCentral.send(peer, new GetVerticesRequestMessage(vertexId0, 1)); - messageCentral.send(peer, new GetVerticesRequestMessage(vertexId1, 1)); - - testObserver.awaitCount(2); - testObserver.assertValueAt(0, v -> v.getVertexId().equals(vertexId0)); - testObserver.assertValueAt(1, v -> v.getVertexId().equals(vertexId1)); - } - + private BFTNode self; + private MessageCentral messageCentral; + private MessageCentralValidatorSync sync; + private Hasher hasher; + + @Before + public void setUp() { + this.self = mock(BFTNode.class); + ECPublicKey pubKey = mock(ECPublicKey.class); + when(self.getKey()).thenReturn(pubKey); + this.messageCentral = MessageCentralMockProvider.get(); + this.hasher = new RandomHasher(); + this.sync = new MessageCentralValidatorSync(messageCentral, hasher); + } + + @Test + public void when_send_response__then_message_central_will_send_response() { + VerifiedVertex vertex = mock(VerifiedVertex.class); + when(vertex.toSerializable()).thenReturn(mock(UnverifiedVertex.class)); + ImmutableList vertices = ImmutableList.of(vertex); + + BFTNode node = mock(BFTNode.class); + ECPublicKey ecPublicKey = mock(ECPublicKey.class); + when(node.getKey()).thenReturn(ecPublicKey); + + sync.verticesResponseDispatcher().dispatch(node, new GetVerticesResponse(vertices)); + verify(messageCentral, times(1)).send(any(), any(GetVerticesResponseMessage.class)); + } + + @Test + public void when_send_error_response__then_message_central_will_send_error_response() { + QuorumCertificate qc = mock(QuorumCertificate.class); + HighQC highQC = mock(HighQC.class); + when(highQC.highestQC()).thenReturn(qc); + when(highQC.highestCommittedQC()).thenReturn(qc); + BFTNode node = mock(BFTNode.class); + ECPublicKey ecPublicKey = mock(ECPublicKey.class); + when(node.getKey()).thenReturn(ecPublicKey); + final var request = new GetVerticesRequest(HashUtils.random256(), 3); + + sync.verticesErrorResponseDispatcher() + .dispatch(node, new GetVerticesErrorResponse(highQC, request)); + + verify(messageCentral, times(1)) + .send(eq(NodeId.fromPublicKey(ecPublicKey)), any(GetVerticesErrorResponseMessage.class)); + } + + @Test + public void when_subscribed_to_rpc_requests__then_should_receive_requests() { + HashCode vertexId0 = mock(HashCode.class); + HashCode vertexId1 = mock(HashCode.class); + + final var peer = NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); + TestSubscriber testObserver = + sync.requests().map(RemoteEvent::getEvent).test(); + messageCentral.send(peer, new GetVerticesRequestMessage(vertexId0, 1)); + messageCentral.send(peer, new GetVerticesRequestMessage(vertexId1, 1)); + + testObserver.awaitCount(2); + testObserver.assertValueAt(0, v -> v.getVertexId().equals(vertexId0)); + testObserver.assertValueAt(1, v -> v.getVertexId().equals(vertexId1)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/StatusResponseMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/StatusResponseMessageTest.java index 12b224f265..b9b311c645 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/StatusResponseMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/StatusResponseMessageTest.java @@ -71,17 +71,17 @@ import org.junit.Test; public class StatusResponseMessageTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(StatusResponseMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(StatusResponseMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new StatusResponseMessage(null); - } -} \ No newline at end of file + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new StatusResponseMessage(null); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageSerializeTest.java index 9ac129d651..00fa693ad1 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageSerializeTest.java @@ -70,13 +70,12 @@ import org.radix.serialization.SerializeMessageObject; public class SyncRequestMessageSerializeTest extends SerializeMessageObject { - public SyncRequestMessageSerializeTest() { - super(SyncRequestMessage.class, SyncRequestMessageSerializeTest::get); - } - - private static SyncRequestMessage get() { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - return new SyncRequestMessage(LedgerProof.genesis(accumulatorState, null, 0).toDto()); - } + public SyncRequestMessageSerializeTest() { + super(SyncRequestMessage.class, SyncRequestMessageSerializeTest::get); + } + private static SyncRequestMessage get() { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + return new SyncRequestMessage(LedgerProof.genesis(accumulatorState, null, 0).toDto()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageTest.java index 6d13357acb..9427c291cc 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncRequestMessageTest.java @@ -71,17 +71,17 @@ import org.junit.Test; public class SyncRequestMessageTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncRequestMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncRequestMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new SyncRequestMessage(null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new SyncRequestMessage(null); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageSerializeTest.java index a6ee5cbd87..fdf78215ef 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageSerializeTest.java @@ -72,16 +72,16 @@ import org.radix.serialization.SerializeMessageObject; public class SyncResponseMessageSerializeTest extends SerializeMessageObject { - public SyncResponseMessageSerializeTest() { - super(SyncResponseMessage.class, SyncResponseMessageSerializeTest::get); - } + public SyncResponseMessageSerializeTest() { + super(SyncResponseMessage.class, SyncResponseMessageSerializeTest::get); + } - private static SyncResponseMessage get() { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - return new SyncResponseMessage(new DtoTxnsAndProof( - ImmutableList.of(), - LedgerProof.genesis(accumulatorState, null, 0).toDto(), - LedgerProof.genesis(accumulatorState, null, 0).toDto() - )); - } + private static SyncResponseMessage get() { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + return new SyncResponseMessage( + new DtoTxnsAndProof( + ImmutableList.of(), + LedgerProof.genesis(accumulatorState, null, 0).toDto(), + LedgerProof.genesis(accumulatorState, null, 0).toDto())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageTest.java index f59046ccad..5e08c7653b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/middleware2/network/SyncResponseMessageTest.java @@ -71,17 +71,17 @@ import org.junit.Test; public class SyncResponseMessageTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncResponseMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncResponseMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() { - new SyncRequestMessage(null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() { + new SyncRequestMessage(null); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/EnvironmentHostIpTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/EnvironmentHostIpTest.java index e4d7ba164b..1e23080432 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/EnvironmentHostIpTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/EnvironmentHostIpTest.java @@ -64,43 +64,42 @@ package com.radixdlt.network.hostip; -import java.util.Optional; - -import org.junit.Test; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.Optional; +import org.junit.Test; + public class EnvironmentHostIpTest { - @Test - public void testNull() { - EnvironmentHostIp ehip = make(); - assertFalse(ehip.hostIp(null).isPresent()); - } + @Test + public void testNull() { + EnvironmentHostIp ehip = make(); + assertFalse(ehip.hostIp(null).isPresent()); + } - @Test - public void testEmpty() { - EnvironmentHostIp ehip = make(); - assertFalse(ehip.hostIp("").isPresent()); - } + @Test + public void testEmpty() { + EnvironmentHostIp ehip = make(); + assertFalse(ehip.hostIp("").isPresent()); + } - @Test - public void testInvalid() { - EnvironmentHostIp ehip = make(); - assertFalse(ehip.hostIp("notexist123456789.radixdlt.com").isPresent()); - } + @Test + public void testInvalid() { + EnvironmentHostIp ehip = make(); + assertFalse(ehip.hostIp("notexist123456789.radixdlt.com").isPresent()); + } - @Test - public void testValid() { - EnvironmentHostIp ehip = make(); - Optional host = ehip.hostIp("127.0.0.1"); - assertTrue(host.isPresent()); - assertEquals("127.0.0.1", host.get()); - } + @Test + public void testValid() { + EnvironmentHostIp ehip = make(); + Optional host = ehip.hostIp("127.0.0.1"); + assertTrue(host.isPresent()); + assertEquals("127.0.0.1", host.get()); + } - private static EnvironmentHostIp make() { - return (EnvironmentHostIp) EnvironmentHostIp.create(); - } + private static EnvironmentHostIp make() { + return (EnvironmentHostIp) EnvironmentHostIp.create(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NetworkQueryHostIpTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NetworkQueryHostIpTest.java index c738793c45..3506edc101 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NetworkQueryHostIpTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NetworkQueryHostIpTest.java @@ -64,6 +64,10 @@ package com.radixdlt.network.hostip; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.radixdlt.properties.RuntimeProperties; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.HttpURLConnection; @@ -72,100 +76,92 @@ import java.util.Collections; import java.util.List; import java.util.Optional; - import org.junit.Test; -import com.radixdlt.properties.RuntimeProperties; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - public class NetworkQueryHostIpTest { - @Test - public void testPropertyNull() { - RuntimeProperties properties = mock(RuntimeProperties.class); - when(properties.get(eq(NetworkQueryHostIp.QUERY_URLS_PROPERTY), any())).thenReturn(null); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(properties); - assertEquals(NetworkQueryHostIp.DEFAULT_QUERY_URLS.size(), nqhip.count()); - } + @Test + public void testPropertyNull() { + RuntimeProperties properties = mock(RuntimeProperties.class); + when(properties.get(eq(NetworkQueryHostIp.QUERY_URLS_PROPERTY), any())).thenReturn(null); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(properties); + assertEquals(NetworkQueryHostIp.DEFAULT_QUERY_URLS.size(), nqhip.count()); + } - @Test - public void testPropertyEmpty() { - RuntimeProperties properties = mock(RuntimeProperties.class); - when(properties.get(eq(NetworkQueryHostIp.QUERY_URLS_PROPERTY), any())).thenReturn(""); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(properties); - assertEquals(NetworkQueryHostIp.DEFAULT_QUERY_URLS.size(), nqhip.count()); - } + @Test + public void testPropertyEmpty() { + RuntimeProperties properties = mock(RuntimeProperties.class); + when(properties.get(eq(NetworkQueryHostIp.QUERY_URLS_PROPERTY), any())).thenReturn(""); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(properties); + assertEquals(NetworkQueryHostIp.DEFAULT_QUERY_URLS.size(), nqhip.count()); + } - @Test - public void testPropertyNotEmpty() { - RuntimeProperties properties = mock(RuntimeProperties.class); - when(properties.get(eq(NetworkQueryHostIp.QUERY_URLS_PROPERTY), any())) - .thenReturn("http://localhost,http://8.8.8.8"); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(properties); - assertEquals(2, nqhip.count()); - } + @Test + public void testPropertyNotEmpty() { + RuntimeProperties properties = mock(RuntimeProperties.class); + when(properties.get(eq(NetworkQueryHostIp.QUERY_URLS_PROPERTY), any())) + .thenReturn("http://localhost,http://8.8.8.8"); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(properties); + assertEquals(2, nqhip.count()); + } - @Test(expected = IllegalArgumentException.class) - public void testCollectionEmpty() { - NetworkQueryHostIp.create(Collections.emptyList()); - } + @Test(expected = IllegalArgumentException.class) + public void testCollectionEmpty() { + NetworkQueryHostIp.create(Collections.emptyList()); + } - @Test - public void testCollectionNotEmptyQueryFailed() throws IOException { - HttpURLConnection conn = mock(HttpURLConnection.class); - doReturn(404).when(conn).getResponseCode(); - URL url = mock(URL.class); - doReturn(conn).when(url).openConnection(); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(List.of(url)); - Optional host = nqhip.hostIp(); - assertFalse(host.isPresent()); - } + @Test + public void testCollectionNotEmptyQueryFailed() throws IOException { + HttpURLConnection conn = mock(HttpURLConnection.class); + doReturn(404).when(conn).getResponseCode(); + URL url = mock(URL.class); + doReturn(conn).when(url).openConnection(); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(List.of(url)); + Optional host = nqhip.hostIp(); + assertFalse(host.isPresent()); + } - @Test - public void testCollectionNotEmptyQueryFailed2() throws IOException { - URL url = mock(URL.class); - doThrow(new IOException("test exception")).when(url).openConnection(); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(List.of(url)); - Optional host = nqhip.hostIp(); - assertFalse(host.isPresent()); - } + @Test + public void testCollectionNotEmptyQueryFailed2() throws IOException { + URL url = mock(URL.class); + doThrow(new IOException("test exception")).when(url).openConnection(); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(List.of(url)); + Optional host = nqhip.hostIp(); + assertFalse(host.isPresent()); + } - @Test - public void testCollectionNotEmpty() throws IOException { - String data = "127.0.0.1"; - try (ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))) { - HttpURLConnection conn = mock(HttpURLConnection.class); - doReturn(input).when(conn).getInputStream(); - URL url = mock(URL.class); - doReturn(conn).when(url).openConnection(); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(List.of(url)); - Optional host = nqhip.hostIp(); - assertTrue(host.isPresent()); - assertEquals(data, host.get()); - } - } + @Test + public void testCollectionNotEmpty() throws IOException { + String data = "127.0.0.1"; + try (ByteArrayInputStream input = + new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))) { + HttpURLConnection conn = mock(HttpURLConnection.class); + doReturn(input).when(conn).getInputStream(); + URL url = mock(URL.class); + doReturn(conn).when(url).openConnection(); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(List.of(url)); + Optional host = nqhip.hostIp(); + assertTrue(host.isPresent()); + assertEquals(data, host.get()); + } + } - @Test - public void testCollectionAllDifferent() throws IOException { - List urls = List.of( - makeUrl("127.0.0.1"), - makeUrl("127.0.0.2"), - makeUrl("127.0.0.3"), - makeUrl("127.0.0.4") - ); - NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(urls); - Optional host = nqhip.hostIp(); - assertFalse(host.isPresent()); - } + @Test + public void testCollectionAllDifferent() throws IOException { + List urls = + List.of( + makeUrl("127.0.0.1"), makeUrl("127.0.0.2"), makeUrl("127.0.0.3"), makeUrl("127.0.0.4")); + NetworkQueryHostIp nqhip = (NetworkQueryHostIp) NetworkQueryHostIp.create(urls); + Optional host = nqhip.hostIp(); + assertFalse(host.isPresent()); + } - private static URL makeUrl(String data) throws IOException { - ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); - HttpURLConnection conn = mock(HttpURLConnection.class); - doReturn(input).when(conn).getInputStream(); - URL url = mock(URL.class); - doReturn(conn).when(url).openConnection(); - return url; - } + private static URL makeUrl(String data) throws IOException { + ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); + HttpURLConnection conn = mock(HttpURLConnection.class); + doReturn(input).when(conn).getInputStream(); + URL url = mock(URL.class); + doReturn(conn).when(url).openConnection(); + return url; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIpTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIpTest.java index b505cfa3f7..53023e6e9b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIpTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/NonroutableInterfaceHostIpTest.java @@ -64,6 +64,10 @@ package com.radixdlt.network.hostip; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.google.common.collect.Iterators; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; @@ -71,124 +75,119 @@ import java.util.List; import java.util.Optional; import java.util.Vector; - import org.junit.Test; -import com.google.common.collect.Iterators; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -//FIXME: test uses reflective access to java.net.NetworkInterface, might cause issues for Java 17 transition +// FIXME: test uses reflective access to java.net.NetworkInterface, might cause issues for Java 17 +// transition public class NonroutableInterfaceHostIpTest { - @Test - public void testEmpty() { - NonroutableInterfaceHostIp nrihip = make(); - assertFalse(nrihip.hostIp(Collections.emptyIterator()).isPresent()); - } + @Test + public void testEmpty() { + NonroutableInterfaceHostIp nrihip = make(); + assertFalse(nrihip.hostIp(Collections.emptyIterator()).isPresent()); + } - @Test - public void testLoopback() throws UnknownHostException { - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testLoopback() throws UnknownHostException { + NonroutableInterfaceHostIp nrihip = make(); - InetAddress loopback = InetAddress.getByName("127.0.0.1"); - Vector addresses = new Vector<>(List.of(loopback)); + InetAddress loopback = InetAddress.getByName("127.0.0.1"); + Vector addresses = new Vector<>(List.of(loopback)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - assertFalse(nrihip.hostIp(Iterators.forArray(ni)).isPresent()); - } + assertFalse(nrihip.hostIp(Iterators.forArray(ni)).isPresent()); + } - @Test - public void testLinkLocal() throws UnknownHostException { - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testLinkLocal() throws UnknownHostException { + NonroutableInterfaceHostIp nrihip = make(); - InetAddress linklocal = InetAddress.getByName("169.254.0.0"); - Vector addresses = new Vector<>(List.of(linklocal)); + InetAddress linklocal = InetAddress.getByName("169.254.0.0"); + Vector addresses = new Vector<>(List.of(linklocal)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - assertFalse(nrihip.hostIp(Iterators.forArray(ni)).isPresent()); - } + assertFalse(nrihip.hostIp(Iterators.forArray(ni)).isPresent()); + } - @Test - public void testMulticast() throws UnknownHostException { - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testMulticast() throws UnknownHostException { + NonroutableInterfaceHostIp nrihip = make(); - InetAddress linklocal = InetAddress.getByName("224.0.0.1"); - Vector addresses = new Vector<>(List.of(linklocal)); + InetAddress linklocal = InetAddress.getByName("224.0.0.1"); + Vector addresses = new Vector<>(List.of(linklocal)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - assertFalse(nrihip.hostIp(Iterators.forArray(ni)).isPresent()); - } + assertFalse(nrihip.hostIp(Iterators.forArray(ni)).isPresent()); + } - @Test - public void testNonRoutable() throws UnknownHostException { - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testNonRoutable() throws UnknownHostException { + NonroutableInterfaceHostIp nrihip = make(); - InetAddress nonroutable = InetAddress.getByName("192.168.0.1"); - Vector addresses = new Vector<>(List.of(nonroutable)); + InetAddress nonroutable = InetAddress.getByName("192.168.0.1"); + Vector addresses = new Vector<>(List.of(nonroutable)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = nrihip.hostIp(Iterators.forArray(ni)); - assertTrue(result.isPresent()); - assertEquals("192.168.0.1", result.get()); - } + Optional result = nrihip.hostIp(Iterators.forArray(ni)); + assertTrue(result.isPresent()); + assertEquals("192.168.0.1", result.get()); + } - @Test - public void testRoutable() throws UnknownHostException { - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testRoutable() throws UnknownHostException { + NonroutableInterfaceHostIp nrihip = make(); - InetAddress routable = InetAddress.getByName("8.8.8.8"); - Vector addresses = new Vector<>(List.of(routable)); + InetAddress routable = InetAddress.getByName("8.8.8.8"); + Vector addresses = new Vector<>(List.of(routable)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = nrihip.hostIp(Iterators.forArray(ni)); - assertTrue(result.isPresent()); - assertEquals("8.8.8.8", result.get()); - } + Optional result = nrihip.hostIp(Iterators.forArray(ni)); + assertTrue(result.isPresent()); + assertEquals("8.8.8.8", result.get()); + } - @Test - public void testTooManyAddresses() throws UnknownHostException { - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testTooManyAddresses() throws UnknownHostException { + NonroutableInterfaceHostIp nrihip = make(); - InetAddress addr1 = InetAddress.getByName("192.168.0.2"); - InetAddress addr2 = InetAddress.getByName("192.168.0.3"); - Vector addresses = new Vector<>(List.of(addr1, addr2)); + InetAddress addr1 = InetAddress.getByName("192.168.0.2"); + InetAddress addr2 = InetAddress.getByName("192.168.0.3"); + Vector addresses = new Vector<>(List.of(addr1, addr2)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = nrihip.hostIp(Iterators.forArray(ni)); - assertFalse(result.isPresent()); - } + Optional result = nrihip.hostIp(Iterators.forArray(ni)); + assertFalse(result.isPresent()); + } - @Test - public void testInvalidAddress() { - // Not sure how this can really happen without a O/S fail - NonroutableInterfaceHostIp nrihip = make(); + @Test + public void testInvalidAddress() { + // Not sure how this can really happen without a O/S fail + NonroutableInterfaceHostIp nrihip = make(); - InetAddress badAddr = mock(InetAddress.class); - when(badAddr.getHostAddress()).thenReturn("a:b"); - Vector addresses = new Vector<>(List.of(badAddr)); + InetAddress badAddr = mock(InetAddress.class); + when(badAddr.getHostAddress()).thenReturn("a:b"); + Vector addresses = new Vector<>(List.of(badAddr)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = nrihip.hostIp(Iterators.forArray(ni)); - assertFalse(result.isPresent()); - } + Optional result = nrihip.hostIp(Iterators.forArray(ni)); + assertFalse(result.isPresent()); + } - private static NonroutableInterfaceHostIp make() { - return (NonroutableInterfaceHostIp) NonroutableInterfaceHostIp.create(); - } + private static NonroutableInterfaceHostIp make() { + return (NonroutableInterfaceHostIp) NonroutableInterfaceHostIp.create(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RoutableInterfaceHostIpTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RoutableInterfaceHostIpTest.java index 46d8c922e8..60bac9b544 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RoutableInterfaceHostIpTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RoutableInterfaceHostIpTest.java @@ -64,6 +64,10 @@ package com.radixdlt.network.hostip; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.google.common.collect.Iterators; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; @@ -71,122 +75,116 @@ import java.util.List; import java.util.Optional; import java.util.Vector; - import org.junit.Test; -import com.google.common.collect.Iterators; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - public class RoutableInterfaceHostIpTest { - @Test - public void testEmpty() { - RoutableInterfaceHostIp rihip = make(); - assertFalse(rihip.hostIp(Collections.emptyIterator()).isPresent()); - } + @Test + public void testEmpty() { + RoutableInterfaceHostIp rihip = make(); + assertFalse(rihip.hostIp(Collections.emptyIterator()).isPresent()); + } - @Test - public void testLoopback() throws UnknownHostException { - RoutableInterfaceHostIp rihip = make(); + @Test + public void testLoopback() throws UnknownHostException { + RoutableInterfaceHostIp rihip = make(); - InetAddress loopback = InetAddress.getByName("127.0.0.1"); - Vector addresses = new Vector<>(List.of(loopback)); + InetAddress loopback = InetAddress.getByName("127.0.0.1"); + Vector addresses = new Vector<>(List.of(loopback)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - assertFalse(rihip.hostIp(Iterators.forArray(ni)).isPresent()); - } + assertFalse(rihip.hostIp(Iterators.forArray(ni)).isPresent()); + } - @Test - public void testLinkLocal() throws UnknownHostException { - RoutableInterfaceHostIp rihip = make(); + @Test + public void testLinkLocal() throws UnknownHostException { + RoutableInterfaceHostIp rihip = make(); - InetAddress linklocal = InetAddress.getByName("169.254.0.0"); - Vector addresses = new Vector<>(List.of(linklocal)); + InetAddress linklocal = InetAddress.getByName("169.254.0.0"); + Vector addresses = new Vector<>(List.of(linklocal)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - assertFalse(rihip.hostIp(Iterators.forArray(ni)).isPresent()); - } + assertFalse(rihip.hostIp(Iterators.forArray(ni)).isPresent()); + } - @Test - public void testMulticast() throws UnknownHostException { - RoutableInterfaceHostIp rihip = make(); + @Test + public void testMulticast() throws UnknownHostException { + RoutableInterfaceHostIp rihip = make(); - InetAddress linklocal = InetAddress.getByName("224.0.0.1"); - Vector addresses = new Vector<>(List.of(linklocal)); + InetAddress linklocal = InetAddress.getByName("224.0.0.1"); + Vector addresses = new Vector<>(List.of(linklocal)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - assertFalse(rihip.hostIp(Iterators.forArray(ni)).isPresent()); - } + assertFalse(rihip.hostIp(Iterators.forArray(ni)).isPresent()); + } - @Test - public void testNonRoutable() throws UnknownHostException { - RoutableInterfaceHostIp rihip = make(); + @Test + public void testNonRoutable() throws UnknownHostException { + RoutableInterfaceHostIp rihip = make(); - InetAddress nonroutable = InetAddress.getByName("192.168.0.1"); - Vector addresses = new Vector<>(List.of(nonroutable)); + InetAddress nonroutable = InetAddress.getByName("192.168.0.1"); + Vector addresses = new Vector<>(List.of(nonroutable)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = rihip.hostIp(Iterators.forArray(ni)); - assertFalse(result.isPresent()); - } + Optional result = rihip.hostIp(Iterators.forArray(ni)); + assertFalse(result.isPresent()); + } - @Test - public void testRoutable() throws UnknownHostException { - RoutableInterfaceHostIp rihip = make(); + @Test + public void testRoutable() throws UnknownHostException { + RoutableInterfaceHostIp rihip = make(); - InetAddress routable = InetAddress.getByName("8.8.8.8"); - Vector addresses = new Vector<>(List.of(routable)); + InetAddress routable = InetAddress.getByName("8.8.8.8"); + Vector addresses = new Vector<>(List.of(routable)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = rihip.hostIp(Iterators.forArray(ni)); - assertTrue(result.isPresent()); - assertEquals("8.8.8.8", result.get()); - } + Optional result = rihip.hostIp(Iterators.forArray(ni)); + assertTrue(result.isPresent()); + assertEquals("8.8.8.8", result.get()); + } - @Test - public void testTooManyAddresses() throws UnknownHostException { - RoutableInterfaceHostIp rihip = make(); + @Test + public void testTooManyAddresses() throws UnknownHostException { + RoutableInterfaceHostIp rihip = make(); - InetAddress addr1 = InetAddress.getByName("8.8.8.8"); - InetAddress addr2 = InetAddress.getByName("8.8.4.4"); - Vector addresses = new Vector<>(List.of(addr1, addr2)); + InetAddress addr1 = InetAddress.getByName("8.8.8.8"); + InetAddress addr2 = InetAddress.getByName("8.8.4.4"); + Vector addresses = new Vector<>(List.of(addr1, addr2)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = rihip.hostIp(Iterators.forArray(ni)); - assertFalse(result.isPresent()); - } + Optional result = rihip.hostIp(Iterators.forArray(ni)); + assertFalse(result.isPresent()); + } - @Test - public void testInvalidAddress() { - // Not sure how this can really happen without a O/S fail - RoutableInterfaceHostIp rihip = make(); + @Test + public void testInvalidAddress() { + // Not sure how this can really happen without a O/S fail + RoutableInterfaceHostIp rihip = make(); - InetAddress badAddr = mock(InetAddress.class); - when(badAddr.getHostAddress()).thenReturn("a:b"); - Vector addresses = new Vector<>(List.of(badAddr)); + InetAddress badAddr = mock(InetAddress.class); + when(badAddr.getHostAddress()).thenReturn("a:b"); + Vector addresses = new Vector<>(List.of(badAddr)); - NetworkInterface ni = mock(NetworkInterface.class); - when(ni.getInetAddresses()).thenReturn(addresses.elements()); + NetworkInterface ni = mock(NetworkInterface.class); + when(ni.getInetAddresses()).thenReturn(addresses.elements()); - Optional result = rihip.hostIp(Iterators.forArray(ni)); - assertFalse(result.isPresent()); - } + Optional result = rihip.hostIp(Iterators.forArray(ni)); + assertFalse(result.isPresent()); + } - private static RoutableInterfaceHostIp make() { - return (RoutableInterfaceHostIp) RoutableInterfaceHostIp.create(); - } + private static RoutableInterfaceHostIp make() { + return (RoutableInterfaceHostIp) RoutableInterfaceHostIp.create(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RuntimePropertiesHostIpTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RuntimePropertiesHostIpTest.java index 2870f520ad..9c1ef5cdac 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RuntimePropertiesHostIpTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/hostip/RuntimePropertiesHostIpTest.java @@ -64,42 +64,40 @@ package com.radixdlt.network.hostip; -import java.util.Optional; - -import org.junit.Test; - -import com.radixdlt.properties.RuntimeProperties; - import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import com.radixdlt.properties.RuntimeProperties; +import java.util.Optional; +import org.junit.Test; + public class RuntimePropertiesHostIpTest { - @Test - public void testEmpty() { - RuntimePropertiesHostIp rphi1 = make(""); - assertFalse(rphi1.hostIp().isPresent()); - RuntimePropertiesHostIp rphi2 = make(null); - assertFalse(rphi2.hostIp().isPresent()); - } + @Test + public void testEmpty() { + RuntimePropertiesHostIp rphi1 = make(""); + assertFalse(rphi1.hostIp().isPresent()); + RuntimePropertiesHostIp rphi2 = make(null); + assertFalse(rphi2.hostIp().isPresent()); + } - @Test - public void testInvalid() { - RuntimePropertiesHostIp rphi = make("a:b"); - assertFalse(rphi.hostIp().isPresent()); - } + @Test + public void testInvalid() { + RuntimePropertiesHostIp rphi = make("a:b"); + assertFalse(rphi.hostIp().isPresent()); + } - @Test - public void testValid() { - RuntimePropertiesHostIp rphi = make("192.168.0.1"); - Optional host = rphi.hostIp(); - assertTrue(host.isPresent()); - assertEquals("192.168.0.1", host.get()); - } + @Test + public void testValid() { + RuntimePropertiesHostIp rphi = make("192.168.0.1"); + Optional host = rphi.hostIp(); + assertTrue(host.isPresent()); + assertEquals("192.168.0.1", host.get()); + } - private static RuntimePropertiesHostIp make(String value) { - RuntimeProperties properties = mock(RuntimeProperties.class); - when(properties.get(eq(RuntimePropertiesHostIp.HOST_IP_PROPERTY), any())).thenReturn(value); - return (RuntimePropertiesHostIp) RuntimePropertiesHostIp.create(properties); - } + private static RuntimePropertiesHostIp make(String value) { + RuntimeProperties properties = mock(RuntimeProperties.class); + when(properties.get(eq(RuntimePropertiesHostIp.HOST_IP_PROPERTY), any())).thenReturn(value); + return (RuntimePropertiesHostIp) RuntimePropertiesHostIp.create(properties); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/InboundMessageTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/InboundMessageTest.java index abbbabb817..0f214db4f2 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/InboundMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/InboundMessageTest.java @@ -64,33 +64,31 @@ package com.radixdlt.network.messaging; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.network.p2p.NodeId; import org.junit.Before; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class InboundMessageTest { - private final NodeId nodeId = NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); - private final byte[] message = new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - }; - private InboundMessage inboundMessage; + private final NodeId nodeId = NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); + private final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + private InboundMessage inboundMessage; - @Before - public void setUp() { - this.inboundMessage = new InboundMessage(0L, nodeId, this.message); - } + @Before + public void setUp() { + this.inboundMessage = new InboundMessage(0L, nodeId, this.message); + } - @Test - public void testSource() { - assertThat(inboundMessage.source()).isEqualTo(nodeId); - } + @Test + public void testSource() { + assertThat(inboundMessage.source()).isEqualTo(nodeId); + } - @Test - public void testMessage() { - assertThat(inboundMessage.message()).isEqualTo(message); - } + @Test + public void testMessage() { + assertThat(inboundMessage.message()).isEqualTo(message); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralConfigurationTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralConfigurationTest.java index 3998f2b33f..80a3a5cc29 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralConfigurationTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralConfigurationTest.java @@ -64,10 +64,6 @@ package com.radixdlt.network.messaging; -import org.junit.Test; - -import com.radixdlt.properties.RuntimeProperties; - import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -75,20 +71,24 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.radixdlt.properties.RuntimeProperties; +import org.junit.Test; + public class MessageCentralConfigurationTest { - @Test - public void fromRuntimeProperties() { - RuntimeProperties properties = mock(RuntimeProperties.class); + @Test + public void fromRuntimeProperties() { + RuntimeProperties properties = mock(RuntimeProperties.class); - when(properties.get(eq("messaging.inbound.queue_max"), anyInt())).thenReturn(100); - when(properties.get(eq("messaging.outbound.queue_max"), anyInt())).thenReturn(102); - when(properties.get(eq("messaging.time_to_live"), anyLong())).thenReturn(104L); + when(properties.get(eq("messaging.inbound.queue_max"), anyInt())).thenReturn(100); + when(properties.get(eq("messaging.outbound.queue_max"), anyInt())).thenReturn(102); + when(properties.get(eq("messaging.time_to_live"), anyLong())).thenReturn(104L); - MessageCentralConfiguration config = MessageCentralConfiguration.fromRuntimeProperties(properties); + MessageCentralConfiguration config = + MessageCentralConfiguration.fromRuntimeProperties(properties); - assertEquals(100, config.messagingInboundQueueMax(-1)); - assertEquals(102, config.messagingOutboundQueueMax(-1)); - assertEquals(104, config.messagingTimeToLive(-1)); - } -} \ No newline at end of file + assertEquals(100, config.messagingInboundQueueMax(-1)); + assertEquals(102, config.messagingOutboundQueueMax(-1)); + assertEquals(104, config.messagingTimeToLive(-1)); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralImplTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralImplTest.java index 138acfd0ed..0a4aef4efb 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralImplTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralImplTest.java @@ -1,5 +1,73 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.network.messaging; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.inject.Provider; import com.radixdlt.counters.SystemCounters; import com.radixdlt.middleware2.network.ConsensusEventMessage; @@ -11,86 +79,78 @@ import com.radixdlt.utils.TimeSupplier; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.observers.TestObserver; +import java.util.Comparator; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.radix.network.messaging.Message; -import java.util.Comparator; - -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - @RunWith(MockitoJUnitRunner.class) public class MessageCentralImplTest { - @Mock - private MessageCentralConfiguration messageCentralConfig; + @Mock private MessageCentralConfiguration messageCentralConfig; - @Mock - private Serialization serialization; + @Mock private Serialization serialization; - @Mock - private PeerManager peerManager; + @Mock private PeerManager peerManager; - @Mock - private InboundMessage inboundMessage; + @Mock private InboundMessage inboundMessage; - @Mock - private TimeSupplier timeSupplier; + @Mock private TimeSupplier timeSupplier; - @Mock - private EventQueueFactory outboundEventQueueFactory; + @Mock private EventQueueFactory outboundEventQueueFactory; - @Mock - private SystemCounters systemCounters; + @Mock private SystemCounters systemCounters; - @Mock - private Provider peerControl; + @Mock private Provider peerControl; - @Test - public void when_messagesOf_is_called__then_underlying_pipeline_should_run_on_rxjava_computation_pool() throws Exception { - // given - when(messageCentralConfig.messagingOutboundQueueMax(anyInt())).thenReturn(1); + @Test + public void + when_messagesOf_is_called__then_underlying_pipeline_should_run_on_rxjava_computation_pool() + throws Exception { + // given + when(messageCentralConfig.messagingOutboundQueueMax(anyInt())).thenReturn(1); - when(serialization.fromDson(any(byte[].class), eq(Message.class))) - .thenReturn(mock(ConsensusEventMessage.class)); + when(serialization.fromDson(any(byte[].class), eq(Message.class))) + .thenReturn(mock(ConsensusEventMessage.class)); - when(inboundMessage.message()).thenReturn(Compress.compress("".getBytes())); - when(inboundMessage.source()).thenReturn(mock(NodeId.class)); + when(inboundMessage.message()).thenReturn(Compress.compress("".getBytes())); + when(inboundMessage.source()).thenReturn(mock(NodeId.class)); - Observable inboundMessages = Observable.create(emitter -> { - emitter.onNext(inboundMessage); - emitter.onComplete(); - }); - when(peerManager.messages()).thenReturn(inboundMessages); + Observable inboundMessages = + Observable.create( + emitter -> { + emitter.onNext(inboundMessage); + emitter.onComplete(); + }); + when(peerManager.messages()).thenReturn(inboundMessages); - when(outboundEventQueueFactory.createEventQueue(anyInt(), any(Comparator.class))) - .thenReturn(new SimplePriorityBlockingQueue<>(1, OutboundMessageEvent.comparator())); + when(outboundEventQueueFactory.createEventQueue(anyInt(), any(Comparator.class))) + .thenReturn(new SimplePriorityBlockingQueue<>(1, OutboundMessageEvent.comparator())); - MessageCentralImpl messageCentral = new MessageCentralImpl( + MessageCentralImpl messageCentral = + new MessageCentralImpl( messageCentralConfig, serialization, peerManager, timeSupplier, outboundEventQueueFactory, systemCounters, - peerControl - ); + peerControl); - TestObserver observer = TestObserver.create(); + TestObserver observer = TestObserver.create(); - // when - messageCentral.messagesOf(ConsensusEventMessage.class) - .map(e -> Thread.currentThread().getName()) - .subscribe(observer); + // when + messageCentral + .messagesOf(ConsensusEventMessage.class) + .map(e -> Thread.currentThread().getName()) + .subscribe(observer); - messageCentral.close(); - observer.await(); + messageCentral.close(); + observer.await(); - //then - observer.assertValue(v -> v.startsWith("RxComputationThreadPool")); - } + // then + observer.assertValue(v -> v.startsWith("RxComputationThreadPool")); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralMockProvider.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralMockProvider.java index aa6a2c8f0a..e0b2258e8c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralMockProvider.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageCentralMockProvider.java @@ -64,33 +64,38 @@ package com.radixdlt.network.messaging; -import io.reactivex.rxjava3.subjects.PublishSubject; -import org.radix.network.messaging.Message; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -public class MessageCentralMockProvider { +import io.reactivex.rxjava3.subjects.PublishSubject; +import org.radix.network.messaging.Message; - private MessageCentralMockProvider() { - } +public class MessageCentralMockProvider { - public static MessageCentral get() { - final PublishSubject> messageProcessor = PublishSubject.create(); - final MessageCentral messageCentral = mock(MessageCentral.class); + private MessageCentralMockProvider() {} - doAnswer(invocation -> { - messageProcessor.onNext(new MessageFromPeer(invocation.getArgument(0), invocation.getArgument(1))); - return null; - }).when(messageCentral).send(any(), any()); + public static MessageCentral get() { + final PublishSubject> messageProcessor = PublishSubject.create(); + final MessageCentral messageCentral = mock(MessageCentral.class); - doAnswer(invocation -> - messageProcessor - .filter(p -> ((Class) invocation.getArgument(0)).isInstance(p.getMessage())) - ).when(messageCentral).messagesOf(any()); + doAnswer( + invocation -> { + messageProcessor.onNext( + new MessageFromPeer( + invocation.getArgument(0), invocation.getArgument(1))); + return null; + }) + .when(messageCentral) + .send(any(), any()); - return messageCentral; - } + doAnswer( + invocation -> + messageProcessor.filter( + p -> ((Class) invocation.getArgument(0)).isInstance(p.getMessage()))) + .when(messageCentral) + .messagesOf(any()); + return messageCentral; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageFromPeerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageFromPeerTest.java index ca43edb70a..31a303f6f9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageFromPeerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessageFromPeerTest.java @@ -69,10 +69,8 @@ public class MessageFromPeerTest { - @Test - public void test_equals() { - EqualsVerifier.forClass(MessageFromPeer.class) - .verify(); - } + @Test + public void test_equals() { + EqualsVerifier.forClass(MessageFromPeer.class).verify(); + } } - diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessagePreprocessorTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessagePreprocessorTest.java index b817fe24c3..fcf2e663f6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessagePreprocessorTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/MessagePreprocessorTest.java @@ -1,11 +1,79 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.network.messaging; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -import org.radix.network.messaging.Message; -import org.radix.time.Time; +import static com.radixdlt.utils.SerializerTestDataGenerator.randomProposal; +import static com.radixdlt.utils.SerializerTestDataGenerator.randomVote; +import static com.radixdlt.utils.functional.Tuple.tuple; +import static java.security.AccessController.doPrivileged; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import com.google.common.collect.Streams; import com.google.common.hash.HashCode; @@ -31,136 +99,143 @@ import com.radixdlt.serialization.Serialization; import com.radixdlt.utils.Compress; import com.radixdlt.utils.functional.Tuple.Tuple2; - import java.io.IOException; import java.security.PrivilegedAction; import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.stream.Stream; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import static com.radixdlt.utils.SerializerTestDataGenerator.randomProposal; -import static com.radixdlt.utils.SerializerTestDataGenerator.randomVote; -import static com.radixdlt.utils.functional.Tuple.tuple; - -import static java.security.AccessController.doPrivileged; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.radix.network.messaging.Message; +import org.radix.time.Time; @RunWith(Parameterized.class) public class MessagePreprocessorTest { - //Classes which have no zero-parameter constructors are excluded: - // GetPeersMessage PeerPingMessage PeerPongMessage StatusRequestMessage - - //Classes which are valid regardless from constructor parameters are excluded - // PeersResponseMessage - - @SuppressWarnings("unchecked") - private static final List> TEST_VECTORS = List.of( - tuple(new GetVerticesErrorResponseMessage(mock(HighQC.class), mock(GetVerticesRequestMessage.class)), "highQC"), - tuple(new GetVerticesErrorResponseMessage(mock(HighQC.class), mock(GetVerticesRequestMessage.class)), "request"), - tuple(new GetVerticesRequestMessage(mock(HashCode.class), 1), "vertexId"), - tuple(new GetVerticesResponseMessage(mock(List.class)), "vertices"), - tuple(new LedgerStatusUpdateMessage(mock(LedgerProof.class)), "header"), - tuple(new MempoolAddMessage(mock(List.class)), "txns"), - tuple(new StatusResponseMessage(mock(LedgerProof.class)), "header"), - tuple(new SyncRequestMessage(mock(DtoLedgerProof.class)), "currentHeader"), - tuple(new SyncResponseMessage(mock(DtoTxnsAndProof.class)), "commands") - ); - private static final Serialization SERIALIZATION = DefaultSerialization.getInstance(); - - private final SystemCountersImpl counters = new SystemCountersImpl(); - private final MessageCentralConfiguration config = mock(MessageCentralConfiguration.class); - private final PeerControl peerControl = mock(PeerControl.class); - private final MessagePreprocessor messagePreprocessor = new MessagePreprocessor( - counters, - config, - System::currentTimeMillis, - SERIALIZATION, - () -> peerControl - ); - - private final Class clazz; - private final InboundMessage inboundMessage; - - public MessagePreprocessorTest(Class clazz, InboundMessage inboundMessage) { - this.clazz = clazz; - this.inboundMessage = inboundMessage; - } - - @Parameters - public static Collection testParameters() { - var consensusMessages = Stream.of( - prepareTestMessage(new ConsensusEventMessage(randomProposal()), "proposal"), - prepareTestMessage(new ConsensusEventMessage(randomProposal()), "vote", randomVote()), - prepareTestMessage(new ConsensusEventMessage(randomVote()), "vote"), - prepareTestMessage(new ConsensusEventMessage(randomVote()), "proposal", randomProposal()) - ); - - return Streams.concat( - TEST_VECTORS.stream() - .map(tuple -> tuple.map(MessagePreprocessorTest::prepareTestMessage)), - consensusMessages - ).toList(); - } - - private static Object[] prepareTestMessage(Message message, String field) { - return prepareTestMessage(message, field, null); - } - - private static Object[] prepareTestMessage(Message message, String field, Object value) { - var source = NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); - var inboundMessage = generateMessage(source, message, field, value); - - return new Object[]{message.getClass(), inboundMessage}; - } - - private static InboundMessage generateMessage(NodeId source, Message message, String field, Object value) { - try { - setField(message, field, value); - } catch (Exception e) { - fail("Unable to set field " + field - + " for message of type " + message.getClass() - + " because of " + e.getMessage()); - - throw new RuntimeException("unreachable"); // tame compiler - } - - return new InboundMessage(Time.currentTimestamp(), source, serialize(message)); - } - - private static byte[] serialize(Message message) { - try { - return Compress.compress(SERIALIZATION.toDson(message, DsonOutput.Output.WIRE)); - } catch (IOException e) { - fail("Unable to serialize message of type " + message.getClass() - + " because of " + e.getMessage()); - throw new RuntimeException("unreachable"); // tame compiler - } - } - - private static void setField(Object instance, String fieldName, Object toSet) throws Exception { - var field = instance.getClass().getDeclaredField(fieldName); - - doPrivileged((PrivilegedAction) () -> { - field.setAccessible(true); - return null; - }); - - field.set(instance, toSet); - } - - @Test - public void invalid_message_is_not_accepted_and_peer_is_banned() { - var result = messagePreprocessor.process(inboundMessage); - - assertFalse(result.isSuccess()); - - verify(peerControl).banPeer(eq(inboundMessage.source()), eq(Duration.ofMinutes(5)), anyString()); - } + // Classes which have no zero-parameter constructors are excluded: + // GetPeersMessage PeerPingMessage PeerPongMessage StatusRequestMessage + + // Classes which are valid regardless from constructor parameters are excluded + // PeersResponseMessage + + @SuppressWarnings("unchecked") + private static final List> TEST_VECTORS = + List.of( + tuple( + new GetVerticesErrorResponseMessage( + mock(HighQC.class), mock(GetVerticesRequestMessage.class)), + "highQC"), + tuple( + new GetVerticesErrorResponseMessage( + mock(HighQC.class), mock(GetVerticesRequestMessage.class)), + "request"), + tuple(new GetVerticesRequestMessage(mock(HashCode.class), 1), "vertexId"), + tuple(new GetVerticesResponseMessage(mock(List.class)), "vertices"), + tuple(new LedgerStatusUpdateMessage(mock(LedgerProof.class)), "header"), + tuple(new MempoolAddMessage(mock(List.class)), "txns"), + tuple(new StatusResponseMessage(mock(LedgerProof.class)), "header"), + tuple(new SyncRequestMessage(mock(DtoLedgerProof.class)), "currentHeader"), + tuple(new SyncResponseMessage(mock(DtoTxnsAndProof.class)), "commands")); + + private static final Serialization SERIALIZATION = DefaultSerialization.getInstance(); + + private final SystemCountersImpl counters = new SystemCountersImpl(); + private final MessageCentralConfiguration config = mock(MessageCentralConfiguration.class); + private final PeerControl peerControl = mock(PeerControl.class); + private final MessagePreprocessor messagePreprocessor = + new MessagePreprocessor( + counters, config, System::currentTimeMillis, SERIALIZATION, () -> peerControl); + + private final Class clazz; + private final InboundMessage inboundMessage; + + public MessagePreprocessorTest(Class clazz, InboundMessage inboundMessage) { + this.clazz = clazz; + this.inboundMessage = inboundMessage; + } + + @Parameters + public static Collection testParameters() { + var consensusMessages = + Stream.of( + prepareTestMessage(new ConsensusEventMessage(randomProposal()), "proposal"), + prepareTestMessage(new ConsensusEventMessage(randomProposal()), "vote", randomVote()), + prepareTestMessage(new ConsensusEventMessage(randomVote()), "vote"), + prepareTestMessage( + new ConsensusEventMessage(randomVote()), "proposal", randomProposal())); + + return Streams.concat( + TEST_VECTORS.stream() + .map(tuple -> tuple.map(MessagePreprocessorTest::prepareTestMessage)), + consensusMessages) + .toList(); + } + + private static Object[] prepareTestMessage(Message message, String field) { + return prepareTestMessage(message, field, null); + } + + private static Object[] prepareTestMessage(Message message, String field, Object value) { + var source = NodeId.fromPublicKey(ECKeyPair.generateNew().getPublicKey()); + var inboundMessage = generateMessage(source, message, field, value); + + return new Object[] {message.getClass(), inboundMessage}; + } + + private static InboundMessage generateMessage( + NodeId source, Message message, String field, Object value) { + try { + setField(message, field, value); + } catch (Exception e) { + fail( + "Unable to set field " + + field + + " for message of type " + + message.getClass() + + " because of " + + e.getMessage()); + + throw new RuntimeException("unreachable"); // tame compiler + } + + return new InboundMessage(Time.currentTimestamp(), source, serialize(message)); + } + + private static byte[] serialize(Message message) { + try { + return Compress.compress(SERIALIZATION.toDson(message, DsonOutput.Output.WIRE)); + } catch (IOException e) { + fail( + "Unable to serialize message of type " + + message.getClass() + + " because of " + + e.getMessage()); + throw new RuntimeException("unreachable"); // tame compiler + } + } + + private static void setField(Object instance, String fieldName, Object toSet) throws Exception { + var field = instance.getClass().getDeclaredField(fieldName); + + doPrivileged( + (PrivilegedAction) + () -> { + field.setAccessible(true); + return null; + }); + + field.set(instance, toSet); + } + + @Test + public void invalid_message_is_not_accepted_and_peer_is_banned() { + var result = messagePreprocessor.process(inboundMessage); + + assertFalse(result.isSuccess()); + + verify(peerControl) + .banPeer(eq(inboundMessage.source()), eq(Duration.ofMinutes(5)), anyString()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/OutboundMessageEventTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/OutboundMessageEventTest.java index d51fe55a75..d535b6bae0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/OutboundMessageEventTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/OutboundMessageEventTest.java @@ -64,105 +64,101 @@ package com.radixdlt.network.messaging; -import java.util.ArrayList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import com.google.common.collect.Lists; import com.radixdlt.network.p2p.NodeId; +import java.util.ArrayList; +import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; import org.radix.network.messages.PeerPingMessage; import org.radix.network.messages.PeerPongMessage; import org.radix.network.messaging.Message; -import com.google.common.collect.Lists; -import nl.jqno.equalsverifier.EqualsVerifier; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - public class OutboundMessageEventTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(OutboundMessageEvent.class) - .withRedefinedSuperclass() - .verify(); - } - - @Test - public void sensibleToString() { - NodeId peer = mock(NodeId.class); - Message message = mock(Message.class); - long nanoTimeDiff = 123456789L; - OutboundMessageEvent event = new OutboundMessageEvent(peer, message, nanoTimeDiff); - - String s = event.toString(); - assertThat(s) - .contains(OutboundMessageEvent.class.getSimpleName()) - .contains(peer.toString()) - .contains(message.toString()) - .contains(String.valueOf(nanoTimeDiff)) - .contains("priority=0"); - } - - @Test - public void peerPingToString() { - NodeId peer = mock(NodeId.class); - Message message = mock(PeerPingMessage.class); - long nanoTimeDiff = 123456789L; - OutboundMessageEvent event = new OutboundMessageEvent(peer, message, nanoTimeDiff); - - String s = event.toString(); - assertThat(s) - .contains(OutboundMessageEvent.class.getSimpleName()) - .contains(peer.toString()) - .contains(message.toString()) - .contains(String.valueOf(nanoTimeDiff)) - .contains("priority=" + Integer.MIN_VALUE); - } - - @Test - public void peerPongToString() { - NodeId peer = mock(NodeId.class); - Message message = mock(PeerPongMessage.class); - long nanoTimeDiff = 123456789L; - OutboundMessageEvent event = new OutboundMessageEvent(peer, message, nanoTimeDiff); - - String s = event.toString(); - assertThat(s) - .contains(OutboundMessageEvent.class.getSimpleName()) - .contains(peer.toString()) - .contains(message.toString()) - .contains(String.valueOf(nanoTimeDiff)) - .contains("priority=" + Integer.MIN_VALUE); - } - - @Test - public void comparatorCheck() { - ArrayList events = Lists.newArrayList(); - events.add(makeMessageEventFor(Message.class)); - events.add(makeMessageEventFor(Message.class)); - events.add(makeMessageEventFor(Message.class)); - events.add(makeMessageEventFor(PeerPingMessage.class)); - assertFalse(events.get(0).message() instanceof PeerPingMessage); - assertFalse(events.get(0).message() instanceof PeerPongMessage); - events.sort(OutboundMessageEvent.comparator()); - assertTrue(events.get(0).message() instanceof PeerPingMessage); - - events.clear(); - events.add(makeMessageEventFor(Message.class)); - events.add(makeMessageEventFor(Message.class)); - events.add(makeMessageEventFor(Message.class)); - events.add(makeMessageEventFor(PeerPongMessage.class)); - assertFalse(events.get(0).message() instanceof PeerPingMessage); - assertFalse(events.get(0).message() instanceof PeerPongMessage); - events.sort(OutboundMessageEvent.comparator()); - assertTrue(events.get(0).message() instanceof PeerPongMessage); - } - - private OutboundMessageEvent makeMessageEventFor(Class cls) { - NodeId peer = mock(NodeId.class); - Message message = mock(cls); - long nanoTimeDiff = 123456789L; - return new OutboundMessageEvent(peer, message, nanoTimeDiff); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(OutboundMessageEvent.class).withRedefinedSuperclass().verify(); + } + + @Test + public void sensibleToString() { + NodeId peer = mock(NodeId.class); + Message message = mock(Message.class); + long nanoTimeDiff = 123456789L; + OutboundMessageEvent event = new OutboundMessageEvent(peer, message, nanoTimeDiff); + + String s = event.toString(); + assertThat(s) + .contains(OutboundMessageEvent.class.getSimpleName()) + .contains(peer.toString()) + .contains(message.toString()) + .contains(String.valueOf(nanoTimeDiff)) + .contains("priority=0"); + } + + @Test + public void peerPingToString() { + NodeId peer = mock(NodeId.class); + Message message = mock(PeerPingMessage.class); + long nanoTimeDiff = 123456789L; + OutboundMessageEvent event = new OutboundMessageEvent(peer, message, nanoTimeDiff); + + String s = event.toString(); + assertThat(s) + .contains(OutboundMessageEvent.class.getSimpleName()) + .contains(peer.toString()) + .contains(message.toString()) + .contains(String.valueOf(nanoTimeDiff)) + .contains("priority=" + Integer.MIN_VALUE); + } + + @Test + public void peerPongToString() { + NodeId peer = mock(NodeId.class); + Message message = mock(PeerPongMessage.class); + long nanoTimeDiff = 123456789L; + OutboundMessageEvent event = new OutboundMessageEvent(peer, message, nanoTimeDiff); + + String s = event.toString(); + assertThat(s) + .contains(OutboundMessageEvent.class.getSimpleName()) + .contains(peer.toString()) + .contains(message.toString()) + .contains(String.valueOf(nanoTimeDiff)) + .contains("priority=" + Integer.MIN_VALUE); + } + + @Test + public void comparatorCheck() { + ArrayList events = Lists.newArrayList(); + events.add(makeMessageEventFor(Message.class)); + events.add(makeMessageEventFor(Message.class)); + events.add(makeMessageEventFor(Message.class)); + events.add(makeMessageEventFor(PeerPingMessage.class)); + assertFalse(events.get(0).message() instanceof PeerPingMessage); + assertFalse(events.get(0).message() instanceof PeerPongMessage); + events.sort(OutboundMessageEvent.comparator()); + assertTrue(events.get(0).message() instanceof PeerPingMessage); + + events.clear(); + events.add(makeMessageEventFor(Message.class)); + events.add(makeMessageEventFor(Message.class)); + events.add(makeMessageEventFor(Message.class)); + events.add(makeMessageEventFor(PeerPongMessage.class)); + assertFalse(events.get(0).message() instanceof PeerPingMessage); + assertFalse(events.get(0).message() instanceof PeerPongMessage); + events.sort(OutboundMessageEvent.comparator()); + assertTrue(events.get(0).message() instanceof PeerPongMessage); + } + + private OutboundMessageEvent makeMessageEventFor(Class cls) { + NodeId peer = mock(NodeId.class); + Message message = mock(cls); + long nanoTimeDiff = 123456789L; + return new OutboundMessageEvent(peer, message, nanoTimeDiff); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueueTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueueTest.java index 3919e6b6e2..cc641b92d8 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueueTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/messaging/SimplePriorityBlockingQueueTest.java @@ -64,52 +64,53 @@ package com.radixdlt.network.messaging; -import org.junit.Test; - import static org.junit.Assert.*; +import org.junit.Test; + public class SimplePriorityBlockingQueueTest { - static final class TestObject implements Comparable { - final int i; + static final class TestObject implements Comparable { + final int i; - TestObject(int i) { - this.i = i; - } + TestObject(int i) { + this.i = i; + } - @Override - public int compareTo(TestObject o) { - // All the same prio class - return 0; - } - } + @Override + public int compareTo(TestObject o) { + // All the same prio class + return 0; + } + } - @Test - public void testOrdering() throws InterruptedException { - // For this test, we need to ensure that we are not interrupted - Thread.interrupted(); + @Test + public void testOrdering() throws InterruptedException { + // For this test, we need to ensure that we are not interrupted + Thread.interrupted(); - SimplePriorityBlockingQueue test = new SimplePriorityBlockingQueue<>(100, TestObject::compareTo); + SimplePriorityBlockingQueue test = + new SimplePriorityBlockingQueue<>(100, TestObject::compareTo); - final int testObjects = 1000; - for (int i = 0; i < testObjects; ++i) { - assertEquals(i, test.size()); - assertTrue(test.offer(new TestObject(i))); - } + final int testObjects = 1000; + for (int i = 0; i < testObjects; ++i) { + assertEquals(i, test.size()); + assertTrue(test.offer(new TestObject(i))); + } - for (int i = 0; i < testObjects; ++i) { - assertEquals(i, test.take().i); - } - } + for (int i = 0; i < testObjects; ++i) { + assertEquals(i, test.take().i); + } + } - @Test - public void sensibleToString() { - SimplePriorityBlockingQueue test = new SimplePriorityBlockingQueue<>(100, Long::compare); + @Test + public void sensibleToString() { + SimplePriorityBlockingQueue test = new SimplePriorityBlockingQueue<>(100, Long::compare); - assertTrue(test.offer(4321L)); - assertTrue(test.offer(1234L)); + assertTrue(test.offer(4321L)); + assertTrue(test.offer(1234L)); - String s = test.toString(); - assertEquals("[1234, 4321]", s); - } + String s = test.toString(); + assertEquals("[1234, 4321]", s); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/FailedHandshakeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/FailedHandshakeTest.java index a50abcfbd7..1a113fb855 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/FailedHandshakeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/FailedHandshakeTest.java @@ -64,49 +64,50 @@ package com.radixdlt.network.p2p; +import static org.junit.Assert.assertTrue; + import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.identifiers.NodeAddressing; import com.radixdlt.network.p2p.test.DeterministicP2PNetworkTest; -import org.junit.After; -import org.junit.Test; - import java.net.URI; import java.util.Set; - -import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Test; public final class FailedHandshakeTest extends DeterministicP2PNetworkTest { - @After - public void cleanup() { - testNetworkRunner.cleanup(); - } + @After + public void cleanup() { + testNetworkRunner.cleanup(); + } - @Test - public void test_failed_handshake() throws Exception { - setupTestRunner(2, defaultProperties()); + @Test + public void test_failed_handshake() throws Exception { + setupTestRunner(2, defaultProperties()); - final var correctUri = uriOfNode(1); + final var correctUri = uriOfNode(1); - final var messedUpUri = RadixNodeUri.fromUri(new URI( - String.format( - "radix://%s@%s:%s", - NodeAddressing.of(correctUri.getNetworkNodeHrp(), ECKeyPair.generateNew().getPublicKey()), - correctUri.getHost(), - correctUri.getPort() - ) - )); + final var messedUpUri = + RadixNodeUri.fromUri( + new URI( + String.format( + "radix://%s@%s:%s", + NodeAddressing.of( + correctUri.getNetworkNodeHrp(), ECKeyPair.generateNew().getPublicKey()), + correctUri.getHost(), + correctUri.getPort()))); - testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(messedUpUri)); + testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(messedUpUri)); - final var channel1Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(messedUpUri.getNodeId()); + final var channel1Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(messedUpUri.getNodeId()); - processAll(); + processAll(); - assertTrue(channel1Future.isCompletedExceptionally()); + assertTrue(channel1Future.isCompletedExceptionally()); - final var entry = testNetworkRunner.addressBook(0).findById(messedUpUri.getNodeId()).orElseThrow(); - assertTrue(entry.getKnownAddresses().stream().findFirst().orElseThrow().blacklisted()); - } + final var entry = + testNetworkRunner.addressBook(0).findById(messedUpUri.getNodeId()).orElseThrow(); + assertTrue(entry.getKnownAddresses().stream().findFirst().orElseThrow().blacklisted()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/NoOpPeerControl.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/NoOpPeerControl.java index bcadf43115..3f4d60b2b1 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/NoOpPeerControl.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/NoOpPeerControl.java @@ -1,10 +1,74 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.network.p2p; import java.time.Duration; public final class NoOpPeerControl implements PeerControl { - @Override - public void banPeer(NodeId nodeId, Duration banDuration, String reason) { - // no-op - } + @Override + public void banPeer(NodeId nodeId, Duration banDuration, String reason) { + // no-op + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/PeerManagerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/PeerManagerTest.java index c76f8ed605..0f28e13c2c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/PeerManagerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/PeerManagerTest.java @@ -64,138 +64,149 @@ package com.radixdlt.network.p2p; -import com.radixdlt.network.p2p.test.DeterministicP2PNetworkTest; -import org.junit.After; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import com.radixdlt.network.p2p.test.DeterministicP2PNetworkTest; import java.time.Duration; import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Test; public final class PeerManagerTest extends DeterministicP2PNetworkTest { - @After - public void cleanup() { - testNetworkRunner.cleanup(); - } + @After + public void cleanup() { + testNetworkRunner.cleanup(); + } - @Test - public void when_findOrCreateChannel_then_should_create_if_not_exists() throws Exception { - setupTestRunner(3, defaultProperties()); + @Test + public void when_findOrCreateChannel_then_should_create_if_not_exists() throws Exception { + setupTestRunner(3, defaultProperties()); - testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(uriOfNode(1))); - final var channelFuture = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(1).getNodeId()); + testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(uriOfNode(1))); + final var channelFuture = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(1).getNodeId()); - processForCount(3); + processForCount(3); - assertEquals(uriOfNode(1), channelFuture.get().getUri().get()); + assertEquals(uriOfNode(1), channelFuture.get().getUri().get()); - assertEquals(1L, testNetworkRunner.peerManager(0).activeChannels().size()); - assertEquals(1L, testNetworkRunner.peerManager(1).activeChannels().size()); - } + assertEquals(1L, testNetworkRunner.peerManager(0).activeChannels().size()); + assertEquals(1L, testNetworkRunner.peerManager(1).activeChannels().size()); + } - @Test - public void should_disconnect_the_least_used_channels_when_over_limit() throws Exception { - final var props = defaultProperties(); - props.set("network.p2p.max_outbound_channels", 3); // 3 outbound channels allowed - setupTestRunner(5, props); + @Test + public void should_disconnect_the_least_used_channels_when_over_limit() throws Exception { + final var props = defaultProperties(); + props.set("network.p2p.max_outbound_channels", 3); // 3 outbound channels allowed + setupTestRunner(5, props); - testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(uriOfNode(1), uriOfNode(2), uriOfNode(3), uriOfNode(4))); - final var channel1Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(1).getNodeId()); + testNetworkRunner + .addressBook(0) + .addUncheckedPeers(Set.of(uriOfNode(1), uriOfNode(2), uriOfNode(3), uriOfNode(4))); + final var channel1Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(1).getNodeId()); - final var channel2Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(2).getNodeId()); + final var channel2Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(2).getNodeId()); - final var channel3Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(3).getNodeId()); + final var channel3Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(3).getNodeId()); - processAll(); + processAll(); - // two messages sent over node1 channel - channel1Future.get().send(new byte[] {0x01}); - channel1Future.get().send(new byte[] {0x02}); + // two messages sent over node1 channel + channel1Future.get().send(new byte[] {0x01}); + channel1Future.get().send(new byte[] {0x02}); - // one messages sent over node2 channel - channel2Future.get().send(new byte[] {0x03}); + // one messages sent over node2 channel + channel2Future.get().send(new byte[] {0x03}); - // three messages sent over node3 channel - channel3Future.get().send(new byte[] {0x01}); - channel3Future.get().send(new byte[] {0x01}); - channel3Future.get().send(new byte[] {0x01}); + // three messages sent over node3 channel + channel3Future.get().send(new byte[] {0x01}); + channel3Future.get().send(new byte[] {0x01}); + channel3Future.get().send(new byte[] {0x01}); - final var channel4Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(4).getNodeId()); + final var channel4Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(4).getNodeId()); - processAll(); + processAll(); - assertEquals(3L, testNetworkRunner.peerManager(0).activeChannels().size()); - assertEquals(1L, testNetworkRunner.peerManager(1).activeChannels().size()); - assertEquals(0L, testNetworkRunner.peerManager(2).activeChannels().size()); // node2 should be disconnected - assertEquals(1L, testNetworkRunner.peerManager(3).activeChannels().size()); - assertEquals(1L, testNetworkRunner.peerManager(4).activeChannels().size()); - } + assertEquals(3L, testNetworkRunner.peerManager(0).activeChannels().size()); + assertEquals(1L, testNetworkRunner.peerManager(1).activeChannels().size()); + assertEquals( + 0L, + testNetworkRunner.peerManager(2).activeChannels().size()); // node2 should be disconnected + assertEquals(1L, testNetworkRunner.peerManager(3).activeChannels().size()); + assertEquals(1L, testNetworkRunner.peerManager(4).activeChannels().size()); + } - @Test - public void should_not_connect_to_banned_peers() throws Exception { - final var props = defaultProperties(); - setupTestRunner(5, props); + @Test + public void should_not_connect_to_banned_peers() throws Exception { + final var props = defaultProperties(); + setupTestRunner(5, props); - testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(uriOfNode(1), uriOfNode(2), uriOfNode(3), uriOfNode(4))); - testNetworkRunner.addressBook(1).addUncheckedPeers(Set.of(uriOfNode(0))); + testNetworkRunner + .addressBook(0) + .addUncheckedPeers(Set.of(uriOfNode(1), uriOfNode(2), uriOfNode(3), uriOfNode(4))); + testNetworkRunner.addressBook(1).addUncheckedPeers(Set.of(uriOfNode(0))); - // ban node1 and node3 on node0 - testNetworkRunner.getInstance(0, PeerControl.class).banPeer(uriOfNode(1).getNodeId(), Duration.ofHours(1), ""); - testNetworkRunner.getInstance(0, PeerControl.class).banPeer(uriOfNode(3).getNodeId(), Duration.ofHours(1), ""); + // ban node1 and node3 on node0 + testNetworkRunner + .getInstance(0, PeerControl.class) + .banPeer(uriOfNode(1).getNodeId(), Duration.ofHours(1), ""); + testNetworkRunner + .getInstance(0, PeerControl.class) + .banPeer(uriOfNode(3).getNodeId(), Duration.ofHours(1), ""); - // try outbound connection (to node3) - final var channel1Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(3).getNodeId()); + // try outbound connection (to node3) + final var channel1Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(3).getNodeId()); - processAll(); + processAll(); - assertTrue(channel1Future.isCompletedExceptionally()); - assertEquals(0L, testNetworkRunner.peerManager(0).activeChannels().size()); - assertEquals(0L, testNetworkRunner.peerManager(3).activeChannels().size()); + assertTrue(channel1Future.isCompletedExceptionally()); + assertEquals(0L, testNetworkRunner.peerManager(0).activeChannels().size()); + assertEquals(0L, testNetworkRunner.peerManager(3).activeChannels().size()); - // try inbound connection (from node1) + // try inbound connection (from node1) - final var channel2Future = testNetworkRunner.peerManager(1) - .findOrCreateChannel(uriOfNode(0).getNodeId()); + final var channel2Future = + testNetworkRunner.peerManager(1).findOrCreateChannel(uriOfNode(0).getNodeId()); - processAll(); + processAll(); - assertEquals(0L, testNetworkRunner.peerManager(0).activeChannels().size()); - assertEquals(0L, testNetworkRunner.peerManager(1).activeChannels().size()); - } + assertEquals(0L, testNetworkRunner.peerManager(0).activeChannels().size()); + assertEquals(0L, testNetworkRunner.peerManager(1).activeChannels().size()); + } - @Test - public void should_disconnect_just_banned_peer() throws Exception { - final var props = defaultProperties(); - setupTestRunner(2, props); + @Test + public void should_disconnect_just_banned_peer() throws Exception { + final var props = defaultProperties(); + setupTestRunner(2, props); - testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(uriOfNode(1))); + testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of(uriOfNode(1))); - final var channel1Future = testNetworkRunner.peerManager(0) - .findOrCreateChannel(uriOfNode(1).getNodeId()); + final var channel1Future = + testNetworkRunner.peerManager(0).findOrCreateChannel(uriOfNode(1).getNodeId()); - processAll(); + processAll(); - // assert the connections is successful - assertTrue(channel1Future.isDone()); - assertEquals(1L, testNetworkRunner.peerManager(0).activeChannels().size()); - assertEquals(1L, testNetworkRunner.peerManager(1).activeChannels().size()); + // assert the connections is successful + assertTrue(channel1Future.isDone()); + assertEquals(1L, testNetworkRunner.peerManager(0).activeChannels().size()); + assertEquals(1L, testNetworkRunner.peerManager(1).activeChannels().size()); - // ban node0 on node1 - testNetworkRunner.getInstance(1, PeerControl.class).banPeer(uriOfNode(0).getNodeId(), Duration.ofHours(1), ""); + // ban node0 on node1 + testNetworkRunner + .getInstance(1, PeerControl.class) + .banPeer(uriOfNode(0).getNodeId(), Duration.ofHours(1), ""); - processAll(); + processAll(); - // assert connection closed - assertEquals(0L, testNetworkRunner.peerManager(0).activeChannels().size()); - assertEquals(0L, testNetworkRunner.peerManager(1).activeChannels().size()); - } + // assert connection closed + assertEquals(0L, testNetworkRunner.peerManager(0).activeChannels().size()); + assertEquals(0L, testNetworkRunner.peerManager(1).activeChannels().size()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/RadixNodeUriTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/RadixNodeUriTest.java index 28e19e68fd..a9116a330c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/RadixNodeUriTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/RadixNodeUriTest.java @@ -68,13 +68,13 @@ import org.junit.Test; public final class RadixNodeUriTest { - @Test - public void equals_test() { - EqualsVerifier.forClass(RadixNodeUri.class).verify(); - } + @Test + public void equals_test() { + EqualsVerifier.forClass(RadixNodeUri.class).verify(); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException() throws Exception { - RadixNodeUri.deserialize(null); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException() throws Exception { + RadixNodeUri.deserialize(null); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeEmptyTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeEmptyTest.java index 300ba2a6c1..6372d8308f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeEmptyTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeEmptyTest.java @@ -67,23 +67,24 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.network.p2p.NodeId; -import org.radix.serialization.SerializeMessageObject; - import java.time.Instant; import java.util.Optional; import java.util.Random; +import org.radix.serialization.SerializeMessageObject; public class AddressBookEntrySerializeEmptyTest extends SerializeMessageObject { - public AddressBookEntrySerializeEmptyTest() { - super(AddressBookEntry.class, AddressBookEntrySerializeEmptyTest::get); - } + public AddressBookEntrySerializeEmptyTest() { + super(AddressBookEntry.class, AddressBookEntrySerializeEmptyTest::get); + } - private static AddressBookEntry get() { - final var rnd = new Random(); - final var keyPair = ECKeyPair.generateNew(); - final var bannedUntil = rnd.nextBoolean() - ? Optional.of(Instant.ofEpochMilli(Math.abs(rnd.nextLong()))) - : Optional.empty(); - return new AddressBookEntry(NodeId.fromPublicKey(keyPair.getPublicKey()), bannedUntil, ImmutableSet.of()); - } + private static AddressBookEntry get() { + final var rnd = new Random(); + final var keyPair = ECKeyPair.generateNew(); + final var bannedUntil = + rnd.nextBoolean() + ? Optional.of(Instant.ofEpochMilli(Math.abs(rnd.nextLong()))) + : Optional.empty(); + return new AddressBookEntry( + NodeId.fromPublicKey(keyPair.getPublicKey()), bannedUntil, ImmutableSet.of()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeTest.java index 77a33d31a3..7b964493ec 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntrySerializeTest.java @@ -70,36 +70,36 @@ import com.radixdlt.network.p2p.RadixNodeUri; import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry; import com.radixdlt.network.p2p.addressbook.AddressBookEntry.PeerAddressEntry.LatestConnectionStatus; -import org.radix.serialization.SerializeMessageObject; - import java.time.Instant; import java.util.Optional; import java.util.Random; +import org.radix.serialization.SerializeMessageObject; public class AddressBookEntrySerializeTest extends SerializeMessageObject { - public AddressBookEntrySerializeTest() { - super(AddressBookEntry.class, AddressBookEntrySerializeTest::get); - } + public AddressBookEntrySerializeTest() { + super(AddressBookEntry.class, AddressBookEntrySerializeTest::get); + } - private static AddressBookEntry get() { - final var rnd = new Random(); - final var keyPair = ECKeyPair.generateNew(); - final var bannedUntil = rnd.nextBoolean() - ? Optional.of(Instant.ofEpochMilli(Math.abs(rnd.nextLong()))) - : Optional.empty(); - final var uri = RadixNodeUri.fromPubKeyAndAddress( - 1, - keyPair.getPublicKey(), - "127.0.0.1", - 30000 - ); - final var blacklistedUntil = rnd.nextBoolean() - ? Optional.of(Instant.ofEpochMilli(Math.abs(rnd.nextLong()))) - : Optional.empty(); - final var latestConnectionStatus = rnd.nextBoolean() - ? Optional.of(rnd.nextBoolean() ? LatestConnectionStatus.FAILURE : LatestConnectionStatus.SUCCESS) - : Optional.empty(); - final var addressEntry = new PeerAddressEntry(uri, latestConnectionStatus, blacklistedUntil); - return new AddressBookEntry(NodeId.fromPublicKey(keyPair.getPublicKey()), bannedUntil, ImmutableSet.of(addressEntry)); - } + private static AddressBookEntry get() { + final var rnd = new Random(); + final var keyPair = ECKeyPair.generateNew(); + final var bannedUntil = + rnd.nextBoolean() + ? Optional.of(Instant.ofEpochMilli(Math.abs(rnd.nextLong()))) + : Optional.empty(); + final var uri = + RadixNodeUri.fromPubKeyAndAddress(1, keyPair.getPublicKey(), "127.0.0.1", 30000); + final var blacklistedUntil = + rnd.nextBoolean() + ? Optional.of(Instant.ofEpochMilli(Math.abs(rnd.nextLong()))) + : Optional.empty(); + final var latestConnectionStatus = + rnd.nextBoolean() + ? Optional.of( + rnd.nextBoolean() ? LatestConnectionStatus.FAILURE : LatestConnectionStatus.SUCCESS) + : Optional.empty(); + final var addressEntry = new PeerAddressEntry(uri, latestConnectionStatus, blacklistedUntil); + return new AddressBookEntry( + NodeId.fromPublicKey(keyPair.getPublicKey()), bannedUntil, ImmutableSet.of(addressEntry)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntryTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntryTest.java index c84facd8ff..1b8bbddc2b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntryTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookEntryTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public class AddressBookEntryTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(AddressBookEntry.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(AddressBookEntry.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookTest.java index 4baac0865f..17059d6be6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/AddressBookTest.java @@ -64,98 +64,105 @@ package com.radixdlt.network.p2p.addressbook; +import static com.radixdlt.utils.TypedMocks.rmock; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableSet; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.RadixNodeUri; -import org.junit.Test; - import java.util.Set; - -import static com.radixdlt.utils.TypedMocks.rmock; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; public final class AddressBookTest { - @Test - public void address_book_should_filter_out_peers_with_different_network_hrp() { - final var self = - RadixNodeUri.fromPubKeyAndAddress(1, ECKeyPair.generateNew().getPublicKey(), "1.1.1.1", 30000); - final var invalidPeer = - RadixNodeUri.fromPubKeyAndAddress(2, ECKeyPair.generateNew().getPublicKey(), "2.2.2.2", 30000); - - final var persistence = new InMemoryAddressBookPersistence(); - persistence.saveEntry(AddressBookEntry.create(invalidPeer)); // insert directly into storage - - final var sut = new AddressBook(self, rmock(EventDispatcher.class), persistence); - assertTrue(sut.knownPeers().isEmpty()); // invalid peer should be filtered out at init - assertTrue(sut.findById(invalidPeer.getNodeId()).isEmpty()); - - sut.addUncheckedPeers(Set.of(invalidPeer)); // add after initial cleanup - assertTrue(sut.knownPeers().isEmpty()); // should also be filtered out - } - - @Test - public void address_book_should_sort_entries_by_latest_connection_status() { - final var self = RadixNodeUri.fromPubKeyAndAddress(1, ECKeyPair.generateNew().getPublicKey(), "127.0.0.10", 30303); - final var peerKey = ECKeyPair.generateNew().getPublicKey(); - final var peerId = NodeId.fromPublicKey(peerKey); - final var addr1 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.1", 30303); - final var addr2 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.2", 30303); - final var addr3 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.3", 30303); - final var addr4 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.4", 30303); - - final var sut = new AddressBook(self, rmock(EventDispatcher.class), new InMemoryAddressBookPersistence()); - - sut.addUncheckedPeers(ImmutableSet.of(addr1, addr2, addr3, addr4)); - - sut.addOrUpdatePeerWithSuccessfulConnection(addr1); - final var bestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); - assertEquals(addr1, bestAddr); - - sut.addOrUpdatePeerWithSuccessfulConnection(addr2); - final var bestAddr2 = sut.findBestKnownAddressById(peerId).orElseThrow(); - assertTrue(bestAddr2 == addr1 || bestAddr2 == addr2); - - sut.addOrUpdatePeerWithFailedConnection(addr1); - final var bestAddr3 = sut.findBestKnownAddressById(peerId).orElseThrow(); - assertEquals(addr2, bestAddr3); - - sut.addOrUpdatePeerWithFailedConnection(addr2); - final var bestAddr4 = sut.findBestKnownAddressById(peerId).orElseThrow(); - assertTrue(bestAddr4 == addr3 || bestAddr4 == addr4); - - sut.addOrUpdatePeerWithSuccessfulConnection(addr4); - final var bestAddr5 = sut.findBestKnownAddressById(peerId).orElseThrow(); - assertEquals(addr4, bestAddr5); - } - - @Test - public void address_book_should_cycle_failed_connection_uris() { - final var self = RadixNodeUri.fromPubKeyAndAddress(1, ECKeyPair.generateNew().getPublicKey(), "127.0.0.10", 30303); - final var peerKey = ECKeyPair.generateNew().getPublicKey(); - final var peerId = NodeId.fromPublicKey(peerKey); - final var addr1 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.1", 30303); - final var addr2 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.2", 30303); - final var addr3 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.3", 30303); - - final var sut = new AddressBook(self, rmock(EventDispatcher.class), new InMemoryAddressBookPersistence()); - - sut.addUncheckedPeers(ImmutableSet.of(addr1, addr2, addr3)); - - var prevPrevBestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); - sut.addOrUpdatePeerWithFailedConnection(prevPrevBestAddr); - var prevBestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); - for (int i = 0; i < 50; i++) { - sut.addOrUpdatePeerWithFailedConnection(prevBestAddr); - final var currBestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); - assertNotEquals(prevBestAddr, currBestAddr); - assertNotEquals(prevPrevBestAddr, prevBestAddr); - prevPrevBestAddr = prevBestAddr; - prevBestAddr = currBestAddr; - } - } + @Test + public void address_book_should_filter_out_peers_with_different_network_hrp() { + final var self = + RadixNodeUri.fromPubKeyAndAddress( + 1, ECKeyPair.generateNew().getPublicKey(), "1.1.1.1", 30000); + final var invalidPeer = + RadixNodeUri.fromPubKeyAndAddress( + 2, ECKeyPair.generateNew().getPublicKey(), "2.2.2.2", 30000); + + final var persistence = new InMemoryAddressBookPersistence(); + persistence.saveEntry(AddressBookEntry.create(invalidPeer)); // insert directly into storage + + final var sut = new AddressBook(self, rmock(EventDispatcher.class), persistence); + assertTrue(sut.knownPeers().isEmpty()); // invalid peer should be filtered out at init + assertTrue(sut.findById(invalidPeer.getNodeId()).isEmpty()); + + sut.addUncheckedPeers(Set.of(invalidPeer)); // add after initial cleanup + assertTrue(sut.knownPeers().isEmpty()); // should also be filtered out + } + + @Test + public void address_book_should_sort_entries_by_latest_connection_status() { + final var self = + RadixNodeUri.fromPubKeyAndAddress( + 1, ECKeyPair.generateNew().getPublicKey(), "127.0.0.10", 30303); + final var peerKey = ECKeyPair.generateNew().getPublicKey(); + final var peerId = NodeId.fromPublicKey(peerKey); + final var addr1 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.1", 30303); + final var addr2 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.2", 30303); + final var addr3 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.3", 30303); + final var addr4 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.4", 30303); + + final var sut = + new AddressBook(self, rmock(EventDispatcher.class), new InMemoryAddressBookPersistence()); + + sut.addUncheckedPeers(ImmutableSet.of(addr1, addr2, addr3, addr4)); + + sut.addOrUpdatePeerWithSuccessfulConnection(addr1); + final var bestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); + assertEquals(addr1, bestAddr); + + sut.addOrUpdatePeerWithSuccessfulConnection(addr2); + final var bestAddr2 = sut.findBestKnownAddressById(peerId).orElseThrow(); + assertTrue(bestAddr2 == addr1 || bestAddr2 == addr2); + + sut.addOrUpdatePeerWithFailedConnection(addr1); + final var bestAddr3 = sut.findBestKnownAddressById(peerId).orElseThrow(); + assertEquals(addr2, bestAddr3); + + sut.addOrUpdatePeerWithFailedConnection(addr2); + final var bestAddr4 = sut.findBestKnownAddressById(peerId).orElseThrow(); + assertTrue(bestAddr4 == addr3 || bestAddr4 == addr4); + + sut.addOrUpdatePeerWithSuccessfulConnection(addr4); + final var bestAddr5 = sut.findBestKnownAddressById(peerId).orElseThrow(); + assertEquals(addr4, bestAddr5); + } + + @Test + public void address_book_should_cycle_failed_connection_uris() { + final var self = + RadixNodeUri.fromPubKeyAndAddress( + 1, ECKeyPair.generateNew().getPublicKey(), "127.0.0.10", 30303); + final var peerKey = ECKeyPair.generateNew().getPublicKey(); + final var peerId = NodeId.fromPublicKey(peerKey); + final var addr1 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.1", 30303); + final var addr2 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.2", 30303); + final var addr3 = RadixNodeUri.fromPubKeyAndAddress(1, peerKey, "127.0.0.3", 30303); + + final var sut = + new AddressBook(self, rmock(EventDispatcher.class), new InMemoryAddressBookPersistence()); + + sut.addUncheckedPeers(ImmutableSet.of(addr1, addr2, addr3)); + + var prevPrevBestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); + sut.addOrUpdatePeerWithFailedConnection(prevPrevBestAddr); + var prevBestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); + for (int i = 0; i < 50; i++) { + sut.addOrUpdatePeerWithFailedConnection(prevBestAddr); + final var currBestAddr = sut.findBestKnownAddressById(peerId).orElseThrow(); + assertNotEquals(prevBestAddr, currBestAddr); + assertNotEquals(prevPrevBestAddr, prevBestAddr); + prevPrevBestAddr = prevBestAddr; + prevBestAddr = currBestAddr; + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/InMemoryAddressBookPersistence.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/InMemoryAddressBookPersistence.java index 63b8dbee98..33f12f0402 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/InMemoryAddressBookPersistence.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/addressbook/InMemoryAddressBookPersistence.java @@ -66,42 +66,41 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.network.p2p.NodeId; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public final class InMemoryAddressBookPersistence implements AddressBookPersistence { - private final Map entries = new ConcurrentHashMap<>(); + private final Map entries = new ConcurrentHashMap<>(); - @Override - public void open() { - // no-op - } + @Override + public void open() { + // no-op + } - @Override - public void reset() { - entries.clear(); - } + @Override + public void reset() { + entries.clear(); + } - @Override - public void close() { - // no-op - } + @Override + public void close() { + // no-op + } - @Override - public boolean saveEntry(AddressBookEntry entry) { - entries.put(entry.getNodeId(), entry); - return true; - } + @Override + public boolean saveEntry(AddressBookEntry entry) { + entries.put(entry.getNodeId(), entry); + return true; + } - @Override - public boolean removeEntry(NodeId nodeId) { - entries.remove(nodeId); - return true; - } + @Override + public boolean removeEntry(NodeId nodeId) { + entries.remove(nodeId); + return true; + } - @Override - public ImmutableList getAllEntries() { - return ImmutableList.copyOf(entries.values()); - } + @Override + public ImmutableList getAllEntries() { + return ImmutableList.copyOf(entries.values()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/DiscoverPeersTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/DiscoverPeersTest.java index 11e9de4f79..bbce1141ef 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/DiscoverPeersTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/DiscoverPeersTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class DiscoverPeersTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(DiscoverPeers.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(DiscoverPeers.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/GetPeersTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/GetPeersTest.java index cf3b7033a1..aa207c5721 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/GetPeersTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/GetPeersTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class GetPeersTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(GetPeers.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(GetPeers.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeerDiscoveryTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeerDiscoveryTest.java index 2f9bfdb6f6..4c48fc6b2a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeerDiscoveryTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeerDiscoveryTest.java @@ -64,6 +64,9 @@ package com.radixdlt.network.p2p.discovery; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableSet; import com.google.inject.Key; import com.radixdlt.consensus.bft.BFTNode; @@ -72,50 +75,58 @@ import com.radixdlt.network.p2p.NodeId; import com.radixdlt.network.p2p.RadixNodeUri; import com.radixdlt.network.p2p.test.DeterministicP2PNetworkTest; -import org.junit.Test; - import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; public final class PeerDiscoveryTest extends DeterministicP2PNetworkTest { - @Test - public void when_discover_peers_then_should_connect_to_some_peers() throws Exception { - setupTestRunner(5, defaultProperties()); + @Test + public void when_discover_peers_then_should_connect_to_some_peers() throws Exception { + setupTestRunner(5, defaultProperties()); - // add 4 peers to the addr book - testNetworkRunner.addressBook(0).addUncheckedPeers(Set.of( - testNetworkRunner.getUri(1), - testNetworkRunner.getUri(2), - testNetworkRunner.getUri(3), - testNetworkRunner.getUri(4) - )); + // add 4 peers to the addr book + testNetworkRunner + .addressBook(0) + .addUncheckedPeers( + Set.of( + testNetworkRunner.getUri(1), + testNetworkRunner.getUri(2), + testNetworkRunner.getUri(3), + testNetworkRunner.getUri(4))); - testNetworkRunner.getInstance(0, new Key>() { }) - .dispatch(DiscoverPeers.create()); + testNetworkRunner + .getInstance(0, new Key>() {}) + .dispatch(DiscoverPeers.create()); - processForCount(10); + processForCount(10); - // with 10 slots (default), max num of peers to connect is 3 (10/2 - 2) - assertEquals(3L, testNetworkRunner.peerManager(0).activeChannels().size()); - } + // with 10 slots (default), max num of peers to connect is 3 (10/2 - 2) + assertEquals(3L, testNetworkRunner.peerManager(0).activeChannels().size()); + } - @Test - public void when_unexpected_response_then_ban_peer() throws Exception { - setupTestRunner(1, defaultProperties()); + @Test + public void when_unexpected_response_then_ban_peer() throws Exception { + setupTestRunner(1, defaultProperties()); - final var unexpectedSender = BFTNode.random(); - final var peersResponse = PeersResponse.create( - ImmutableSet.of(RadixNodeUri.fromPubKeyAndAddress(0, ECKeyPair.generateNew().getPublicKey(), "127.0.0.1", 1234)) - ); + final var unexpectedSender = BFTNode.random(); + final var peersResponse = + PeersResponse.create( + ImmutableSet.of( + RadixNodeUri.fromPubKeyAndAddress( + 0, ECKeyPair.generateNew().getPublicKey(), "127.0.0.1", 1234))); - testNetworkRunner.getInstance(0, PeerDiscovery.class).peersResponseRemoteEventProcessor() - .process(unexpectedSender, peersResponse); + testNetworkRunner + .getInstance(0, PeerDiscovery.class) + .peersResponseRemoteEventProcessor() + .process(unexpectedSender, peersResponse); - processAll(); + processAll(); - assertTrue(testNetworkRunner.addressBook(0).findById(NodeId.fromPublicKey(unexpectedSender.getKey())).get().isBanned()); - } + assertTrue( + testNetworkRunner + .addressBook(0) + .findById(NodeId.fromPublicKey(unexpectedSender.getKey())) + .get() + .isBanned()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeersResponseTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeersResponseTest.java index 080e4294d7..dc32fa8a8c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeersResponseTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/discovery/PeersResponseTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class PeersResponseTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(PeersResponse.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(PeersResponse.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitorTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitorTest.java index ef7eb57f31..8f54d24c2e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitorTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerLivenessMonitorTest.java @@ -1,5 +1,80 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.network.p2p.liveness; +import static com.radixdlt.utils.TypedMocks.rmock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.environment.EventDispatcher; @@ -11,86 +86,89 @@ import com.radixdlt.network.p2p.PeerEvent.PeerLostLiveness; import com.radixdlt.network.p2p.PeersView; import com.radixdlt.properties.RuntimeProperties; +import java.util.stream.Stream; import org.apache.commons.cli.ParseException; import org.json.JSONObject; import org.junit.Before; import org.junit.Test; -import java.util.stream.Stream; - -import static com.radixdlt.utils.TypedMocks.rmock; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - public class PeerLivenessMonitorTest { - private P2PConfig p2PConfig; - private PeersView peersView; - private EventDispatcher peerEventDispatcher; - private RemoteEventDispatcher pingEventDispatcher; - private RemoteEventDispatcher pongEventDispatcher; - private ScheduledEventDispatcher pingTimeoutEventDispatcher; - - private PeerLivenessMonitor sut; - - @Before - public void setup() throws ParseException { - this.p2PConfig = P2PConfig.fromRuntimeProperties(new RuntimeProperties(new JSONObject(), new String[] {})); - this.peersView = mock(PeersView.class); - this.peerEventDispatcher = rmock(EventDispatcher.class); - this.pingEventDispatcher = rmock(RemoteEventDispatcher.class); - this.pongEventDispatcher = rmock(RemoteEventDispatcher.class); - this.pingTimeoutEventDispatcher = rmock(ScheduledEventDispatcher.class); - - this.sut = new PeerLivenessMonitor(p2PConfig, peersView, - peerEventDispatcher, pingEventDispatcher, pongEventDispatcher, pingTimeoutEventDispatcher); - } - - @Test - public void should_ping_peer_when_triggered_and_setup_a_timeout_and_emit_an_event_when_timeout() { - final var peer1 = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - when(peersView.peers()).thenReturn(Stream.of(PeersView.PeerInfo.fromBftNode(peer1))); - - this.sut.peersLivenessCheckTriggerEventProcessor().process(PeersLivenessCheckTrigger.create()); - - verify(pingEventDispatcher, times(1)).dispatch(eq(peer1), any()); - verify(pingTimeoutEventDispatcher, times(1)).dispatch(any(), anyLong()); - - this.sut.pingTimeoutEventProcessor().process(PeerPingTimeout.create(NodeId.fromPublicKey(peer1.getKey()))); - - verify(peerEventDispatcher, times(1)).dispatch(argThat(arg -> - arg instanceof PeerLostLiveness - && ((PeerLostLiveness) arg).getNodeId().equals(NodeId.fromPublicKey(peer1.getKey())) - )); - } - - @Test - public void should_ignore_obsolete_timeout() { - final var peer1 = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - when(peersView.peers()).thenReturn(Stream.of(PeersView.PeerInfo.fromBftNode(peer1))); - - this.sut.peersLivenessCheckTriggerEventProcessor().process(PeersLivenessCheckTrigger.create()); - - verify(pingEventDispatcher, times(1)).dispatch(eq(peer1), any()); - verify(pingTimeoutEventDispatcher, times(1)).dispatch(any(), anyLong()); - - this.sut.pongRemoteEventProcessor().process(peer1, Pong.create()); - this.sut.pingTimeoutEventProcessor().process(PeerPingTimeout.create(NodeId.fromPublicKey(peer1.getKey()))); - - verifyNoInteractions(peerEventDispatcher); - } - - @Test - public void should_respond_with_pong_to_ping() { - final var peer1 = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - this.sut.pingRemoteEventProcessor().process(peer1, Ping.create()); - verify(pongEventDispatcher, times(1)).dispatch(eq(peer1), any()); - } + private P2PConfig p2PConfig; + private PeersView peersView; + private EventDispatcher peerEventDispatcher; + private RemoteEventDispatcher pingEventDispatcher; + private RemoteEventDispatcher pongEventDispatcher; + private ScheduledEventDispatcher pingTimeoutEventDispatcher; + + private PeerLivenessMonitor sut; + + @Before + public void setup() throws ParseException { + this.p2PConfig = + P2PConfig.fromRuntimeProperties(new RuntimeProperties(new JSONObject(), new String[] {})); + this.peersView = mock(PeersView.class); + this.peerEventDispatcher = rmock(EventDispatcher.class); + this.pingEventDispatcher = rmock(RemoteEventDispatcher.class); + this.pongEventDispatcher = rmock(RemoteEventDispatcher.class); + this.pingTimeoutEventDispatcher = rmock(ScheduledEventDispatcher.class); + + this.sut = + new PeerLivenessMonitor( + p2PConfig, + peersView, + peerEventDispatcher, + pingEventDispatcher, + pongEventDispatcher, + pingTimeoutEventDispatcher); + } + + @Test + public void should_ping_peer_when_triggered_and_setup_a_timeout_and_emit_an_event_when_timeout() { + final var peer1 = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + when(peersView.peers()).thenReturn(Stream.of(PeersView.PeerInfo.fromBftNode(peer1))); + + this.sut.peersLivenessCheckTriggerEventProcessor().process(PeersLivenessCheckTrigger.create()); + + verify(pingEventDispatcher, times(1)).dispatch(eq(peer1), any()); + verify(pingTimeoutEventDispatcher, times(1)).dispatch(any(), anyLong()); + + this.sut + .pingTimeoutEventProcessor() + .process(PeerPingTimeout.create(NodeId.fromPublicKey(peer1.getKey()))); + + verify(peerEventDispatcher, times(1)) + .dispatch( + argThat( + arg -> + arg instanceof PeerLostLiveness + && ((PeerLostLiveness) arg) + .getNodeId() + .equals(NodeId.fromPublicKey(peer1.getKey())))); + } + + @Test + public void should_ignore_obsolete_timeout() { + final var peer1 = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + when(peersView.peers()).thenReturn(Stream.of(PeersView.PeerInfo.fromBftNode(peer1))); + + this.sut.peersLivenessCheckTriggerEventProcessor().process(PeersLivenessCheckTrigger.create()); + + verify(pingEventDispatcher, times(1)).dispatch(eq(peer1), any()); + verify(pingTimeoutEventDispatcher, times(1)).dispatch(any(), anyLong()); + + this.sut.pongRemoteEventProcessor().process(peer1, Pong.create()); + this.sut + .pingTimeoutEventProcessor() + .process(PeerPingTimeout.create(NodeId.fromPublicKey(peer1.getKey()))); + + verifyNoInteractions(peerEventDispatcher); + } + + @Test + public void should_respond_with_pong_to_ping() { + final var peer1 = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + this.sut.pingRemoteEventProcessor().process(peer1, Ping.create()); + verify(pongEventDispatcher, times(1)).dispatch(eq(peer1), any()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerPingTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerPingTimeoutTest.java index 796488c6dd..06b6eca8b2 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerPingTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeerPingTimeoutTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class PeerPingTimeoutTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(PeerPingTimeout.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(PeerPingTimeout.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTriggerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTriggerTest.java index 76a6fdbf2e..19d33a54dd 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTriggerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PeersLivenessCheckTriggerTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class PeersLivenessCheckTriggerTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(PeersLivenessCheckTrigger.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(PeersLivenessCheckTrigger.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PingTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PingTest.java index 46057cb1e6..c2a6891d19 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PingTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PingTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class PingTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(Ping.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(Ping.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PongTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PongTest.java index 7bc579a99a..bc1eaaa801 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PongTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/liveness/PongTest.java @@ -68,9 +68,8 @@ import org.junit.Test; public final class PongTest { - @Test - public void equalsTest() { - EqualsVerifier.forClass(Pong.class) - .verify(); - } + @Test + public void equalsTest() { + EqualsVerifier.forClass(Pong.class).verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/DeterministicP2PNetworkTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/DeterministicP2PNetworkTest.java index 314ac2d9f7..0efdf095a2 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/DeterministicP2PNetworkTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/DeterministicP2PNetworkTest.java @@ -78,56 +78,60 @@ import org.json.JSONObject; public class DeterministicP2PNetworkTest { - protected P2PTestNetworkRunner testNetworkRunner; + protected P2PTestNetworkRunner testNetworkRunner; - protected RuntimeProperties defaultProperties() { - try { - final var props = new RuntimeProperties(new JSONObject(), new String[] {}); - props.set("network.p2p.max_inbound_channels", 10); - props.set("network.p2p.max_outbound_channels", 10); - return props; - } catch (ParseException e) { - throw new RuntimeException(e); - } - } + protected RuntimeProperties defaultProperties() { + try { + final var props = new RuntimeProperties(new JSONObject(), new String[] {}); + props.set("network.p2p.max_inbound_channels", 10); + props.set("network.p2p.max_outbound_channels", 10); + return props; + } catch (ParseException e) { + throw new RuntimeException(e); + } + } - protected void setupTestRunner(int numNodes, RuntimeProperties properties) throws Exception { - this.testNetworkRunner = P2PTestNetworkRunner.create(numNodes, P2PConfig.fromRuntimeProperties(properties)); - } + protected void setupTestRunner(int numNodes, RuntimeProperties properties) throws Exception { + this.testNetworkRunner = + P2PTestNetworkRunner.create(numNodes, P2PConfig.fromRuntimeProperties(properties)); + } - protected void processForCount(int messageCount) { - for (int i = 0; i < messageCount; i++) { - processNext(); - } - } + protected void processForCount(int messageCount) { + for (int i = 0; i < messageCount; i++) { + processNext(); + } + } - protected void processAll() { - while (!testNetworkRunner.getDeterministicNetwork().allMessages().isEmpty()) { - processNext(); - } - } + protected void processAll() { + while (!testNetworkRunner.getDeterministicNetwork().allMessages().isEmpty()) { + processNext(); + } + } - protected Timed processNext() { - final var msg = testNetworkRunner.getDeterministicNetwork().nextMessage(); - final var nodeIndex = msg.value().channelId().receiverIndex(); - final var injector = testNetworkRunner.getNode(nodeIndex).injector; - withThreadCtx(injector, () -> - injector.getInstance(DeterministicProcessor.class) - .handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()) - ); - return msg; - } + protected Timed processNext() { + final var msg = testNetworkRunner.getDeterministicNetwork().nextMessage(); + final var nodeIndex = msg.value().channelId().receiverIndex(); + final var injector = testNetworkRunner.getNode(nodeIndex).injector; + withThreadCtx( + injector, + () -> + injector + .getInstance(DeterministicProcessor.class) + .handleMessage( + msg.value().origin(), msg.value().message(), msg.value().typeLiteral())); + return msg; + } - private void withThreadCtx(Injector injector, Runnable r) { - ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); - try { - r.run(); - } finally { - ThreadContext.remove("self"); - } - } + private void withThreadCtx(Injector injector, Runnable r) { + ThreadContext.put("self", " " + injector.getInstance(Key.get(String.class, Self.class))); + try { + r.run(); + } finally { + ThreadContext.remove("self"); + } + } - protected RadixNodeUri uriOfNode(int nodeIndex) { - return testNetworkRunner.getUri(nodeIndex); - } + protected RadixNodeUri uriOfNode(int nodeIndex) { + return testNetworkRunner.getUri(nodeIndex); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/MockP2PNetwork.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/MockP2PNetwork.java index 75963bc351..734eda187c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/MockP2PNetwork.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/MockP2PNetwork.java @@ -64,6 +64,10 @@ package com.radixdlt.network.p2p.test; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.common.collect.ImmutableList; import com.google.inject.Key; import com.radixdlt.counters.SystemCounters; @@ -76,93 +80,100 @@ import com.radixdlt.networks.Addressing; import com.radixdlt.networks.Network; import com.radixdlt.serialization.Serialization; - -import java.security.SecureRandom; -import java.util.Optional; - import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.SocketChannel; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import java.security.SecureRandom; +import java.util.Optional; final class MockP2PNetwork { - private ImmutableList nodes; - - // this needs to be mutable due to circular dependency in runner - void setNodes(ImmutableList nodes) { - this.nodes = nodes; - } - - void createChannel(int clientPeerIndex, RadixNodeUri serverPeerUri) { - final var clientPeer = nodes.get(clientPeerIndex); - final var serverPeer = nodes.stream() - .filter(p -> p.uri.getHost().equals(serverPeerUri.getHost()) && p.uri.getPort() == serverPeerUri.getPort()) - .findAny().get(); - - final var clientSocketChannel = mock(SocketChannel.class); - final var serverSocketChannel = mock(SocketChannel.class); - - final var clientChannel = new PeerChannel( - clientPeer.injector.getInstance(P2PConfig.class), - Addressing.ofNetwork(Network.LOCALNET), - 1, - clientPeer.injector.getInstance(SystemCounters.class), - clientPeer.injector.getInstance(Serialization.class), - new SecureRandom(), - ECKeyOps.fromKeyPair(clientPeer.keyPair), - clientPeer.injector.getInstance(new Key>() { }), - Optional.of(serverPeerUri), - clientSocketChannel, - Optional.empty() - ); - - final var serverChannel = new PeerChannel( - serverPeer.injector.getInstance(P2PConfig.class), - Addressing.ofNetwork(Network.LOCALNET), - 1, - serverPeer.injector.getInstance(SystemCounters.class), - serverPeer.injector.getInstance(Serialization.class), - new SecureRandom(), - ECKeyOps.fromKeyPair(serverPeer.keyPair), - serverPeer.injector.getInstance(new Key>() { }), - Optional.empty(), - serverSocketChannel, - Optional.empty() - ); - - when(clientSocketChannel.writeAndFlush(any())).thenAnswer(inv -> { - final var rawData = inv.getArgument(0); - serverChannel.channelRead0(null, (ByteBuf) rawData); - return null; - }); - - when(serverSocketChannel.writeAndFlush(any())).thenAnswer(inv -> { - final var rawData = inv.getArgument(0); - clientChannel.channelRead0(null, (ByteBuf) rawData); - return null; - }); - - when(clientSocketChannel.close()).thenAnswer(inv -> { - final var mockChannel = mock(ChannelHandlerContext.class); - when(mockChannel.channel()).thenReturn(mock(Channel.class)); - clientChannel.channelInactive(mockChannel); - serverChannel.channelInactive(mockChannel); - return null; - }); - - when(serverSocketChannel.close()).thenAnswer(inv -> { - final var mockChannel = mock(ChannelHandlerContext.class); - when(mockChannel.channel()).thenReturn(mock(Channel.class)); - serverChannel.channelInactive(mockChannel); - clientChannel.channelInactive(mockChannel); - return null; - }); - - serverChannel.channelActive(null); - clientChannel.channelActive(null); - } + private ImmutableList nodes; + + // this needs to be mutable due to circular dependency in runner + void setNodes(ImmutableList nodes) { + this.nodes = nodes; + } + + void createChannel(int clientPeerIndex, RadixNodeUri serverPeerUri) { + final var clientPeer = nodes.get(clientPeerIndex); + final var serverPeer = + nodes.stream() + .filter( + p -> + p.uri.getHost().equals(serverPeerUri.getHost()) + && p.uri.getPort() == serverPeerUri.getPort()) + .findAny() + .get(); + + final var clientSocketChannel = mock(SocketChannel.class); + final var serverSocketChannel = mock(SocketChannel.class); + + final var clientChannel = + new PeerChannel( + clientPeer.injector.getInstance(P2PConfig.class), + Addressing.ofNetwork(Network.LOCALNET), + 1, + clientPeer.injector.getInstance(SystemCounters.class), + clientPeer.injector.getInstance(Serialization.class), + new SecureRandom(), + ECKeyOps.fromKeyPair(clientPeer.keyPair), + clientPeer.injector.getInstance(new Key>() {}), + Optional.of(serverPeerUri), + clientSocketChannel, + Optional.empty()); + + final var serverChannel = + new PeerChannel( + serverPeer.injector.getInstance(P2PConfig.class), + Addressing.ofNetwork(Network.LOCALNET), + 1, + serverPeer.injector.getInstance(SystemCounters.class), + serverPeer.injector.getInstance(Serialization.class), + new SecureRandom(), + ECKeyOps.fromKeyPair(serverPeer.keyPair), + serverPeer.injector.getInstance(new Key>() {}), + Optional.empty(), + serverSocketChannel, + Optional.empty()); + + when(clientSocketChannel.writeAndFlush(any())) + .thenAnswer( + inv -> { + final var rawData = inv.getArgument(0); + serverChannel.channelRead0(null, (ByteBuf) rawData); + return null; + }); + + when(serverSocketChannel.writeAndFlush(any())) + .thenAnswer( + inv -> { + final var rawData = inv.getArgument(0); + clientChannel.channelRead0(null, (ByteBuf) rawData); + return null; + }); + + when(clientSocketChannel.close()) + .thenAnswer( + inv -> { + final var mockChannel = mock(ChannelHandlerContext.class); + when(mockChannel.channel()).thenReturn(mock(Channel.class)); + clientChannel.channelInactive(mockChannel); + serverChannel.channelInactive(mockChannel); + return null; + }); + + when(serverSocketChannel.close()) + .thenAnswer( + inv -> { + final var mockChannel = mock(ChannelHandlerContext.class); + when(mockChannel.channel()).thenReturn(mock(Channel.class)); + serverChannel.channelInactive(mockChannel); + clientChannel.channelInactive(mockChannel); + return null; + }); + + serverChannel.channelActive(null); + clientChannel.channelActive(null); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/P2PTestNetworkRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/P2PTestNetworkRunner.java index a9bc1ce57a..ffa17a4a68 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/P2PTestNetworkRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/P2PTestNetworkRunner.java @@ -103,144 +103,153 @@ import com.radixdlt.store.DatabaseCacheSize; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.DatabaseLocation; -import org.apache.commons.cli.ParseException; -import org.json.JSONObject; -import org.junit.rules.TemporaryFolder; - import java.io.IOException; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.cli.ParseException; +import org.json.JSONObject; +import org.junit.rules.TemporaryFolder; public final class P2PTestNetworkRunner { - private final ImmutableList nodes; - private final DeterministicNetwork deterministicNetwork; - - private P2PTestNetworkRunner( - ImmutableList nodes, - DeterministicNetwork deterministicNetwork - ) { - this.nodes = Objects.requireNonNull(nodes); - this.deterministicNetwork = Objects.requireNonNull(deterministicNetwork); - } - - public static P2PTestNetworkRunner create(int numNodes, P2PConfig p2pConfig) throws Exception { - final var nodesKeys = IntStream.range(0, numNodes) - .mapToObj(unused -> ECKeyPair.generateNew()) - .collect(ImmutableList.toImmutableList()); - - final var network = new DeterministicNetwork( - nodesKeys.stream().map(key -> BFTNode.create(key.getPublicKey())).collect(Collectors.toList()), - MessageSelector.firstSelector(), - MessageMutator.nothing() - ); - - final var p2pNetwork = new MockP2PNetwork(); - - final var builder = ImmutableList.builder(); - for (int i = 0; i < numNodes; i++) { - final var nodeKey = nodesKeys.get(i); - final var uri = RadixNodeUri.fromPubKeyAndAddress( - 1, nodeKey.getPublicKey(), "127.0.0.1", p2pConfig.listenPort() + i); - final var injector = createInjector(p2pNetwork, network, p2pConfig, nodeKey, uri, i); - builder.add(new TestNode(injector, uri, nodeKey)); - } - - final var injectors = builder.build(); - - p2pNetwork.setNodes(injectors); - - return new P2PTestNetworkRunner(injectors, network); - } - - private static Injector createInjector( - MockP2PNetwork p2pNetwork, - DeterministicNetwork network, - P2PConfig p2pConfig, - ECKeyPair nodeKey, - RadixNodeUri selfUri, - int selfNodeIndex - ) throws ParseException { - final var properties = new RuntimeProperties(new JSONObject(), new String[] {}); - return Guice.createInjector( - Modules.override(new P2PModule(properties)).with( - new AbstractModule() { - @Override - protected void configure() { - bind(PeerOutboundBootstrap.class) - .toInstance(uri -> p2pNetwork.createChannel(selfNodeIndex, uri)); - bind(P2PConfig.class).toInstance(p2pConfig); - bind(RadixNodeUri.class).annotatedWith(Self.class).toInstance(selfUri); - bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - } - } - ), - new PeerDiscoveryModule(), - new PeerLivenessMonitorModule(), - new DispatcherModule(), - new AbstractModule() { - @Override - protected void configure() { - final var dbDir = new TemporaryFolder(); - try { - dbDir.create(); - } catch (IOException e) { - throw new RuntimeException(e); - } - bindConstant().annotatedWith(NetworkId.class).to(Network.LOCALNET.getId()); - bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); - bindConstant().annotatedWith(DatabaseLocation.class).to(dbDir.getRoot().getAbsolutePath()); - bindConstant().annotatedWith(DatabaseCacheSize.class).to(100_000L); - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(nodeKey); - bind(ECPublicKey.class).annotatedWith(Self.class).toInstance(nodeKey.getPublicKey()); - bind(BFTNode.class).annotatedWith(Self.class).toInstance(BFTNode.create(nodeKey.getPublicKey())); - bind(String.class).annotatedWith(Self.class).toInstance( - Addressing.ofNetwork(Network.LOCALNET).forValidators().of(nodeKey.getPublicKey()).substring(0, 10) - ); - bind(ECKeyOps.class).toInstance(ECKeyOps.fromKeyPair(nodeKey)); - bind(Environment.class).toInstance(network.createSender(BFTNode.create(nodeKey.getPublicKey()))); - bind(RuntimeProperties.class).toInstance(properties); - bind(Serialization.class).toInstance(DefaultSerialization.getInstance()); - bind(DeterministicProcessor.class); - Multibinder.newSetBinder(binder(), StartProcessorOnRunner.class); - } - } - ); - } - - public void cleanup() { - this.nodes.forEach(node -> { - node.injector.getInstance(DatabaseEnvironment.class).stop(); - }); - } - - public RadixNodeUri getUri(int nodeIndex) { - return this.nodes.get(nodeIndex).uri; - } - - public PeerManager peerManager(int nodeIndex) { - return getInstance(nodeIndex, PeerManager.class); - } - - public AddressBook addressBook(int nodeIndex) { - return getInstance(nodeIndex, AddressBook.class); - } - - public T getInstance(int nodeIndex, Class clazz) { - return this.nodes.get(nodeIndex).injector.getInstance(clazz); - } - - public T getInstance(int nodeIndex, Key key) { - return this.nodes.get(nodeIndex).injector.getInstance(key); - } - - public DeterministicNetwork getDeterministicNetwork() { - return this.deterministicNetwork; - } - - public TestNode getNode(int index) { - return this.nodes.get(index); - } - + private final ImmutableList nodes; + private final DeterministicNetwork deterministicNetwork; + + private P2PTestNetworkRunner( + ImmutableList nodes, DeterministicNetwork deterministicNetwork) { + this.nodes = Objects.requireNonNull(nodes); + this.deterministicNetwork = Objects.requireNonNull(deterministicNetwork); + } + + public static P2PTestNetworkRunner create(int numNodes, P2PConfig p2pConfig) throws Exception { + final var nodesKeys = + IntStream.range(0, numNodes) + .mapToObj(unused -> ECKeyPair.generateNew()) + .collect(ImmutableList.toImmutableList()); + + final var network = + new DeterministicNetwork( + nodesKeys.stream() + .map(key -> BFTNode.create(key.getPublicKey())) + .collect(Collectors.toList()), + MessageSelector.firstSelector(), + MessageMutator.nothing()); + + final var p2pNetwork = new MockP2PNetwork(); + + final var builder = ImmutableList.builder(); + for (int i = 0; i < numNodes; i++) { + final var nodeKey = nodesKeys.get(i); + final var uri = + RadixNodeUri.fromPubKeyAndAddress( + 1, nodeKey.getPublicKey(), "127.0.0.1", p2pConfig.listenPort() + i); + final var injector = createInjector(p2pNetwork, network, p2pConfig, nodeKey, uri, i); + builder.add(new TestNode(injector, uri, nodeKey)); + } + + final var injectors = builder.build(); + + p2pNetwork.setNodes(injectors); + + return new P2PTestNetworkRunner(injectors, network); + } + + private static Injector createInjector( + MockP2PNetwork p2pNetwork, + DeterministicNetwork network, + P2PConfig p2pConfig, + ECKeyPair nodeKey, + RadixNodeUri selfUri, + int selfNodeIndex) + throws ParseException { + final var properties = new RuntimeProperties(new JSONObject(), new String[] {}); + return Guice.createInjector( + Modules.override(new P2PModule(properties)) + .with( + new AbstractModule() { + @Override + protected void configure() { + bind(PeerOutboundBootstrap.class) + .toInstance(uri -> p2pNetwork.createChannel(selfNodeIndex, uri)); + bind(P2PConfig.class).toInstance(p2pConfig); + bind(RadixNodeUri.class).annotatedWith(Self.class).toInstance(selfUri); + bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); + } + }), + new PeerDiscoveryModule(), + new PeerLivenessMonitorModule(), + new DispatcherModule(), + new AbstractModule() { + @Override + protected void configure() { + final var dbDir = new TemporaryFolder(); + try { + dbDir.create(); + } catch (IOException e) { + throw new RuntimeException(e); + } + bindConstant().annotatedWith(NetworkId.class).to(Network.LOCALNET.getId()); + bind(Addressing.class).toInstance(Addressing.ofNetwork(Network.LOCALNET)); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(dbDir.getRoot().getAbsolutePath()); + bindConstant().annotatedWith(DatabaseCacheSize.class).to(100_000L); + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(nodeKey); + bind(ECPublicKey.class).annotatedWith(Self.class).toInstance(nodeKey.getPublicKey()); + bind(BFTNode.class) + .annotatedWith(Self.class) + .toInstance(BFTNode.create(nodeKey.getPublicKey())); + bind(String.class) + .annotatedWith(Self.class) + .toInstance( + Addressing.ofNetwork(Network.LOCALNET) + .forValidators() + .of(nodeKey.getPublicKey()) + .substring(0, 10)); + bind(ECKeyOps.class).toInstance(ECKeyOps.fromKeyPair(nodeKey)); + bind(Environment.class) + .toInstance(network.createSender(BFTNode.create(nodeKey.getPublicKey()))); + bind(RuntimeProperties.class).toInstance(properties); + bind(Serialization.class).toInstance(DefaultSerialization.getInstance()); + bind(DeterministicProcessor.class); + Multibinder.newSetBinder(binder(), StartProcessorOnRunner.class); + } + }); + } + + public void cleanup() { + this.nodes.forEach( + node -> { + node.injector.getInstance(DatabaseEnvironment.class).stop(); + }); + } + + public RadixNodeUri getUri(int nodeIndex) { + return this.nodes.get(nodeIndex).uri; + } + + public PeerManager peerManager(int nodeIndex) { + return getInstance(nodeIndex, PeerManager.class); + } + + public AddressBook addressBook(int nodeIndex) { + return getInstance(nodeIndex, AddressBook.class); + } + + public T getInstance(int nodeIndex, Class clazz) { + return this.nodes.get(nodeIndex).injector.getInstance(clazz); + } + + public T getInstance(int nodeIndex, Key key) { + return this.nodes.get(nodeIndex).injector.getInstance(key); + } + + public DeterministicNetwork getDeterministicNetwork() { + return this.deterministicNetwork; + } + + public TestNode getNode(int index) { + return this.nodes.get(index); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/TestNode.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/TestNode.java index 3ba5ef6038..59f0224b6e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/TestNode.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/test/TestNode.java @@ -69,13 +69,13 @@ import com.radixdlt.network.p2p.RadixNodeUri; final class TestNode { - Injector injector; - RadixNodeUri uri; - ECKeyPair keyPair; + Injector injector; + RadixNodeUri uri; + ECKeyPair keyPair; - TestNode(Injector injector, RadixNodeUri uri, ECKeyPair keyPair) { - this.injector = injector; - this.uri = uri; - this.keyPair = keyPair; - } + TestNode(Injector injector, RadixNodeUri uri, ECKeyPair keyPair) { + this.injector = injector; + this.uri = uri; + this.keyPair = keyPair; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/FrameCodecTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/FrameCodecTest.java index 818d647516..a9cdc2bda6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/FrameCodecTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/FrameCodecTest.java @@ -64,6 +64,8 @@ package com.radixdlt.network.p2p.transport; +import static org.junit.Assert.assertArrayEquals; + import com.radixdlt.DefaultSerialization; import com.radixdlt.crypto.ECKeyOps; import com.radixdlt.crypto.ECKeyPair; @@ -73,54 +75,61 @@ import com.radixdlt.serialization.Serialization; import com.radixdlt.utils.Pair; import io.netty.buffer.Unpooled; -import org.junit.Test; import java.io.ByteArrayOutputStream; import java.security.SecureRandom; - -import static org.junit.Assert.assertArrayEquals; +import org.junit.Test; public final class FrameCodecTest { - private final Serialization serialization = DefaultSerialization.getInstance(); - private final SecureRandom secureRandom = new SecureRandom(); + private final Serialization serialization = DefaultSerialization.getInstance(); + private final SecureRandom secureRandom = new SecureRandom(); - @Test - public void test_frame_codec_write_read() throws Exception { - final var nodeKey1 = ECKeyPair.generateNew(); - final var nodeKey2 = ECKeyPair.generateNew(); + @Test + public void test_frame_codec_write_read() throws Exception { + final var nodeKey1 = ECKeyPair.generateNew(); + final var nodeKey2 = ECKeyPair.generateNew(); - final var secrets = agreeSecrets(nodeKey1, nodeKey2); + final var secrets = agreeSecrets(nodeKey1, nodeKey2); - final var frameCodec1 = new FrameCodec(secrets.getFirst()); - final var frameCodec2 = new FrameCodec(secrets.getSecond()); + final var frameCodec1 = new FrameCodec(secrets.getFirst()); + final var frameCodec2 = new FrameCodec(secrets.getSecond()); - final var messageCount = 1000; - for (int i = 0; i < messageCount; i++) { - final var direction = secureRandom.nextBoolean(); - final var source = direction ? frameCodec1 : frameCodec2; - final var destination = direction ? frameCodec2 : frameCodec1; + final var messageCount = 1000; + for (int i = 0; i < messageCount; i++) { + final var direction = secureRandom.nextBoolean(); + final var source = direction ? frameCodec1 : frameCodec2; + final var destination = direction ? frameCodec2 : frameCodec1; - final var messageLength = secureRandom.nextInt(1024 * 10); - final var message = new byte[messageLength]; - secureRandom.nextBytes(message); + final var messageLength = secureRandom.nextInt(1024 * 10); + final var message = new byte[messageLength]; + secureRandom.nextBytes(message); - final var baos = new ByteArrayOutputStream(); - source.writeFrame(message, baos); - final var readFrame = destination.tryReadSingleFrame(Unpooled.wrappedBuffer(baos.toByteArray())); + final var baos = new ByteArrayOutputStream(); + source.writeFrame(message, baos); + final var readFrame = + destination.tryReadSingleFrame(Unpooled.wrappedBuffer(baos.toByteArray())); - assertArrayEquals(message, readFrame.get()); - } - } + assertArrayEquals(message, readFrame.get()); + } + } - private Pair agreeSecrets(ECKeyPair nodeKey1, ECKeyPair nodeKey2) throws Exception { - final var handshaker1 = new AuthHandshaker(serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey1), (byte) 0x01); - final var handshaker2 = new AuthHandshaker(serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey2), (byte) 0x01); + private Pair agreeSecrets(ECKeyPair nodeKey1, ECKeyPair nodeKey2) + throws Exception { + final var handshaker1 = + new AuthHandshaker( + serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey1), (byte) 0x01); + final var handshaker2 = + new AuthHandshaker( + serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey2), (byte) 0x01); - final var initMessage = handshaker1.initiate(nodeKey2.getPublicKey()); - final var handshaker2ResultPair = handshaker2.handleInitialMessage(Unpooled.wrappedBuffer(initMessage)); - final var handshaker2Result = (AuthHandshakeSuccess) handshaker2ResultPair.getSecond(); - final var responseMessage = handshaker2ResultPair.getFirst(); - final var handshaker1Result = (AuthHandshakeSuccess) handshaker1.handleResponseMessage(Unpooled.wrappedBuffer(responseMessage)); + final var initMessage = handshaker1.initiate(nodeKey2.getPublicKey()); + final var handshaker2ResultPair = + handshaker2.handleInitialMessage(Unpooled.wrappedBuffer(initMessage)); + final var handshaker2Result = (AuthHandshakeSuccess) handshaker2ResultPair.getSecond(); + final var responseMessage = handshaker2ResultPair.getFirst(); + final var handshaker1Result = + (AuthHandshakeSuccess) + handshaker1.handleResponseMessage(Unpooled.wrappedBuffer(responseMessage)); - return Pair.of(handshaker1Result.getSecrets(), handshaker2Result.getSecrets()); - } + return Pair.of(handshaker1Result.getSecrets(), handshaker2Result.getSecrets()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LogSinkTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LogSinkTest.java index 0aeb05cdb8..a9b43f68a3 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LogSinkTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LogSinkTest.java @@ -64,46 +64,46 @@ package com.radixdlt.network.p2p.transport; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + import com.radixdlt.network.p2p.transport.logging.LogSink; import org.apache.logging.log4j.Logger; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - public class LogSinkTest { - private Logger log; - private LogSink logSink; + private Logger log; + private LogSink logSink; - @Before - public void beforeTest() { - this.log = mock(Logger.class); - this.logSink = LogSink.using(log); - } + @Before + public void beforeTest() { + this.log = mock(Logger.class); + this.logSink = LogSink.using(log); + } - @Test - public void testIsTraceEnabled() { - when(log.isTraceEnabled()).thenReturn(true); // false is default + @Test + public void testIsTraceEnabled() { + when(log.isTraceEnabled()).thenReturn(true); // false is default - assertTrue(logSink.isTraceEnabled()); - verify(log, times(1)).isTraceEnabled(); - verifyNoMoreInteractions(log); - } + assertTrue(logSink.isTraceEnabled()); + verify(log, times(1)).isTraceEnabled(); + verifyNoMoreInteractions(log); + } - @Test - public void testTraceMessage() { - logSink.trace("baz"); - verify(log, times(1)).trace("baz"); - verifyNoMoreInteractions(log); - } + @Test + public void testTraceMessage() { + logSink.trace("baz"); + verify(log, times(1)).trace("baz"); + verifyNoMoreInteractions(log); + } - @Test - public void testTraceThrowable() { - Throwable t = new Throwable(); - logSink.trace("bar", t); - verify(log, times(1)).trace("bar", t); - verifyNoMoreInteractions(log); - } + @Test + public void testTraceThrowable() { + Throwable t = new Throwable(); + logSink.trace("bar", t); + verify(log, times(1)).trace("bar", t); + verifyNoMoreInteractions(log); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LoggingHandlerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LoggingHandlerTest.java index c8cfbd332a..24f6d7fe89 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LoggingHandlerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/LoggingHandlerTest.java @@ -64,15 +64,10 @@ package com.radixdlt.network.p2p.transport; -import java.net.SocketAddress; +import static org.mockito.Mockito.*; import com.radixdlt.network.p2p.transport.logging.LogSink; import com.radixdlt.network.p2p.transport.logging.LoggingHandler; -import org.junit.Before; -import org.junit.Test; - -import static org.mockito.Mockito.*; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.buffer.ByteBufUtil; @@ -82,344 +77,350 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.util.internal.StringUtil; +import java.net.SocketAddress; +import org.junit.Before; +import org.junit.Test; -/** - * Tests for Netty logging handler. - */ +/** Tests for Netty logging handler. */ public class LoggingHandlerTest { - private LogSink logger; - private LoggingHandler handler; - private ChannelHandlerContext ctx; - private Channel channel; - private boolean isTraceEnabled = true; - - @Before - public void setUp() { - this.logger = mock(LogSink.class); - when(this.logger.isTraceEnabled()).thenReturn(this.isTraceEnabled); - this.handler = new LoggingHandler(logger, true); - - this.channel = mock(Channel.class); - - this.ctx = mock(ChannelHandlerContext.class); - doReturn(this.channel).when(this.ctx).channel(); - } - - @Test - public void testChannelRegisteredChannelHandlerContext() throws Exception { - this.handler.channelRegistered(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " REGISTERED"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelUnregisteredChannelHandlerContext() throws Exception { - this.handler.channelUnregistered(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " UNREGISTERED"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelActiveChannelHandlerContext() throws Exception { - this.handler.channelActive(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " ACTIVE"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelInactiveChannelHandlerContext() throws Exception { - this.handler.channelInactive(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " INACTIVE"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testExceptionCaughtChannelHandlerContextThrowable() throws Exception { - Exception ex = new Exception("test exception"); - this.handler.exceptionCaught(ctx, ex); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " EXCEPTION: java.lang.Exception: test exception", ex); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventTriggeredChannelHandlerContextObject() throws Exception { - Object randomObject = new Object(); - this.handler.userEventTriggered(ctx, randomObject); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: " + randomObject.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testBindChannelHandlerContextSocketAddressChannelPromise() throws Exception { - SocketAddress addr = mock(SocketAddress.class); - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.bind(ctx, addr, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " BIND: " + addr.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testConnectChannelHandlerContextSocketAddressSocketAddressChannelPromise() throws Exception { - SocketAddress addr1 = mock(SocketAddress.class); - SocketAddress addr2 = mock(SocketAddress.class); - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.connect(ctx, addr1, addr2, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " CONNECT: " + addr1.toString() + ", " + addr2.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testDisconnectChannelHandlerContextChannelPromise() throws Exception { - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.disconnect(ctx, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " DISCONNECT"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testCloseChannelHandlerContextChannelPromise() throws Exception { - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.close(ctx, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " CLOSE"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testDeregisterChannelHandlerContextChannelPromise() throws Exception { - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.deregister(ctx, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " DEREGISTER"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelReadCompleteChannelHandlerContext() throws Exception { - this.handler.channelReadComplete(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " READ_COMPLETE"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelReadChannelHandlerContextObject() throws Exception { - Object randomObject = new Object(); - this.handler.channelRead(ctx, randomObject); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " READ: " + randomObject.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testWriteChannelHandlerContextObjectChannelPromise() throws Exception { - Object randomObject = new Object(); - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.write(ctx, randomObject, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " WRITE: " + randomObject.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelWritabilityChangedChannelHandlerContext() throws Exception { - this.handler.channelWritabilityChanged(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " WRITABILITY_CHANGED"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testFlushChannelHandlerContext() throws Exception { - this.handler.flush(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " FLUSH"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testConnectChannelHandlerContextSocketAddressNullChannelPromise() throws Exception { - SocketAddress addr1 = mock(SocketAddress.class); - ChannelPromise promise = mock(ChannelPromise.class); - this.handler.connect(ctx, addr1, null, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " CONNECT: " + addr1.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventTriggeredChannelHandlerContextEmptyByteBuf() throws Exception { - ByteBuf byteBuf = Unpooled.EMPTY_BUFFER; - this.handler.userEventTriggered(ctx, byteBuf); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: 0B"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventTriggeredChannelHandlerContextByteBuf() throws Exception { - byte[] bytes = new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D' - }; - ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); - this.handler.userEventTriggered(ctx, byteBuf); - - String newline = StringUtil.NEWLINE; - StringBuilder sb = new StringBuilder(this.channel.toString()) - .append(" USER_EVENT: ") - .append(bytes.length) - .append('B') - .append(newline); - ByteBufUtil.appendPrettyHexDump(sb, byteBuf); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(sb.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventTriggeredChannelHandlerContextEmptyByteBufHolder() throws Exception { - ByteBuf byteBuf = Unpooled.EMPTY_BUFFER; - ByteBufHolder byteBufHolder = new DefaultByteBufHolder(byteBuf); - this.handler.userEventTriggered(ctx, byteBufHolder); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: " + byteBufHolder.toString() + ", 0B"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventTriggeredChannelHandlerContextByteBufHolder() throws Exception { - byte[] bytes = new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D' - }; - ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); - ByteBufHolder byteBufHolder = new DefaultByteBufHolder(byteBuf); - this.handler.userEventTriggered(ctx, byteBufHolder); - - String newline = StringUtil.NEWLINE; - StringBuilder sb = new StringBuilder(this.channel.toString()) - .append(" USER_EVENT: ") - .append(byteBufHolder.toString()) - .append(", ") - .append(bytes.length) - .append('B') - .append(newline); - ByteBufUtil.appendPrettyHexDump(sb, byteBuf); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(sb.toString()); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelReadCompleteChannelHandlerContextNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - nologHandler.channelReadComplete(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " READ_COMPLETE"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelReadChannelHandlerContextObjectNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - Object randomObject = new Object(); - nologHandler.channelRead(ctx, randomObject); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " READ: Object"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testWriteChannelHandlerContextObjectChannelPromiseNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - Object randomObject = new Object(); - ChannelPromise promise = mock(ChannelPromise.class); - nologHandler.write(ctx, randomObject, promise); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " WRITE: Object"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testChannelWritabilityChangedChannelHandlerContextNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - nologHandler.channelWritabilityChanged(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " WRITABILITY_CHANGED"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testFlushChannelHandlerContextNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - nologHandler.flush(ctx); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " FLUSH"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventByteBufNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - byte[] bytes = new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D' - }; - ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); - nologHandler.userEventTriggered(ctx, byteBuf); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: " + bytes.length + "B"); - verifyNoMoreInteractions(logger); - } - - @Test - public void testUserEventByteBufHolderNoDetails() throws Exception { - LoggingHandler nologHandler = new LoggingHandler(this.logger, false); - byte[] bytes = new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D' - }; - ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); - ByteBufHolder byteBufHolder = new DefaultByteBufHolder(byteBuf); - nologHandler.userEventTriggered(ctx, byteBufHolder); - - verify(logger, times(1)).isTraceEnabled(); - verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: " + byteBufHolder.toString() + ", " + bytes.length + "B"); - verifyNoMoreInteractions(logger); - } - + private LogSink logger; + private LoggingHandler handler; + private ChannelHandlerContext ctx; + private Channel channel; + private boolean isTraceEnabled = true; + + @Before + public void setUp() { + this.logger = mock(LogSink.class); + when(this.logger.isTraceEnabled()).thenReturn(this.isTraceEnabled); + this.handler = new LoggingHandler(logger, true); + + this.channel = mock(Channel.class); + + this.ctx = mock(ChannelHandlerContext.class); + doReturn(this.channel).when(this.ctx).channel(); + } + + @Test + public void testChannelRegisteredChannelHandlerContext() throws Exception { + this.handler.channelRegistered(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " REGISTERED"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelUnregisteredChannelHandlerContext() throws Exception { + this.handler.channelUnregistered(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " UNREGISTERED"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelActiveChannelHandlerContext() throws Exception { + this.handler.channelActive(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " ACTIVE"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelInactiveChannelHandlerContext() throws Exception { + this.handler.channelInactive(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " INACTIVE"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testExceptionCaughtChannelHandlerContextThrowable() throws Exception { + Exception ex = new Exception("test exception"); + this.handler.exceptionCaught(ctx, ex); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)) + .trace(this.channel.toString() + " EXCEPTION: java.lang.Exception: test exception", ex); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventTriggeredChannelHandlerContextObject() throws Exception { + Object randomObject = new Object(); + this.handler.userEventTriggered(ctx, randomObject); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)) + .trace(this.channel.toString() + " USER_EVENT: " + randomObject.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testBindChannelHandlerContextSocketAddressChannelPromise() throws Exception { + SocketAddress addr = mock(SocketAddress.class); + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.bind(ctx, addr, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " BIND: " + addr.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testConnectChannelHandlerContextSocketAddressSocketAddressChannelPromise() + throws Exception { + SocketAddress addr1 = mock(SocketAddress.class); + SocketAddress addr2 = mock(SocketAddress.class); + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.connect(ctx, addr1, addr2, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)) + .trace(this.channel.toString() + " CONNECT: " + addr1.toString() + ", " + addr2.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testDisconnectChannelHandlerContextChannelPromise() throws Exception { + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.disconnect(ctx, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " DISCONNECT"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testCloseChannelHandlerContextChannelPromise() throws Exception { + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.close(ctx, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " CLOSE"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testDeregisterChannelHandlerContextChannelPromise() throws Exception { + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.deregister(ctx, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " DEREGISTER"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelReadCompleteChannelHandlerContext() throws Exception { + this.handler.channelReadComplete(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " READ_COMPLETE"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelReadChannelHandlerContextObject() throws Exception { + Object randomObject = new Object(); + this.handler.channelRead(ctx, randomObject); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " READ: " + randomObject.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testWriteChannelHandlerContextObjectChannelPromise() throws Exception { + Object randomObject = new Object(); + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.write(ctx, randomObject, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " WRITE: " + randomObject.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelWritabilityChangedChannelHandlerContext() throws Exception { + this.handler.channelWritabilityChanged(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " WRITABILITY_CHANGED"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testFlushChannelHandlerContext() throws Exception { + this.handler.flush(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " FLUSH"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testConnectChannelHandlerContextSocketAddressNullChannelPromise() throws Exception { + SocketAddress addr1 = mock(SocketAddress.class); + ChannelPromise promise = mock(ChannelPromise.class); + this.handler.connect(ctx, addr1, null, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " CONNECT: " + addr1.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventTriggeredChannelHandlerContextEmptyByteBuf() throws Exception { + ByteBuf byteBuf = Unpooled.EMPTY_BUFFER; + this.handler.userEventTriggered(ctx, byteBuf); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: 0B"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventTriggeredChannelHandlerContextByteBuf() throws Exception { + byte[] bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D'}; + ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); + this.handler.userEventTriggered(ctx, byteBuf); + + String newline = StringUtil.NEWLINE; + StringBuilder sb = + new StringBuilder(this.channel.toString()) + .append(" USER_EVENT: ") + .append(bytes.length) + .append('B') + .append(newline); + ByteBufUtil.appendPrettyHexDump(sb, byteBuf); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(sb.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventTriggeredChannelHandlerContextEmptyByteBufHolder() throws Exception { + ByteBuf byteBuf = Unpooled.EMPTY_BUFFER; + ByteBufHolder byteBufHolder = new DefaultByteBufHolder(byteBuf); + this.handler.userEventTriggered(ctx, byteBufHolder); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)) + .trace(this.channel.toString() + " USER_EVENT: " + byteBufHolder.toString() + ", 0B"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventTriggeredChannelHandlerContextByteBufHolder() throws Exception { + byte[] bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D'}; + ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); + ByteBufHolder byteBufHolder = new DefaultByteBufHolder(byteBuf); + this.handler.userEventTriggered(ctx, byteBufHolder); + + String newline = StringUtil.NEWLINE; + StringBuilder sb = + new StringBuilder(this.channel.toString()) + .append(" USER_EVENT: ") + .append(byteBufHolder.toString()) + .append(", ") + .append(bytes.length) + .append('B') + .append(newline); + ByteBufUtil.appendPrettyHexDump(sb, byteBuf); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(sb.toString()); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelReadCompleteChannelHandlerContextNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + nologHandler.channelReadComplete(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " READ_COMPLETE"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelReadChannelHandlerContextObjectNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + Object randomObject = new Object(); + nologHandler.channelRead(ctx, randomObject); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " READ: Object"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testWriteChannelHandlerContextObjectChannelPromiseNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + Object randomObject = new Object(); + ChannelPromise promise = mock(ChannelPromise.class); + nologHandler.write(ctx, randomObject, promise); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " WRITE: Object"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testChannelWritabilityChangedChannelHandlerContextNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + nologHandler.channelWritabilityChanged(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " WRITABILITY_CHANGED"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testFlushChannelHandlerContextNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + nologHandler.flush(ctx); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " FLUSH"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventByteBufNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + byte[] bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D'}; + ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); + nologHandler.userEventTriggered(ctx, byteBuf); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)).trace(this.channel.toString() + " USER_EVENT: " + bytes.length + "B"); + verifyNoMoreInteractions(logger); + } + + @Test + public void testUserEventByteBufHolderNoDetails() throws Exception { + LoggingHandler nologHandler = new LoggingHandler(this.logger, false); + byte[] bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D'}; + ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); + ByteBufHolder byteBufHolder = new DefaultByteBufHolder(byteBuf); + nologHandler.userEventTriggered(ctx, byteBufHolder); + + verify(logger, times(1)).isTraceEnabled(); + verify(logger, times(1)) + .trace( + this.channel.toString() + + " USER_EVENT: " + + byteBufHolder.toString() + + ", " + + bytes.length + + "B"); + verifyNoMoreInteractions(logger); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakerTest.java index afda1d7981..b2fbb4b8a6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/network/p2p/transport/handshake/AuthHandshakerTest.java @@ -64,51 +64,63 @@ package com.radixdlt.network.p2p.transport.handshake; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + import com.radixdlt.DefaultSerialization; import com.radixdlt.crypto.ECKeyOps; import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.serialization.Serialization; -import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeSuccess; import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeError; +import com.radixdlt.network.p2p.transport.handshake.AuthHandshakeResult.AuthHandshakeSuccess; +import com.radixdlt.serialization.Serialization; import io.netty.buffer.Unpooled; -import org.junit.Test; import java.security.SecureRandom; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; public final class AuthHandshakerTest { - private final Serialization serialization = DefaultSerialization.getInstance(); - private final SecureRandom secureRandom = new SecureRandom(); + private final Serialization serialization = DefaultSerialization.getInstance(); + private final SecureRandom secureRandom = new SecureRandom(); - @Test - public void test_auth_handshake() throws Exception { - final var nodeKey1 = ECKeyPair.generateNew(); - final var nodeKey2 = ECKeyPair.generateNew(); - final var handshaker1 = new AuthHandshaker(serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey1), (byte) 0x01); - final var handshaker2 = new AuthHandshaker(serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey2), (byte) 0x01); + @Test + public void test_auth_handshake() throws Exception { + final var nodeKey1 = ECKeyPair.generateNew(); + final var nodeKey2 = ECKeyPair.generateNew(); + final var handshaker1 = + new AuthHandshaker( + serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey1), (byte) 0x01); + final var handshaker2 = + new AuthHandshaker( + serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey2), (byte) 0x01); - final var initMessage = handshaker1.initiate(nodeKey2.getPublicKey()); - final var handshaker2ResultPair = handshaker2.handleInitialMessage(Unpooled.wrappedBuffer(initMessage)); - final var handshaker2Result = (AuthHandshakeSuccess) handshaker2ResultPair.getSecond(); - final var responseMessage = handshaker2ResultPair.getFirst(); - final var handshaker1Result = (AuthHandshakeSuccess) handshaker1.handleResponseMessage(Unpooled.wrappedBuffer(responseMessage)); + final var initMessage = handshaker1.initiate(nodeKey2.getPublicKey()); + final var handshaker2ResultPair = + handshaker2.handleInitialMessage(Unpooled.wrappedBuffer(initMessage)); + final var handshaker2Result = (AuthHandshakeSuccess) handshaker2ResultPair.getSecond(); + final var responseMessage = handshaker2ResultPair.getFirst(); + final var handshaker1Result = + (AuthHandshakeSuccess) + handshaker1.handleResponseMessage(Unpooled.wrappedBuffer(responseMessage)); - assertArrayEquals(handshaker1Result.getSecrets().aes, handshaker2Result.getSecrets().aes); - assertArrayEquals(handshaker1Result.getSecrets().mac, handshaker2Result.getSecrets().mac); - assertArrayEquals(handshaker1Result.getSecrets().token, handshaker2Result.getSecrets().token); - } + assertArrayEquals(handshaker1Result.getSecrets().aes, handshaker2Result.getSecrets().aes); + assertArrayEquals(handshaker1Result.getSecrets().mac, handshaker2Result.getSecrets().mac); + assertArrayEquals(handshaker1Result.getSecrets().token, handshaker2Result.getSecrets().token); + } - @Test - public void test_auth_handshake_fail_on_network_id_mismatch() { - final var nodeKey1 = ECKeyPair.generateNew(); - final var nodeKey2 = ECKeyPair.generateNew(); - final var handshaker1 = new AuthHandshaker(serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey1), (byte) 0x01); - final var handshaker2 = new AuthHandshaker(serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey2), (byte) 0x02); + @Test + public void test_auth_handshake_fail_on_network_id_mismatch() { + final var nodeKey1 = ECKeyPair.generateNew(); + final var nodeKey2 = ECKeyPair.generateNew(); + final var handshaker1 = + new AuthHandshaker( + serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey1), (byte) 0x01); + final var handshaker2 = + new AuthHandshaker( + serialization, secureRandom, ECKeyOps.fromKeyPair(nodeKey2), (byte) 0x02); - final var initMessage = handshaker1.initiate(nodeKey2.getPublicKey()); - final var handshaker2ResultPair = handshaker2.handleInitialMessage(Unpooled.wrappedBuffer(initMessage)); - assertTrue(handshaker2ResultPair.getSecond() instanceof AuthHandshakeError); - assertArrayEquals(new byte[] {0x02}, handshaker2ResultPair.getFirst()); - } + final var initMessage = handshaker1.initiate(nodeKey2.getPublicKey()); + final var handshaker2ResultPair = + handshaker2.handleInitialMessage(Unpooled.wrappedBuffer(initMessage)); + assertTrue(handshaker2ResultPair.getSecond() instanceof AuthHandshakeError); + assertArrayEquals(new byte[] {0x02}, handshaker2ResultPair.getFirst()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/PersistedPropertiesTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/PersistedPropertiesTest.java index ab5ab4218b..2e80c0c266 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/PersistedPropertiesTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/PersistedPropertiesTest.java @@ -64,141 +64,137 @@ package com.radixdlt.properties; +import static org.junit.Assert.*; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; - +import nl.jqno.equalsverifier.EqualsVerifier; import org.apache.commons.cli.ParseException; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; - -import nl.jqno.equalsverifier.EqualsVerifier; - public class PersistedPropertiesTest { - private PersistedProperties properties; - - @Before - public void setUp() { - this.properties = new PersistedProperties(); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(PersistedProperties.class) - .usingGetClass() - .verify(); - } - - @Test - public void testLoadFromResources() throws ParseException, IOException { - final String testProperties = "test.properties"; - Files.deleteIfExists(Paths.get(testProperties)); - assertFalse(Files.exists(Paths.get(testProperties))); - - this.properties.load(testProperties); // Should load from resources - - assertEquals("a", this.properties.get("a")); - assertEquals("b", this.properties.get("b")); - assertTrue(this.properties.get("c") == null); - assertEquals("abc", this.properties.get("aaa")); - assertEquals("\" \"", this.properties.get("bbb")); - } - - @Test - public void testLoadFromDisk() throws ParseException, IOException { - final String testProperties = "test.properties"; - Files.deleteIfExists(Paths.get(testProperties)); - assertFalse(Files.exists(Paths.get(testProperties))); - - this.properties.load(testProperties); // Should load from resources - this.properties.set("c", "c"); - this.properties.save(testProperties); - assertTrue(Files.exists(Paths.get(testProperties))); // Now have properties file on disk - - PersistedProperties newCut = new PersistedProperties(); - newCut.load(testProperties); // Should load from file - - assertEquals("a", this.properties.get("a")); - assertEquals("b", this.properties.get("b")); - assertEquals("c", this.properties.get("c")); - } - - @Test(expected = ParseException.class) - public void testLoadNoDefaultThrowsException() throws IOException, ParseException { - final String testProperties = "notexist.properties"; - Files.deleteIfExists(Paths.get(testProperties)); - assertFalse(Files.exists(Paths.get(testProperties))); - - this.properties.load(testProperties); - } - - @Test(expected = IllegalArgumentException.class) - public void testGetInt() { - populateData(); - assertEquals(12, this.properties.get("int.exist", -1)); - assertEquals(-1, this.properties.get("int.notexist", -1)); - - this.properties.get("string.exist", -1); - } - - @Test(expected = IllegalArgumentException.class) - public void testGetLong() { - populateData(); - assertEquals(12L, this.properties.get("long.exist", -1L)); - assertEquals(-1L, this.properties.get("int.notexist", -1L)); - - this.properties.get("string.exist", -1L); - } - - @Test(expected = IllegalArgumentException.class) - public void testGetDouble() { - populateData(); - assertEquals(1.2, this.properties.get("double.exist", -1.0), Math.ulp(1.2)); - assertEquals(-1.0, this.properties.get("double.notexist", -1.0), Math.ulp(1.0)); - - this.properties.get("string.exist", -1.0); - } - - @Test - public void testGetBoolean() { - populateData(); - assertTrue(this.properties.get("bool.true", false)); - assertTrue(this.properties.get("bool.true1", false)); - assertFalse(this.properties.get("bool.false", true)); - assertFalse(this.properties.get("bool.false0", true)); - assertTrue(this.properties.get("bool.notexist", true)); - assertFalse(this.properties.get("bool.notexist", false)); - } - - @Test - public void testGetString() { - populateData(); - assertEquals("string", this.properties.get("string.exist", "default")); - assertEquals("default", this.properties.get("string.notexist", "default")); - } - - @Test - public void testToString() { - populateData(); - - var result = this.properties.toString(); - assertTrue(result.contains("int.exist")); - assertTrue(result.contains("long.exist")); - assertTrue(result.contains("bool.true")); - assertTrue(result.contains("bool.true1")); - } - - private void populateData() { - this.properties.set("int.exist", "12"); - this.properties.set("long.exist", "12"); - this.properties.set("double.exist", "1.2"); - this.properties.set("bool.true", "true"); - this.properties.set("bool.false", "false"); - this.properties.set("bool.true1", "1"); - this.properties.set("bool.false0", "0"); - this.properties.set("string.exist", "string"); - } + private PersistedProperties properties; + + @Before + public void setUp() { + this.properties = new PersistedProperties(); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(PersistedProperties.class).usingGetClass().verify(); + } + + @Test + public void testLoadFromResources() throws ParseException, IOException { + final String testProperties = "test.properties"; + Files.deleteIfExists(Paths.get(testProperties)); + assertFalse(Files.exists(Paths.get(testProperties))); + + this.properties.load(testProperties); // Should load from resources + + assertEquals("a", this.properties.get("a")); + assertEquals("b", this.properties.get("b")); + assertTrue(this.properties.get("c") == null); + assertEquals("abc", this.properties.get("aaa")); + assertEquals("\" \"", this.properties.get("bbb")); + } + + @Test + public void testLoadFromDisk() throws ParseException, IOException { + final String testProperties = "test.properties"; + Files.deleteIfExists(Paths.get(testProperties)); + assertFalse(Files.exists(Paths.get(testProperties))); + + this.properties.load(testProperties); // Should load from resources + this.properties.set("c", "c"); + this.properties.save(testProperties); + assertTrue(Files.exists(Paths.get(testProperties))); // Now have properties file on disk + + PersistedProperties newCut = new PersistedProperties(); + newCut.load(testProperties); // Should load from file + + assertEquals("a", this.properties.get("a")); + assertEquals("b", this.properties.get("b")); + assertEquals("c", this.properties.get("c")); + } + + @Test(expected = ParseException.class) + public void testLoadNoDefaultThrowsException() throws IOException, ParseException { + final String testProperties = "notexist.properties"; + Files.deleteIfExists(Paths.get(testProperties)); + assertFalse(Files.exists(Paths.get(testProperties))); + + this.properties.load(testProperties); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetInt() { + populateData(); + assertEquals(12, this.properties.get("int.exist", -1)); + assertEquals(-1, this.properties.get("int.notexist", -1)); + + this.properties.get("string.exist", -1); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetLong() { + populateData(); + assertEquals(12L, this.properties.get("long.exist", -1L)); + assertEquals(-1L, this.properties.get("int.notexist", -1L)); + + this.properties.get("string.exist", -1L); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetDouble() { + populateData(); + assertEquals(1.2, this.properties.get("double.exist", -1.0), Math.ulp(1.2)); + assertEquals(-1.0, this.properties.get("double.notexist", -1.0), Math.ulp(1.0)); + + this.properties.get("string.exist", -1.0); + } + + @Test + public void testGetBoolean() { + populateData(); + assertTrue(this.properties.get("bool.true", false)); + assertTrue(this.properties.get("bool.true1", false)); + assertFalse(this.properties.get("bool.false", true)); + assertFalse(this.properties.get("bool.false0", true)); + assertTrue(this.properties.get("bool.notexist", true)); + assertFalse(this.properties.get("bool.notexist", false)); + } + + @Test + public void testGetString() { + populateData(); + assertEquals("string", this.properties.get("string.exist", "default")); + assertEquals("default", this.properties.get("string.notexist", "default")); + } + + @Test + public void testToString() { + populateData(); + + var result = this.properties.toString(); + assertTrue(result.contains("int.exist")); + assertTrue(result.contains("long.exist")); + assertTrue(result.contains("bool.true")); + assertTrue(result.contains("bool.true1")); + } + + private void populateData() { + this.properties.set("int.exist", "12"); + this.properties.set("long.exist", "12"); + this.properties.set("double.exist", "1.2"); + this.properties.set("bool.true", "true"); + this.properties.set("bool.false", "false"); + this.properties.set("bool.true1", "1"); + this.properties.set("bool.false0", "0"); + this.properties.set("string.exist", "string"); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/RuntimePropertiesTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/RuntimePropertiesTest.java index 700f43c653..806302ef1e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/RuntimePropertiesTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/properties/RuntimePropertiesTest.java @@ -64,90 +64,89 @@ package com.radixdlt.properties; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.*; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; - +import nl.jqno.equalsverifier.EqualsVerifier; import org.apache.commons.cli.ParseException; import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; - -import nl.jqno.equalsverifier.EqualsVerifier; - public class RuntimePropertiesTest { - private static final String TEST_PROPERTIES = "test.properties"; - private static final String TEST_OPTION = "test option"; + private static final String TEST_PROPERTIES = "test.properties"; + private static final String TEST_OPTION = "test option"; - private final JSONObject options = new JSONObject( - "{\n" - + " \"config\":{\n" - + " \"short\":\"c\",\n" - + " \"desc\":\"The configuration to load\",\n" - + " \"has_arg\":true\n" - + " },\n" - + " \"test_arg\":{\n" - + " \"short\":\"ta\",\n" - + " \"desc\":\"Test option with arg\",\n" - + " \"has_arg\":true\n" - + " },\n" - + " \"test_noarg\":{\n" - + " \"short\":\"tn\",\n" - + " \"desc\":\"Test option with no arg\",\n" - + " \"has_arg\":false\n" - + " },\n" - + "}" - ); - private RuntimeProperties properties; + private final JSONObject options = + new JSONObject( + "{\n" + + " \"config\":{\n" + + " \"short\":\"c\",\n" + + " \"desc\":\"The configuration to load\",\n" + + " \"has_arg\":true\n" + + " },\n" + + " \"test_arg\":{\n" + + " \"short\":\"ta\",\n" + + " \"desc\":\"Test option with arg\",\n" + + " \"has_arg\":true\n" + + " },\n" + + " \"test_noarg\":{\n" + + " \"short\":\"tn\",\n" + + " \"desc\":\"Test option with no arg\",\n" + + " \"has_arg\":false\n" + + " },\n" + + "}"); + private RuntimeProperties properties; - @Before - public void setUp() throws ParseException, IOException { - String[] cmdLine = new String[] { - "-c", TEST_PROPERTIES, - "-ta", TEST_OPTION, - "-tn" - }; - Files.deleteIfExists(Paths.get(TEST_PROPERTIES)); - assertFalse(Files.exists(Paths.get(TEST_PROPERTIES))); - this.properties = new RuntimeProperties(options, cmdLine); - Files.deleteIfExists(Paths.get(TEST_PROPERTIES)); - assertFalse(Files.exists(Paths.get(TEST_PROPERTIES))); - } + @Before + public void setUp() throws ParseException, IOException { + String[] cmdLine = + new String[] { + "-c", TEST_PROPERTIES, + "-ta", TEST_OPTION, + "-tn" + }; + Files.deleteIfExists(Paths.get(TEST_PROPERTIES)); + assertFalse(Files.exists(Paths.get(TEST_PROPERTIES))); + this.properties = new RuntimeProperties(options, cmdLine); + Files.deleteIfExists(Paths.get(TEST_PROPERTIES)); + assertFalse(Files.exists(Paths.get(TEST_PROPERTIES))); + } - @After - public void shutDown() throws IOException { - Files.deleteIfExists(Paths.get(TEST_PROPERTIES)); - } + @After + public void shutDown() throws IOException { + Files.deleteIfExists(Paths.get(TEST_PROPERTIES)); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(RuntimeProperties.class) - .usingGetClass() - .withNonnullFields("commandLine") - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(RuntimeProperties.class) + .usingGetClass() + .withNonnullFields("commandLine") + .verify(); + } - @Test - public void testGet() { - assertThat(this.properties.get("a")).isEqualTo("a"); - assertThat(this.properties.get("b")).isEqualTo("b"); - assertThat(this.properties.get("c")).isEqualTo(TEST_PROPERTIES); - assertThat(this.properties.get("ta")).isEqualTo(TEST_OPTION); - assertThat(this.properties.get("tn")).isEqualTo("1"); - assertThat(this.properties.get("notexist")).isNull(); - } + @Test + public void testGet() { + assertThat(this.properties.get("a")).isEqualTo("a"); + assertThat(this.properties.get("b")).isEqualTo("b"); + assertThat(this.properties.get("c")).isEqualTo(TEST_PROPERTIES); + assertThat(this.properties.get("ta")).isEqualTo(TEST_OPTION); + assertThat(this.properties.get("tn")).isEqualTo("1"); + assertThat(this.properties.get("notexist")).isNull(); + } - @Test - public void testToString() { - var result = this.properties.toString(); - System.out.println(result); + @Test + public void testToString() { + var result = this.properties.toString(); + System.out.println(result); - assertTrue(result.contains("[aaa, a, b, bbb]")); - assertTrue(result.contains("args=[]")); - } + assertTrue(result.contains("[aaa, a, b, bbb]")); + assertTrue(result.contains("args=[]")); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/MockedRecoveryModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/MockedRecoveryModule.java index 4d80356494..e5a7436164 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/MockedRecoveryModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/MockedRecoveryModule.java @@ -70,9 +70,9 @@ import com.radixdlt.consensus.BFTConfiguration; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; +import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.UnverifiedVertex; -import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.VerifiedVertex; @@ -86,68 +86,60 @@ import com.radixdlt.ledger.AccumulatorState; import com.radixdlt.store.LastEpochProof; import com.radixdlt.store.LastProof; - import java.util.Optional; -/** - * Starting configuration for simulation/deterministic steady state tests. - */ +/** Starting configuration for simulation/deterministic steady state tests. */ public class MockedRecoveryModule extends AbstractModule { - private final HashCode genesisHash; + private final HashCode genesisHash; - public MockedRecoveryModule() { - this(HashUtils.zero256()); - } + public MockedRecoveryModule() { + this(HashUtils.zero256()); + } - public MockedRecoveryModule(HashCode genesisHash) { - this.genesisHash = genesisHash; - } + public MockedRecoveryModule(HashCode genesisHash) { + this.genesisHash = genesisHash; + } - @Provides - private ViewUpdate view(BFTConfiguration configuration, ProposerElection proposerElection) { - HighQC highQC = configuration.getVertexStoreState().getHighQC(); - View view = highQC.highestQC().getView().next(); - final BFTNode leader = proposerElection.getProposer(view); - final BFTNode nextLeader = proposerElection.getProposer(view.next()); + @Provides + private ViewUpdate view(BFTConfiguration configuration, ProposerElection proposerElection) { + HighQC highQC = configuration.getVertexStoreState().getHighQC(); + View view = highQC.highestQC().getView().next(); + final BFTNode leader = proposerElection.getProposer(view); + final BFTNode nextLeader = proposerElection.getProposer(view.next()); - return ViewUpdate.create(view, highQC, leader, nextLeader); - } + return ViewUpdate.create(view, highQC, leader, nextLeader); + } - @Provides - private BFTConfiguration configuration( - @LastEpochProof LedgerProof proof, - BFTValidatorSet validatorSet, - Hasher hasher - ) { - var accumulatorState = new AccumulatorState(0, genesisHash); - UnverifiedVertex genesis = UnverifiedVertex.createGenesis(LedgerHeader.genesis(accumulatorState, validatorSet, 0)); - VerifiedVertex verifiedGenesis = new VerifiedVertex(genesis, genesisHash); - LedgerHeader nextLedgerHeader = LedgerHeader.create( - proof.getEpoch() + 1, - View.genesis(), - proof.getAccumulatorState(), - proof.timestamp() - ); - var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesis, nextLedgerHeader); - var proposerElection = new WeightedRotatingLeaders(validatorSet); - return new BFTConfiguration( - proposerElection, - validatorSet, - VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesis, Optional.empty(), hasher) - ); - } + @Provides + private BFTConfiguration configuration( + @LastEpochProof LedgerProof proof, BFTValidatorSet validatorSet, Hasher hasher) { + var accumulatorState = new AccumulatorState(0, genesisHash); + UnverifiedVertex genesis = + UnverifiedVertex.createGenesis(LedgerHeader.genesis(accumulatorState, validatorSet, 0)); + VerifiedVertex verifiedGenesis = new VerifiedVertex(genesis, genesisHash); + LedgerHeader nextLedgerHeader = + LedgerHeader.create( + proof.getEpoch() + 1, View.genesis(), proof.getAccumulatorState(), proof.timestamp()); + var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesis, nextLedgerHeader); + var proposerElection = new WeightedRotatingLeaders(validatorSet); + return new BFTConfiguration( + proposerElection, + validatorSet, + VerifiedVertexStoreState.create( + HighQC.from(genesisQC), verifiedGenesis, Optional.empty(), hasher)); + } - @Provides - @LastEpochProof - public LedgerProof lastEpochProof(BFTValidatorSet validatorSet) { - var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); - return LedgerProof.genesis(accumulatorState, validatorSet, 0); - } + @Provides + @LastEpochProof + public LedgerProof lastEpochProof(BFTValidatorSet validatorSet) { + var accumulatorState = new AccumulatorState(0, HashUtils.zero256()); + return LedgerProof.genesis(accumulatorState, validatorSet, 0); + } - @Provides - @LastProof - private LedgerProof lastProof(BFTConfiguration bftConfiguration) { - return bftConfiguration.getVertexStoreState().getRootHeader(); - } + @Provides + @LastProof + private LedgerProof lastProof(BFTConfiguration bftConfiguration) { + return bftConfiguration.getVertexStoreState().getRootHeader(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/RecoveryTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/RecoveryTest.java index 3deb1ace8e..e536b0fe8f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/RecoveryTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/recovery/RecoveryTest.java @@ -64,25 +64,9 @@ package com.radixdlt.recovery; -import com.google.common.collect.ClassToInstanceMap; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.application.system.FeeTable; -import com.radixdlt.environment.Environment; -import com.radixdlt.environment.deterministic.LastEventsModule; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import org.assertj.core.api.Condition; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; +import static org.assertj.core.api.Assertions.assertThat; +import com.google.common.collect.ClassToInstanceMap; import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; import com.google.inject.Guice; @@ -90,11 +74,14 @@ import com.google.inject.Key; import com.google.inject.TypeLiteral; import com.radixdlt.PersistedNodeForTestingModule; +import com.radixdlt.application.system.FeeTable; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.Self; +import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.epoch.EpochView; import com.radixdlt.consensus.epoch.EpochViewUpdate; import com.radixdlt.consensus.epoch.Epoched; @@ -103,7 +90,9 @@ import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; +import com.radixdlt.environment.Environment; import com.radixdlt.environment.deterministic.DeterministicProcessor; +import com.radixdlt.environment.deterministic.LastEventsModule; import com.radixdlt.environment.deterministic.network.ControlledMessage; import com.radixdlt.environment.deterministic.network.DeterministicNetwork; import com.radixdlt.environment.deterministic.network.MessageMutator; @@ -112,13 +101,16 @@ import com.radixdlt.network.p2p.PeersView; import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseEnvironment; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.LastEpochProof; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; import com.radixdlt.sync.CommittedReader; - +import io.reactivex.rxjava3.schedulers.Timed; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -126,229 +118,225 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Stream; - -import io.reactivex.rxjava3.schedulers.Timed; - -import static org.assertj.core.api.Assertions.assertThat; +import org.assertj.core.api.Condition; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** - * Verifies that on restarts (simulated via creation of new injectors) that the application - * state is the same as last seen. + * Verifies that on restarts (simulated via creation of new injectors) that the application state is + * the same as last seen. */ @RunWith(Parameterized.class) public class RecoveryTest { - @Parameters - public static Collection parameters() { - return List.of( - new Object[]{10L, 80}, - new Object[]{10L, 90}, - new Object[]{10L, 100}, - new Object[]{10L, 500}, - new Object[]{1000000L, 100} - ); - } - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - private DeterministicNetwork network; - private Injector currentInjector; - private ECKeyPair ecKeyPair = ECKeyPair.generateNew(); - private final long epochCeilingView; - private final int processForCount; - - public RecoveryTest(long epochCeilingView, int processForCount) { - this.epochCeilingView = epochCeilingView; - this.processForCount = processForCount; - this.network = new DeterministicNetwork( - List.of(BFTNode.create(ecKeyPair.getPublicKey())), - MessageSelector.firstSelector(), - MessageMutator.nothing() - ); - } - - @Before - public void setup() { - this.currentInjector = createRunner(ecKeyPair); - this.currentInjector.getInstance(DeterministicProcessor.class).start(); - } - - @After - public void teardown() { - if (this.currentInjector != null) { - this.currentInjector.getInstance(BerkeleyLedgerEntryStore.class).close(); - this.currentInjector.getInstance(PersistentSafetyStateStore.class).close(); - this.currentInjector.getInstance(DatabaseEnvironment.class).stop(); - } - } - - private Injector createRunner(ECKeyPair ecKeyPair) { - final BFTNode self = BFTNode.create(ecKeyPair.getPublicKey()); - - return Guice.createInjector( - new MockedGenesisModule( - Set.of(ecKeyPair.getPublicKey()), - Amount.ofTokens(1000), - Amount.ofTokens(100) - ), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule( - new RERulesConfig( - Set.of("xrd"), - Pattern.compile("[a-z0-9]+"), - FeeTable.noFees(), - 1024 * 1024, - OptionalInt.of(50), - epochCeilingView, - 2, - Amount.ofTokens(10), - 1, - Amount.ofTokens(10), - 9800, - 10 - )), - new ForksModule(), - MempoolConfig.asModule(10, 10), - new LastEventsModule(EpochViewUpdate.class, Vote.class), - new AbstractModule() { - @Override - protected void configure() { - bind(PeersView.class).toInstance(Stream::of); - bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); - bind(new TypeLiteral>() { }).toInstance(ImmutableList.of(self)); - bind(Environment.class).toInstance(network.createSender(BFTNode.create(self.getKey()))); - bindConstant().annotatedWith(DatabaseLocation.class) - .to(folder.getRoot().getAbsolutePath() + "/RADIXDB_RECOVERY_TEST_" + self); - } - }, - new PersistedNodeForTestingModule() - ); - } - - private RadixEngine getRadixEngine() { - return currentInjector.getInstance(Key.get(new TypeLiteral>() { })); - } - - private CommittedReader getCommittedReader() { - return currentInjector.getInstance(CommittedReader.class); - } - - private EpochView getLastEpochView() { - return currentInjector.getInstance(Key.get(new TypeLiteral>() { })) - .getInstance(EpochViewUpdate.class).getEpochView(); - } - - private Vote getLastVote() { - return currentInjector.getInstance(Key.get(new TypeLiteral>() { })) - .getInstance(Vote.class); - } - - private void restartNode() { - this.network.dropMessages(m -> m.channelId().receiverIndex() == 0 && m.channelId().senderIndex() == 0); - this.currentInjector.getInstance(BerkeleyLedgerEntryStore.class).close(); - this.currentInjector.getInstance(PersistentSafetyStateStore.class).close(); - this.currentInjector.getInstance(DatabaseEnvironment.class).stop(); - this.currentInjector = createRunner(ecKeyPair); - var processor = currentInjector.getInstance(DeterministicProcessor.class); - processor.start(); - } - - private void processForCount(int messageCount) { - for (int i = 0; i < messageCount; i++) { - Timed msg = this.network.nextMessage(); - var runner = currentInjector.getInstance(DeterministicProcessor.class); - runner.handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); - } - } - - @Test - public void on_reboot_should_load_same_last_header() { - // Arrange - processForCount(processForCount); - var reader = getCommittedReader(); - Optional proof = reader.getLastProof(); - - // Act - restartNode(); - - // Assert - var restartedReader = getCommittedReader(); - Optional restartedProof = restartedReader.getLastProof(); - assertThat(restartedProof).isEqualTo(proof); - } - - @Test - public void on_reboot_should_load_same_last_epoch_header() { - // Arrange - processForCount(processForCount); - var epochView = getLastEpochView(); - - // Act - restartNode(); - - // Assert - LedgerProof restartedEpochProof = currentInjector.getInstance( - Key.get(LedgerProof.class, LastEpochProof.class) - ); - - assertThat(restartedEpochProof.isEndOfEpoch()).isTrue(); - assertThat( - restartedEpochProof.getEpoch() == epochView.getEpoch() - 1 - || (restartedEpochProof.getEpoch() == epochView.getEpoch() - && epochView.getView().number() > epochCeilingView + 3) - ).isTrue(); - } - - @Test - public void on_reboot_should_load_same_last_vote() { - // Arrange - processForCount(processForCount); - Vote vote = getLastVote(); - - // Act - restartNode(); - - // Assert - SafetyState safetyState = currentInjector.getInstance(SafetyState.class); - assertThat( - safetyState.getLastVotedView().equals(vote.getView()) - || (safetyState.getLastVotedView().equals(View.genesis()) - && vote.getView().equals(View.of(epochCeilingView + 3))) - ).isTrue(); - } - - @Test - public void on_reboot_should_only_emit_pacemaker_events() { - // Arrange - processForCount(processForCount); - - // Act - restartNode(); - - // Assert - assertThat(network.allMessages()) - .hasSize(3) - .haveExactly( - 1, - new Condition<>( - msg -> Epoched.isInstance(msg.message(), ScheduledLocalTimeout.class), - "A single epoched scheduled timeout has been emitted" - ) - ) - .haveExactly( - 1, - new Condition<>( - msg -> msg.message() instanceof ScheduledLocalTimeout, - "A single scheduled timeout update has been emitted" - ) - ) - .haveExactly( - 1, - new Condition<>( - msg -> msg.message() instanceof Proposal, - "A proposal has been emitted" - ) - ); - } + @Parameters + public static Collection parameters() { + return List.of( + new Object[] {10L, 80}, + new Object[] {10L, 90}, + new Object[] {10L, 100}, + new Object[] {10L, 500}, + new Object[] {1000000L, 100}); + } + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + private DeterministicNetwork network; + private Injector currentInjector; + private ECKeyPair ecKeyPair = ECKeyPair.generateNew(); + private final long epochCeilingView; + private final int processForCount; + + public RecoveryTest(long epochCeilingView, int processForCount) { + this.epochCeilingView = epochCeilingView; + this.processForCount = processForCount; + this.network = + new DeterministicNetwork( + List.of(BFTNode.create(ecKeyPair.getPublicKey())), + MessageSelector.firstSelector(), + MessageMutator.nothing()); + } + + @Before + public void setup() { + this.currentInjector = createRunner(ecKeyPair); + this.currentInjector.getInstance(DeterministicProcessor.class).start(); + } + + @After + public void teardown() { + if (this.currentInjector != null) { + this.currentInjector.getInstance(BerkeleyLedgerEntryStore.class).close(); + this.currentInjector.getInstance(PersistentSafetyStateStore.class).close(); + this.currentInjector.getInstance(DatabaseEnvironment.class).stop(); + } + } + + private Injector createRunner(ECKeyPair ecKeyPair) { + final BFTNode self = BFTNode.create(ecKeyPair.getPublicKey()); + + return Guice.createInjector( + new MockedGenesisModule( + Set.of(ecKeyPair.getPublicKey()), Amount.ofTokens(1000), Amount.ofTokens(100)), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + new RERulesConfig( + Set.of("xrd"), + Pattern.compile("[a-z0-9]+"), + FeeTable.noFees(), + 1024 * 1024, + OptionalInt.of(50), + epochCeilingView, + 2, + Amount.ofTokens(10), + 1, + Amount.ofTokens(10), + 9800, + 10)), + new ForksModule(), + MempoolConfig.asModule(10, 10), + new LastEventsModule(EpochViewUpdate.class, Vote.class), + new AbstractModule() { + @Override + protected void configure() { + bind(PeersView.class).toInstance(Stream::of); + bind(ECKeyPair.class).annotatedWith(Self.class).toInstance(ecKeyPair); + bind(new TypeLiteral>() {}).toInstance(ImmutableList.of(self)); + bind(Environment.class).toInstance(network.createSender(BFTNode.create(self.getKey()))); + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath() + "/RADIXDB_RECOVERY_TEST_" + self); + } + }, + new PersistedNodeForTestingModule()); + } + + private RadixEngine getRadixEngine() { + return currentInjector.getInstance( + Key.get(new TypeLiteral>() {})); + } + + private CommittedReader getCommittedReader() { + return currentInjector.getInstance(CommittedReader.class); + } + + private EpochView getLastEpochView() { + return currentInjector + .getInstance(Key.get(new TypeLiteral>() {})) + .getInstance(EpochViewUpdate.class) + .getEpochView(); + } + + private Vote getLastVote() { + return currentInjector + .getInstance(Key.get(new TypeLiteral>() {})) + .getInstance(Vote.class); + } + + private void restartNode() { + this.network.dropMessages( + m -> m.channelId().receiverIndex() == 0 && m.channelId().senderIndex() == 0); + this.currentInjector.getInstance(BerkeleyLedgerEntryStore.class).close(); + this.currentInjector.getInstance(PersistentSafetyStateStore.class).close(); + this.currentInjector.getInstance(DatabaseEnvironment.class).stop(); + this.currentInjector = createRunner(ecKeyPair); + var processor = currentInjector.getInstance(DeterministicProcessor.class); + processor.start(); + } + + private void processForCount(int messageCount) { + for (int i = 0; i < messageCount; i++) { + Timed msg = this.network.nextMessage(); + var runner = currentInjector.getInstance(DeterministicProcessor.class); + runner.handleMessage(msg.value().origin(), msg.value().message(), msg.value().typeLiteral()); + } + } + + @Test + public void on_reboot_should_load_same_last_header() { + // Arrange + processForCount(processForCount); + var reader = getCommittedReader(); + Optional proof = reader.getLastProof(); + + // Act + restartNode(); + + // Assert + var restartedReader = getCommittedReader(); + Optional restartedProof = restartedReader.getLastProof(); + assertThat(restartedProof).isEqualTo(proof); + } + + @Test + public void on_reboot_should_load_same_last_epoch_header() { + // Arrange + processForCount(processForCount); + var epochView = getLastEpochView(); + + // Act + restartNode(); + + // Assert + LedgerProof restartedEpochProof = + currentInjector.getInstance(Key.get(LedgerProof.class, LastEpochProof.class)); + + assertThat(restartedEpochProof.isEndOfEpoch()).isTrue(); + assertThat( + restartedEpochProof.getEpoch() == epochView.getEpoch() - 1 + || (restartedEpochProof.getEpoch() == epochView.getEpoch() + && epochView.getView().number() > epochCeilingView + 3)) + .isTrue(); + } + + @Test + public void on_reboot_should_load_same_last_vote() { + // Arrange + processForCount(processForCount); + Vote vote = getLastVote(); + + // Act + restartNode(); + + // Assert + SafetyState safetyState = currentInjector.getInstance(SafetyState.class); + assertThat( + safetyState.getLastVotedView().equals(vote.getView()) + || (safetyState.getLastVotedView().equals(View.genesis()) + && vote.getView().equals(View.of(epochCeilingView + 3)))) + .isTrue(); + } + + @Test + public void on_reboot_should_only_emit_pacemaker_events() { + // Arrange + processForCount(processForCount); + + // Act + restartNode(); + + // Assert + assertThat(network.allMessages()) + .hasSize(3) + .haveExactly( + 1, + new Condition<>( + msg -> Epoched.isInstance(msg.message(), ScheduledLocalTimeout.class), + "A single epoched scheduled timeout has been emitted")) + .haveExactly( + 1, + new Condition<>( + msg -> msg.message() instanceof ScheduledLocalTimeout, + "A single scheduled timeout update has been emitted")) + .haveExactly( + 1, + new Condition<>( + msg -> msg.message() instanceof Proposal, "A proposal has been emitted")); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteExecutor.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteExecutor.java index 30f8d6c864..cb5335aef4 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteExecutor.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteExecutor.java @@ -64,6 +64,9 @@ package com.radixdlt.sanitytestsuite; +import static java.util.stream.Collectors.toMap; +import static org.junit.Assert.assertEquals; + import com.google.common.collect.ImmutableList; import com.radixdlt.sanitytestsuite.model.SanityTestSuiteRoot; import com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner; @@ -72,72 +75,65 @@ import com.radixdlt.sanitytestsuite.scenario.keysign.KeySignTestScenarioRunner; import com.radixdlt.sanitytestsuite.scenario.keyverify.KeyVerifyTestScenarioRunner; import com.radixdlt.sanitytestsuite.scenario.radixhashing.RadixHashingTestScenarioRunner; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.Ignore; -import org.junit.Test; - import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; - -import static java.util.stream.Collectors.toMap; -import static org.junit.Assert.assertEquals; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Ignore; +import org.junit.Test; public final class SanityTestSuiteExecutor { - private static final Logger LOG = LogManager.getLogger(); - private static final String SANITY_TEST_SUITE_JSON_FILE_NAME = "sanity_test_suite.json"; + private static final Logger LOG = LogManager.getLogger(); + private static final String SANITY_TEST_SUITE_JSON_FILE_NAME = "sanity_test_suite.json"; - private final List> testScenarios = ImmutableList.of( - new HashingTestScenarioRunner(), - new RadixHashingTestScenarioRunner(), - new KeyGenTestScenarioRunner(), - new KeySignTestScenarioRunner(), - new KeyVerifyTestScenarioRunner() - ); + private final List> testScenarios = + ImmutableList.of( + new HashingTestScenarioRunner(), + new RadixHashingTestScenarioRunner(), + new KeyGenTestScenarioRunner(), + new KeySignTestScenarioRunner(), + new KeyVerifyTestScenarioRunner()); - @Test - @Ignore - public void test_sanity_suite() { - var sanityTestSuiteRoot = sanityTestSuiteRootFromFile(); - var scenarioRunnerMap = makeScenarioRunnerMap(); + @Test + @Ignore + public void test_sanity_suite() { + var sanityTestSuiteRoot = sanityTestSuiteRootFromFile(); + var scenarioRunnerMap = makeScenarioRunnerMap(); - assertEquals( - scenarioRunnerMap.keySet(), - sanityTestSuiteRoot.suite.scenarios.stream() - .map(s -> s.identifier) - .collect(Collectors.toSet()) - ); + assertEquals( + scenarioRunnerMap.keySet(), + sanityTestSuiteRoot.suite.scenarios.stream() + .map(s -> s.identifier) + .collect(Collectors.toSet())); - for (var scenario : sanityTestSuiteRoot.suite.scenarios) { - var scenarioRunner = scenarioRunnerMap.get(scenario.identifier); - // Run test scenario - LOG.debug("🔮 Running scenario: {}", scenario.name); + for (var scenario : sanityTestSuiteRoot.suite.scenarios) { + var scenarioRunner = scenarioRunnerMap.get(scenario.identifier); + // Run test scenario + LOG.debug("🔮 Running scenario: {}", scenario.name); - try { - scenarioRunner.accept(scenario); - LOG.info("✅ Test of scenario '{}' passed", scenario.name); - } catch (AssertionError testAssertionError) { - var failDebugInfo = scenario.failDescriptionWithAssertionError(testAssertionError); - LOG.error(failDebugInfo); - throw new AssertionError(failDebugInfo, testAssertionError); - } - } - } + try { + scenarioRunner.accept(scenario); + LOG.info("✅ Test of scenario '{}' passed", scenario.name); + } catch (AssertionError testAssertionError) { + var failDebugInfo = scenario.failDescriptionWithAssertionError(testAssertionError); + LOG.error(failDebugInfo); + throw new AssertionError(failDebugInfo, testAssertionError); + } + } + } - private SanityTestSuiteRoot sanityTestSuiteRootFromFile() { - return new SanityTestSuiteTestLoader() - .sanityTestSuiteRootFromFileNamed(SANITY_TEST_SUITE_JSON_FILE_NAME); - } + private SanityTestSuiteRoot sanityTestSuiteRootFromFile() { + return new SanityTestSuiteTestLoader() + .sanityTestSuiteRootFromFileNamed(SANITY_TEST_SUITE_JSON_FILE_NAME); + } - private Map> makeScenarioRunnerMap() { - return testScenarios.stream() - .collect( - toMap( - SanityTestScenarioRunner::testScenarioIdentifier, - s -> scenario -> s.executeTest(scenario) - ) - ); - } + private Map> makeScenarioRunnerMap() { + return testScenarios.stream() + .collect( + toMap( + SanityTestScenarioRunner::testScenarioIdentifier, + s -> scenario -> s.executeTest(scenario))); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteTestLoader.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteTestLoader.java index 019197bf00..d46074ed45 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteTestLoader.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/SanityTestSuiteTestLoader.java @@ -64,56 +64,56 @@ package com.radixdlt.sanitytestsuite; +import static com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner.sha256Hash; +import static org.junit.Assert.assertEquals; + import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.io.Files; import com.radixdlt.sanitytestsuite.model.SanityTestSuiteRoot; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.JSONFormatter; - import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import static com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner.sha256Hash; -import static org.junit.Assert.assertEquals; - public final class SanityTestSuiteTestLoader { - private final ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper = new ObjectMapper(); - public SanityTestSuiteRoot sanityTestSuiteRootFromFileNamed(String sanityTestJSONFileName) { - try { - var sanityTestSuiteRoot = readTestSuiteContent(sanityTestJSONFileName); - var calculated = calculateSuiteHash(sanityTestSuiteRoot); - var expected = sanityTestSuiteRoot.integrity.hashOfSuite; + public SanityTestSuiteRoot sanityTestSuiteRootFromFileNamed(String sanityTestJSONFileName) { + try { + var sanityTestSuiteRoot = readTestSuiteContent(sanityTestJSONFileName); + var calculated = calculateSuiteHash(sanityTestSuiteRoot); + var expected = sanityTestSuiteRoot.integrity.hashOfSuite; - // Compare saved hash in file with calculated hash of test. - assertEquals(prepareMessage(sanityTestSuiteRoot), expected, calculated); + // Compare saved hash in file with calculated hash of test. + assertEquals(prepareMessage(sanityTestSuiteRoot), expected, calculated); - return sanityTestSuiteRoot; - } catch (IOException e) { - throw new IllegalStateException("failed to sanity test suite", e); - } - } + return sanityTestSuiteRoot; + } catch (IOException e) { + throw new IllegalStateException("failed to sanity test suite", e); + } + } - private String prepareMessage(SanityTestSuiteRoot sanityTest) { - return String.format( - "Mismatch between calculated hash of test suite and expected (bundled hash), implementation info: %s", - sanityTest.integrity.implementationInfo - ); - } + private String prepareMessage(SanityTestSuiteRoot sanityTest) { + return String.format( + "Mismatch between calculated hash of test suite and expected (bundled hash), implementation" + + " info: %s", + sanityTest.integrity.implementationInfo); + } - private String calculateSuiteHash(SanityTestSuiteRoot sanityTest) { - final var suiteStringRaw = JSONFormatter.sortPrettyPrintObject(sanityTest.suite); - final var suiteString = suiteStringRaw.replace("\r\n", "\n"); // Fix CRLF line ends - final var suiteBytes = suiteString.getBytes(StandardCharsets.UTF_8); - return Bytes.toHexString(sha256Hash(suiteBytes)); - } + private String calculateSuiteHash(SanityTestSuiteRoot sanityTest) { + final var suiteStringRaw = JSONFormatter.sortPrettyPrintObject(sanityTest.suite); + final var suiteString = suiteStringRaw.replace("\r\n", "\n"); // Fix CRLF line ends + final var suiteBytes = suiteString.getBytes(StandardCharsets.UTF_8); + return Bytes.toHexString(sha256Hash(suiteBytes)); + } - private SanityTestSuiteRoot readTestSuiteContent(String sanityTestJSONFileName) throws IOException { - var resource = getClass().getClassLoader().getResource(sanityTestJSONFileName); - File file = new File(resource.getFile()); + private SanityTestSuiteRoot readTestSuiteContent(String sanityTestJSONFileName) + throws IOException { + var resource = getClass().getClassLoader().getResource(sanityTestJSONFileName); + File file = new File(resource.getFile()); - var jsonFileContent = Files.asCharSource(file, StandardCharsets.UTF_8).read(); - return mapper.readValue(jsonFileContent, SanityTestSuiteRoot.class); - } + var jsonFileContent = Files.asCharSource(file, StandardCharsets.UTF_8).read(); + return mapper.readValue(jsonFileContent, SanityTestSuiteRoot.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestSuiteRoot.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestSuiteRoot.java index 8bc788d326..4e4552d514 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestSuiteRoot.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestSuiteRoot.java @@ -69,131 +69,127 @@ // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class SanityTestSuiteRoot { - public final Integrity integrity; - public final Suite suite; - - private SanityTestSuiteRoot() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.integrity = null; - this.suite = null; - } - - public static final class Suite { - public final List scenarios; - - - private Suite() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.scenarios = null; - } - - public static final class Scenario { - public final Description description; - public final String identifier; - public final String name; - public final Tests tests; - - private Scenario() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.description = null; - this.identifier = null; - this.name = null; - this.tests = null; - } - - public static final class Description { - public final String implementationInfo; - public final String purpose; - public final String troubleshooting; - - private Description() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.implementationInfo = null; - this.purpose = null; - this.troubleshooting = null; - } - } - - public static final class Tests { - public final Source source; - public final List> vectors; - - private Tests() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.source = null; - this.vectors = null; - } - - public static final class Source { - public final String link; - public final String comment; - public final String originalSourceLink; - public final ModifiedByTool modifiedByTool; - - private Source() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.link = null; - this.comment = null; - this.originalSourceLink = null; - this.modifiedByTool = null; - } - - public static final class ModifiedByTool { - public final String expression; - public final ToolInfo tool; - - private ModifiedByTool() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.expression = null; - this.tool = null; - } - - public static final class ToolInfo { - public String name; - public String link; - public String version; - } - - } - } - } - - public String failDescriptionWithAssertionError(AssertionError testAssertionError) { - return String.format( - "\n⚠️⚠️⚠️\nFailed test scenario: '%s'\n" + - "Identifier: '%s'\n" + - "Purpose of scenario: '%s'\n" + - "Troubleshooting: '%s'\n" + - "Implementation info: '%s'\n" + - "Test vectors found at: '%s'\n" + - "Test vectors modified?: '%s'\n" + - "Failure reason: '%s'\n⚠️⚠️⚠️\n", - this.name, - this.identifier, - this.description.purpose, - this.description.troubleshooting, - this.description.implementationInfo, - this.tests.source.link, - this.tests.source.modifiedByTool == null - ? "NO" - : "YES, modified with tool (see 'expression' for how): " - + this.tests.source.modifiedByTool.tool.link, - testAssertionError.getLocalizedMessage() - ); - } - } - } - - public static final class Integrity { - public final String hashOfSuite; - public final String implementationInfo; - - private Integrity() { - /* Jackson will properly populate this fields during deserialize from JSON. */ - this.hashOfSuite = null; - this.implementationInfo = null; - } - - } + public final Integrity integrity; + public final Suite suite; + + private SanityTestSuiteRoot() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.integrity = null; + this.suite = null; + } + + public static final class Suite { + public final List scenarios; + + private Suite() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.scenarios = null; + } + + public static final class Scenario { + public final Description description; + public final String identifier; + public final String name; + public final Tests tests; + + private Scenario() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.description = null; + this.identifier = null; + this.name = null; + this.tests = null; + } + + public static final class Description { + public final String implementationInfo; + public final String purpose; + public final String troubleshooting; + + private Description() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.implementationInfo = null; + this.purpose = null; + this.troubleshooting = null; + } + } + + public static final class Tests { + public final Source source; + public final List> vectors; + + private Tests() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.source = null; + this.vectors = null; + } + + public static final class Source { + public final String link; + public final String comment; + public final String originalSourceLink; + public final ModifiedByTool modifiedByTool; + + private Source() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.link = null; + this.comment = null; + this.originalSourceLink = null; + this.modifiedByTool = null; + } + + public static final class ModifiedByTool { + public final String expression; + public final ToolInfo tool; + + private ModifiedByTool() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.expression = null; + this.tool = null; + } + + public static final class ToolInfo { + public String name; + public String link; + public String version; + } + } + } + } + + public String failDescriptionWithAssertionError(AssertionError testAssertionError) { + return String.format( + "\n⚠️⚠️⚠️\nFailed test scenario: '%s'\n" + + "Identifier: '%s'\n" + + "Purpose of scenario: '%s'\n" + + "Troubleshooting: '%s'\n" + + "Implementation info: '%s'\n" + + "Test vectors found at: '%s'\n" + + "Test vectors modified?: '%s'\n" + + "Failure reason: '%s'\n⚠️⚠️⚠️\n", + this.name, + this.identifier, + this.description.purpose, + this.description.troubleshooting, + this.description.implementationInfo, + this.tests.source.link, + this.tests.source.modifiedByTool == null + ? "NO" + : "YES, modified with tool (see 'expression' for how): " + + this.tests.source.modifiedByTool.tool.link, + testAssertionError.getLocalizedMessage()); + } + } + } + + public static final class Integrity { + public final String hashOfSuite; + public final String implementationInfo; + + private Integrity() { + /* Jackson will properly populate this fields during deserialize from JSON. */ + this.hashOfSuite = null; + this.implementationInfo = null; + } + } } -// CHECKSTYLE:ON checkstyle:VisibilityModifier \ No newline at end of file +// CHECKSTYLE:ON checkstyle:VisibilityModifier diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestVector.java index f826f6a39d..183b127201 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/model/SanityTestVector.java @@ -68,12 +68,13 @@ /** * Sanity test vector interface. + * * @param - type of input value * @param - type of expected value */ public abstract class SanityTestVector { - public E expected; - public I input; + public E expected; + public I input; } -// CHECKSTYLE:ON \ No newline at end of file +// CHECKSTYLE:ON diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/SanityTestScenarioRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/SanityTestScenarioRunner.java index ef513e029d..93b0e97e12 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/SanityTestScenarioRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/SanityTestScenarioRunner.java @@ -64,52 +64,51 @@ package com.radixdlt.sanitytestsuite.scenario; +import static org.junit.Assert.assertEquals; + import com.fasterxml.jackson.databind.ObjectMapper; import com.radixdlt.sanitytestsuite.model.SanityTestSuiteRoot.Suite.Scenario; import com.radixdlt.sanitytestsuite.model.SanityTestVector; import com.radixdlt.utils.JSONFormatter; - import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import static org.junit.Assert.assertEquals; - public abstract class SanityTestScenarioRunner> { - private final ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper = new ObjectMapper(); + + public abstract String testScenarioIdentifier(); - public abstract String testScenarioIdentifier(); - public abstract Class testVectorType(); + public abstract Class testVectorType(); - public abstract void doRunTestVector(TestVector testVector) throws AssertionError; + public abstract void doRunTestVector(TestVector testVector) throws AssertionError; - public void executeTest(final Scenario scenario) { - assertEquals(testScenarioIdentifier(), scenario.identifier); - var testVectorIndex = 0; + public void executeTest(final Scenario scenario) { + assertEquals(testScenarioIdentifier(), scenario.identifier); + var testVectorIndex = 0; - for (var testVectorInput : scenario.tests.vectors) { - var testVector = mapper.convertValue(testVectorInput, this.testVectorType()); + for (var testVectorInput : scenario.tests.vectors) { + var testVector = mapper.convertValue(testVectorInput, this.testVectorType()); - try { - doRunTestVector(testVector); - testVectorIndex++; - } catch (AssertionError e) { - String msg = String.format( - "Failing test vector index: %d, vector: %s", - testVectorIndex, - JSONFormatter.sortPrettyPrintObject(testVector) - ); - throw new AssertionError(msg, e); - } - } - } + try { + doRunTestVector(testVector); + testVectorIndex++; + } catch (AssertionError e) { + String msg = + String.format( + "Failing test vector index: %d, vector: %s", + testVectorIndex, JSONFormatter.sortPrettyPrintObject(testVector)); + throw new AssertionError(msg, e); + } + } + } - public static byte[] sha256Hash(final byte[] bytes) { - try { - var hasher = MessageDigest.getInstance("SHA-256"); - hasher.update(bytes); - return hasher.digest(); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError("Failed to run test, found no hasher", e); - } - } -} \ No newline at end of file + public static byte[] sha256Hash(final byte[] bytes) { + try { + var hasher = MessageDigest.getInstance("SHA-256"); + hasher.update(bytes); + return hasher.digest(); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("Failed to run test, found no hasher", e); + } + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/deserialization/DeserializationTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/deserialization/DeserializationTestVector.java index 66bd144c57..3b66359114 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/deserialization/DeserializationTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/deserialization/DeserializationTestVector.java @@ -64,24 +64,22 @@ package com.radixdlt.sanitytestsuite.scenario.deserialization; +import static com.radixdlt.sanitytestsuite.scenario.deserialization.DeserializationTestVector.Expected; +import static com.radixdlt.sanitytestsuite.scenario.deserialization.DeserializationTestVector.Input; import com.radixdlt.sanitytestsuite.model.SanityTestVector; - import java.util.Map; -import static com.radixdlt.sanitytestsuite.scenario.deserialization.DeserializationTestVector.Expected; -import static com.radixdlt.sanitytestsuite.scenario.deserialization.DeserializationTestVector.Input; - // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class DeserializationTestVector extends SanityTestVector { - public static final class Expected { - public Map arguments; - } + public static final class Expected { + public Map arguments; + } - public static final class Input { - public Map json; - public Map dson; - public String typeSerialization; - } + public static final class Input { + public Map json; + public Map dson; + public String typeSerialization; + } } // CHECKSTYLE:ON diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestScenarioRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestScenarioRunner.java index 60879f5f85..aa0fd3fd80 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestScenarioRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestScenarioRunner.java @@ -64,26 +64,26 @@ package com.radixdlt.sanitytestsuite.scenario.hashing; +import static org.junit.Assert.assertEquals; + import com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner; import com.radixdlt.utils.Bytes; -import static org.junit.Assert.assertEquals; - public final class HashingTestScenarioRunner extends SanityTestScenarioRunner { - @Override - public String testScenarioIdentifier() { - return "hashing"; - } + @Override + public String testScenarioIdentifier() { + return "hashing"; + } - @Override - public Class testVectorType() { - return HashingTestVector.class; - } + @Override + public Class testVectorType() { + return HashingTestVector.class; + } - @Override - public void doRunTestVector(HashingTestVector testVector) throws AssertionError { - var hashHex = Bytes.toHexString(sha256Hash(testVector.input.bytesToHash())); + @Override + public void doRunTestVector(HashingTestVector testVector) throws AssertionError { + var hashHex = Bytes.toHexString(sha256Hash(testVector.input.bytesToHash())); - assertEquals(testVector.expected.hash, hashHex); - } + assertEquals(testVector.expected.hash, hashHex); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestVector.java index a6485f5cff..3bd0b0493c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/hashing/HashingTestVector.java @@ -64,25 +64,24 @@ package com.radixdlt.sanitytestsuite.scenario.hashing; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - -import java.nio.charset.StandardCharsets; - import static com.radixdlt.sanitytestsuite.scenario.hashing.HashingTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.hashing.HashingTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; +import java.nio.charset.StandardCharsets; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class HashingTestVector extends SanityTestVector { - static final class Expected { - public String hash; - } + static final class Expected { + public String hash; + } - static final class Input { - public String stringToHash; + static final class Input { + public String stringToHash; - public byte[] bytesToHash() { - return this.stringToHash.getBytes(StandardCharsets.UTF_8); - } - } + public byte[] bytesToHash() { + return this.stringToHash.getBytes(StandardCharsets.UTF_8); + } + } } -// CHECKSTYLE:ON checkstyle:VisibilityModifier \ No newline at end of file +// CHECKSTYLE:ON checkstyle:VisibilityModifier diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/jsonserialization/JsonSerializationTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/jsonserialization/JsonSerializationTestVector.java index e3a4c50768..c1d85f93cb 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/jsonserialization/JsonSerializationTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/jsonserialization/JsonSerializationTestVector.java @@ -64,22 +64,21 @@ package com.radixdlt.sanitytestsuite.scenario.jsonserialization; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - -import java.util.Map; - import static com.radixdlt.sanitytestsuite.scenario.jsonserialization.JsonSerializationTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.jsonserialization.JsonSerializationTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; +import java.util.Map; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class JsonSerializationTestVector extends SanityTestVector { - public static final class Expected { - public String jsonPrettyPrinted; - } + public static final class Expected { + public String jsonPrettyPrinted; + } - public static final class Input { - public Map arguments; - public String typeSerialization; - } + public static final class Input { + public Map arguments; + public String typeSerialization; + } } -// CHECKSTYLE:ON \ No newline at end of file +// CHECKSTYLE:ON diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestScenarioRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestScenarioRunner.java index 15f656fdf3..239aad8873 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestScenarioRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestScenarioRunner.java @@ -64,35 +64,35 @@ package com.radixdlt.sanitytestsuite.scenario.keygen; +import static com.radixdlt.utils.Bytes.fromHexString; +import static junit.framework.TestCase.assertEquals; + import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner; -import static com.radixdlt.utils.Bytes.fromHexString; -import static junit.framework.TestCase.assertEquals; - public final class KeyGenTestScenarioRunner extends SanityTestScenarioRunner { - @Override - public String testScenarioIdentifier() { - return "secp256k1"; - } + @Override + public String testScenarioIdentifier() { + return "secp256k1"; + } - @Override - public Class testVectorType() { - return KeyGenTestVector.class; - } + @Override + public Class testVectorType() { + return KeyGenTestVector.class; + } - @Override - public void doRunTestVector(KeyGenTestVector testVector) throws AssertionError { - try { - var publicKey = - ECKeyPair.fromPrivateKey(fromHexString(testVector.input.privateKey)).getPublicKey(); - var expectedPublicKey = - ECPublicKey.fromBytes(fromHexString(testVector.expected.uncompressedPublicKey)); + @Override + public void doRunTestVector(KeyGenTestVector testVector) throws AssertionError { + try { + var publicKey = + ECKeyPair.fromPrivateKey(fromHexString(testVector.input.privateKey)).getPublicKey(); + var expectedPublicKey = + ECPublicKey.fromBytes(fromHexString(testVector.expected.uncompressedPublicKey)); - assertEquals(publicKey, expectedPublicKey); - } catch (Exception e) { - throw new AssertionError("Failed to create PublicKeys", e); - } - } + assertEquals(publicKey, expectedPublicKey); + } catch (Exception e) { + throw new AssertionError("Failed to create PublicKeys", e); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestVector.java index e41329a581..1130904751 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keygen/KeyGenTestVector.java @@ -64,19 +64,19 @@ package com.radixdlt.sanitytestsuite.scenario.keygen; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - import static com.radixdlt.sanitytestsuite.scenario.keygen.KeyGenTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.keygen.KeyGenTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class KeyGenTestVector extends SanityTestVector { - public static final class Expected { - public String uncompressedPublicKey; - } + public static final class Expected { + public String uncompressedPublicKey; + } - public static final class Input { - public String privateKey; - } + public static final class Input { + public String privateKey; + } } -// CHECKSTYLE:ON checkstyle:VisibilityModifier \ No newline at end of file +// CHECKSTYLE:ON checkstyle:VisibilityModifier diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestScenarioRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestScenarioRunner.java index ec409b24ee..cb3e22c7d9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestScenarioRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestScenarioRunner.java @@ -64,47 +64,39 @@ package com.radixdlt.sanitytestsuite.scenario.keysign; +import static org.junit.Assert.assertEquals; + import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner; import com.radixdlt.utils.Bytes; - import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; - public final class KeySignTestScenarioRunner extends SanityTestScenarioRunner { - @Override - public String testScenarioIdentifier() { - return "ecdsa_signing"; - } - - @Override - public Class testVectorType() { - return KeySignTestVector.class; - } + @Override + public String testScenarioIdentifier() { + return "ecdsa_signing"; + } - @Override - public void doRunTestVector(KeySignTestVector testVector) throws AssertionError { - ECKeyPair keyPair = null; - try { - keyPair = ECKeyPair.fromPrivateKey(Bytes.fromHexString(testVector.input.privateKey)); - } catch (Exception e) { - throw new AssertionError("Failed to construct private key from hex", e); - } + @Override + public Class testVectorType() { + return KeySignTestVector.class; + } - byte[] unhashedEncodedMessage = testVector.input.messageToSign.getBytes(StandardCharsets.UTF_8); - byte[] hashedMessageToSign = sha256Hash(unhashedEncodedMessage); - ECDSASignature signature = keyPair.sign(hashedMessageToSign, true, true); - assertEquals( - testVector.expected.signature.r, - signature.getR().toString(16) - ); - assertEquals( - testVector.expected.signature.s, - signature.getS().toString(16) - ); + @Override + public void doRunTestVector(KeySignTestVector testVector) throws AssertionError { + ECKeyPair keyPair = null; + try { + keyPair = ECKeyPair.fromPrivateKey(Bytes.fromHexString(testVector.input.privateKey)); + } catch (Exception e) { + throw new AssertionError("Failed to construct private key from hex", e); + } - } + byte[] unhashedEncodedMessage = testVector.input.messageToSign.getBytes(StandardCharsets.UTF_8); + byte[] hashedMessageToSign = sha256Hash(unhashedEncodedMessage); + ECDSASignature signature = keyPair.sign(hashedMessageToSign, true, true); + assertEquals(testVector.expected.signature.r, signature.getR().toString(16)); + assertEquals(testVector.expected.signature.s, signature.getS().toString(16)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestVector.java index fe8ba7d5c4..82b54b8b60 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keysign/KeySignTestVector.java @@ -64,25 +64,27 @@ package com.radixdlt.sanitytestsuite.scenario.keysign; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - import static com.radixdlt.sanitytestsuite.scenario.keysign.KeySignTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.keysign.KeySignTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class KeySignTestVector extends SanityTestVector { - public static final class Input { - public String privateKey; - public String messageToSign; - } - public static final class Expected { - public static final class Signature { - public String r; - public String s; - public String der; - } - public String k; - public Signature signature; - } + public static final class Input { + public String privateKey; + public String messageToSign; + } + + public static final class Expected { + public static final class Signature { + public String r; + public String s; + public String der; + } + + public String k; + public Signature signature; + } } -// CHECKSTYLE:ON checkstyle:VisibilityModifier \ No newline at end of file +// CHECKSTYLE:ON checkstyle:VisibilityModifier diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestScenarioRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestScenarioRunner.java index 78fea60f25..b187d8c19d 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestScenarioRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestScenarioRunner.java @@ -64,40 +64,39 @@ package com.radixdlt.sanitytestsuite.scenario.keyverify; +import static com.radixdlt.utils.Bytes.fromHexString; +import static org.junit.Assert.assertEquals; + import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner; -import static com.radixdlt.utils.Bytes.fromHexString; -import static org.junit.Assert.assertEquals; - -public final class KeyVerifyTestScenarioRunner extends SanityTestScenarioRunner { - @Override - public String testScenarioIdentifier() { - return "ecdsa_verification"; - } +public final class KeyVerifyTestScenarioRunner + extends SanityTestScenarioRunner { + @Override + public String testScenarioIdentifier() { + return "ecdsa_verification"; + } - @Override - public Class testVectorType() { - return KeyVerifyTestVector.class; - } + @Override + public Class testVectorType() { + return KeyVerifyTestVector.class; + } - @Override - public void doRunTestVector(KeyVerifyTestVector testVector) throws AssertionError { + @Override + public void doRunTestVector(KeyVerifyTestVector testVector) throws AssertionError { - ECPublicKey publicKey = null; - try { - publicKey = ECPublicKey.fromBytes(fromHexString(testVector.input.publicKeyUncompressed)); - } catch (Exception e) { - throw new AssertionError("Failed to construct public key from hex", e); - } - ECDSASignature signature = ECDSASignature.decodeFromDER(fromHexString(testVector.input.signatureDerEncoded)); + ECPublicKey publicKey = null; + try { + publicKey = ECPublicKey.fromBytes(fromHexString(testVector.input.publicKeyUncompressed)); + } catch (Exception e) { + throw new AssertionError("Failed to construct public key from hex", e); + } + ECDSASignature signature = + ECDSASignature.decodeFromDER(fromHexString(testVector.input.signatureDerEncoded)); - byte[] hashedMessageToVerify = sha256Hash(fromHexString(testVector.input.msg)); + byte[] hashedMessageToVerify = sha256Hash(fromHexString(testVector.input.msg)); - assertEquals( - testVector.expected.isValid, - publicKey.verify(hashedMessageToVerify, signature) - ); - } + assertEquals(testVector.expected.isValid, publicKey.verify(hashedMessageToVerify, signature)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestVector.java index e7b1f55eea..db54bab989 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/keyverify/KeyVerifyTestVector.java @@ -64,23 +64,23 @@ package com.radixdlt.sanitytestsuite.scenario.keyverify; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - import static com.radixdlt.sanitytestsuite.scenario.keyverify.KeyVerifyTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.keyverify.KeyVerifyTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class KeyVerifyTestVector extends SanityTestVector { - public static final class Input { - public String comment; - public int wycheProofVectorId; - public String msg; - public String publicKeyUncompressed; - public String signatureDerEncoded; - } + public static final class Input { + public String comment; + public int wycheProofVectorId; + public String msg; + public String publicKeyUncompressed; + public String signatureDerEncoded; + } - public static final class Expected { - public boolean isValid; - } + public static final class Expected { + public boolean isValid; + } } -// CHECKSTYLE:ON checkstyle:VisibilityModifier \ No newline at end of file +// CHECKSTYLE:ON checkstyle:VisibilityModifier diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestScenarioRunner.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestScenarioRunner.java index 81086bb4d0..d1e1b8ef77 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestScenarioRunner.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestScenarioRunner.java @@ -64,28 +64,29 @@ package com.radixdlt.sanitytestsuite.scenario.radixhashing; +import static org.junit.Assert.assertEquals; + import com.radixdlt.crypto.HashUtils; import com.radixdlt.sanitytestsuite.scenario.SanityTestScenarioRunner; import com.radixdlt.utils.Bytes; -import static org.junit.Assert.assertEquals; - -public final class RadixHashingTestScenarioRunner extends SanityTestScenarioRunner { +public final class RadixHashingTestScenarioRunner + extends SanityTestScenarioRunner { - @Override - public String testScenarioIdentifier() { - return "radix_hashing"; - } + @Override + public String testScenarioIdentifier() { + return "radix_hashing"; + } - @Override - public Class testVectorType() { - return RadixHashingTestVector.class; - } + @Override + public Class testVectorType() { + return RadixHashingTestVector.class; + } - @Override - public void doRunTestVector(RadixHashingTestVector testVector) throws AssertionError { - var hashHex = Bytes.toHexString(HashUtils.sha256(testVector.input.bytesToHash()).asBytes()); + @Override + public void doRunTestVector(RadixHashingTestVector testVector) throws AssertionError { + var hashHex = Bytes.toHexString(HashUtils.sha256(testVector.input.bytesToHash()).asBytes()); - assertEquals(testVector.expected.hashOfHash, hashHex); - } + assertEquals(testVector.expected.hashOfHash, hashHex); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestVector.java index b98b6979fa..4569e19506 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/radixhashing/RadixHashingTestVector.java @@ -64,24 +64,24 @@ package com.radixdlt.sanitytestsuite.scenario.radixhashing; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - -import java.nio.charset.StandardCharsets; - import static com.radixdlt.sanitytestsuite.scenario.radixhashing.RadixHashingTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.radixhashing.RadixHashingTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; +import java.nio.charset.StandardCharsets; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class RadixHashingTestVector extends SanityTestVector { - public static final class Expected { - public String hashOfHash; - } + public static final class Expected { + public String hashOfHash; + } + + public static final class Input { + public String stringToHash; - public static final class Input { - public String stringToHash; - public byte[] bytesToHash() { - return this.stringToHash.getBytes(StandardCharsets.UTF_8); - } - } + public byte[] bytesToHash() { + return this.stringToHash.getBytes(StandardCharsets.UTF_8); + } + } } -// CHECKSTYLE:ON checkstyle:VisibilityModifier \ No newline at end of file +// CHECKSTYLE:ON checkstyle:VisibilityModifier diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/serialization/SerializationTestVector.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/serialization/SerializationTestVector.java index 4e696f41d0..daf0bbec72 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/serialization/SerializationTestVector.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sanitytestsuite/scenario/serialization/SerializationTestVector.java @@ -64,23 +64,22 @@ package com.radixdlt.sanitytestsuite.scenario.serialization; -import com.radixdlt.sanitytestsuite.model.SanityTestVector; - -import java.util.Map; - import static com.radixdlt.sanitytestsuite.scenario.serialization.SerializationTestVector.Expected; import static com.radixdlt.sanitytestsuite.scenario.serialization.SerializationTestVector.Input; +import com.radixdlt.sanitytestsuite.model.SanityTestVector; +import java.util.Map; + // CHECKSTYLE:OFF checkstyle:VisibilityModifier public final class SerializationTestVector extends SanityTestVector { - public static final class Expected { - public String jsonPrettyPrinted; - public Map dson; - } + public static final class Expected { + public String jsonPrettyPrinted; + public Map dson; + } - public static final class Input { - public Map arguments; - public String typeSerialization; - } + public static final class Input { + public Map arguments; + public String typeSerialization; + } } -// CHECKSTYLE:ON \ No newline at end of file +// CHECKSTYLE:ON diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/AtomsRemovedFromMempoolTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/AtomsRemovedFromMempoolTest.java index c17c288f2a..9d1ec71172 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/AtomsRemovedFromMempoolTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/AtomsRemovedFromMempoolTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class AtomsRemovedFromMempoolTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(TxnsRemovedFromMempool.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(TxnsRemovedFromMempool.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/InvalidProposedTxnTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/InvalidProposedTxnTest.java index 4e21227fe5..0f701a13e6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/InvalidProposedTxnTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/InvalidProposedTxnTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class InvalidProposedTxnTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(InvalidProposedTxn.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(InvalidProposedTxn.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/LedgerAndBFTProofTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/LedgerAndBFTProofTest.java index 9b99b3a188..9bc3ce6ec1 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/LedgerAndBFTProofTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/LedgerAndBFTProofTest.java @@ -64,17 +64,16 @@ package com.radixdlt.statecomputer; - import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; public class LedgerAndBFTProofTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(LedgerAndBFTProof.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(LedgerAndBFTProof.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedMempoolStateComputerModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedMempoolStateComputerModule.java index 531bd2d86f..ff17597729 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedMempoolStateComputerModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedMempoolStateComputerModule.java @@ -80,86 +80,82 @@ import com.radixdlt.ledger.MockPrepared; import com.radixdlt.ledger.StateComputerLedger; import com.radixdlt.ledger.VerifiedTxnsAndProof; +import com.radixdlt.mempool.Mempool; import com.radixdlt.mempool.MempoolAdd; import com.radixdlt.mempool.MempoolMaxSize; -import com.radixdlt.mempool.SimpleMempool; -import com.radixdlt.mempool.Mempool; import com.radixdlt.mempool.MempoolRejectedException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.Nullable; +import com.radixdlt.mempool.SimpleMempool; import java.util.List; import java.util.Map; import java.util.Random; import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Simple Mempool state computer - */ +/** Simple Mempool state computer */ public class MockedMempoolStateComputerModule extends AbstractModule { - private static final Logger log = LogManager.getLogger(); + private static final Logger log = LogManager.getLogger(); - @Override - protected void configure() { - bind(new TypeLiteral>() { }).to(new TypeLiteral>() { }).in(Scopes.SINGLETON); - } + @Override + protected void configure() { + bind(new TypeLiteral>() {}) + .to(new TypeLiteral>() {}) + .in(Scopes.SINGLETON); + } - @Provides - @Singleton - private Mempool mempool( - SystemCounters systemCounters, - Random random, - @MempoolMaxSize int mempoolMaxSize - ) { - return new SimpleMempool(systemCounters, mempoolMaxSize, random); - } + @Provides + @Singleton + private Mempool mempool( + SystemCounters systemCounters, Random random, @MempoolMaxSize int mempoolMaxSize) { + return new SimpleMempool(systemCounters, mempoolMaxSize, random); + } - @Provides - @Singleton - private StateComputerLedger.StateComputer stateComputer( - Mempool mempool, - EventDispatcher ledgerUpdateDispatcher, - SystemCounters counters - ) { - return new StateComputerLedger.StateComputer() { - @Override - public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { - mempoolAdd.txns().forEach(txn -> { - try { - mempool.add(txn); - counters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); - } catch (MempoolRejectedException e) { - log.error(e); - } - }); - } + @Provides + @Singleton + private StateComputerLedger.StateComputer stateComputer( + Mempool mempool, + EventDispatcher ledgerUpdateDispatcher, + SystemCounters counters) { + return new StateComputerLedger.StateComputer() { + @Override + public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { + mempoolAdd + .txns() + .forEach( + txn -> { + try { + mempool.add(txn); + counters.set( + SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); + } catch (MempoolRejectedException e) { + log.error(e); + } + }); + } - @Override - public List getNextTxnsFromMempool(List prepared) { - return mempool.getTxns(1, List.of()); - } + @Override + public List getNextTxnsFromMempool(List prepared) { + return mempool.getTxns(1, List.of()); + } - @Override - public StateComputerLedger.StateComputerResult prepare( - List previous, - VerifiedVertex vertex, - long timestamp - ) { - return new StateComputerLedger.StateComputerResult( - vertex.getTxns().stream().map(MockPrepared::new).collect(Collectors.toList()), - Map.of() - ); - } + @Override + public StateComputerLedger.StateComputerResult prepare( + List previous, VerifiedVertex vertex, long timestamp) { + return new StateComputerLedger.StateComputerResult( + vertex.getTxns().stream().map(MockPrepared::new).collect(Collectors.toList()), + Map.of()); + } - @Override - public void commit(VerifiedTxnsAndProof txnsAndProof, VerifiedVertexStoreState vertexStoreState) { - mempool.committed(txnsAndProof.getTxns()); - counters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); + @Override + public void commit( + VerifiedTxnsAndProof txnsAndProof, VerifiedVertexStoreState vertexStoreState) { + mempool.committed(txnsAndProof.getTxns()); + counters.set(SystemCounters.CounterType.MEMPOOL_CURRENT_SIZE, mempool.getCount()); - var ledgerUpdate = new LedgerUpdate(txnsAndProof, ImmutableClassToInstanceMap.of()); - ledgerUpdateDispatcher.dispatch(ledgerUpdate); - } - }; - } + var ledgerUpdate = new LedgerUpdate(txnsAndProof, ImmutableClassToInstanceMap.of()); + ledgerUpdateDispatcher.dispatch(ledgerUpdate); + } + }; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputer.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputer.java index 7be917adae..cacf68284f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputer.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputer.java @@ -87,73 +87,69 @@ import com.radixdlt.ledger.StateComputerLedger.StateComputer; import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.mempool.MempoolAdd; - -import javax.annotation.Nullable; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.Nullable; public final class MockedStateComputer implements StateComputer { - private final EventDispatcher ledgerUpdateDispatcher; - private final Hasher hasher; + private final EventDispatcher ledgerUpdateDispatcher; + private final Hasher hasher; - @Inject - public MockedStateComputer( - EventDispatcher ledgerUpdateDispatcher, - Hasher hasher - ) { - this.ledgerUpdateDispatcher = ledgerUpdateDispatcher; - this.hasher = hasher; - } + @Inject + public MockedStateComputer(EventDispatcher ledgerUpdateDispatcher, Hasher hasher) { + this.ledgerUpdateDispatcher = ledgerUpdateDispatcher; + this.hasher = hasher; + } - @Override - public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { - } + @Override + public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) {} - @Override - public List getNextTxnsFromMempool(List prepared) { - return List.of(); - } + @Override + public List getNextTxnsFromMempool(List prepared) { + return List.of(); + } - @Override - public StateComputerLedger.StateComputerResult prepare( - List previous, - VerifiedVertex vertex, - long timestamp - ) { - return new StateComputerLedger.StateComputerResult( - vertex.getTxns().stream().map(MockPrepared::new).collect(Collectors.toList()), - Map.of() - ); - } + @Override + public StateComputerLedger.StateComputerResult prepare( + List previous, VerifiedVertex vertex, long timestamp) { + return new StateComputerLedger.StateComputerResult( + vertex.getTxns().stream().map(MockPrepared::new).collect(Collectors.toList()), Map.of()); + } - @Override - public void commit(VerifiedTxnsAndProof txnsAndProof, VerifiedVertexStoreState vertexStoreState) { - var output = txnsAndProof.getProof().getNextValidatorSet().map(validatorSet -> { - LedgerProof header = txnsAndProof.getProof(); - UnverifiedVertex genesisVertex = UnverifiedVertex.createGenesis(header.getRaw()); - VerifiedVertex verifiedGenesisVertex = new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); - LedgerHeader nextLedgerHeader = LedgerHeader.create( - header.getEpoch() + 1, - View.genesis(), - header.getAccumulatorState(), - header.timestamp() - ); - QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); - final var initialState = - VerifiedVertexStoreState.create( - HighQC.from(genesisQC), - verifiedGenesisVertex, - Optional.empty(), - hasher - ); - var proposerElection = new WeightedRotatingLeaders(validatorSet); - var bftConfiguration = new BFTConfiguration(proposerElection, validatorSet, initialState); - return new EpochChange(header, bftConfiguration); - }).map(e -> ImmutableClassToInstanceMap.of(EpochChange.class, e)).orElse(ImmutableClassToInstanceMap.of()); + @Override + public void commit(VerifiedTxnsAndProof txnsAndProof, VerifiedVertexStoreState vertexStoreState) { + var output = + txnsAndProof + .getProof() + .getNextValidatorSet() + .map( + validatorSet -> { + LedgerProof header = txnsAndProof.getProof(); + UnverifiedVertex genesisVertex = UnverifiedVertex.createGenesis(header.getRaw()); + VerifiedVertex verifiedGenesisVertex = + new VerifiedVertex(genesisVertex, hasher.hash(genesisVertex)); + LedgerHeader nextLedgerHeader = + LedgerHeader.create( + header.getEpoch() + 1, + View.genesis(), + header.getAccumulatorState(), + header.timestamp()); + QuorumCertificate genesisQC = + QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); + final var initialState = + VerifiedVertexStoreState.create( + HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher); + var proposerElection = new WeightedRotatingLeaders(validatorSet); + var bftConfiguration = + new BFTConfiguration(proposerElection, validatorSet, initialState); + return new EpochChange(header, bftConfiguration); + }) + .map(e -> ImmutableClassToInstanceMap.of(EpochChange.class, e)) + .orElse(ImmutableClassToInstanceMap.of()); - var ledgerUpdate = new LedgerUpdate(txnsAndProof, output); - ledgerUpdateDispatcher.dispatch(ledgerUpdate); - } + var ledgerUpdate = new LedgerUpdate(txnsAndProof, output); + ledgerUpdateDispatcher.dispatch(ledgerUpdate); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerModule.java index a9f1fa6279..78a5166a10 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerModule.java @@ -69,8 +69,8 @@ import com.radixdlt.ledger.StateComputerLedger.StateComputer; public class MockedStateComputerModule extends AbstractModule { - @Override - public void configure() { - bind(StateComputer.class).to(MockedStateComputer.class).in(Scopes.SINGLETON); - } + @Override + public void configure() { + bind(StateComputer.class).to(MockedStateComputer.class).in(Scopes.SINGLETON); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochs.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochs.java index 68ea8cf99d..632d3a9ff4 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochs.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochs.java @@ -76,66 +76,60 @@ import com.radixdlt.environment.EventDispatcher; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.MockPrepared; -import com.radixdlt.ledger.StateComputerLedger.StateComputerResult; import com.radixdlt.ledger.StateComputerLedger.PreparedTxn; import com.radixdlt.ledger.StateComputerLedger.StateComputer; +import com.radixdlt.ledger.StateComputerLedger.StateComputerResult; import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.mempool.MempoolAdd; - -import javax.annotation.Nullable; import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.Nullable; public final class MockedStateComputerWithEpochs implements StateComputer { - private final Function validatorSetMapping; - private final View epochHighView; - private final MockedStateComputer stateComputer; + private final Function validatorSetMapping; + private final View epochHighView; + private final MockedStateComputer stateComputer; - @Inject - public MockedStateComputerWithEpochs( - @EpochCeilingView View epochHighView, - Function validatorSetMapping, - EventDispatcher ledgerUpdateDispatcher, - Hasher hasher - ) { - this.validatorSetMapping = Objects.requireNonNull(validatorSetMapping); - this.epochHighView = Objects.requireNonNull(epochHighView); - this.stateComputer = new MockedStateComputer(ledgerUpdateDispatcher, hasher); - } + @Inject + public MockedStateComputerWithEpochs( + @EpochCeilingView View epochHighView, + Function validatorSetMapping, + EventDispatcher ledgerUpdateDispatcher, + Hasher hasher) { + this.validatorSetMapping = Objects.requireNonNull(validatorSetMapping); + this.epochHighView = Objects.requireNonNull(epochHighView); + this.stateComputer = new MockedStateComputer(ledgerUpdateDispatcher, hasher); + } - @Override - public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) { - } + @Override + public void addToMempool(MempoolAdd mempoolAdd, @Nullable BFTNode origin) {} - @Override - public List getNextTxnsFromMempool(List prepared) { - return List.of(); - } + @Override + public List getNextTxnsFromMempool(List prepared) { + return List.of(); + } - @Override - public StateComputerResult prepare( - List previous, - VerifiedVertex vertex, - long timestamp - ) { - var view = vertex.getView(); - var epoch = vertex.getParentHeader().getLedgerHeader().getEpoch(); - var next = vertex.getTxns(); - if (view.compareTo(epochHighView) >= 0) { - return new StateComputerResult( - next.stream().map(MockPrepared::new).collect(Collectors.toList()), - ImmutableMap.of(), - validatorSetMapping.apply(epoch + 1) - ); - } else { - return stateComputer.prepare(previous, vertex, timestamp); - } - } + @Override + public StateComputerResult prepare( + List previous, VerifiedVertex vertex, long timestamp) { + var view = vertex.getView(); + var epoch = vertex.getParentHeader().getLedgerHeader().getEpoch(); + var next = vertex.getTxns(); + if (view.compareTo(epochHighView) >= 0) { + return new StateComputerResult( + next.stream().map(MockPrepared::new).collect(Collectors.toList()), + ImmutableMap.of(), + validatorSetMapping.apply(epoch + 1)); + } else { + return stateComputer.prepare(previous, vertex, timestamp); + } + } - @Override - public void commit(VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { - this.stateComputer.commit(verifiedTxnsAndProof, vertexStoreState); - } + @Override + public void commit( + VerifiedTxnsAndProof verifiedTxnsAndProof, VerifiedVertexStoreState vertexStoreState) { + this.stateComputer.commit(verifiedTxnsAndProof, vertexStoreState); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochsModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochsModule.java index 2d46f8d682..e59820f430 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochsModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/MockedStateComputerWithEpochsModule.java @@ -69,8 +69,8 @@ import com.radixdlt.ledger.StateComputerLedger.StateComputer; public class MockedStateComputerWithEpochsModule extends AbstractModule { - @Override - protected void configure() { - bind(StateComputer.class).to(MockedStateComputerWithEpochs.class).in(Scopes.SINGLETON); - } + @Override + protected void configure() { + bind(StateComputer.class).to(MockedStateComputerWithEpochs.class).in(Scopes.SINGLETON); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RadixEngineStateComputerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RadixEngineStateComputerTest.java index 9988c94533..d42e8884f0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RadixEngineStateComputerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RadixEngineStateComputerTest.java @@ -78,23 +78,23 @@ import com.google.inject.TypeLiteral; import com.radixdlt.DefaultSerialization; import com.radixdlt.application.system.NextValidatorSetEvent; +import com.radixdlt.application.system.state.RoundData; import com.radixdlt.application.tokens.Amount; +import com.radixdlt.atom.SubstateId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; -import com.radixdlt.atom.SubstateId; import com.radixdlt.atom.TxLowLevelBuilder; import com.radixdlt.atom.Txn; import com.radixdlt.atom.TxnConstructionRequest; -import com.radixdlt.atom.actions.RegisterValidator; import com.radixdlt.atom.actions.NextEpoch; import com.radixdlt.atom.actions.NextRound; -import com.radixdlt.application.system.state.RoundData; +import com.radixdlt.atom.actions.RegisterValidator; import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.Sha256Hasher; +import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.UnverifiedVertex; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidator; @@ -104,9 +104,9 @@ import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.consensus.liveness.WeightedRotatingLeaders; +import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.exceptions.ConstraintMachineException; import com.radixdlt.constraintmachine.exceptions.InvalidPermissionException; -import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; import com.radixdlt.crypto.ECKeyPair; @@ -138,7 +138,6 @@ import com.radixdlt.sync.CommittedReader; import com.radixdlt.utils.TypedMocks; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -150,336 +149,348 @@ import org.junit.rules.TemporaryFolder; public class RadixEngineStateComputerTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Inject - @Genesis - private VerifiedTxnsAndProof genesisTxns; - - @Inject - private RadixEngine radixEngine; - - @Inject - private RadixEngineStateComputer sut; - - @Inject - private RERules rules; - - @Inject - private ProposerElection proposerElection; - - private Serialization serialization = DefaultSerialization.getInstance(); - private InMemoryEngineStore engineStore; - private ImmutableList registeredNodes = ImmutableList.of( - ECKeyPair.generateNew(), - ECKeyPair.generateNew() - ); - private ECKeyPair unregisteredNode = ECKeyPair.generateNew(); - - private static final Hasher hasher = Sha256Hasher.withDefaultSerialization(); - - private Module getExternalModule() { - return new AbstractModule() { - @Override - public void configure() { - var validatorSet = BFTValidatorSet.from(registeredNodes.stream().map(ECKeyPair::getPublicKey) - .map(BFTNode::create) - .map(n -> BFTValidator.from(n, UInt256.ONE))); - - bind(ProposerElection.class).toInstance(new WeightedRotatingLeaders(validatorSet)); - bind(Serialization.class).toInstance(serialization); - bind(Hasher.class).toInstance(Sha256Hasher.withDefaultSerialization()); - bind(new TypeLiteral>() { }).toInstance(engineStore); - bind(PersistentVertexStore.class).toInstance(mock(PersistentVertexStore.class)); - - install(MempoolConfig.asModule(10, 10)); - install(new MainnetForkConfigsModule()); - install(new ForksModule()); - install(new RadixEngineForksLatestOnlyModule()); - - // HACK - bind(CommittedReader.class).toInstance(CommittedReader.mocked()); - - bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); - - bind(new TypeLiteral>() { }) - .toInstance(TypedMocks.rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(TypedMocks.rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(TypedMocks.rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(TypedMocks.rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(TypedMocks.rmock(EventDispatcher.class)); - bind(new TypeLiteral>() { }) - .toInstance(TypedMocks.rmock(EventDispatcher.class)); - - bind(SystemCounters.class).to(SystemCountersImpl.class); - } - }; - } - - private void setupGenesis() throws RadixEngineException { - var branch = radixEngine.transientBranch(); - var processed = branch.execute(genesisTxns.getTxns(), PermissionLevel.SYSTEM); - var genesisValidatorSet = processed.getProcessedTxns().get(0).getEvents().stream() - .filter(NextValidatorSetEvent.class::isInstance) - .map(NextValidatorSetEvent.class::cast) - .findFirst() - .map(e -> BFTValidatorSet.from( - e.nextValidators().stream() - .map(v -> BFTValidator.from(BFTNode.create(v.getValidatorKey()), v.getAmount()))) - ).orElseThrow(() -> new IllegalStateException("No validator set in genesis.")); - radixEngine.deleteBranches(); - - var genesisLedgerHeader = LedgerProof.genesis( - new AccumulatorState(0, hasher.hash(genesisTxns.getTxns().get(0).getId())), - genesisValidatorSet, - 0 - ); - if (!genesisLedgerHeader.isEndOfEpoch()) { - throw new IllegalStateException("Genesis must be end of epoch"); - } - radixEngine.execute(genesisTxns.getTxns(), LedgerAndBFTProof.create(genesisLedgerHeader), PermissionLevel.SYSTEM); - } - - @Before - public void setup() throws RadixEngineException { - this.engineStore = new InMemoryEngineStore<>(); - Guice.createInjector( - new RadixEngineCheckpointModule(), - new RadixEngineStateComputerModule(), - new RadixEngineModule(), - new MockedGenesisModule( - registeredNodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), - Amount.ofTokens(1000), - Amount.ofTokens(100) - ), - getExternalModule() - ).injectMembers(this); - setupGenesis(); - } - - private Txn systemUpdateTxn(long nextView, long nextEpoch) throws TxBuilderException { - TxBuilder builder; - if (nextEpoch >= 2) { - var request = TxnConstructionRequest.create() - .action(new NextRound(10, true, 0, v -> proposerElection.getProposer(View.of(v)).getKey())) - .action(new NextEpoch(0)); - builder = radixEngine.construct(request); - } else { - builder = radixEngine.construct(new NextRound( - nextView, - false, - 0, - i -> registeredNodes.get(0).getPublicKey() - )); - } - - return builder.buildWithoutSignature(); - } - - private Txn systemUpdateCommand(long nextView, long nextEpoch) throws TxBuilderException { - return systemUpdateTxn(nextView, nextEpoch); - } - - private Txn registerCommand(ECKeyPair keyPair) throws TxBuilderException { - return radixEngine.construct(new RegisterValidator(keyPair.getPublicKey())) - .signAndBuild(keyPair::sign); - } - - @Test - @Ignore("Ignore for now given need for more refactoring to get this test to work") - public void executing_non_epoch_high_view_should_return_no_validator_set() { - // Arrange - var v = UnverifiedVertex.create(mock(QuorumCertificate.class), View.of(9), List.of(), BFTNode.random()); - var vertex = new VerifiedVertex(v, mock(HashCode.class)); - - // Action - var result = sut.prepare(List.of(), vertex, 0); - - // Assert - assertThat(result.getSuccessfulCommands()).hasSize(1); - assertThat(result.getFailedCommands()).isEmpty(); - assertThat(result.getNextValidatorSet()).isEmpty(); - } - - @Test - public void executing_epoch_high_view_should_return_next_validator_set() { - // Arrange - var qc = mock(QuorumCertificate.class); - var parentHeader = mock(BFTHeader.class); - when(parentHeader.getView()).thenReturn(View.of(0)); - when(qc.getProposed()).thenReturn(parentHeader); - var unverified = UnverifiedVertex.create(qc, View.of(11), List.of(), BFTNode.random()); - var vertex = new VerifiedVertex(unverified, mock(HashCode.class)); - - // Act - StateComputerResult result = sut.prepare(List.of(), vertex, 0); - - // Assert - assertThat(result.getSuccessfulCommands()).hasSize(1); - assertThat(result.getFailedCommands()).isEmpty(); - assertThat(result.getNextValidatorSet()).hasValueSatisfying(set -> - assertThat(set.getValidators()) - .isNotEmpty() - .allMatch(v -> v.getNode().getKey().equals(unregisteredNode.getPublicKey()) - || registeredNodes.stream().anyMatch(k -> k.getPublicKey().equals(v.getNode().getKey()))) - ); - } - - @Test - public void executing_epoch_high_view_with_register_should_not_return_new_next_validator_set() throws Exception { - // Arrange - ECKeyPair keyPair = ECKeyPair.generateNew(); - var txn = registerCommand(keyPair); - BFTNode node = BFTNode.create(keyPair.getPublicKey()); - var qc = mock(QuorumCertificate.class); - var parentHeader = mock(BFTHeader.class); - when(parentHeader.getView()).thenReturn(View.of(0)); - when(qc.getProposed()).thenReturn(parentHeader); - var v = UnverifiedVertex.create(qc, View.of(11), List.of(txn), BFTNode.random()); - var vertex = new VerifiedVertex(v, mock(HashCode.class)); - - // Act - StateComputerResult result = sut.prepare(List.of(), vertex, 0); - - // Assert - assertThat(result.getSuccessfulCommands()).hasSize(1); // since high view, command is not executed - assertThat(result.getNextValidatorSet()).hasValueSatisfying(s -> { - assertThat(s.getValidators()).hasSize(2); - assertThat(s.getValidators()).extracting(BFTValidator::getNode).doesNotContain(node); - }); - } - - @Test - public void preparing_system_update_from_vertex_should_fail() throws TxBuilderException { - // Arrange - var txn = radixEngine.construct(new NextRound(1, false, 0, i -> proposerElection.getProposer(View.of(i)).getKey())) - .buildWithoutSignature(); - var illegalTxn = TxLowLevelBuilder.newBuilder(rules.getSerialization()) - .down(SubstateId.ofSubstate(txn.getId(), 1)) - .up(new RoundData(2, 0)) - .end() - .build(); - var v = UnverifiedVertex.create( - mock(QuorumCertificate.class), - View.of(1), - List.of(illegalTxn), - proposerElection.getProposer(View.of(1)) - ); - var vertex = new VerifiedVertex(v, mock(HashCode.class)); - - // Act - var result = sut.prepare(ImmutableList.of(), vertex, 0); - - // Assert - assertThat(result.getSuccessfulCommands()).hasSize(1); - assertThat(result.getFailedCommands()).hasValueSatisfying( - new Condition<>( - e -> { - RadixEngineException ex = (RadixEngineException) e; - ConstraintMachineException cmException = (ConstraintMachineException) ex.getCause(); - return cmException.getCause() instanceof InvalidPermissionException; - }, - "Is invalid_execution_permission error" - ) - - ); - } - - // TODO: should catch this and log it somewhere as proof of byzantine quorum - @Test - // Note that checking upper bound view for epoch now requires additional - // state not easily obtained where checked in the RadixEngine - @Ignore("FIXME: Reinstate when upper bound on epoch view is in place.") - public void committing_epoch_high_views_should_fail() throws TxBuilderException { - // Arrange - var cmd0 = systemUpdateCommand(10, 1); - var ledgerProof = new LedgerProof( - HashUtils.random256(), - LedgerHeader.create(0, View.of(11), new AccumulatorState(3, HashUtils.zero256()), 0), - new TimestampedECDSASignatures() - ); - var commandsAndProof = VerifiedTxnsAndProof.create( - ImmutableList.of(cmd0), - ledgerProof - ); - - // Act - // Assert - assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) - .isInstanceOf(ByzantineQuorumException.class); - } - - // TODO: should catch this and log it somewhere as proof of byzantine quorum - @Test - public void committing_epoch_change_with_additional_cmds_should_fail() throws Exception { - // Arrange - ECKeyPair keyPair = ECKeyPair.generateNew(); - var cmd0 = systemUpdateCommand(0, 2); - var cmd1 = registerCommand(keyPair); - var ledgerProof = new LedgerProof( - HashUtils.random256(), - LedgerHeader.create(0, View.of(9), new AccumulatorState(3, HashUtils.zero256()), 0), - new TimestampedECDSASignatures() - ); - var commandsAndProof = VerifiedTxnsAndProof.create( - ImmutableList.of(cmd0, cmd1), - ledgerProof - ); - - // Act - // Assert - assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) - .isInstanceOf(ByzantineQuorumException.class); - } - - // TODO: should catch this and log it somewhere as proof of byzantine quorum - @Test - public void committing_epoch_change_with_different_validator_signed_should_fail() throws Exception { - // Arrange - var cmd1 = systemUpdateCommand(0, 2); - var ledgerProof = new LedgerProof( - HashUtils.random256(), - LedgerHeader.create(0, View.of(9), new AccumulatorState(3, HashUtils.zero256()), 0, - BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))) - ), - new TimestampedECDSASignatures() - ); - var commandsAndProof = VerifiedTxnsAndProof.create( - ImmutableList.of(cmd1), - ledgerProof - ); - - // Act - // Assert - assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) - .isInstanceOf(ByzantineQuorumException.class); - } - - // TODO: should catch this and log it somewhere as proof of byzantine quorum - @Test - public void committing_epoch_change_when_there_shouldnt_be_one__should_fail() throws TxBuilderException { - // Arrange - var cmd0 = systemUpdateCommand(1, 1); - var ledgerProof = new LedgerProof( - HashUtils.random256(), - LedgerHeader.create(0, View.of(9), new AccumulatorState(3, HashUtils.zero256()), 0, - BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))) - ), - new TimestampedECDSASignatures() - ); - var commandsAndProof = VerifiedTxnsAndProof.create( - ImmutableList.of(cmd0), - ledgerProof - ); - - // Act - // Assert - assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) - .isInstanceOf(ByzantineQuorumException.class); - } + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Inject @Genesis private VerifiedTxnsAndProof genesisTxns; + + @Inject private RadixEngine radixEngine; + + @Inject private RadixEngineStateComputer sut; + + @Inject private RERules rules; + + @Inject private ProposerElection proposerElection; + + private Serialization serialization = DefaultSerialization.getInstance(); + private InMemoryEngineStore engineStore; + private ImmutableList registeredNodes = + ImmutableList.of(ECKeyPair.generateNew(), ECKeyPair.generateNew()); + private ECKeyPair unregisteredNode = ECKeyPair.generateNew(); + + private static final Hasher hasher = Sha256Hasher.withDefaultSerialization(); + + private Module getExternalModule() { + return new AbstractModule() { + @Override + public void configure() { + var validatorSet = + BFTValidatorSet.from( + registeredNodes.stream() + .map(ECKeyPair::getPublicKey) + .map(BFTNode::create) + .map(n -> BFTValidator.from(n, UInt256.ONE))); + + bind(ProposerElection.class).toInstance(new WeightedRotatingLeaders(validatorSet)); + bind(Serialization.class).toInstance(serialization); + bind(Hasher.class).toInstance(Sha256Hasher.withDefaultSerialization()); + bind(new TypeLiteral>() {}).toInstance(engineStore); + bind(PersistentVertexStore.class).toInstance(mock(PersistentVertexStore.class)); + + install(MempoolConfig.asModule(10, 10)); + install(new MainnetForkConfigsModule()); + install(new ForksModule()); + install(new RadixEngineForksLatestOnlyModule()); + + // HACK + bind(CommittedReader.class).toInstance(CommittedReader.mocked()); + + bind(LedgerAccumulator.class).to(SimpleLedgerAccumulatorAndVerifier.class); + + bind(new TypeLiteral>() {}) + .toInstance(TypedMocks.rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(TypedMocks.rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(TypedMocks.rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(TypedMocks.rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(TypedMocks.rmock(EventDispatcher.class)); + bind(new TypeLiteral>() {}) + .toInstance(TypedMocks.rmock(EventDispatcher.class)); + + bind(SystemCounters.class).to(SystemCountersImpl.class); + } + }; + } + + private void setupGenesis() throws RadixEngineException { + var branch = radixEngine.transientBranch(); + var processed = branch.execute(genesisTxns.getTxns(), PermissionLevel.SYSTEM); + var genesisValidatorSet = + processed.getProcessedTxns().get(0).getEvents().stream() + .filter(NextValidatorSetEvent.class::isInstance) + .map(NextValidatorSetEvent.class::cast) + .findFirst() + .map( + e -> + BFTValidatorSet.from( + e.nextValidators().stream() + .map( + v -> + BFTValidator.from( + BFTNode.create(v.getValidatorKey()), v.getAmount())))) + .orElseThrow(() -> new IllegalStateException("No validator set in genesis.")); + radixEngine.deleteBranches(); + + var genesisLedgerHeader = + LedgerProof.genesis( + new AccumulatorState(0, hasher.hash(genesisTxns.getTxns().get(0).getId())), + genesisValidatorSet, + 0); + if (!genesisLedgerHeader.isEndOfEpoch()) { + throw new IllegalStateException("Genesis must be end of epoch"); + } + radixEngine.execute( + genesisTxns.getTxns(), + LedgerAndBFTProof.create(genesisLedgerHeader), + PermissionLevel.SYSTEM); + } + + @Before + public void setup() throws RadixEngineException { + this.engineStore = new InMemoryEngineStore<>(); + Guice.createInjector( + new RadixEngineCheckpointModule(), + new RadixEngineStateComputerModule(), + new RadixEngineModule(), + new MockedGenesisModule( + registeredNodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toSet()), + Amount.ofTokens(1000), + Amount.ofTokens(100)), + getExternalModule()) + .injectMembers(this); + setupGenesis(); + } + + private Txn systemUpdateTxn(long nextView, long nextEpoch) throws TxBuilderException { + TxBuilder builder; + if (nextEpoch >= 2) { + var request = + TxnConstructionRequest.create() + .action( + new NextRound( + 10, true, 0, v -> proposerElection.getProposer(View.of(v)).getKey())) + .action(new NextEpoch(0)); + builder = radixEngine.construct(request); + } else { + builder = + radixEngine.construct( + new NextRound(nextView, false, 0, i -> registeredNodes.get(0).getPublicKey())); + } + + return builder.buildWithoutSignature(); + } + + private Txn systemUpdateCommand(long nextView, long nextEpoch) throws TxBuilderException { + return systemUpdateTxn(nextView, nextEpoch); + } + + private Txn registerCommand(ECKeyPair keyPair) throws TxBuilderException { + return radixEngine + .construct(new RegisterValidator(keyPair.getPublicKey())) + .signAndBuild(keyPair::sign); + } + + @Test + @Ignore("Ignore for now given need for more refactoring to get this test to work") + public void executing_non_epoch_high_view_should_return_no_validator_set() { + // Arrange + var v = + UnverifiedVertex.create( + mock(QuorumCertificate.class), View.of(9), List.of(), BFTNode.random()); + var vertex = new VerifiedVertex(v, mock(HashCode.class)); + + // Action + var result = sut.prepare(List.of(), vertex, 0); + + // Assert + assertThat(result.getSuccessfulCommands()).hasSize(1); + assertThat(result.getFailedCommands()).isEmpty(); + assertThat(result.getNextValidatorSet()).isEmpty(); + } + + @Test + public void executing_epoch_high_view_should_return_next_validator_set() { + // Arrange + var qc = mock(QuorumCertificate.class); + var parentHeader = mock(BFTHeader.class); + when(parentHeader.getView()).thenReturn(View.of(0)); + when(qc.getProposed()).thenReturn(parentHeader); + var unverified = UnverifiedVertex.create(qc, View.of(11), List.of(), BFTNode.random()); + var vertex = new VerifiedVertex(unverified, mock(HashCode.class)); + + // Act + StateComputerResult result = sut.prepare(List.of(), vertex, 0); + + // Assert + assertThat(result.getSuccessfulCommands()).hasSize(1); + assertThat(result.getFailedCommands()).isEmpty(); + assertThat(result.getNextValidatorSet()) + .hasValueSatisfying( + set -> + assertThat(set.getValidators()) + .isNotEmpty() + .allMatch( + v -> + v.getNode().getKey().equals(unregisteredNode.getPublicKey()) + || registeredNodes.stream() + .anyMatch(k -> k.getPublicKey().equals(v.getNode().getKey())))); + } + + @Test + public void executing_epoch_high_view_with_register_should_not_return_new_next_validator_set() + throws Exception { + // Arrange + ECKeyPair keyPair = ECKeyPair.generateNew(); + var txn = registerCommand(keyPair); + BFTNode node = BFTNode.create(keyPair.getPublicKey()); + var qc = mock(QuorumCertificate.class); + var parentHeader = mock(BFTHeader.class); + when(parentHeader.getView()).thenReturn(View.of(0)); + when(qc.getProposed()).thenReturn(parentHeader); + var v = UnverifiedVertex.create(qc, View.of(11), List.of(txn), BFTNode.random()); + var vertex = new VerifiedVertex(v, mock(HashCode.class)); + + // Act + StateComputerResult result = sut.prepare(List.of(), vertex, 0); + + // Assert + assertThat(result.getSuccessfulCommands()) + .hasSize(1); // since high view, command is not executed + assertThat(result.getNextValidatorSet()) + .hasValueSatisfying( + s -> { + assertThat(s.getValidators()).hasSize(2); + assertThat(s.getValidators()).extracting(BFTValidator::getNode).doesNotContain(node); + }); + } + + @Test + public void preparing_system_update_from_vertex_should_fail() throws TxBuilderException { + // Arrange + var txn = + radixEngine + .construct( + new NextRound(1, false, 0, i -> proposerElection.getProposer(View.of(i)).getKey())) + .buildWithoutSignature(); + var illegalTxn = + TxLowLevelBuilder.newBuilder(rules.getSerialization()) + .down(SubstateId.ofSubstate(txn.getId(), 1)) + .up(new RoundData(2, 0)) + .end() + .build(); + var v = + UnverifiedVertex.create( + mock(QuorumCertificate.class), + View.of(1), + List.of(illegalTxn), + proposerElection.getProposer(View.of(1))); + var vertex = new VerifiedVertex(v, mock(HashCode.class)); + + // Act + var result = sut.prepare(ImmutableList.of(), vertex, 0); + + // Assert + assertThat(result.getSuccessfulCommands()).hasSize(1); + assertThat(result.getFailedCommands()) + .hasValueSatisfying( + new Condition<>( + e -> { + RadixEngineException ex = (RadixEngineException) e; + ConstraintMachineException cmException = + (ConstraintMachineException) ex.getCause(); + return cmException.getCause() instanceof InvalidPermissionException; + }, + "Is invalid_execution_permission error")); + } + + // TODO: should catch this and log it somewhere as proof of byzantine quorum + @Test + // Note that checking upper bound view for epoch now requires additional + // state not easily obtained where checked in the RadixEngine + @Ignore("FIXME: Reinstate when upper bound on epoch view is in place.") + public void committing_epoch_high_views_should_fail() throws TxBuilderException { + // Arrange + var cmd0 = systemUpdateCommand(10, 1); + var ledgerProof = + new LedgerProof( + HashUtils.random256(), + LedgerHeader.create(0, View.of(11), new AccumulatorState(3, HashUtils.zero256()), 0), + new TimestampedECDSASignatures()); + var commandsAndProof = VerifiedTxnsAndProof.create(ImmutableList.of(cmd0), ledgerProof); + + // Act + // Assert + assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) + .isInstanceOf(ByzantineQuorumException.class); + } + + // TODO: should catch this and log it somewhere as proof of byzantine quorum + @Test + public void committing_epoch_change_with_additional_cmds_should_fail() throws Exception { + // Arrange + ECKeyPair keyPair = ECKeyPair.generateNew(); + var cmd0 = systemUpdateCommand(0, 2); + var cmd1 = registerCommand(keyPair); + var ledgerProof = + new LedgerProof( + HashUtils.random256(), + LedgerHeader.create(0, View.of(9), new AccumulatorState(3, HashUtils.zero256()), 0), + new TimestampedECDSASignatures()); + var commandsAndProof = VerifiedTxnsAndProof.create(ImmutableList.of(cmd0, cmd1), ledgerProof); + + // Act + // Assert + assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) + .isInstanceOf(ByzantineQuorumException.class); + } + + // TODO: should catch this and log it somewhere as proof of byzantine quorum + @Test + public void committing_epoch_change_with_different_validator_signed_should_fail() + throws Exception { + // Arrange + var cmd1 = systemUpdateCommand(0, 2); + var ledgerProof = + new LedgerProof( + HashUtils.random256(), + LedgerHeader.create( + 0, + View.of(9), + new AccumulatorState(3, HashUtils.zero256()), + 0, + BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE)))), + new TimestampedECDSASignatures()); + var commandsAndProof = VerifiedTxnsAndProof.create(ImmutableList.of(cmd1), ledgerProof); + + // Act + // Assert + assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) + .isInstanceOf(ByzantineQuorumException.class); + } + + // TODO: should catch this and log it somewhere as proof of byzantine quorum + @Test + public void committing_epoch_change_when_there_shouldnt_be_one__should_fail() + throws TxBuilderException { + // Arrange + var cmd0 = systemUpdateCommand(1, 1); + var ledgerProof = + new LedgerProof( + HashUtils.random256(), + LedgerHeader.create( + 0, + View.of(9), + new AccumulatorState(3, HashUtils.zero256()), + 0, + BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE)))), + new TimestampedECDSASignatures()); + var commandsAndProof = VerifiedTxnsAndProof.create(ImmutableList.of(cmd0), ledgerProof); + + // Act + // Assert + assertThatThrownBy(() -> sut.commit(commandsAndProof, null)) + .isInstanceOf(ByzantineQuorumException.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RandomHashTxnsGenerator.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RandomHashTxnsGenerator.java index 3e689e14ef..09a4edb53a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RandomHashTxnsGenerator.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/RandomHashTxnsGenerator.java @@ -69,15 +69,12 @@ import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.liveness.NextTxnsGenerator; import com.radixdlt.crypto.HashUtils; - import java.util.List; -/** - * Generates new random hash commands - */ +/** Generates new random hash commands */ public final class RandomHashTxnsGenerator implements NextTxnsGenerator { - @Override - public List generateNextTxns(View view, List prepared) { - return List.of(Txn.create(HashUtils.random256().asBytes())); - } + @Override + public List generateNextTxns(View view, List prepared) { + return List.of(Txn.create(HashUtils.random256().asBytes())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/StakesTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/StakesTest.java index 050e2a97de..3e9ae42987 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/StakesTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/StakesTest.java @@ -70,11 +70,10 @@ import org.junit.Test; public class StakesTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(Rewards.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(Rewards.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/checkpoint/MockedGenesisModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/checkpoint/MockedGenesisModule.java index ece01960b4..d94f8635d0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/checkpoint/MockedGenesisModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/checkpoint/MockedGenesisModule.java @@ -64,68 +64,74 @@ package com.radixdlt.statecomputer.checkpoint; -import com.google.inject.Key; -import com.google.inject.multibindings.OptionalBinder; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.atom.actions.StakeTokens; -import com.radixdlt.crypto.ECPublicKey; -import com.radixdlt.identifiers.REAddr; -import org.radix.TokenIssuance; - import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; +import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.OptionalBinder; +import com.radixdlt.application.tokens.Amount; import com.radixdlt.atom.TxAction; +import com.radixdlt.atom.actions.StakeTokens; +import com.radixdlt.crypto.ECPublicKey; +import com.radixdlt.identifiers.REAddr; import com.radixdlt.ledger.VerifiedTxnsAndProof; - import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.radix.TokenIssuance; /** - * Genesis atom to be used with tests. Given a set of parameters the genesis transaction - * generated should be deterministic. + * Genesis atom to be used with tests. Given a set of parameters the genesis transaction generated + * should be deterministic. */ public final class MockedGenesisModule extends AbstractModule { - private final Set validators; - private final Amount xrdPerValidator; - private final Amount stakePerValidator; + private final Set validators; + private final Amount xrdPerValidator; + private final Amount stakePerValidator; - public MockedGenesisModule(Set validators, Amount xrdPerValidator, Amount stakePerValidator) { - this.validators = validators; - this.xrdPerValidator = xrdPerValidator; - this.stakePerValidator = stakePerValidator; - } + public MockedGenesisModule( + Set validators, Amount xrdPerValidator, Amount stakePerValidator) { + this.validators = validators; + this.xrdPerValidator = xrdPerValidator; + this.stakePerValidator = stakePerValidator; + } - @Override - public void configure() { - bind(new TypeLiteral>() { }).annotatedWith(Genesis.class).toInstance(validators); - bind(new TypeLiteral() { }).annotatedWith(Genesis.class).toProvider(GenesisProvider.class).in(Scopes.SINGLETON); - OptionalBinder.newOptionalBinder(binder(), Key.get(new TypeLiteral>() { }, Genesis.class)); - } + @Override + public void configure() { + bind(new TypeLiteral>() {}) + .annotatedWith(Genesis.class) + .toInstance(validators); + bind(new TypeLiteral() {}) + .annotatedWith(Genesis.class) + .toProvider(GenesisProvider.class) + .in(Scopes.SINGLETON); + OptionalBinder.newOptionalBinder( + binder(), Key.get(new TypeLiteral>() {}, Genesis.class)); + } - @Provides - @Genesis - public long timestamp() { - return 1234L; - } + @Provides + @Genesis + public long timestamp() { + return 1234L; + } - @Provides - @Genesis - public Set stakeDelegations(@Genesis Set validators) { - return validators.stream() - .map(v -> new StakeTokens(REAddr.ofPubKeyAccount(v), v, stakePerValidator.toSubunits())) - .collect(Collectors.toSet()); - } + @Provides + @Genesis + public Set stakeDelegations(@Genesis Set validators) { + return validators.stream() + .map(v -> new StakeTokens(REAddr.ofPubKeyAccount(v), v, stakePerValidator.toSubunits())) + .collect(Collectors.toSet()); + } - @Provides - @Genesis - public ImmutableList tokenIssuanceList(@Genesis Set validators) { - return validators.stream().map(v -> TokenIssuance.of(v, xrdPerValidator.toSubunits())) - .sorted(Comparator.comparing(t -> t.receiver().toHex())) - .collect(ImmutableList.toImmutableList()); - } + @Provides + @Genesis + public ImmutableList tokenIssuanceList(@Genesis Set validators) { + return validators.stream() + .map(v -> TokenIssuance.of(v, xrdPerValidator.toSubunits())) + .sorted(Comparator.comparing(t -> t.receiver().toHex())) + .collect(ImmutableList.toImmutableList()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/radixengine/MutableTokenAndResourceFeeTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/radixengine/MutableTokenAndResourceFeeTest.java index 9ed273be1a..f9c2c5d4d8 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/radixengine/MutableTokenAndResourceFeeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/statecomputer/radixengine/MutableTokenAndResourceFeeTest.java @@ -64,206 +64,182 @@ package com.radixdlt.statecomputer.radixengine; -import com.radixdlt.application.tokens.Amount; -import com.radixdlt.application.system.FeeTable; -import com.radixdlt.application.tokens.state.TokenResource; -import com.radixdlt.constraintmachine.exceptions.InvalidPermissionException; -import com.radixdlt.constraintmachine.exceptions.ReservedSymbolException; -import com.radixdlt.serialization.DeserializeException; -import com.radixdlt.statecomputer.forks.ForksModule; -import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; -import com.radixdlt.statecomputer.forks.RERulesConfig; -import com.radixdlt.utils.PrivateKeys; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.radixdlt.SingleNodeAndPeersDeterministicNetworkModule; +import com.radixdlt.application.system.FeeTable; +import com.radixdlt.application.tokens.Amount; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.atom.MutableTokenDefinition; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.TxnConstructionRequest; import com.radixdlt.atom.actions.CreateMutableToken; import com.radixdlt.atom.actions.MintToken; import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.constraintmachine.exceptions.InvalidPermissionException; +import com.radixdlt.constraintmachine.exceptions.ReservedSymbolException; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.RadixEngineException; import com.radixdlt.identifiers.REAddr; import com.radixdlt.mempool.MempoolConfig; +import com.radixdlt.serialization.DeserializeException; import com.radixdlt.statecomputer.LedgerAndBFTProof; import com.radixdlt.statecomputer.checkpoint.MockedGenesisModule; +import com.radixdlt.statecomputer.forks.ForksModule; +import com.radixdlt.statecomputer.forks.MainnetForkConfigsModule; +import com.radixdlt.statecomputer.forks.RERulesConfig; import com.radixdlt.statecomputer.forks.RadixEngineForksLatestOnlyModule; import com.radixdlt.store.DatabaseLocation; import com.radixdlt.store.LastStoredProof; import com.radixdlt.store.berkeley.BerkeleyLedgerEntryStore; +import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Map; import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class MutableTokenAndResourceFeeTest { - private static final ECKeyPair VALIDATOR_KEY = PrivateKeys.ofNumeric(1); - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Inject - private RadixEngine sut; - - @Inject - private BerkeleyLedgerEntryStore ledgerEntryStore; - - // FIXME: Hack, need this in order to cause provider for genesis to be stored - @Inject - @LastStoredProof - private LedgerProof ledgerProof; - - private Injector createInjector() { - return Guice.createInjector( - MempoolConfig.asModule(1000, 10), - new MainnetForkConfigsModule(), - new RadixEngineForksLatestOnlyModule(RERulesConfig.testingDefault().overrideFeeTable( - FeeTable.create( - Amount.zero(), - Map.of(TokenResource.class, Amount.ofTokens(1)) - ) - )), - new ForksModule(), - new SingleNodeAndPeersDeterministicNetworkModule(VALIDATOR_KEY, 0), - new MockedGenesisModule( - Set.of(VALIDATOR_KEY.getPublicKey()), - Amount.ofTokens(101), - Amount.ofTokens(100) - ), - new AbstractModule() { - @Override - protected void configure() { - bindConstant().annotatedWith(DatabaseLocation.class).to(folder.getRoot().getAbsolutePath()); - } - } - ); - } - - @Test - public void cannot_create_xrd_token() throws Exception { - // Arrange - createInjector().injectMembers(this); - var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); - var tokDef = new MutableTokenDefinition( - VALIDATOR_KEY.getPublicKey(), - "xrd", - "XRD", - "XRD", - null, - null - ); - var txn = sut.construct( - TxnConstructionRequest.create() - .feePayer(account) - .action(new CreateMutableToken(tokDef)) - ).signAndBuild(VALIDATOR_KEY::sign); - - // Act/Assert - assertThatThrownBy(() -> sut.execute(List.of(txn))) - .hasRootCauseInstanceOf(ReservedSymbolException.class); - } - - @Test - public void cannot_mint_xrd_token() throws Exception { - // Arrange - createInjector().injectMembers(this); - - // Act/Assert - var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); - var txn = sut.construct(new MintToken(REAddr.ofNativeToken(), account, UInt256.SEVEN)) - .signAndBuild(VALIDATOR_KEY::sign); - assertThatThrownBy(() -> sut.execute(List.of(txn))) - .hasRootCauseInstanceOf(InvalidPermissionException.class); - } - - @Test - public void atomic_token_creation_with_fees_and_spend_should_succeed() throws Exception { - // Arrange - createInjector().injectMembers(this); - var tokDef = new MutableTokenDefinition( - VALIDATOR_KEY.getPublicKey(), - "test", - "test", - "desc", - null, - null - ); - - var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(VALIDATOR_KEY.getPublicKey(), "test"); - var txn = sut.construct( - TxnConstructionRequest.create() - .feePayer(account) - .createMutableToken(tokDef) - .mint(tokenAddr, account, UInt256.SEVEN) - .transfer(tokenAddr, account, account, UInt256.FIVE) - ).signAndBuild(VALIDATOR_KEY::sign); - - // Act/Assert - var branch = sut.transientBranch(); - branch.execute(List.of(txn)); - } - - @Test - public void mint_to_non_account_address_should_fail() throws Exception { - // Arrange - createInjector().injectMembers(this); - var tokDef = new MutableTokenDefinition( - VALIDATOR_KEY.getPublicKey(), - "test", - "test", - "desc", - null, - null - ); - - var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(VALIDATOR_KEY.getPublicKey(), "test"); - var txn = sut.construct( - TxnConstructionRequest.create() - .feePayer(account) - .createMutableToken(tokDef) - .mint(tokenAddr, REAddr.ofHashedKey(VALIDATOR_KEY.getPublicKey(), "test"), UInt256.SEVEN) - ).signAndBuild(VALIDATOR_KEY::sign); - - // Act/Assert - assertThatThrownBy(() -> sut.execute(List.of(txn))).hasRootCauseInstanceOf(DeserializeException.class); - } - - @Test - public void can_create_no_description_token() throws TxBuilderException, RadixEngineException { - // Arrange - createInjector().injectMembers(this); - var tokDef = new MutableTokenDefinition( - VALIDATOR_KEY.getPublicKey(), - "test", - "test", - null, - null, - null - ); - var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); - var txn = sut.construct( - TxnConstructionRequest.create() - .feePayer(account) - .action(new CreateMutableToken(tokDef)) - ).signAndBuild(VALIDATOR_KEY::sign); - - var branch = sut.transientBranch(); - // Act/Assert - branch.execute(List.of(txn)); - } + private static final ECKeyPair VALIDATOR_KEY = PrivateKeys.ofNumeric(1); + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Inject private RadixEngine sut; + + @Inject private BerkeleyLedgerEntryStore ledgerEntryStore; + + // FIXME: Hack, need this in order to cause provider for genesis to be stored + @Inject @LastStoredProof private LedgerProof ledgerProof; + + private Injector createInjector() { + return Guice.createInjector( + MempoolConfig.asModule(1000, 10), + new MainnetForkConfigsModule(), + new RadixEngineForksLatestOnlyModule( + RERulesConfig.testingDefault() + .overrideFeeTable( + FeeTable.create( + Amount.zero(), Map.of(TokenResource.class, Amount.ofTokens(1))))), + new ForksModule(), + new SingleNodeAndPeersDeterministicNetworkModule(VALIDATOR_KEY, 0), + new MockedGenesisModule( + Set.of(VALIDATOR_KEY.getPublicKey()), Amount.ofTokens(101), Amount.ofTokens(100)), + new AbstractModule() { + @Override + protected void configure() { + bindConstant() + .annotatedWith(DatabaseLocation.class) + .to(folder.getRoot().getAbsolutePath()); + } + }); + } + + @Test + public void cannot_create_xrd_token() throws Exception { + // Arrange + createInjector().injectMembers(this); + var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); + var tokDef = + new MutableTokenDefinition(VALIDATOR_KEY.getPublicKey(), "xrd", "XRD", "XRD", null, null); + var txn = + sut.construct( + TxnConstructionRequest.create() + .feePayer(account) + .action(new CreateMutableToken(tokDef))) + .signAndBuild(VALIDATOR_KEY::sign); + + // Act/Assert + assertThatThrownBy(() -> sut.execute(List.of(txn))) + .hasRootCauseInstanceOf(ReservedSymbolException.class); + } + + @Test + public void cannot_mint_xrd_token() throws Exception { + // Arrange + createInjector().injectMembers(this); + + // Act/Assert + var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); + var txn = + sut.construct(new MintToken(REAddr.ofNativeToken(), account, UInt256.SEVEN)) + .signAndBuild(VALIDATOR_KEY::sign); + assertThatThrownBy(() -> sut.execute(List.of(txn))) + .hasRootCauseInstanceOf(InvalidPermissionException.class); + } + + @Test + public void atomic_token_creation_with_fees_and_spend_should_succeed() throws Exception { + // Arrange + createInjector().injectMembers(this); + var tokDef = + new MutableTokenDefinition( + VALIDATOR_KEY.getPublicKey(), "test", "test", "desc", null, null); + + var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(VALIDATOR_KEY.getPublicKey(), "test"); + var txn = + sut.construct( + TxnConstructionRequest.create() + .feePayer(account) + .createMutableToken(tokDef) + .mint(tokenAddr, account, UInt256.SEVEN) + .transfer(tokenAddr, account, account, UInt256.FIVE)) + .signAndBuild(VALIDATOR_KEY::sign); + + // Act/Assert + var branch = sut.transientBranch(); + branch.execute(List.of(txn)); + } + + @Test + public void mint_to_non_account_address_should_fail() throws Exception { + // Arrange + createInjector().injectMembers(this); + var tokDef = + new MutableTokenDefinition( + VALIDATOR_KEY.getPublicKey(), "test", "test", "desc", null, null); + + var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(VALIDATOR_KEY.getPublicKey(), "test"); + var txn = + sut.construct( + TxnConstructionRequest.create() + .feePayer(account) + .createMutableToken(tokDef) + .mint( + tokenAddr, + REAddr.ofHashedKey(VALIDATOR_KEY.getPublicKey(), "test"), + UInt256.SEVEN)) + .signAndBuild(VALIDATOR_KEY::sign); + + // Act/Assert + assertThatThrownBy(() -> sut.execute(List.of(txn))) + .hasRootCauseInstanceOf(DeserializeException.class); + } + + @Test + public void can_create_no_description_token() throws TxBuilderException, RadixEngineException { + // Arrange + createInjector().injectMembers(this); + var tokDef = + new MutableTokenDefinition(VALIDATOR_KEY.getPublicKey(), "test", "test", null, null, null); + var account = REAddr.ofPubKeyAccount(VALIDATOR_KEY.getPublicKey()); + var txn = + sut.construct( + TxnConstructionRequest.create() + .feePayer(account) + .action(new CreateMutableToken(tokDef))) + .signAndBuild(VALIDATOR_KEY::sign); + + var branch = sut.transientBranch(); + // Act/Assert + branch.execute(List.of(txn)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/MockedRadixEngineStoreModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/MockedRadixEngineStoreModule.java index ea4ade9011..00e7c98e39 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/MockedRadixEngineStoreModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/MockedRadixEngineStoreModule.java @@ -72,14 +72,14 @@ import com.radixdlt.statecomputer.LedgerAndBFTProof; public class MockedRadixEngineStoreModule extends AbstractModule { - @Override - public void configure() { - bind(Serialization.class).toInstance(DefaultSerialization.getInstance()); - } + @Override + public void configure() { + bind(Serialization.class).toInstance(DefaultSerialization.getInstance()); + } - @Provides - @Singleton - private EngineStore engineStore() { - return new InMemoryEngineStore<>(); - } + @Provides + @Singleton + private EngineStore engineStore() { + return new InMemoryEngineStore<>(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStoreTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStoreTest.java index 16007b23b4..914d08fa0f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStoreTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/BerkeleySafetyStateStoreTest.java @@ -64,8 +64,16 @@ package com.radixdlt.store.berkeley; -import org.junit.Test; -import org.mockito.ArgumentCaptor; +import static com.radixdlt.utils.SerializerTestDataGenerator.randomView; +import static com.radixdlt.utils.SerializerTestDataGenerator.randomVote; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import com.radixdlt.DefaultSerialization; import com.radixdlt.consensus.safety.SafetyState; @@ -76,60 +84,53 @@ import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.Environment; import com.sleepycat.je.OperationStatus; - import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import static com.radixdlt.utils.SerializerTestDataGenerator.randomView; -import static com.radixdlt.utils.SerializerTestDataGenerator.randomVote; +import org.junit.Test; +import org.mockito.ArgumentCaptor; public class BerkeleySafetyStateStoreTest { - @Test - public void should_be_able_to_restore_committed_state() { - final var db = mock(Database.class); - final var env = mock(Environment.class); - final var dbEnv = mock(DatabaseEnvironment.class); - final var tx = mock(com.sleepycat.je.Transaction.class); - when(dbEnv.getEnvironment()).thenReturn(env); - when(env.openDatabase(any(), any(), any())).thenReturn(db); + @Test + public void should_be_able_to_restore_committed_state() { + final var db = mock(Database.class); + final var env = mock(Environment.class); + final var dbEnv = mock(DatabaseEnvironment.class); + final var tx = mock(com.sleepycat.je.Transaction.class); + when(dbEnv.getEnvironment()).thenReturn(env); + when(env.openDatabase(any(), any(), any())).thenReturn(db); - final var store = new BerkeleySafetyStateStore(dbEnv, DefaultSerialization.getInstance(), new SystemCountersImpl()); + final var store = + new BerkeleySafetyStateStore( + dbEnv, DefaultSerialization.getInstance(), new SystemCountersImpl()); - final var safetyState = new SafetyState(randomView(), Optional.of(randomVote())); + final var safetyState = new SafetyState(randomView(), Optional.of(randomVote())); - when(env.beginTransaction(any(), any())).thenReturn(tx); + when(env.beginTransaction(any(), any())).thenReturn(tx); - when(db.put(any(), any(), any())).thenReturn(OperationStatus.SUCCESS); + when(db.put(any(), any(), any())).thenReturn(OperationStatus.SUCCESS); - ArgumentCaptor entryCaptor = ArgumentCaptor.forClass(DatabaseEntry.class); + ArgumentCaptor entryCaptor = ArgumentCaptor.forClass(DatabaseEntry.class); - store.commitState(safetyState); + store.commitState(safetyState); - verify(db, times(1)).put(any(), any(), entryCaptor.capture()); - verify(tx, times(1)).commit(); - verifyNoMoreInteractions(tx); + verify(db, times(1)).put(any(), any(), entryCaptor.capture()); + verify(tx, times(1)).commit(); + verifyNoMoreInteractions(tx); - final var cursor = mock(Cursor.class); - when(db.openCursor(any(), any())).thenReturn(cursor); + final var cursor = mock(Cursor.class); + when(db.openCursor(any(), any())).thenReturn(cursor); - when(cursor.getLast(any(), any(), any())).thenAnswer(invocation -> { - DatabaseEntry entry = (DatabaseEntry) invocation.getArguments()[1]; - entry.setData(entryCaptor.getValue().getData()); - return OperationStatus.SUCCESS; - }); + when(cursor.getLast(any(), any(), any())) + .thenAnswer( + invocation -> { + DatabaseEntry entry = (DatabaseEntry) invocation.getArguments()[1]; + entry.setData(entryCaptor.getValue().getData()); + return OperationStatus.SUCCESS; + }); - var state = store.get(); + var state = store.get(); - assertTrue(state.isPresent()); - assertEquals(safetyState, state.get()); - } + assertTrue(state.isPresent()); + assertEquals(safetyState, state.get()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/SerializedVertexStoreStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/SerializedVertexStoreStateTest.java index 653f02e309..849df49fea 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/SerializedVertexStoreStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/SerializedVertexStoreStateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SerializedVertexStoreStateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SerializedVertexStoreState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(SerializedVertexStoreState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/atom/SimpleAppendLogTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/atom/SimpleAppendLogTest.java index a08c72d911..7107ab65b9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/atom/SimpleAppendLogTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/store/berkeley/atom/SimpleAppendLogTest.java @@ -64,95 +64,94 @@ package com.radixdlt.store.berkeley.atom; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import com.radixdlt.counters.SystemCounters; - -import java.io.IOException; - +import static com.radixdlt.store.berkeley.atom.AppendLog.openCompressed; +import static com.radixdlt.store.berkeley.atom.AppendLog.openSimple; import static org.junit.Assert.assertArrayEquals; import static org.mockito.Mockito.mock; -import static com.radixdlt.store.berkeley.atom.AppendLog.openCompressed; -import static com.radixdlt.store.berkeley.atom.AppendLog.openSimple; +import com.radixdlt.counters.SystemCounters; +import java.io.IOException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class SimpleAppendLogTest { - private final SystemCounters systemCounters = mock(SystemCounters.class); + private final SystemCounters systemCounters = mock(SystemCounters.class); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Test - public void appendLogCanBeCreated() throws IOException { - String path = createTempPath(); + @Test + public void appendLogCanBeCreated() throws IOException { + String path = createTempPath(); - readAfterWrite(openSimple(path)); - } + readAfterWrite(openSimple(path)); + } - @Test - public void appendLogCanBeReadFromTheBeginning() throws IOException { - var path = createTempPath(); + @Test + public void appendLogCanBeReadFromTheBeginning() throws IOException { + var path = createTempPath(); - writeLogEntriesAndClose(openSimple(path)); + writeLogEntriesAndClose(openSimple(path)); - readSequentially(openSimple(path)); - } + readSequentially(openSimple(path)); + } - @Test - public void compressedAppendLogCanBeCreated() throws IOException { - String path = createTempPath(); + @Test + public void compressedAppendLogCanBeCreated() throws IOException { + String path = createTempPath(); - readAfterWrite(openCompressed(path, systemCounters)); - } + readAfterWrite(openCompressed(path, systemCounters)); + } - @Test - public void compressedAppendLogCanBeReadFromTheBeginning() throws IOException { - var path = createTempPath(); + @Test + public void compressedAppendLogCanBeReadFromTheBeginning() throws IOException { + var path = createTempPath(); - writeLogEntriesAndClose(openCompressed(path, systemCounters)); + writeLogEntriesAndClose(openCompressed(path, systemCounters)); - readSequentially(openCompressed(path, systemCounters)); - } + readSequentially(openCompressed(path, systemCounters)); + } - private String createTempPath() throws IOException { - return folder.newFile().getAbsolutePath(); - } + private String createTempPath() throws IOException { + return folder.newFile().getAbsolutePath(); + } - private void readSequentially(final AppendLog newAppendLog) throws IOException { - long pos; + private void readSequentially(final AppendLog newAppendLog) throws IOException { + long pos; - pos = checkSingleChunk(newAppendLog, 0L, new byte[]{0x01}); - pos = checkSingleChunk(newAppendLog, pos, new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}); - pos = checkSingleChunk(newAppendLog, pos, new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x0C, 0x7F, -1}); - } + pos = checkSingleChunk(newAppendLog, 0L, new byte[] {0x01}); + pos = checkSingleChunk(newAppendLog, pos, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + pos = + checkSingleChunk( + newAppendLog, pos, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x0C, 0x7F, -1}); + } - private void writeLogEntriesAndClose(final AppendLog appendLog) throws IOException { - var s0 = appendLog.write(new byte[]{0x01}, 0); - var s1 = appendLog.write(new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}, s0); - appendLog.write(new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x0C, 0x7F, -1}, s0 + s1); - appendLog.close(); - } + private void writeLogEntriesAndClose(final AppendLog appendLog) throws IOException { + var s0 = appendLog.write(new byte[] {0x01}, 0); + var s1 = appendLog.write(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}, s0); + appendLog.write(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x0C, 0x7F, -1}, s0 + s1); + appendLog.close(); + } - private void readAfterWrite(final AppendLog appendLog) throws IOException { - checkReadAfterWrite(appendLog, new byte[]{0x01}); - checkReadAfterWrite(appendLog, new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}); - checkReadAfterWrite(appendLog, new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x0C, 0x7F, -1}); - } + private void readAfterWrite(final AppendLog appendLog) throws IOException { + checkReadAfterWrite(appendLog, new byte[] {0x01}); + checkReadAfterWrite(appendLog, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + checkReadAfterWrite(appendLog, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x0C, 0x7F, -1}); + } - private void checkReadAfterWrite(AppendLog appendLog, byte[] data) throws IOException { - long pos = appendLog.position(); - appendLog.write(data, pos); + private void checkReadAfterWrite(AppendLog appendLog, byte[] data) throws IOException { + long pos = appendLog.position(); + appendLog.write(data, pos); - assertArrayEquals(data, appendLog.read(pos)); - } + assertArrayEquals(data, appendLog.read(pos)); + } - private long checkSingleChunk(AppendLog appendLog, long offset, byte[] expect) throws IOException { - var result = appendLog.readChunk(offset); + private long checkSingleChunk(AppendLog appendLog, long offset, byte[] expect) + throws IOException { + var result = appendLog.readChunk(offset); - assertArrayEquals(expect, result.getFirst()); + assertArrayEquals(expect, result.getFirst()); - return offset + result.getSecond() + Integer.BYTES; - } -} \ No newline at end of file + return offset + result.getSecond() + Integer.BYTES; + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/InMemoryCommittedReader.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/InMemoryCommittedReader.java index 7636f6e7cd..2f1414fb47 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/InMemoryCommittedReader.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/InMemoryCommittedReader.java @@ -69,90 +69,84 @@ import com.radixdlt.consensus.LedgerProof; import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.EventProcessor; -import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.DtoLedgerProof; import com.radixdlt.ledger.LedgerAccumulatorVerifier; +import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.VerifiedTxnsAndProof; - import java.util.List; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.TreeMap; -/** - * A correct in memory committed reader used for testing - */ +/** A correct in memory committed reader used for testing */ class InMemoryCommittedReader implements CommittedReader { - private final Object lock = new Object(); - private final TreeMap commandsAndProof = new TreeMap<>(); - private final LedgerAccumulatorVerifier accumulatorVerifier; - private final Hasher hasher; - private final TreeMap epochProofs = new TreeMap<>(); + private final Object lock = new Object(); + private final TreeMap commandsAndProof = new TreeMap<>(); + private final LedgerAccumulatorVerifier accumulatorVerifier; + private final Hasher hasher; + private final TreeMap epochProofs = new TreeMap<>(); - @Inject - InMemoryCommittedReader( - LedgerAccumulatorVerifier accumulatorVerifier, - Hasher hasher - ) { - this.accumulatorVerifier = Objects.requireNonNull(accumulatorVerifier); - this.hasher = Objects.requireNonNull(hasher); - } + @Inject + InMemoryCommittedReader(LedgerAccumulatorVerifier accumulatorVerifier, Hasher hasher) { + this.accumulatorVerifier = Objects.requireNonNull(accumulatorVerifier); + this.hasher = Objects.requireNonNull(hasher); + } - public EventProcessor updateProcessor() { - return update -> { - synchronized (lock) { - var commands = update.getNewTxns(); - long firstVersion = update.getTail().getStateVersion() - commands.size() + 1; - for (long version = firstVersion; version <= update.getTail().getStateVersion(); version++) { - int index = (int) (version - firstVersion); - commandsAndProof.put( - version, - VerifiedTxnsAndProof.create( - commands.subList(index, commands.size()), - update.getTail() - ) - ); - } + public EventProcessor updateProcessor() { + return update -> { + synchronized (lock) { + var commands = update.getNewTxns(); + long firstVersion = update.getTail().getStateVersion() - commands.size() + 1; + for (long version = firstVersion; + version <= update.getTail().getStateVersion(); + version++) { + int index = (int) (version - firstVersion); + commandsAndProof.put( + version, + VerifiedTxnsAndProof.create( + commands.subList(index, commands.size()), update.getTail())); + } - if (update.getTail().isEndOfEpoch()) { - this.epochProofs.put(update.getTail().getEpoch() + 1, update.getTail()); - } - } - }; - } + if (update.getTail().isEndOfEpoch()) { + this.epochProofs.put(update.getTail().getEpoch() + 1, update.getTail()); + } + } + }; + } - @Override - public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { - synchronized (lock) { - final long stateVersion = start.getLedgerHeader().getAccumulatorState().getStateVersion(); - Entry entry = commandsAndProof.higherEntry(stateVersion); + @Override + public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { + synchronized (lock) { + final long stateVersion = start.getLedgerHeader().getAccumulatorState().getStateVersion(); + Entry entry = commandsAndProof.higherEntry(stateVersion); - if (entry != null) { - List txns = accumulatorVerifier - .verifyAndGetExtension( - start.getLedgerHeader().getAccumulatorState(), - entry.getValue().getTxns(), - txn -> txn.getId().asHashCode(), - entry.getValue().getProof().getAccumulatorState() - ).orElseThrow(() -> new RuntimeException()); + if (entry != null) { + List txns = + accumulatorVerifier + .verifyAndGetExtension( + start.getLedgerHeader().getAccumulatorState(), + entry.getValue().getTxns(), + txn -> txn.getId().asHashCode(), + entry.getValue().getProof().getAccumulatorState()) + .orElseThrow(() -> new RuntimeException()); - return VerifiedTxnsAndProof.create(txns, entry.getValue().getProof()); - } + return VerifiedTxnsAndProof.create(txns, entry.getValue().getProof()); + } - return null; - } - } + return null; + } + } - @Override - public Optional getEpochProof(long epoch) { - synchronized (lock) { - return Optional.ofNullable(epochProofs.get(epoch)); - } - } + @Override + public Optional getEpochProof(long epoch) { + synchronized (lock) { + return Optional.ofNullable(epochProofs.get(epoch)); + } + } - @Override - public Optional getLastProof() { - return Optional.ofNullable(commandsAndProof.lastEntry()).map(p -> p.getValue().getProof()); - } + @Override + public Optional getLastProof() { + return Optional.ofNullable(commandsAndProof.lastEntry()).map(p -> p.getValue().getProof()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/LocalSyncServiceTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/LocalSyncServiceTest.java index 7c629cdb4c..a1ee186494 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/LocalSyncServiceTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/LocalSyncServiceTest.java @@ -67,14 +67,14 @@ import static com.radixdlt.utils.TypedMocks.rmock; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; -import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.anyLong; import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.common.collect.ImmutableList; @@ -88,16 +88,11 @@ import com.radixdlt.environment.ScheduledEventDispatcher; import com.radixdlt.identifiers.AID; import com.radixdlt.ledger.AccumulatorState; -import com.radixdlt.ledger.LedgerAccumulatorVerifier; import com.radixdlt.ledger.DtoLedgerProof; import com.radixdlt.ledger.DtoTxnsAndProof; +import com.radixdlt.ledger.LedgerAccumulatorVerifier; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.VerifiedTxnsAndProof; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.stream.Stream; - import com.radixdlt.network.p2p.PeersView; import com.radixdlt.network.p2p.PeersView.PeerInfo; import com.radixdlt.sync.LocalSyncService.InvalidSyncResponseHandler; @@ -113,540 +108,587 @@ import com.radixdlt.sync.messages.remote.SyncResponse; import com.radixdlt.sync.validation.RemoteSyncResponseSignaturesVerifier; import com.radixdlt.sync.validation.RemoteSyncResponseValidatorSetVerifier; +import java.util.Arrays; +import java.util.Comparator; +import java.util.stream.Stream; import org.junit.Before; import org.junit.Test; public class LocalSyncServiceTest { - private LocalSyncService localSyncService; - private RemoteEventDispatcher statusRequestDispatcher; - private ScheduledEventDispatcher syncCheckReceiveStatusTimeoutDispatcher; - private RemoteEventDispatcher syncRequestDispatcher; - private ScheduledEventDispatcher syncRequestTimeoutDispatcher; - private ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher; - private SyncConfig syncConfig; - private SystemCounters systemCounters; - private PeersView peersView; - private Comparator accComparator; - private RemoteSyncResponseValidatorSetVerifier validatorSetVerifier; - private RemoteSyncResponseSignaturesVerifier signaturesVerifier; - private LedgerAccumulatorVerifier accumulatorVerifier; - private VerifiedSyncResponseHandler verifiedSyncResponseHandler; - private InvalidSyncResponseHandler invalidSyncResponseHandler; - - @Before - public void setUp() { - this.statusRequestDispatcher = rmock(RemoteEventDispatcher.class); - this.syncCheckReceiveStatusTimeoutDispatcher = rmock(ScheduledEventDispatcher.class); - this.syncRequestDispatcher = rmock(RemoteEventDispatcher.class); - this.syncRequestTimeoutDispatcher = rmock(ScheduledEventDispatcher.class); - this.syncLedgerUpdateTimeoutDispatcher = rmock(ScheduledEventDispatcher.class); - this.syncConfig = SyncConfig.of(1000L, 10, 10000L); - this.systemCounters = mock(SystemCounters.class); - this.peersView = mock(PeersView.class); - this.accComparator = Comparator.comparingLong(AccumulatorState::getStateVersion); - this.validatorSetVerifier = mock(RemoteSyncResponseValidatorSetVerifier.class); - this.signaturesVerifier = mock(RemoteSyncResponseSignaturesVerifier.class); - this.accumulatorVerifier = mock(LedgerAccumulatorVerifier.class); - this.verifiedSyncResponseHandler = mock(VerifiedSyncResponseHandler.class); - this.invalidSyncResponseHandler = mock(InvalidSyncResponseHandler.class); - } - - private void setupSyncServiceWithState(SyncState syncState) { - this.localSyncService = new LocalSyncService( - statusRequestDispatcher, - syncCheckReceiveStatusTimeoutDispatcher, - syncRequestDispatcher, - syncRequestTimeoutDispatcher, - syncLedgerUpdateTimeoutDispatcher, - syncConfig, - systemCounters, - peersView, - accComparator, - validatorSetVerifier, - signaturesVerifier, - accumulatorVerifier, - verifiedSyncResponseHandler, - invalidSyncResponseHandler, - syncState - ); - } - - @Test - public void when_sync_check_is_triggered_at_idle__then_should_ask_peers_for_their_statuses() { - final var peer1 = createPeer(); - final var peer2 = createPeer(); - final var peer3 = createPeer(); - - setupPeersView(peer1, peer2, peer3); - - final LedgerProof currentHeader = mock(LedgerProof.class); - this.setupSyncServiceWithState(SyncState.IdleState.init(currentHeader)); - - this.localSyncService.syncCheckTriggerEventProcessor().process(SyncCheckTrigger.create()); - - verify(statusRequestDispatcher, times(1)).dispatch(eq(peer1), any()); - verify(statusRequestDispatcher, times(1)).dispatch(eq(peer2), any()); - verify(statusRequestDispatcher, times(1)).dispatch(eq(peer2), any()); - } - - @Test - public void when_sync_check_is_triggered_at_non_idle__then_should_be_ignored() { - final LedgerProof currentHeader = mock(LedgerProof.class); - - this.setupSyncServiceWithState(SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of())); - this.localSyncService.syncCheckTriggerEventProcessor().process(SyncCheckTrigger.create()); - - this.setupSyncServiceWithState(SyncState.SyncingState.init(currentHeader, ImmutableList.of(), currentHeader)); - this.localSyncService.syncCheckTriggerEventProcessor().process(SyncCheckTrigger.create()); - - verifyNoMoreInteractions(peersView); - verifyNoMoreInteractions(statusRequestDispatcher); - } - - @Test - public void when_status_response_received_at_non_sync_check__then_should_be_ignored() { - final LedgerProof currentHeader = mock(LedgerProof.class); - final LedgerProof statusHeader = mock(LedgerProof.class); - final BFTNode sender = createPeer(); - - this.setupSyncServiceWithState(SyncState.IdleState.init(currentHeader)); - this.localSyncService.statusResponseEventProcessor().process(sender, StatusResponse.create(statusHeader)); - - this.setupSyncServiceWithState(SyncState.SyncingState.init(currentHeader, ImmutableList.of(), currentHeader)); - this.localSyncService.statusResponseEventProcessor().process(sender, StatusResponse.create(statusHeader)); - - verifyNoMoreInteractions(peersView); - verifyNoMoreInteractions(statusRequestDispatcher); - verifyNoMoreInteractions(syncRequestDispatcher); - verifyNoMoreInteractions(syncRequestTimeoutDispatcher); - } - - @Test - public void when_unexpected_status_response_received__then_should_be_ignored() { - final LedgerProof currentHeader = mock(LedgerProof.class); - final LedgerProof statusHeader = mock(LedgerProof.class); - final BFTNode expectedPeer = createPeer(); - final BFTNode unexpectedPeer = createPeer(); - - this.setupSyncServiceWithState(SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(expectedPeer))); - this.localSyncService.statusResponseEventProcessor().process(unexpectedPeer, StatusResponse.create(statusHeader)); - - verifyNoMoreInteractions(peersView); - verifyNoMoreInteractions(statusRequestDispatcher); - verifyNoMoreInteractions(syncRequestDispatcher); - verifyNoMoreInteractions(syncRequestTimeoutDispatcher); - } - - @Test - public void when_duplicate_status_response_received__then_should_be_ignored() { - final LedgerProof currentHeader = mock(LedgerProof.class); - final LedgerProof statusHeader = mock(LedgerProof.class); - final BFTNode expectedPeer = createPeer(); - final BFTNode alreadyReceivedPeer = createPeer(); - - final var syncState = - SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(expectedPeer)) - .withStatusResponse(alreadyReceivedPeer, StatusResponse.create(statusHeader)); - - this.setupSyncServiceWithState(syncState); - this.localSyncService.statusResponseEventProcessor().process(alreadyReceivedPeer, StatusResponse.create(statusHeader)); - - verifyNoMoreInteractions(peersView); - verifyNoMoreInteractions(statusRequestDispatcher); - verifyNoMoreInteractions(syncRequestDispatcher); - verifyNoMoreInteractions(syncRequestTimeoutDispatcher); - } - - @Test - public void when_all_status_responses_received__then_should_start_sync() { - final LedgerProof currentHeader = createHeaderAtStateVersion(10L); - final LedgerProof statusHeader1 = createHeaderAtStateVersion(2L); - final LedgerProof statusHeader2 = createHeaderAtStateVersion(20L); - final LedgerProof statusHeader3 = createHeaderAtStateVersion(15L); - final BFTNode waiting1 = createPeer(); - final BFTNode waiting2 = createPeer(); - final BFTNode waiting3 = createPeer(); - - final var syncState = SyncState.SyncCheckState.init( - currentHeader, ImmutableSet.of(waiting1, waiting2, waiting3)); - this.setupSyncServiceWithState(syncState); - - setupPeersView(waiting2); - - this.localSyncService.statusResponseEventProcessor().process(waiting1, StatusResponse.create(statusHeader1)); - this.localSyncService.statusResponseEventProcessor().process(waiting2, StatusResponse.create(statusHeader2)); - this.localSyncService.statusResponseEventProcessor().process(waiting3, StatusResponse.create(statusHeader3)); - - verify(syncRequestDispatcher, times(1)).dispatch(eq(waiting2), any()); - } - - @Test - public void when_status_timeout_with_no_responses__then_should_reschedule_another_check() { - final LedgerProof currentHeader = createHeaderAtStateVersion(10L); - final BFTNode waiting1 = createPeer(); - setupPeersView(waiting1); - - final var syncState = SyncState.SyncCheckState.init( - currentHeader, ImmutableSet.of(waiting1)); - this.setupSyncServiceWithState(syncState); - - - this.localSyncService.syncCheckReceiveStatusTimeoutEventProcessor().process( - SyncCheckReceiveStatusTimeout.create() - ); - - verifyNoMoreInteractions(syncRequestDispatcher); - } - - @Test - public void when_status_timeout_with_at_least_one_response__then_should_start_sync() { - final LedgerProof currentHeader = createHeaderAtStateVersion(10L); - final LedgerProof statusHeader1 = createHeaderAtStateVersion(12L); - final LedgerProof statusHeader2 = createHeaderAtStateVersion(20L); - final var waiting1 = createPeer(); - final var waiting2 = createPeer(); - setupPeersView(waiting1, waiting2); - - final var syncState = SyncState.SyncCheckState.init( - currentHeader, ImmutableSet.of(waiting1, waiting2)); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.statusResponseEventProcessor().process(waiting1, StatusResponse.create(statusHeader1)); - - this.localSyncService.syncCheckReceiveStatusTimeoutEventProcessor().process( - SyncCheckReceiveStatusTimeout.create() - ); - - // even though statusHeader2 is more up to date, it should be ignored because was received - // after a timeout event - this.localSyncService.statusResponseEventProcessor().process(waiting2, StatusResponse.create(statusHeader2)); - - verify(syncRequestDispatcher, times(1)).dispatch(eq(waiting1), any()); - } - - @Test - public void when_syncing_timeout__then_should_remove_candidate_and_retry_with_other_candidate() { - final var currentHeader = createHeaderAtStateVersion(10L); - final var targetHeader = createHeaderAtStateVersion(20L); - - final var peer1 = createPeer(); - final var peer2 = createPeer(); - setupPeersView(peer1, peer2); - - final var requestId = 1L; - final var originalCandidates = ImmutableList.of(peer1, peer2); - final var syncState = SyncState.SyncingState.init( - currentHeader, originalCandidates, targetHeader).withPendingRequest(peer1, requestId); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.syncRequestTimeoutEventProcessor() - .process(SyncRequestTimeout.create(peer1, requestId)); - - verify(syncRequestDispatcher, times(1)).dispatch(eq(peer2), any()); - } - - @Test - public void when_syncing_timeout_for_different_peer_same_request_id__then_should_ignore() { - final var currentHeader = createHeaderAtStateVersion(10L); - final var targetHeader = createHeaderAtStateVersion(20L); - - final var peer1 = createPeer(); - final var peer2 = createPeer(); - setupPeersView(peer1, peer2); - - final var requestId = 1L; - final var originalCandidates = ImmutableList.of(peer1, peer2); - final var syncState = SyncState.SyncingState.init( - currentHeader, originalCandidates, targetHeader).withPendingRequest(peer1, requestId); - this.setupSyncServiceWithState(syncState); - - // waiting for response from peer1, but got a timeout for peer2 - this.localSyncService.syncRequestTimeoutEventProcessor() - .process(SyncRequestTimeout.create(peer2, requestId)); - - verifyNoMoreInteractions(syncRequestDispatcher); - } - - @Test - public void when_syncing_timeout_for_same_peer_different_request_id__then_should_ignore() { - final var currentHeader = createHeaderAtStateVersion(10L); - final var targetHeader = createHeaderAtStateVersion(20L); - - final var peer1 = mock(BFTNode.class); - final var peer2 = mock(BFTNode.class); - when(peersView.peers()).thenAnswer(i -> Stream.of(peer1, peer2)); - - final var originalCandidates = ImmutableList.of(peer1, peer2); - final var syncState = SyncState.SyncingState.init( - currentHeader, originalCandidates, targetHeader).withPendingRequest(peer1, 2L); - this.setupSyncServiceWithState(syncState); - - // waiting for response for request id 2, but got a timeout for 1 - this.localSyncService.syncRequestTimeoutEventProcessor() - .process(SyncRequestTimeout.create(peer1, 1L)); - - verifyNoMoreInteractions(syncRequestDispatcher); - } - - @Test - public void when_received_a_valid_response__then_should_send_verified() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(20L); - - final var peer1 = createPeer(); - setupPeersView(peer1); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1), targetHeader).withPendingRequest(peer1, 1L); - this.setupSyncServiceWithState(syncState); - - final var syncResponse = createValidMockedSyncResponse(); - - this.localSyncService.syncResponseEventProcessor().process(peer1, syncResponse); - - verify(verifiedSyncResponseHandler, times(1)).handleVerifiedSyncResponse(syncResponse); - verify(syncLedgerUpdateTimeoutDispatcher, times(1)).dispatch(any(), anyLong()); - verifyNoMoreInteractions(syncRequestDispatcher); - } - - @Test - public void when_received_ledger_update_and_fully_synced__then_should_wait_for_another_sync_trigger() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(20L); - - final var peer1 = createPeer(); - setupPeersView(peer1); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1), targetHeader).withPendingRequest(peer1, 1L); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.ledgerUpdateEventProcessor() - .process(ledgerUpdateAtStateVersion(targetHeader.getStateVersion())); - - verifyNoMoreInteractions(syncRequestDispatcher); - } - - @Test - public void when_ledger_update_timeout__then_should_continue_sync() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(21L); - - final var peer1 = createPeer(); - setupPeersView(peer1); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1), targetHeader); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.syncLedgerUpdateTimeoutProcessor().process( - SyncLedgerUpdateTimeout.create(currentHeader.getStateVersion()) - ); - - verify(syncRequestDispatcher, times(1)).dispatch(eq(peer1), any()); - } - - @Test - public void when_obsolete_ledger_update_timeout__then_should_ignore() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(21L); - - final var peer1 = createPeer(); - setupPeersView(peer1); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1), targetHeader); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.syncLedgerUpdateTimeoutProcessor().process( - SyncLedgerUpdateTimeout.create(currentHeader.getStateVersion() - 1) // timeout event for a past state version - ); - - verifyNoInteractions(syncRequestDispatcher); - } - - @Test - public void when_remote_status_update_in_idle__then_should_start_sync() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(21L); - - final var peer1 = createPeer(); - setupPeersView(peer1); - - final var syncState = SyncState.IdleState.init(currentHeader); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.ledgerStatusUpdateEventProcessor().process( - peer1, - LedgerStatusUpdate.create(targetHeader) - ); - - verify(syncRequestDispatcher, times(1)).dispatch(eq(peer1), any()); - } - - @Test - public void when_remote_status_update_in_syncing__then_should_update_target() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(21L); - final var newTargetHeader = createHeaderAtStateVersion(22L); - - final var peer1 = createPeer(); - final var peer2 = createPeer(); - setupPeersView(peer1, peer2); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1), targetHeader).withPendingRequest(peer1, 1L); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.ledgerStatusUpdateEventProcessor().process( - peer2, - LedgerStatusUpdate.create(newTargetHeader) - ); - - assertEquals( - newTargetHeader, - ((SyncState.SyncingState) this.localSyncService.getSyncState()).getTargetHeader() - ); - } - - @Test - public void when_remote_status_update_in_syncing_for_older_header__then_should_do_nothing() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(21L); - final var newTargetHeader = createHeaderAtStateVersion(20L); - - final var peer1 = createPeer(); - final var peer2 = createPeer(); - setupPeersView(peer1, peer2); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1), targetHeader).withPendingRequest(peer1, 1L); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.ledgerStatusUpdateEventProcessor().process( - peer2, - LedgerStatusUpdate.create(newTargetHeader) - ); - - assertEquals(syncState, this.localSyncService.getSyncState()); - } - - @Test - public void when_ledger_status_update__then_should_not_add_duplicate_candidate() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(21L); - final var newTargetHeader = createHeaderAtStateVersion(22L); - final var evenNewerTargetHeader = createHeaderAtStateVersion(23L); - - final var peer1 = mock(BFTNode.class); - final var peer2 = mock(BFTNode.class); - final var peer3 = mock(BFTNode.class); - setupPeersView(peer1, peer2, peer3); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1, peer2), targetHeader).withPendingRequest(peer1, 1L); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.ledgerStatusUpdateEventProcessor().process( - peer3, - LedgerStatusUpdate.create(newTargetHeader) - ); - - // another, newer, ledger update from the same peer - this.localSyncService.ledgerStatusUpdateEventProcessor().process( - peer3, - LedgerStatusUpdate.create(evenNewerTargetHeader) - ); - - assertEquals(peer3, ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(0).get()); - assertEquals(peer1, ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(1).get()); - assertEquals(peer2, ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(2).get()); - assertEquals(peer3, ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(3).get()); - assertEquals(peer1, ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(4).get()); - assertEquals(peer2, ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(5).get()); - } - - @Test - public void when_syncing__then_should_use_round_robin_peers() { - final var currentHeader = createHeaderAtStateVersion(19L); - final var targetHeader = createHeaderAtStateVersion(30L); - - final var peer1 = createPeer(); - final var peer2 = createPeer(); - final var peer3 = createPeer(); - setupPeersView(peer1, peer2, peer3); - - final var syncState = SyncState.SyncingState.init( - currentHeader, ImmutableList.of(peer1, peer2, peer3), targetHeader); - this.setupSyncServiceWithState(syncState); - - this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(20L)); - verify(syncRequestDispatcher, times(1)).dispatch(eq(peer1), any()); - this.localSyncService.syncResponseEventProcessor().process(peer1, createValidMockedSyncResponse()); - this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(21L)); - verify(syncRequestDispatcher, times(1)).dispatch(eq(peer2), any()); - this.localSyncService.syncResponseEventProcessor().process(peer2, createValidMockedSyncResponse()); - this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(22L)); - verify(syncRequestDispatcher, times(1)).dispatch(eq(peer3), any()); - this.localSyncService.syncResponseEventProcessor().process(peer3, createValidMockedSyncResponse()); - this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(23L)); - verify(syncRequestDispatcher, times(2)).dispatch(eq(peer1), any()); - } - - private SyncResponse createValidMockedSyncResponse() { - final var respHeadLedgerHeader = mock(LedgerHeader.class); - final var respHeadAccumulatorState = mock(AccumulatorState.class); - when(respHeadLedgerHeader.getAccumulatorState()).thenReturn(respHeadAccumulatorState); - final var respTailLedgerHeader = mock(LedgerHeader.class); - final var respTailAccumulatorState = mock(AccumulatorState.class); - when(respTailLedgerHeader.getAccumulatorState()).thenReturn(respTailAccumulatorState); - final var respHead = mock(DtoLedgerProof.class); - when(respHead.getLedgerHeader()).thenReturn(respHeadLedgerHeader); - final var respTail = mock(DtoLedgerProof.class); - when(respTail.getLedgerHeader()).thenReturn(respTailLedgerHeader); - final var response = mock(DtoTxnsAndProof.class); - final var txn = mock(Txn.class); - when(txn.getId()).thenReturn(AID.ZERO); - when(response.getTxns()).thenReturn(ImmutableList.of(txn)); - when(response.getHead()).thenReturn(respHead); - when(response.getTail()).thenReturn(respTail); - - final var syncResponse = SyncResponse.create(response); - - when(validatorSetVerifier.verifyValidatorSet(syncResponse)).thenReturn(true); - when(signaturesVerifier.verifyResponseSignatures(syncResponse)).thenReturn(true); - when(accumulatorVerifier.verify( - eq(respHeadAccumulatorState), - any(), - eq(respTailAccumulatorState) - )).thenReturn(true); - - return syncResponse; - } - - private LedgerUpdate ledgerUpdateAtStateVersion(long stateVersion) { - return new LedgerUpdate( - VerifiedTxnsAndProof.create(ImmutableList.of(), createHeaderAtStateVersion(stateVersion)), - ImmutableClassToInstanceMap.of() - ); - } - - private LedgerProof createHeaderAtStateVersion(long version) { - final LedgerProof header = mock(LedgerProof.class); - final AccumulatorState accumulatorState = mock(AccumulatorState.class); - when(header.getAccumulatorState()).thenReturn(accumulatorState); - when(accumulatorState.getStateVersion()).thenReturn(version); - return header; - } - - private void setupPeersView(BFTNode... bftNodes) { - when(peersView.peers()).thenReturn(Stream.of(bftNodes).map(PeerInfo::fromBftNode)); - Arrays.stream(bftNodes).forEach(peer -> - when(peersView.hasPeer(peer)).thenReturn(true) - ); - } - - private BFTNode createPeer() { - return BFTNode.random(); - } + private LocalSyncService localSyncService; + private RemoteEventDispatcher statusRequestDispatcher; + private ScheduledEventDispatcher + syncCheckReceiveStatusTimeoutDispatcher; + private RemoteEventDispatcher syncRequestDispatcher; + private ScheduledEventDispatcher syncRequestTimeoutDispatcher; + private ScheduledEventDispatcher syncLedgerUpdateTimeoutDispatcher; + private SyncConfig syncConfig; + private SystemCounters systemCounters; + private PeersView peersView; + private Comparator accComparator; + private RemoteSyncResponseValidatorSetVerifier validatorSetVerifier; + private RemoteSyncResponseSignaturesVerifier signaturesVerifier; + private LedgerAccumulatorVerifier accumulatorVerifier; + private VerifiedSyncResponseHandler verifiedSyncResponseHandler; + private InvalidSyncResponseHandler invalidSyncResponseHandler; + + @Before + public void setUp() { + this.statusRequestDispatcher = rmock(RemoteEventDispatcher.class); + this.syncCheckReceiveStatusTimeoutDispatcher = rmock(ScheduledEventDispatcher.class); + this.syncRequestDispatcher = rmock(RemoteEventDispatcher.class); + this.syncRequestTimeoutDispatcher = rmock(ScheduledEventDispatcher.class); + this.syncLedgerUpdateTimeoutDispatcher = rmock(ScheduledEventDispatcher.class); + this.syncConfig = SyncConfig.of(1000L, 10, 10000L); + this.systemCounters = mock(SystemCounters.class); + this.peersView = mock(PeersView.class); + this.accComparator = Comparator.comparingLong(AccumulatorState::getStateVersion); + this.validatorSetVerifier = mock(RemoteSyncResponseValidatorSetVerifier.class); + this.signaturesVerifier = mock(RemoteSyncResponseSignaturesVerifier.class); + this.accumulatorVerifier = mock(LedgerAccumulatorVerifier.class); + this.verifiedSyncResponseHandler = mock(VerifiedSyncResponseHandler.class); + this.invalidSyncResponseHandler = mock(InvalidSyncResponseHandler.class); + } + + private void setupSyncServiceWithState(SyncState syncState) { + this.localSyncService = + new LocalSyncService( + statusRequestDispatcher, + syncCheckReceiveStatusTimeoutDispatcher, + syncRequestDispatcher, + syncRequestTimeoutDispatcher, + syncLedgerUpdateTimeoutDispatcher, + syncConfig, + systemCounters, + peersView, + accComparator, + validatorSetVerifier, + signaturesVerifier, + accumulatorVerifier, + verifiedSyncResponseHandler, + invalidSyncResponseHandler, + syncState); + } + + @Test + public void when_sync_check_is_triggered_at_idle__then_should_ask_peers_for_their_statuses() { + final var peer1 = createPeer(); + final var peer2 = createPeer(); + final var peer3 = createPeer(); + + setupPeersView(peer1, peer2, peer3); + + final LedgerProof currentHeader = mock(LedgerProof.class); + this.setupSyncServiceWithState(SyncState.IdleState.init(currentHeader)); + + this.localSyncService.syncCheckTriggerEventProcessor().process(SyncCheckTrigger.create()); + + verify(statusRequestDispatcher, times(1)).dispatch(eq(peer1), any()); + verify(statusRequestDispatcher, times(1)).dispatch(eq(peer2), any()); + verify(statusRequestDispatcher, times(1)).dispatch(eq(peer2), any()); + } + + @Test + public void when_sync_check_is_triggered_at_non_idle__then_should_be_ignored() { + final LedgerProof currentHeader = mock(LedgerProof.class); + + this.setupSyncServiceWithState(SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of())); + this.localSyncService.syncCheckTriggerEventProcessor().process(SyncCheckTrigger.create()); + + this.setupSyncServiceWithState( + SyncState.SyncingState.init(currentHeader, ImmutableList.of(), currentHeader)); + this.localSyncService.syncCheckTriggerEventProcessor().process(SyncCheckTrigger.create()); + + verifyNoMoreInteractions(peersView); + verifyNoMoreInteractions(statusRequestDispatcher); + } + + @Test + public void when_status_response_received_at_non_sync_check__then_should_be_ignored() { + final LedgerProof currentHeader = mock(LedgerProof.class); + final LedgerProof statusHeader = mock(LedgerProof.class); + final BFTNode sender = createPeer(); + + this.setupSyncServiceWithState(SyncState.IdleState.init(currentHeader)); + this.localSyncService + .statusResponseEventProcessor() + .process(sender, StatusResponse.create(statusHeader)); + + this.setupSyncServiceWithState( + SyncState.SyncingState.init(currentHeader, ImmutableList.of(), currentHeader)); + this.localSyncService + .statusResponseEventProcessor() + .process(sender, StatusResponse.create(statusHeader)); + + verifyNoMoreInteractions(peersView); + verifyNoMoreInteractions(statusRequestDispatcher); + verifyNoMoreInteractions(syncRequestDispatcher); + verifyNoMoreInteractions(syncRequestTimeoutDispatcher); + } + + @Test + public void when_unexpected_status_response_received__then_should_be_ignored() { + final LedgerProof currentHeader = mock(LedgerProof.class); + final LedgerProof statusHeader = mock(LedgerProof.class); + final BFTNode expectedPeer = createPeer(); + final BFTNode unexpectedPeer = createPeer(); + + this.setupSyncServiceWithState( + SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(expectedPeer))); + this.localSyncService + .statusResponseEventProcessor() + .process(unexpectedPeer, StatusResponse.create(statusHeader)); + + verifyNoMoreInteractions(peersView); + verifyNoMoreInteractions(statusRequestDispatcher); + verifyNoMoreInteractions(syncRequestDispatcher); + verifyNoMoreInteractions(syncRequestTimeoutDispatcher); + } + + @Test + public void when_duplicate_status_response_received__then_should_be_ignored() { + final LedgerProof currentHeader = mock(LedgerProof.class); + final LedgerProof statusHeader = mock(LedgerProof.class); + final BFTNode expectedPeer = createPeer(); + final BFTNode alreadyReceivedPeer = createPeer(); + + final var syncState = + SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(expectedPeer)) + .withStatusResponse(alreadyReceivedPeer, StatusResponse.create(statusHeader)); + + this.setupSyncServiceWithState(syncState); + this.localSyncService + .statusResponseEventProcessor() + .process(alreadyReceivedPeer, StatusResponse.create(statusHeader)); + + verifyNoMoreInteractions(peersView); + verifyNoMoreInteractions(statusRequestDispatcher); + verifyNoMoreInteractions(syncRequestDispatcher); + verifyNoMoreInteractions(syncRequestTimeoutDispatcher); + } + + @Test + public void when_all_status_responses_received__then_should_start_sync() { + final LedgerProof currentHeader = createHeaderAtStateVersion(10L); + final LedgerProof statusHeader1 = createHeaderAtStateVersion(2L); + final LedgerProof statusHeader2 = createHeaderAtStateVersion(20L); + final LedgerProof statusHeader3 = createHeaderAtStateVersion(15L); + final BFTNode waiting1 = createPeer(); + final BFTNode waiting2 = createPeer(); + final BFTNode waiting3 = createPeer(); + + final var syncState = + SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(waiting1, waiting2, waiting3)); + this.setupSyncServiceWithState(syncState); + + setupPeersView(waiting2); + + this.localSyncService + .statusResponseEventProcessor() + .process(waiting1, StatusResponse.create(statusHeader1)); + this.localSyncService + .statusResponseEventProcessor() + .process(waiting2, StatusResponse.create(statusHeader2)); + this.localSyncService + .statusResponseEventProcessor() + .process(waiting3, StatusResponse.create(statusHeader3)); + + verify(syncRequestDispatcher, times(1)).dispatch(eq(waiting2), any()); + } + + @Test + public void when_status_timeout_with_no_responses__then_should_reschedule_another_check() { + final LedgerProof currentHeader = createHeaderAtStateVersion(10L); + final BFTNode waiting1 = createPeer(); + setupPeersView(waiting1); + + final var syncState = SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(waiting1)); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .syncCheckReceiveStatusTimeoutEventProcessor() + .process(SyncCheckReceiveStatusTimeout.create()); + + verifyNoMoreInteractions(syncRequestDispatcher); + } + + @Test + public void when_status_timeout_with_at_least_one_response__then_should_start_sync() { + final LedgerProof currentHeader = createHeaderAtStateVersion(10L); + final LedgerProof statusHeader1 = createHeaderAtStateVersion(12L); + final LedgerProof statusHeader2 = createHeaderAtStateVersion(20L); + final var waiting1 = createPeer(); + final var waiting2 = createPeer(); + setupPeersView(waiting1, waiting2); + + final var syncState = + SyncState.SyncCheckState.init(currentHeader, ImmutableSet.of(waiting1, waiting2)); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .statusResponseEventProcessor() + .process(waiting1, StatusResponse.create(statusHeader1)); + + this.localSyncService + .syncCheckReceiveStatusTimeoutEventProcessor() + .process(SyncCheckReceiveStatusTimeout.create()); + + // even though statusHeader2 is more up to date, it should be ignored because was received + // after a timeout event + this.localSyncService + .statusResponseEventProcessor() + .process(waiting2, StatusResponse.create(statusHeader2)); + + verify(syncRequestDispatcher, times(1)).dispatch(eq(waiting1), any()); + } + + @Test + public void when_syncing_timeout__then_should_remove_candidate_and_retry_with_other_candidate() { + final var currentHeader = createHeaderAtStateVersion(10L); + final var targetHeader = createHeaderAtStateVersion(20L); + + final var peer1 = createPeer(); + final var peer2 = createPeer(); + setupPeersView(peer1, peer2); + + final var requestId = 1L; + final var originalCandidates = ImmutableList.of(peer1, peer2); + final var syncState = + SyncState.SyncingState.init(currentHeader, originalCandidates, targetHeader) + .withPendingRequest(peer1, requestId); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .syncRequestTimeoutEventProcessor() + .process(SyncRequestTimeout.create(peer1, requestId)); + + verify(syncRequestDispatcher, times(1)).dispatch(eq(peer2), any()); + } + + @Test + public void when_syncing_timeout_for_different_peer_same_request_id__then_should_ignore() { + final var currentHeader = createHeaderAtStateVersion(10L); + final var targetHeader = createHeaderAtStateVersion(20L); + + final var peer1 = createPeer(); + final var peer2 = createPeer(); + setupPeersView(peer1, peer2); + + final var requestId = 1L; + final var originalCandidates = ImmutableList.of(peer1, peer2); + final var syncState = + SyncState.SyncingState.init(currentHeader, originalCandidates, targetHeader) + .withPendingRequest(peer1, requestId); + this.setupSyncServiceWithState(syncState); + + // waiting for response from peer1, but got a timeout for peer2 + this.localSyncService + .syncRequestTimeoutEventProcessor() + .process(SyncRequestTimeout.create(peer2, requestId)); + + verifyNoMoreInteractions(syncRequestDispatcher); + } + + @Test + public void when_syncing_timeout_for_same_peer_different_request_id__then_should_ignore() { + final var currentHeader = createHeaderAtStateVersion(10L); + final var targetHeader = createHeaderAtStateVersion(20L); + + final var peer1 = mock(BFTNode.class); + final var peer2 = mock(BFTNode.class); + when(peersView.peers()).thenAnswer(i -> Stream.of(peer1, peer2)); + + final var originalCandidates = ImmutableList.of(peer1, peer2); + final var syncState = + SyncState.SyncingState.init(currentHeader, originalCandidates, targetHeader) + .withPendingRequest(peer1, 2L); + this.setupSyncServiceWithState(syncState); + + // waiting for response for request id 2, but got a timeout for 1 + this.localSyncService + .syncRequestTimeoutEventProcessor() + .process(SyncRequestTimeout.create(peer1, 1L)); + + verifyNoMoreInteractions(syncRequestDispatcher); + } + + @Test + public void when_received_a_valid_response__then_should_send_verified() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(20L); + + final var peer1 = createPeer(); + setupPeersView(peer1); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1), targetHeader) + .withPendingRequest(peer1, 1L); + this.setupSyncServiceWithState(syncState); + + final var syncResponse = createValidMockedSyncResponse(); + + this.localSyncService.syncResponseEventProcessor().process(peer1, syncResponse); + + verify(verifiedSyncResponseHandler, times(1)).handleVerifiedSyncResponse(syncResponse); + verify(syncLedgerUpdateTimeoutDispatcher, times(1)).dispatch(any(), anyLong()); + verifyNoMoreInteractions(syncRequestDispatcher); + } + + @Test + public void + when_received_ledger_update_and_fully_synced__then_should_wait_for_another_sync_trigger() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(20L); + + final var peer1 = createPeer(); + setupPeersView(peer1); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1), targetHeader) + .withPendingRequest(peer1, 1L); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .ledgerUpdateEventProcessor() + .process(ledgerUpdateAtStateVersion(targetHeader.getStateVersion())); + + verifyNoMoreInteractions(syncRequestDispatcher); + } + + @Test + public void when_ledger_update_timeout__then_should_continue_sync() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(21L); + + final var peer1 = createPeer(); + setupPeersView(peer1); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1), targetHeader); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .syncLedgerUpdateTimeoutProcessor() + .process(SyncLedgerUpdateTimeout.create(currentHeader.getStateVersion())); + + verify(syncRequestDispatcher, times(1)).dispatch(eq(peer1), any()); + } + + @Test + public void when_obsolete_ledger_update_timeout__then_should_ignore() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(21L); + + final var peer1 = createPeer(); + setupPeersView(peer1); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1), targetHeader); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .syncLedgerUpdateTimeoutProcessor() + .process( + SyncLedgerUpdateTimeout.create( + currentHeader.getStateVersion() - 1) // timeout event for a past state version + ); + + verifyNoInteractions(syncRequestDispatcher); + } + + @Test + public void when_remote_status_update_in_idle__then_should_start_sync() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(21L); + + final var peer1 = createPeer(); + setupPeersView(peer1); + + final var syncState = SyncState.IdleState.init(currentHeader); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .ledgerStatusUpdateEventProcessor() + .process(peer1, LedgerStatusUpdate.create(targetHeader)); + + verify(syncRequestDispatcher, times(1)).dispatch(eq(peer1), any()); + } + + @Test + public void when_remote_status_update_in_syncing__then_should_update_target() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(21L); + final var newTargetHeader = createHeaderAtStateVersion(22L); + + final var peer1 = createPeer(); + final var peer2 = createPeer(); + setupPeersView(peer1, peer2); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1), targetHeader) + .withPendingRequest(peer1, 1L); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .ledgerStatusUpdateEventProcessor() + .process(peer2, LedgerStatusUpdate.create(newTargetHeader)); + + assertEquals( + newTargetHeader, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).getTargetHeader()); + } + + @Test + public void when_remote_status_update_in_syncing_for_older_header__then_should_do_nothing() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(21L); + final var newTargetHeader = createHeaderAtStateVersion(20L); + + final var peer1 = createPeer(); + final var peer2 = createPeer(); + setupPeersView(peer1, peer2); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1), targetHeader) + .withPendingRequest(peer1, 1L); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .ledgerStatusUpdateEventProcessor() + .process(peer2, LedgerStatusUpdate.create(newTargetHeader)); + + assertEquals(syncState, this.localSyncService.getSyncState()); + } + + @Test + public void when_ledger_status_update__then_should_not_add_duplicate_candidate() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(21L); + final var newTargetHeader = createHeaderAtStateVersion(22L); + final var evenNewerTargetHeader = createHeaderAtStateVersion(23L); + + final var peer1 = mock(BFTNode.class); + final var peer2 = mock(BFTNode.class); + final var peer3 = mock(BFTNode.class); + setupPeersView(peer1, peer2, peer3); + + final var syncState = + SyncState.SyncingState.init(currentHeader, ImmutableList.of(peer1, peer2), targetHeader) + .withPendingRequest(peer1, 1L); + this.setupSyncServiceWithState(syncState); + + this.localSyncService + .ledgerStatusUpdateEventProcessor() + .process(peer3, LedgerStatusUpdate.create(newTargetHeader)); + + // another, newer, ledger update from the same peer + this.localSyncService + .ledgerStatusUpdateEventProcessor() + .process(peer3, LedgerStatusUpdate.create(evenNewerTargetHeader)); + + assertEquals( + peer3, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(0).get()); + assertEquals( + peer1, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(1).get()); + assertEquals( + peer2, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(2).get()); + assertEquals( + peer3, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(3).get()); + assertEquals( + peer1, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(4).get()); + assertEquals( + peer2, + ((SyncState.SyncingState) this.localSyncService.getSyncState()).peekNthCandidate(5).get()); + } + + @Test + public void when_syncing__then_should_use_round_robin_peers() { + final var currentHeader = createHeaderAtStateVersion(19L); + final var targetHeader = createHeaderAtStateVersion(30L); + + final var peer1 = createPeer(); + final var peer2 = createPeer(); + final var peer3 = createPeer(); + setupPeersView(peer1, peer2, peer3); + + final var syncState = + SyncState.SyncingState.init( + currentHeader, ImmutableList.of(peer1, peer2, peer3), targetHeader); + this.setupSyncServiceWithState(syncState); + + this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(20L)); + verify(syncRequestDispatcher, times(1)).dispatch(eq(peer1), any()); + this.localSyncService + .syncResponseEventProcessor() + .process(peer1, createValidMockedSyncResponse()); + this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(21L)); + verify(syncRequestDispatcher, times(1)).dispatch(eq(peer2), any()); + this.localSyncService + .syncResponseEventProcessor() + .process(peer2, createValidMockedSyncResponse()); + this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(22L)); + verify(syncRequestDispatcher, times(1)).dispatch(eq(peer3), any()); + this.localSyncService + .syncResponseEventProcessor() + .process(peer3, createValidMockedSyncResponse()); + this.localSyncService.ledgerUpdateEventProcessor().process(ledgerUpdateAtStateVersion(23L)); + verify(syncRequestDispatcher, times(2)).dispatch(eq(peer1), any()); + } + + private SyncResponse createValidMockedSyncResponse() { + final var respHeadLedgerHeader = mock(LedgerHeader.class); + final var respHeadAccumulatorState = mock(AccumulatorState.class); + when(respHeadLedgerHeader.getAccumulatorState()).thenReturn(respHeadAccumulatorState); + final var respTailLedgerHeader = mock(LedgerHeader.class); + final var respTailAccumulatorState = mock(AccumulatorState.class); + when(respTailLedgerHeader.getAccumulatorState()).thenReturn(respTailAccumulatorState); + final var respHead = mock(DtoLedgerProof.class); + when(respHead.getLedgerHeader()).thenReturn(respHeadLedgerHeader); + final var respTail = mock(DtoLedgerProof.class); + when(respTail.getLedgerHeader()).thenReturn(respTailLedgerHeader); + final var response = mock(DtoTxnsAndProof.class); + final var txn = mock(Txn.class); + when(txn.getId()).thenReturn(AID.ZERO); + when(response.getTxns()).thenReturn(ImmutableList.of(txn)); + when(response.getHead()).thenReturn(respHead); + when(response.getTail()).thenReturn(respTail); + + final var syncResponse = SyncResponse.create(response); + + when(validatorSetVerifier.verifyValidatorSet(syncResponse)).thenReturn(true); + when(signaturesVerifier.verifyResponseSignatures(syncResponse)).thenReturn(true); + when(accumulatorVerifier.verify( + eq(respHeadAccumulatorState), any(), eq(respTailAccumulatorState))) + .thenReturn(true); + + return syncResponse; + } + + private LedgerUpdate ledgerUpdateAtStateVersion(long stateVersion) { + return new LedgerUpdate( + VerifiedTxnsAndProof.create(ImmutableList.of(), createHeaderAtStateVersion(stateVersion)), + ImmutableClassToInstanceMap.of()); + } + + private LedgerProof createHeaderAtStateVersion(long version) { + final LedgerProof header = mock(LedgerProof.class); + final AccumulatorState accumulatorState = mock(AccumulatorState.class); + when(header.getAccumulatorState()).thenReturn(accumulatorState); + when(accumulatorState.getStateVersion()).thenReturn(version); + return header; + } + + private void setupPeersView(BFTNode... bftNodes) { + when(peersView.peers()).thenReturn(Stream.of(bftNodes).map(PeerInfo::fromBftNode)); + Arrays.stream(bftNodes).forEach(peer -> when(peersView.hasPeer(peer)).thenReturn(true)); + } + + private BFTNode createPeer() { + return BFTNode.random(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedCommittedReaderModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedCommittedReaderModule.java index ac7f7d8a3d..b59733da0f 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedCommittedReaderModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedCommittedReaderModule.java @@ -72,18 +72,15 @@ import com.radixdlt.ledger.LedgerUpdate; public class MockedCommittedReaderModule extends AbstractModule { - @Override - public void configure() { - bind(CommittedReader.class).to(InMemoryCommittedReader.class).in(Scopes.SINGLETON); - bind(InMemoryCommittedReader.class).in(Scopes.SINGLETON); - } + @Override + public void configure() { + bind(CommittedReader.class).to(InMemoryCommittedReader.class).in(Scopes.SINGLETON); + bind(InMemoryCommittedReader.class).in(Scopes.SINGLETON); + } - @Singleton - @ProvidesIntoSet - public EventProcessorOnDispatch eventProcessor(InMemoryCommittedReader reader) { - return new EventProcessorOnDispatch<>( - LedgerUpdate.class, - reader.updateProcessor() - ); - } + @Singleton + @ProvidesIntoSet + public EventProcessorOnDispatch eventProcessor(InMemoryCommittedReader reader) { + return new EventProcessorOnDispatch<>(LedgerUpdate.class, reader.updateProcessor()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedSyncServiceModule.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedSyncServiceModule.java index d7a114b327..f187ca9e3c 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedSyncServiceModule.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/MockedSyncServiceModule.java @@ -93,170 +93,170 @@ import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.LongStream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class MockedSyncServiceModule extends AbstractModule { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private final ConcurrentMap sharedCommittedCommands; - private final ConcurrentMap sharedEpochProofs; + private final ConcurrentMap sharedCommittedCommands; + private final ConcurrentMap sharedEpochProofs; - public MockedSyncServiceModule() { - this.sharedCommittedCommands = new ConcurrentHashMap<>(); - this.sharedEpochProofs = new ConcurrentHashMap<>(); - } + public MockedSyncServiceModule() { + this.sharedCommittedCommands = new ConcurrentHashMap<>(); + this.sharedEpochProofs = new ConcurrentHashMap<>(); + } - @Override - public void configure() { - var eventBinder = Multibinder.newSetBinder(binder(), new TypeLiteral>() { }, LocalEvents.class) - .permitDuplicates(); - eventBinder.addBinding().toInstance(SyncCheckTrigger.class); - eventBinder.addBinding().toInstance(SyncCheckReceiveStatusTimeout.class); - eventBinder.addBinding().toInstance(SyncRequestTimeout.class); - eventBinder.addBinding().toInstance(LocalSyncRequest.class); - eventBinder.addBinding().toInstance(SyncLedgerUpdateTimeout.class); - } + @Override + public void configure() { + var eventBinder = + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, LocalEvents.class) + .permitDuplicates(); + eventBinder.addBinding().toInstance(SyncCheckTrigger.class); + eventBinder.addBinding().toInstance(SyncCheckReceiveStatusTimeout.class); + eventBinder.addBinding().toInstance(SyncRequestTimeout.class); + eventBinder.addBinding().toInstance(LocalSyncRequest.class); + eventBinder.addBinding().toInstance(SyncLedgerUpdateTimeout.class); + } - @Singleton - @ProvidesIntoSet - private EventProcessorOnDispatch ledgerUpdateEventProcessor() { - return new EventProcessorOnDispatch<>( - LedgerUpdate.class, - update -> { - final LedgerProof headerAndProof = update.getTail(); - long stateVersion = headerAndProof.getAccumulatorState().getStateVersion(); - long firstVersion = stateVersion - update.getNewTxns().size() + 1; - for (int i = 0; i < update.getNewTxns().size(); i++) { - sharedCommittedCommands.put(firstVersion + i, update.getNewTxns().get(i)); - } + @Singleton + @ProvidesIntoSet + private EventProcessorOnDispatch ledgerUpdateEventProcessor() { + return new EventProcessorOnDispatch<>( + LedgerUpdate.class, + update -> { + final LedgerProof headerAndProof = update.getTail(); + long stateVersion = headerAndProof.getAccumulatorState().getStateVersion(); + long firstVersion = stateVersion - update.getNewTxns().size() + 1; + for (int i = 0; i < update.getNewTxns().size(); i++) { + sharedCommittedCommands.put(firstVersion + i, update.getNewTxns().get(i)); + } - if (update.getTail().isEndOfEpoch()) { - logger.info("Epoch Proof: " + (update.getTail().getEpoch() + 1)); - sharedEpochProofs.put(update.getTail().getEpoch() + 1, update.getTail()); - } - } - ); - } + if (update.getTail().isEndOfEpoch()) { + logger.info("Epoch Proof: " + (update.getTail().getEpoch() + 1)); + sharedEpochProofs.put(update.getTail().getEpoch() + 1, update.getTail()); + } + }); + } - @ProvidesIntoSet - @Singleton - @ProcessOnDispatch - EventProcessor localSyncRequestEventProcessor( - @LastEpochProof LedgerProof genesis, - EventDispatcher syncCommandsDispatcher - ) { - return new EventProcessor<>() { - long currentVersion = genesis.getStateVersion(); - long currentEpoch = genesis.getEpoch() + 1; + @ProvidesIntoSet + @Singleton + @ProcessOnDispatch + EventProcessor localSyncRequestEventProcessor( + @LastEpochProof LedgerProof genesis, + EventDispatcher syncCommandsDispatcher) { + return new EventProcessor<>() { + long currentVersion = genesis.getStateVersion(); + long currentEpoch = genesis.getEpoch() + 1; - private void syncTo(LedgerProof proof) { - var txns = LongStream.range(currentVersion + 1, proof.getStateVersion() + 1) - .mapToObj(sharedCommittedCommands::get) - .collect(ImmutableList.toImmutableList()); - syncCommandsDispatcher.dispatch(VerifiedTxnsAndProof.create(txns, proof)); - currentVersion = proof.getStateVersion(); - if (proof.isEndOfEpoch()) { - currentEpoch = proof.getEpoch() + 1; - } else { - currentEpoch = proof.getEpoch(); - } - } + private void syncTo(LedgerProof proof) { + var txns = + LongStream.range(currentVersion + 1, proof.getStateVersion() + 1) + .mapToObj(sharedCommittedCommands::get) + .collect(ImmutableList.toImmutableList()); + syncCommandsDispatcher.dispatch(VerifiedTxnsAndProof.create(txns, proof)); + currentVersion = proof.getStateVersion(); + if (proof.isEndOfEpoch()) { + currentEpoch = proof.getEpoch() + 1; + } else { + currentEpoch = proof.getEpoch(); + } + } - @Override - public void process(LocalSyncRequest request) { - while (currentEpoch < request.getTarget().getEpoch()) { - if (!sharedEpochProofs.containsKey(currentEpoch + 1)) { - throw new IllegalStateException("Epoch proof does not exist: " + currentEpoch + 1); - } + @Override + public void process(LocalSyncRequest request) { + while (currentEpoch < request.getTarget().getEpoch()) { + if (!sharedEpochProofs.containsKey(currentEpoch + 1)) { + throw new IllegalStateException("Epoch proof does not exist: " + currentEpoch + 1); + } - syncTo(sharedEpochProofs.get(currentEpoch + 1)); - } + syncTo(sharedEpochProofs.get(currentEpoch + 1)); + } - syncTo(request.getTarget()); + syncTo(request.getTarget()); - final long targetVersion = request.getTarget().getStateVersion(); - var txns = LongStream.range(currentVersion + 1, targetVersion + 1) - .mapToObj(sharedCommittedCommands::get) - .collect(ImmutableList.toImmutableList()); + final long targetVersion = request.getTarget().getStateVersion(); + var txns = + LongStream.range(currentVersion + 1, targetVersion + 1) + .mapToObj(sharedCommittedCommands::get) + .collect(ImmutableList.toImmutableList()); - syncCommandsDispatcher.dispatch(VerifiedTxnsAndProof.create(txns, request.getTarget())); - currentVersion = targetVersion; - currentEpoch = request.getTarget().getEpoch(); - } - }; - } + syncCommandsDispatcher.dispatch(VerifiedTxnsAndProof.create(txns, request.getTarget())); + currentVersion = targetVersion; + currentEpoch = request.getTarget().getEpoch(); + } + }; + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner ledgerStatusUpdateRemoteEventProcessor( - EventDispatcher localSyncRequestEventDispatcher - ) { - return new RemoteEventProcessorOnRunner<>( - Runners.SYNC, - LedgerStatusUpdate.class, - (sender, ev) -> localSyncRequestEventDispatcher.dispatch(new LocalSyncRequest(ev.getHeader(), ImmutableList.of(sender))) - ); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner ledgerStatusUpdateRemoteEventProcessor( + EventDispatcher localSyncRequestEventDispatcher) { + return new RemoteEventProcessorOnRunner<>( + Runners.SYNC, + LedgerStatusUpdate.class, + (sender, ev) -> + localSyncRequestEventDispatcher.dispatch( + new LocalSyncRequest(ev.getHeader(), ImmutableList.of(sender)))); + } - @ProvidesIntoSet - private EventProcessorOnRunner epochsLedgerUpdateEventProcessor() { - return noOpProcessor(LedgerUpdate.class); - } + @ProvidesIntoSet + private EventProcessorOnRunner epochsLedgerUpdateEventProcessor() { + return noOpProcessor(LedgerUpdate.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncCheckTriggerEventProcessor() { - return noOpProcessor(SyncCheckTrigger.class); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncCheckTriggerEventProcessor() { + return noOpProcessor(SyncCheckTrigger.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncCheckReceiveStatusTimeoutEventProcessor() { - return noOpProcessor(SyncCheckReceiveStatusTimeout.class); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncCheckReceiveStatusTimeoutEventProcessor() { + return noOpProcessor(SyncCheckReceiveStatusTimeout.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncRequestTimeoutEventProcessor() { - return noOpProcessor(SyncRequestTimeout.class); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncRequestTimeoutEventProcessor() { + return noOpProcessor(SyncRequestTimeout.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner localSyncRequestEventProcessor() { - return noOpProcessor(LocalSyncRequest.class); - } + @ProvidesIntoSet + private EventProcessorOnRunner localSyncRequestEventProcessor() { + return noOpProcessor(LocalSyncRequest.class); + } - @ProvidesIntoSet - private EventProcessorOnRunner syncLedgerUpdateTimeoutEventProcessor() { - return noOpProcessor(SyncLedgerUpdateTimeout.class); - } + @ProvidesIntoSet + private EventProcessorOnRunner syncLedgerUpdateTimeoutEventProcessor() { + return noOpProcessor(SyncLedgerUpdateTimeout.class); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner statusRequestEventProcessor() { - return noOpRemoteProcessor(StatusRequest.class); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner statusRequestEventProcessor() { + return noOpRemoteProcessor(StatusRequest.class); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner statusResponseEventProcessor() { - return noOpRemoteProcessor(StatusResponse.class); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner statusResponseEventProcessor() { + return noOpRemoteProcessor(StatusResponse.class); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner syncRequestEventProcessor() { - return noOpRemoteProcessor(SyncRequest.class); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner syncRequestEventProcessor() { + return noOpRemoteProcessor(SyncRequest.class); + } - @ProvidesIntoSet - private RemoteEventProcessorOnRunner syncResponseEventProcessor() { - return noOpRemoteProcessor(SyncResponse.class); - } + @ProvidesIntoSet + private RemoteEventProcessorOnRunner syncResponseEventProcessor() { + return noOpRemoteProcessor(SyncResponse.class); + } - private EventProcessorOnRunner noOpProcessor(Class clazz) { - return new EventProcessorOnRunner<>(Runners.SYNC, clazz, ev -> { }); - } + private EventProcessorOnRunner noOpProcessor(Class clazz) { + return new EventProcessorOnRunner<>(Runners.SYNC, clazz, ev -> {}); + } - private RemoteEventProcessorOnRunner noOpRemoteProcessor(Class clazz) { - return new RemoteEventProcessorOnRunner<>(Runners.SYNC, clazz, (sender, ev) -> { }); - } + private RemoteEventProcessorOnRunner noOpRemoteProcessor(Class clazz) { + return new RemoteEventProcessorOnRunner<>(Runners.SYNC, clazz, (sender, ev) -> {}); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/RemoteSyncServiceTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/RemoteSyncServiceTest.java index 2cd4b44dd7..6c928166e4 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/RemoteSyncServiceTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/RemoteSyncServiceTest.java @@ -76,8 +76,8 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.counters.SystemCounters; @@ -92,123 +92,120 @@ import com.radixdlt.sync.messages.remote.StatusResponse; import com.radixdlt.sync.messages.remote.SyncRequest; import com.radixdlt.sync.messages.remote.SyncResponse; - import java.util.Comparator; import java.util.Optional; - import org.junit.Before; import org.junit.Test; public class RemoteSyncServiceTest { - private RemoteSyncService processor; - private PeersView peersView; - private LocalSyncService localSyncService; - private CommittedReader reader; - private RemoteEventDispatcher statusResponseDispatcher; - private RemoteEventDispatcher syncResponseDispatcher; - private RemoteEventDispatcher statusUpdateDispatcher; - - @Before - public void setUp() { - this.peersView = mock(PeersView.class); - this.localSyncService = mock(LocalSyncService.class); - this.reader = mock(CommittedReader.class); - this.statusResponseDispatcher = rmock(RemoteEventDispatcher.class); - this.syncResponseDispatcher = rmock(RemoteEventDispatcher.class); - this.statusUpdateDispatcher = rmock(RemoteEventDispatcher.class); - - final var initialHeader = mock(LedgerProof.class); - final var initialAccumulatorState = mock(AccumulatorState.class); - when(initialHeader.getAccumulatorState()).thenReturn(initialAccumulatorState); - when(initialAccumulatorState.getStateVersion()).thenReturn(1L); - - this.processor = new RemoteSyncService( - peersView, - localSyncService, - reader, - statusResponseDispatcher, - syncResponseDispatcher, - statusUpdateDispatcher, - SyncConfig.of(5000L, 10, 5000L, 10, 50), - mock(SystemCounters.class), - Comparator.comparingLong(AccumulatorState::getStateVersion), - initialHeader); - } - - @Test - public void when_remote_sync_request__then_process_it() { - SyncRequest request = mock(SyncRequest.class); - DtoLedgerProof header = mock(DtoLedgerProof.class); - when(header.getOpaque()).thenReturn(HashUtils.zero256()); - when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); - when(header.getSignatures()).thenReturn(mock(TimestampedECDSASignatures.class)); - when(request.getHeader()).thenReturn(header); - BFTNode node = mock(BFTNode.class); - VerifiedTxnsAndProof verifiedTxnsAndProof = mock(VerifiedTxnsAndProof.class); - LedgerProof verifiedHeader = mock(LedgerProof.class); - when(verifiedHeader.toDto()).thenReturn(header); - when(verifiedTxnsAndProof.getProof()).thenReturn(verifiedHeader); - when(reader.getNextCommittedTxns(any())).thenReturn(verifiedTxnsAndProof); - processor.syncRequestEventProcessor().process(node, SyncRequest.create(header)); - verify(syncResponseDispatcher, times(1)).dispatch(eq(node), any()); - } - - @Test(expected = NullPointerException.class) - public void when_bad_remote_sync_request__then_throw_NPE() { - var node = mock(BFTNode.class); - var verifiedTxnsAndProof = mock(VerifiedTxnsAndProof.class); - var verifiedHeader = mock(LedgerProof.class); - when(verifiedTxnsAndProof.getProof()).thenReturn(verifiedHeader); - when(reader.getNextCommittedTxns(any())).thenReturn(verifiedTxnsAndProof); - - processor.syncRequestEventProcessor().process(node, SyncRequest.create(null)); - verify(syncResponseDispatcher, times(1)).dispatch(eq(node), any()); - } - - @Test - public void when_remote_sync_request_and_unable__then_dont_do_anything() { - SyncRequest request = mock(SyncRequest.class); - DtoLedgerProof header = mock(DtoLedgerProof.class); - when(header.getOpaque()).thenReturn(HashUtils.zero256()); - when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); - when(header.getSignatures()).thenReturn(mock(TimestampedECDSASignatures.class)); - when(request.getHeader()).thenReturn(header); - processor.syncRequestEventProcessor().process(BFTNode.random(), SyncRequest.create(header)); - verify(syncResponseDispatcher, never()).dispatch(any(BFTNode.class), any()); - } - - @Test - public void when_remote_sync_request_and_null_return__then_dont_do_anything() { - DtoLedgerProof header = mock(DtoLedgerProof.class); - when(header.getOpaque()).thenReturn(HashUtils.zero256()); - when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); - when(header.getSignatures()).thenReturn(mock(TimestampedECDSASignatures.class)); - processor.syncRequestEventProcessor().process(BFTNode.random(), SyncRequest.create(header)); - when(reader.getNextCommittedTxns(any())).thenReturn(null); - verify(syncResponseDispatcher, never()).dispatch(any(BFTNode.class), any()); - } - - @Test - public void when_ledger_update_but_syncing__then_dont_send_status_update() { - final var tail = mock(LedgerProof.class); - final var ledgerUpdate = mock(LedgerUpdate.class); - final var accumulatorState = mock(AccumulatorState.class); - when(accumulatorState.getStateVersion()).thenReturn(2L); - when(tail.getAccumulatorState()).thenReturn(accumulatorState); - when(ledgerUpdate.getTail()).thenReturn(tail); - - final var validatorSet = mock(BFTValidatorSet.class); - when(ledgerUpdate.getNextValidatorSet()).thenReturn(Optional.of(validatorSet)); - - when(this.localSyncService.getSyncState()) - .thenReturn(SyncState.SyncingState.init( - mock(LedgerProof.class), - ImmutableList.of(), - mock(LedgerProof.class)) - ); - - verifyNoMoreInteractions(peersView); - verifyNoMoreInteractions(statusUpdateDispatcher); - } + private RemoteSyncService processor; + private PeersView peersView; + private LocalSyncService localSyncService; + private CommittedReader reader; + private RemoteEventDispatcher statusResponseDispatcher; + private RemoteEventDispatcher syncResponseDispatcher; + private RemoteEventDispatcher statusUpdateDispatcher; + + @Before + public void setUp() { + this.peersView = mock(PeersView.class); + this.localSyncService = mock(LocalSyncService.class); + this.reader = mock(CommittedReader.class); + this.statusResponseDispatcher = rmock(RemoteEventDispatcher.class); + this.syncResponseDispatcher = rmock(RemoteEventDispatcher.class); + this.statusUpdateDispatcher = rmock(RemoteEventDispatcher.class); + + final var initialHeader = mock(LedgerProof.class); + final var initialAccumulatorState = mock(AccumulatorState.class); + when(initialHeader.getAccumulatorState()).thenReturn(initialAccumulatorState); + when(initialAccumulatorState.getStateVersion()).thenReturn(1L); + + this.processor = + new RemoteSyncService( + peersView, + localSyncService, + reader, + statusResponseDispatcher, + syncResponseDispatcher, + statusUpdateDispatcher, + SyncConfig.of(5000L, 10, 5000L, 10, 50), + mock(SystemCounters.class), + Comparator.comparingLong(AccumulatorState::getStateVersion), + initialHeader); + } + + @Test + public void when_remote_sync_request__then_process_it() { + SyncRequest request = mock(SyncRequest.class); + DtoLedgerProof header = mock(DtoLedgerProof.class); + when(header.getOpaque()).thenReturn(HashUtils.zero256()); + when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); + when(header.getSignatures()).thenReturn(mock(TimestampedECDSASignatures.class)); + when(request.getHeader()).thenReturn(header); + BFTNode node = mock(BFTNode.class); + VerifiedTxnsAndProof verifiedTxnsAndProof = mock(VerifiedTxnsAndProof.class); + LedgerProof verifiedHeader = mock(LedgerProof.class); + when(verifiedHeader.toDto()).thenReturn(header); + when(verifiedTxnsAndProof.getProof()).thenReturn(verifiedHeader); + when(reader.getNextCommittedTxns(any())).thenReturn(verifiedTxnsAndProof); + processor.syncRequestEventProcessor().process(node, SyncRequest.create(header)); + verify(syncResponseDispatcher, times(1)).dispatch(eq(node), any()); + } + + @Test(expected = NullPointerException.class) + public void when_bad_remote_sync_request__then_throw_NPE() { + var node = mock(BFTNode.class); + var verifiedTxnsAndProof = mock(VerifiedTxnsAndProof.class); + var verifiedHeader = mock(LedgerProof.class); + when(verifiedTxnsAndProof.getProof()).thenReturn(verifiedHeader); + when(reader.getNextCommittedTxns(any())).thenReturn(verifiedTxnsAndProof); + + processor.syncRequestEventProcessor().process(node, SyncRequest.create(null)); + verify(syncResponseDispatcher, times(1)).dispatch(eq(node), any()); + } + + @Test + public void when_remote_sync_request_and_unable__then_dont_do_anything() { + SyncRequest request = mock(SyncRequest.class); + DtoLedgerProof header = mock(DtoLedgerProof.class); + when(header.getOpaque()).thenReturn(HashUtils.zero256()); + when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); + when(header.getSignatures()).thenReturn(mock(TimestampedECDSASignatures.class)); + when(request.getHeader()).thenReturn(header); + processor.syncRequestEventProcessor().process(BFTNode.random(), SyncRequest.create(header)); + verify(syncResponseDispatcher, never()).dispatch(any(BFTNode.class), any()); + } + + @Test + public void when_remote_sync_request_and_null_return__then_dont_do_anything() { + DtoLedgerProof header = mock(DtoLedgerProof.class); + when(header.getOpaque()).thenReturn(HashUtils.zero256()); + when(header.getLedgerHeader()).thenReturn(mock(LedgerHeader.class)); + when(header.getSignatures()).thenReturn(mock(TimestampedECDSASignatures.class)); + processor.syncRequestEventProcessor().process(BFTNode.random(), SyncRequest.create(header)); + when(reader.getNextCommittedTxns(any())).thenReturn(null); + verify(syncResponseDispatcher, never()).dispatch(any(BFTNode.class), any()); + } + + @Test + public void when_ledger_update_but_syncing__then_dont_send_status_update() { + final var tail = mock(LedgerProof.class); + final var ledgerUpdate = mock(LedgerUpdate.class); + final var accumulatorState = mock(AccumulatorState.class); + when(accumulatorState.getStateVersion()).thenReturn(2L); + when(tail.getAccumulatorState()).thenReturn(accumulatorState); + when(ledgerUpdate.getTail()).thenReturn(tail); + + final var validatorSet = mock(BFTValidatorSet.class); + when(ledgerUpdate.getNextValidatorSet()).thenReturn(Optional.of(validatorSet)); + + when(this.localSyncService.getSyncState()) + .thenReturn( + SyncState.SyncingState.init( + mock(LedgerProof.class), ImmutableList.of(), mock(LedgerProof.class))); + + verifyNoMoreInteractions(peersView); + verifyNoMoreInteractions(statusUpdateDispatcher); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SometimesByzantineCommittedReader.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SometimesByzantineCommittedReader.java index d50f14aebb..0d591249f1 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SometimesByzantineCommittedReader.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SometimesByzantineCommittedReader.java @@ -77,200 +77,196 @@ import com.radixdlt.ledger.LedgerAccumulator; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.VerifiedTxnsAndProof; -import org.junit.Ignore; - import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Random; import java.util.function.UnaryOperator; +import org.junit.Ignore; -/** - * A reader which sometimes returns erroneous commands. - */ +/** A reader which sometimes returns erroneous commands. */ public final class SometimesByzantineCommittedReader implements CommittedReader { - private final InMemoryCommittedReader correctReader; - private final LedgerAccumulator accumulator; - private final Hasher hasher; - private ReadType currentReadType; + private final InMemoryCommittedReader correctReader; + private final LedgerAccumulator accumulator; + private final Hasher hasher; + private ReadType currentReadType; - @Inject - public SometimesByzantineCommittedReader(Random random, LedgerAccumulator accumulator, InMemoryCommittedReader correctReader, Hasher hasher) { - this.correctReader = Objects.requireNonNull(correctReader); - this.accumulator = Objects.requireNonNull(accumulator); - this.currentReadType = ReadType.values()[random.nextInt(ReadType.values().length)]; - this.hasher = hasher; - } + @Inject + public SometimesByzantineCommittedReader( + Random random, + LedgerAccumulator accumulator, + InMemoryCommittedReader correctReader, + Hasher hasher) { + this.correctReader = Objects.requireNonNull(correctReader); + this.accumulator = Objects.requireNonNull(accumulator); + this.currentReadType = ReadType.values()[random.nextInt(ReadType.values().length)]; + this.hasher = hasher; + } - public EventProcessor ledgerUpdateEventProcessor() { - return this.correctReader.updateProcessor(); - } + public EventProcessor ledgerUpdateEventProcessor() { + return this.correctReader.updateProcessor(); + } - private static class ByzantineVerifiedCommandsAndProofBuilder { - private DtoLedgerProof request; - private UnaryOperator commandMapper; - private VerifiedTxnsAndProof base; - private TimestampedECDSASignatures overwriteSignatures; - private LedgerAccumulator accumulator; - private Hasher hasher; + private static class ByzantineVerifiedCommandsAndProofBuilder { + private DtoLedgerProof request; + private UnaryOperator commandMapper; + private VerifiedTxnsAndProof base; + private TimestampedECDSASignatures overwriteSignatures; + private LedgerAccumulator accumulator; + private Hasher hasher; - public ByzantineVerifiedCommandsAndProofBuilder hasher(Hasher hasher) { - this.hasher = hasher; - return this; - } + public ByzantineVerifiedCommandsAndProofBuilder hasher(Hasher hasher) { + this.hasher = hasher; + return this; + } - public ByzantineVerifiedCommandsAndProofBuilder accumulator(DtoLedgerProof request, LedgerAccumulator accumulator) { - this.request = request; - this.accumulator = accumulator; - return this; - } + public ByzantineVerifiedCommandsAndProofBuilder accumulator( + DtoLedgerProof request, LedgerAccumulator accumulator) { + this.request = request; + this.accumulator = accumulator; + return this; + } - public ByzantineVerifiedCommandsAndProofBuilder base(VerifiedTxnsAndProof base) { - this.base = base; - return this; - } + public ByzantineVerifiedCommandsAndProofBuilder base(VerifiedTxnsAndProof base) { + this.base = base; + return this; + } - public ByzantineVerifiedCommandsAndProofBuilder replaceCommands(UnaryOperator commandMapper) { - this.commandMapper = commandMapper; - return this; - } + public ByzantineVerifiedCommandsAndProofBuilder replaceCommands( + UnaryOperator commandMapper) { + this.commandMapper = commandMapper; + return this; + } - public ByzantineVerifiedCommandsAndProofBuilder overwriteSignatures(TimestampedECDSASignatures overwriteSignatures) { - this.overwriteSignatures = overwriteSignatures; - return this; - } + public ByzantineVerifiedCommandsAndProofBuilder overwriteSignatures( + TimestampedECDSASignatures overwriteSignatures) { + this.overwriteSignatures = overwriteSignatures; + return this; + } - public VerifiedTxnsAndProof build() { - List txns; - if (commandMapper != null) { - txns = base.getTxns().stream() - .map(commandMapper) - .collect(ImmutableList.toImmutableList()); - } else { - txns = base.getTxns(); - } + public VerifiedTxnsAndProof build() { + List txns; + if (commandMapper != null) { + txns = base.getTxns().stream().map(commandMapper).collect(ImmutableList.toImmutableList()); + } else { + txns = base.getTxns(); + } - AccumulatorState accumulatorState; - if (accumulator != null) { - accumulatorState = request.getLedgerHeader().getAccumulatorState(); - for (var txn : txns) { - accumulatorState = accumulator.accumulate(accumulatorState, txn.getId().asHashCode()); - } - } else { - accumulatorState = base.getProof().getAccumulatorState(); - } + AccumulatorState accumulatorState; + if (accumulator != null) { + accumulatorState = request.getLedgerHeader().getAccumulatorState(); + for (var txn : txns) { + accumulatorState = accumulator.accumulate(accumulatorState, txn.getId().asHashCode()); + } + } else { + accumulatorState = base.getProof().getAccumulatorState(); + } - LedgerHeader ledgerHeader = LedgerHeader.create( - base.getProof().getEpoch(), - base.getProof().getView(), - accumulatorState, - base.getProof().timestamp(), - base.getProof().getNextValidatorSet().orElse(null) - ); - var signatures = overwriteSignatures != null ? overwriteSignatures : base.getProof().getSignatures(); - var headerAndProof = new LedgerProof( - base.getProof().toDto().getOpaque(), - ledgerHeader, - signatures - ); + LedgerHeader ledgerHeader = + LedgerHeader.create( + base.getProof().getEpoch(), + base.getProof().getView(), + accumulatorState, + base.getProof().timestamp(), + base.getProof().getNextValidatorSet().orElse(null)); + var signatures = + overwriteSignatures != null ? overwriteSignatures : base.getProof().getSignatures(); + var headerAndProof = + new LedgerProof(base.getProof().toDto().getOpaque(), ledgerHeader, signatures); - return VerifiedTxnsAndProof.create(txns, headerAndProof); - } - } + return VerifiedTxnsAndProof.create(txns, headerAndProof); + } + } - @Ignore("This is not a test, but JUnit4 picks it as a test for some reason") - private enum ReadType { - GOOD { - @Override - VerifiedTxnsAndProof transform( - DtoLedgerProof request, - VerifiedTxnsAndProof correctCommands, - LedgerAccumulator ledgerAccumulator, - Hasher hasher - ) { - return correctCommands; - } - }, - BAD_COMMANDS { - @Override - VerifiedTxnsAndProof transform( - DtoLedgerProof request, - VerifiedTxnsAndProof correctCommands, - LedgerAccumulator ledgerAccumulator, - Hasher hasher - ) { - return new ByzantineVerifiedCommandsAndProofBuilder() - .hasher(hasher) - .base(correctCommands) - .replaceCommands(cmd -> Txn.create(new byte[]{0})) - .build(); - } - }, - NO_SIGNATURES { - @Override - VerifiedTxnsAndProof transform( - DtoLedgerProof request, - VerifiedTxnsAndProof correctCommands, - LedgerAccumulator accumulator, - Hasher hasher - ) { - return new ByzantineVerifiedCommandsAndProofBuilder() - .hasher(hasher) - .base(correctCommands) - .replaceCommands(cmd -> Txn.create(new byte[]{0})) - .accumulator(request, accumulator) - .overwriteSignatures(new TimestampedECDSASignatures()) - .build(); - } - }, - BAD_SIGNATURES { - @Override - VerifiedTxnsAndProof transform( - DtoLedgerProof request, - VerifiedTxnsAndProof correctCommands, - LedgerAccumulator accumulator, - Hasher hasher - ) { - return new ByzantineVerifiedCommandsAndProofBuilder() - .hasher(hasher) - .base(correctCommands) - .replaceCommands(cmd -> Txn.create(new byte[]{0})) - .accumulator(request, accumulator) - .build(); - } - }; + @Ignore("This is not a test, but JUnit4 picks it as a test for some reason") + private enum ReadType { + GOOD { + @Override + VerifiedTxnsAndProof transform( + DtoLedgerProof request, + VerifiedTxnsAndProof correctCommands, + LedgerAccumulator ledgerAccumulator, + Hasher hasher) { + return correctCommands; + } + }, + BAD_COMMANDS { + @Override + VerifiedTxnsAndProof transform( + DtoLedgerProof request, + VerifiedTxnsAndProof correctCommands, + LedgerAccumulator ledgerAccumulator, + Hasher hasher) { + return new ByzantineVerifiedCommandsAndProofBuilder() + .hasher(hasher) + .base(correctCommands) + .replaceCommands(cmd -> Txn.create(new byte[] {0})) + .build(); + } + }, + NO_SIGNATURES { + @Override + VerifiedTxnsAndProof transform( + DtoLedgerProof request, + VerifiedTxnsAndProof correctCommands, + LedgerAccumulator accumulator, + Hasher hasher) { + return new ByzantineVerifiedCommandsAndProofBuilder() + .hasher(hasher) + .base(correctCommands) + .replaceCommands(cmd -> Txn.create(new byte[] {0})) + .accumulator(request, accumulator) + .overwriteSignatures(new TimestampedECDSASignatures()) + .build(); + } + }, + BAD_SIGNATURES { + @Override + VerifiedTxnsAndProof transform( + DtoLedgerProof request, + VerifiedTxnsAndProof correctCommands, + LedgerAccumulator accumulator, + Hasher hasher) { + return new ByzantineVerifiedCommandsAndProofBuilder() + .hasher(hasher) + .base(correctCommands) + .replaceCommands(cmd -> Txn.create(new byte[] {0})) + .accumulator(request, accumulator) + .build(); + } + }; - abstract VerifiedTxnsAndProof transform( - DtoLedgerProof request, - VerifiedTxnsAndProof correctCommands, - LedgerAccumulator ledgerAccumulator, - Hasher hasher - ); - } + abstract VerifiedTxnsAndProof transform( + DtoLedgerProof request, + VerifiedTxnsAndProof correctCommands, + LedgerAccumulator ledgerAccumulator, + Hasher hasher); + } - @Override - public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { - VerifiedTxnsAndProof correctResult = correctReader.getNextCommittedTxns(start); - // TODO: Make epoch sync byzantine as well - if (start.getLedgerHeader().isEndOfEpoch()) { - return correctResult; - } + @Override + public VerifiedTxnsAndProof getNextCommittedTxns(DtoLedgerProof start) { + VerifiedTxnsAndProof correctResult = correctReader.getNextCommittedTxns(start); + // TODO: Make epoch sync byzantine as well + if (start.getLedgerHeader().isEndOfEpoch()) { + return correctResult; + } - if (correctResult != null) { - currentReadType = ReadType.values()[(currentReadType.ordinal() + 1) % ReadType.values().length]; - return currentReadType.transform(start, correctResult, accumulator, hasher); - } + if (correctResult != null) { + currentReadType = + ReadType.values()[(currentReadType.ordinal() + 1) % ReadType.values().length]; + return currentReadType.transform(start, correctResult, accumulator, hasher); + } - return null; - } + return null; + } - @Override - public Optional getEpochProof(long epoch) { - return correctReader.getEpochProof(epoch); - } + @Override + public Optional getEpochProof(long epoch) { + return correctReader.getEpochProof(epoch); + } - @Override - public Optional getLastProof() { - return correctReader.getLastProof(); - } + @Override + public Optional getLastProof() { + return correctReader.getLastProof(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SyncStateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SyncStateTest.java index aaa50db7c8..6d00c74684 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SyncStateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/SyncStateTest.java @@ -64,6 +64,9 @@ package com.radixdlt.sync; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.hash.HashCode; @@ -74,155 +77,132 @@ import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; - public class SyncStateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncState.IdleState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncState.IdleState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + + EqualsVerifier.forClass(SyncState.SyncCheckState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + + EqualsVerifier.forClass(SyncState.SyncingState.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + + EqualsVerifier.forClass(SyncState.PendingRequest.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } + + @Test + public void idle_state_should_update_current_header() { + final var peer = mock(BFTNode.class); + + final var initialState = SyncState.IdleState.init(mock(LedgerProof.class)); + + final var header2 = mock(LedgerProof.class); + final var newState = initialState.withCurrentHeader(header2); + + assertEquals(header2, newState.getCurrentHeader()); + } + + @Test + public void sync_check_state_should_update_current_header() { + final var peer = mock(BFTNode.class); + + final var initialState = + SyncState.SyncCheckState.init(mock(LedgerProof.class), ImmutableSet.of(peer)); + + final var header2 = mock(LedgerProof.class); + final var newState = initialState.withCurrentHeader(header2); + + assertEquals(header2, newState.getCurrentHeader()); + } + + @Test + public void sync_check_state_should_add_status_response() { + final var peer = mock(BFTNode.class); + + final var initialState = + SyncState.SyncCheckState.init(mock(LedgerProof.class), ImmutableSet.of(peer)); + + final var statusResponse = mock(StatusResponse.class); + final var newState = initialState.withStatusResponse(peer, statusResponse); + + assertTrue(newState.receivedResponseFrom(peer)); + assertEquals(1, newState.responses().size()); + assertTrue(newState.gotAllResponses()); + + final var otherPeer = mock(BFTNode.class); + assertFalse(newState.hasAskedPeer(otherPeer)); + } + + @Test + public void syncing_state_should_update_current_header() { + final var targetHeader = mock(LedgerProof.class); + final var initialState = + SyncState.SyncingState.init(mock(LedgerProof.class), ImmutableList.of(), targetHeader); + + final var header2 = mock(LedgerProof.class); + final var newState = initialState.withCurrentHeader(header2); + + assertEquals(header2, newState.getCurrentHeader()); + } + + @Test + public void syncing_state_should_update_waiting_for_peer() { + final var targetHeader = mock(LedgerProof.class); + final var initialState = + SyncState.SyncingState.init(mock(LedgerProof.class), ImmutableList.of(), targetHeader); + + assertFalse(initialState.waitingForResponse()); + + final var peer = mock(BFTNode.class); + final var newState = initialState.withPendingRequest(peer, 1L); + + assertTrue(newState.waitingForResponse()); + assertTrue(newState.waitingForResponseFrom(peer)); + + assertFalse(newState.clearPendingRequest().waitingForResponse()); + } + + @Test + public void syncing_state_should_update_candidate_peers() { + final var targetHeader = mock(LedgerProof.class); + final var initialState = + SyncState.SyncingState.init(mock(LedgerProof.class), ImmutableList.of(), targetHeader); + + // there's no next candidate peer + assertTrue(initialState.fetchNextCandidatePeer().getSecond().isEmpty()); + + final var candidate1 = mock(BFTNode.class); + final var candidate2 = mock(BFTNode.class); + final var stateWithCandidates = + initialState.addCandidatePeers(ImmutableList.of(candidate1, candidate2)); + + assertEquals(candidate1, stateWithCandidates.peekNthCandidate(0).get()); + assertEquals(candidate2, stateWithCandidates.peekNthCandidate(1).get()); + + final var withRemovedCandidate = stateWithCandidates.removeCandidate(candidate1); + assertEquals(candidate2, withRemovedCandidate.peekNthCandidate(0).get()); + assertEquals(candidate2, withRemovedCandidate.peekNthCandidate(1).get()); + } + + @Test + public void syncing_state_should_update_target_header() { + final var targetHeader1 = mock(LedgerProof.class); + final var targetHeader2 = mock(LedgerProof.class); + final var initialState = + SyncState.SyncingState.init(mock(LedgerProof.class), ImmutableList.of(), targetHeader1); + + assertEquals(targetHeader1, initialState.getTargetHeader()); - EqualsVerifier.forClass(SyncState.SyncCheckState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - - EqualsVerifier.forClass(SyncState.SyncingState.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - - EqualsVerifier.forClass(SyncState.PendingRequest.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } - - @Test - public void idle_state_should_update_current_header() { - final var peer = mock(BFTNode.class); - - final var initialState = SyncState.IdleState.init( - mock(LedgerProof.class) - ); - - final var header2 = mock(LedgerProof.class); - final var newState = initialState.withCurrentHeader(header2); - - assertEquals(header2, newState.getCurrentHeader()); - } - - @Test - public void sync_check_state_should_update_current_header() { - final var peer = mock(BFTNode.class); - - final var initialState = SyncState.SyncCheckState.init( - mock(LedgerProof.class), - ImmutableSet.of(peer) - ); - - final var header2 = mock(LedgerProof.class); - final var newState = initialState.withCurrentHeader(header2); - - assertEquals(header2, newState.getCurrentHeader()); - } - - @Test - public void sync_check_state_should_add_status_response() { - final var peer = mock(BFTNode.class); - - final var initialState = SyncState.SyncCheckState.init( - mock(LedgerProof.class), - ImmutableSet.of(peer) - ); - - final var statusResponse = mock(StatusResponse.class); - final var newState = initialState.withStatusResponse(peer, statusResponse); - - assertTrue(newState.receivedResponseFrom(peer)); - assertEquals(1, newState.responses().size()); - assertTrue(newState.gotAllResponses()); - - final var otherPeer = mock(BFTNode.class); - assertFalse(newState.hasAskedPeer(otherPeer)); - } - - @Test - public void syncing_state_should_update_current_header() { - final var targetHeader = mock(LedgerProof.class); - final var initialState = SyncState.SyncingState.init( - mock(LedgerProof.class), - ImmutableList.of(), - targetHeader - ); - - final var header2 = mock(LedgerProof.class); - final var newState = initialState.withCurrentHeader(header2); - - assertEquals(header2, newState.getCurrentHeader()); - } - - @Test - public void syncing_state_should_update_waiting_for_peer() { - final var targetHeader = mock(LedgerProof.class); - final var initialState = SyncState.SyncingState.init( - mock(LedgerProof.class), - ImmutableList.of(), - targetHeader - ); - - assertFalse(initialState.waitingForResponse()); - - final var peer = mock(BFTNode.class); - final var newState = initialState.withPendingRequest(peer, 1L); - - assertTrue(newState.waitingForResponse()); - assertTrue(newState.waitingForResponseFrom(peer)); - - assertFalse(newState.clearPendingRequest().waitingForResponse()); - } - - @Test - public void syncing_state_should_update_candidate_peers() { - final var targetHeader = mock(LedgerProof.class); - final var initialState = SyncState.SyncingState.init( - mock(LedgerProof.class), - ImmutableList.of(), - targetHeader - ); - - // there's no next candidate peer - assertTrue(initialState.fetchNextCandidatePeer().getSecond().isEmpty()); - - final var candidate1 = mock(BFTNode.class); - final var candidate2 = mock(BFTNode.class); - final var stateWithCandidates = initialState.addCandidatePeers( - ImmutableList.of(candidate1, candidate2) - ); - - assertEquals(candidate1, stateWithCandidates.peekNthCandidate(0).get()); - assertEquals(candidate2, stateWithCandidates.peekNthCandidate(1).get()); - - final var withRemovedCandidate = stateWithCandidates.removeCandidate(candidate1); - assertEquals(candidate2, withRemovedCandidate.peekNthCandidate(0).get()); - assertEquals(candidate2, withRemovedCandidate.peekNthCandidate(1).get()); - } - - @Test - public void syncing_state_should_update_target_header() { - final var targetHeader1 = mock(LedgerProof.class); - final var targetHeader2 = mock(LedgerProof.class); - final var initialState = SyncState.SyncingState.init( - mock(LedgerProof.class), - ImmutableList.of(), - targetHeader1 - ); - - assertEquals(targetHeader1, initialState.getTargetHeader()); - - final var newState = initialState.withTargetHeader(targetHeader2); - - assertEquals(targetHeader2, newState.getTargetHeader()); - } + final var newState = initialState.withTargetHeader(targetHeader2); + assertEquals(targetHeader2, newState.getTargetHeader()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/LocalSyncRequestTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/LocalSyncRequestTest.java index 2eb9099ee9..1bb237feb0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/LocalSyncRequestTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/LocalSyncRequestTest.java @@ -64,9 +64,9 @@ package com.radixdlt.sync.messages.local; +import static com.radixdlt.utils.TypedMocks.rmock; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import static com.radixdlt.utils.TypedMocks.rmock; import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; @@ -78,32 +78,32 @@ import org.junit.Test; public class LocalSyncRequestTest { - private LocalSyncRequest request; - private ImmutableList targetNodes; - private LedgerProof target; + private LocalSyncRequest request; + private ImmutableList targetNodes; + private LedgerProof target; - @Before - public void setup() { - this.targetNodes = rmock(ImmutableList.class); - this.target = mock(LedgerProof.class); - request = new LocalSyncRequest(target, targetNodes); - } + @Before + public void setup() { + this.targetNodes = rmock(ImmutableList.class); + this.target = mock(LedgerProof.class); + request = new LocalSyncRequest(target, targetNodes); + } - @Test - public void testGetters() { - assertThat(request.getTarget()).isEqualTo(target); - assertThat(request.getTargetNodes()).isEqualTo(targetNodes); - } + @Test + public void testGetters() { + assertThat(request.getTarget()).isEqualTo(target); + assertThat(request.getTargetNodes()).isEqualTo(targetNodes); + } - @Test - public void sensibleToString() { - assertThat(request.toString()).contains(LocalSyncRequest.class.getSimpleName()); - } + @Test + public void sensibleToString() { + assertThat(request.toString()).contains(LocalSyncRequest.class.getSimpleName()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(LocalSyncRequest.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(LocalSyncRequest.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeoutTest.java index 760f38786a..5dbdd54ef9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckReceiveStatusTimeoutTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SyncCheckReceiveStatusTimeoutTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncCheckReceiveStatusTimeout.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncCheckReceiveStatusTimeout.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckTriggerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckTriggerTest.java index 73a46f27fd..00aaa13363 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckTriggerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncCheckTriggerTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SyncCheckTriggerTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncCheckTrigger.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncCheckTrigger.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeoutTest.java index 85629ebe73..6d63321a39 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncLedgerUpdateTimeoutTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SyncLedgerUpdateTimeoutTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncLedgerUpdateTimeout.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncLedgerUpdateTimeout.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncRequestTimeoutTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncRequestTimeoutTest.java index a56af845ac..f1e5fbe4d0 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncRequestTimeoutTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/local/SyncRequestTimeoutTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SyncRequestTimeoutTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncRequestTimeout.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncRequestTimeout.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdateTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdateTest.java index b1a37dccce..c70e5ec3a9 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdateTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/LedgerStatusUpdateTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class LedgerStatusUpdateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(LedgerStatusUpdate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(LedgerStatusUpdate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusRequestTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusRequestTest.java index e7e17db934..b91f091e42 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusRequestTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusRequestTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class StatusRequestTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(StatusRequest.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(StatusRequest.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusResponseTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusResponseTest.java index 566be12b08..67108ac1e5 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusResponseTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/StatusResponseTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class StatusResponseTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(StatusResponse.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(StatusResponse.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncRequestTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncRequestTest.java index bdb81c1070..f74e6b9937 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncRequestTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncRequestTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SyncRequestTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncRequest.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncRequest.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncResponseTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncResponseTest.java index 3b5e4b6ab0..54f124514a 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncResponseTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/messages/remote/SyncResponseTest.java @@ -70,10 +70,10 @@ import org.junit.Test; public class SyncResponseTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SyncResponse.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(SyncResponse.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifierTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifierTest.java index 72c2207fa0..9a719aaf3e 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifierTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseValidatorSetVerifierTest.java @@ -73,44 +73,44 @@ import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.ValidationState; -import com.radixdlt.ledger.DtoTxnsAndProof; import com.radixdlt.ledger.DtoLedgerProof; +import com.radixdlt.ledger.DtoTxnsAndProof; import com.radixdlt.sync.messages.remote.SyncResponse; import org.junit.Before; import org.junit.Test; public class RemoteSyncResponseValidatorSetVerifierTest { - private BFTValidatorSet validatorSet; - private RemoteSyncResponseValidatorSetVerifier validatorSetVerifier; - private DtoTxnsAndProof commandsAndProof; + private BFTValidatorSet validatorSet; + private RemoteSyncResponseValidatorSetVerifier validatorSetVerifier; + private DtoTxnsAndProof commandsAndProof; - @Before - public void setup() { - this.validatorSet = mock(BFTValidatorSet.class); - this.validatorSetVerifier = new RemoteSyncResponseValidatorSetVerifier(validatorSet); - commandsAndProof = mock(DtoTxnsAndProof.class); - DtoLedgerProof headerAndProof = mock(DtoLedgerProof.class); - TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); - when(signatures.getSignatures()).thenReturn(ImmutableMap.of()); - when(headerAndProof.getSignatures()).thenReturn(signatures); - when(commandsAndProof.getTail()).thenReturn(headerAndProof); - } + @Before + public void setup() { + this.validatorSet = mock(BFTValidatorSet.class); + this.validatorSetVerifier = new RemoteSyncResponseValidatorSetVerifier(validatorSet); + commandsAndProof = mock(DtoTxnsAndProof.class); + DtoLedgerProof headerAndProof = mock(DtoLedgerProof.class); + TimestampedECDSASignatures signatures = mock(TimestampedECDSASignatures.class); + when(signatures.getSignatures()).thenReturn(ImmutableMap.of()); + when(headerAndProof.getSignatures()).thenReturn(signatures); + when(commandsAndProof.getTail()).thenReturn(headerAndProof); + } - @Test - public void when_process_good_validator_set__then_sends_verified() { - ValidationState validationState = mock(ValidationState.class); - when(validatorSet.newValidationState()).thenReturn(validationState); - when(validationState.complete()).thenReturn(true); + @Test + public void when_process_good_validator_set__then_sends_verified() { + ValidationState validationState = mock(ValidationState.class); + when(validatorSet.newValidationState()).thenReturn(validationState); + when(validationState.complete()).thenReturn(true); - assertTrue(validatorSetVerifier.verifyValidatorSet(SyncResponse.create(commandsAndProof))); - } + assertTrue(validatorSetVerifier.verifyValidatorSet(SyncResponse.create(commandsAndProof))); + } - @Test - public void when_process_bad_validator_set__then_sends_invalid() { - ValidationState validationState = mock(ValidationState.class); - when(validatorSet.newValidationState()).thenReturn(validationState); - when(validationState.complete()).thenReturn(false); + @Test + public void when_process_bad_validator_set__then_sends_invalid() { + ValidationState validationState = mock(ValidationState.class); + when(validatorSet.newValidationState()).thenReturn(validationState); + when(validationState.complete()).thenReturn(false); - assertFalse(validatorSetVerifier.verifyValidatorSet(SyncResponse.create(commandsAndProof))); - } + assertFalse(validatorSetVerifier.verifyValidatorSet(SyncResponse.create(commandsAndProof))); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/DurationParserTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/DurationParserTest.java index ff5ed26117..ac9d3afd14 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/DurationParserTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/DurationParserTest.java @@ -1,108 +1,117 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.utils; - -import org.junit.Test; - -import java.time.Duration; -import java.time.temporal.ChronoUnit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -public class DurationParserTest { - @Test - public void correctDurationsAreParsedProperly() { - assertEquals(Duration.of(123, ChronoUnit.SECONDS), DurationParser.parse("123").orElseThrow(AssertionError::new)); - assertEquals(Duration.of(321, ChronoUnit.SECONDS), DurationParser.parse("321s").orElseThrow(AssertionError::new)); - assertEquals(Duration.of(213, ChronoUnit.MINUTES), DurationParser.parse("213m").orElseThrow(AssertionError::new)); - assertEquals(Duration.of(132, ChronoUnit.HOURS), DurationParser.parse("132h").orElseThrow(AssertionError::new)); - } - - @Test - public void fractionalValuesAreTruncated() { - assertEquals(Duration.of(123, ChronoUnit.SECONDS), DurationParser.parse("123.9").orElseThrow(AssertionError::new)); - } - - @Test - public void emptyInputResultsToEmptyDuration() { - assertFalse(DurationParser.parse("").isPresent()); - } - - @Test - public void nonDigitInputResultsToEmptyDuration() { - assertFalse(DurationParser.parse("abcdef").isPresent()); - } - - @Test - public void zeroValueResultsToEmptyDuration() { - assertFalse(DurationParser.parse("0").isPresent()); - } - - @Test - public void incorrectUnitsResultToEmptyValue() { - assertFalse(DurationParser.parse("123f").isPresent()); - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import org.junit.Test; + +public class DurationParserTest { + @Test + public void correctDurationsAreParsedProperly() { + assertEquals( + Duration.of(123, ChronoUnit.SECONDS), + DurationParser.parse("123").orElseThrow(AssertionError::new)); + assertEquals( + Duration.of(321, ChronoUnit.SECONDS), + DurationParser.parse("321s").orElseThrow(AssertionError::new)); + assertEquals( + Duration.of(213, ChronoUnit.MINUTES), + DurationParser.parse("213m").orElseThrow(AssertionError::new)); + assertEquals( + Duration.of(132, ChronoUnit.HOURS), + DurationParser.parse("132h").orElseThrow(AssertionError::new)); + } + + @Test + public void fractionalValuesAreTruncated() { + assertEquals( + Duration.of(123, ChronoUnit.SECONDS), + DurationParser.parse("123.9").orElseThrow(AssertionError::new)); + } + + @Test + public void emptyInputResultsToEmptyDuration() { + assertFalse(DurationParser.parse("").isPresent()); + } + + @Test + public void nonDigitInputResultsToEmptyDuration() { + assertFalse(DurationParser.parse("abcdef").isPresent()); + } + + @Test + public void zeroValueResultsToEmptyDuration() { + assertFalse(DurationParser.parse("0").isPresent()); + } + + @Test + public void incorrectUnitsResultToEmptyValue() { + assertFalse(DurationParser.parse("123f").isPresent()); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/JSONFormatter.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/JSONFormatter.java index e099ee40b0..8d786a5257 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/JSONFormatter.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/JSONFormatter.java @@ -73,67 +73,61 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; - import java.io.IOException; import java.util.TreeMap; public class JSONFormatter { - private JSONFormatter() { - throw new UnsupportedOperationException("Cannot instantiate."); - } - - static class SortingNodeFactory extends JsonNodeFactory { - @Override - public ObjectNode objectNode() { - return new ObjectNode(this, new TreeMap()); - } - } - - static class MyPrettyPrinter extends DefaultPrettyPrinter { - @Override - public DefaultPrettyPrinter createInstance() { - MyPrettyPrinter printer = new MyPrettyPrinter(); - - DefaultPrettyPrinter.Indenter indenter = - new DefaultIndenter(" ", DefaultIndenter.SYS_LF); - - printer.indentObjectsWith(indenter); - printer.indentArraysWith(indenter); - - return printer; - } - - @Override - public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException { - jg.writeRaw(": "); - } - } - - public static String sortPrettyPrintObject(Object object) { - ObjectMapper mapper = new ObjectMapper(); - try { - var jsonString = mapper.writeValueAsString(object); - - return sortPrettyPrintJSONString(jsonString); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Failed to pretty print object to JSON string", e); - } - } - - public static String sortPrettyPrintJSONString(String uglyJson) { - ObjectMapper mapper = JsonMapper.builder() - .nodeFactory(new SortingNodeFactory()) - .build(); - - try { - var jsonSorted = mapper.readTree(uglyJson); - - return mapper - .writer(new MyPrettyPrinter()) - .writeValueAsString(jsonSorted); - - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Failed to pretty print JSON string", e); - } - } + private JSONFormatter() { + throw new UnsupportedOperationException("Cannot instantiate."); + } + + static class SortingNodeFactory extends JsonNodeFactory { + @Override + public ObjectNode objectNode() { + return new ObjectNode(this, new TreeMap()); + } + } + + static class MyPrettyPrinter extends DefaultPrettyPrinter { + @Override + public DefaultPrettyPrinter createInstance() { + MyPrettyPrinter printer = new MyPrettyPrinter(); + + DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter(" ", DefaultIndenter.SYS_LF); + + printer.indentObjectsWith(indenter); + printer.indentArraysWith(indenter); + + return printer; + } + + @Override + public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException { + jg.writeRaw(": "); + } + } + + public static String sortPrettyPrintObject(Object object) { + ObjectMapper mapper = new ObjectMapper(); + try { + var jsonString = mapper.writeValueAsString(object); + + return sortPrettyPrintJSONString(jsonString); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to pretty print object to JSON string", e); + } + } + + public static String sortPrettyPrintJSONString(String uglyJson) { + ObjectMapper mapper = JsonMapper.builder().nodeFactory(new SortingNodeFactory()).build(); + + try { + var jsonSorted = mapper.readTree(uglyJson); + + return mapper.writer(new MyPrettyPrinter()).writeValueAsString(jsonSorted); + + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to pretty print JSON string", e); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/RandomHasher.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/RandomHasher.java index 5709ea3f67..4dd5572394 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/RandomHasher.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/RandomHasher.java @@ -67,34 +67,33 @@ import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import com.radixdlt.crypto.Hasher; -import org.bouncycastle.util.encoders.Hex; - import java.util.HashMap; import java.util.Map; +import org.bouncycastle.util.encoders.Hex; /** - * A hasher that returns a random hash for any object. - * The same object will always get the same hash. + * A hasher that returns a random hash for any object. The same object will always get the same + * hash. */ public class RandomHasher implements Hasher { - private final Map cache = new HashMap<>(); + private final Map cache = new HashMap<>(); - @Override - public int bytes() { - return 32; - } + @Override + public int bytes() { + return 32; + } - @Override - public HashCode hash(Object o) { - cache.putIfAbsent(o, HashUtils.random256()); - return cache.get(o); - } + @Override + public HashCode hash(Object o) { + cache.putIfAbsent(o, HashUtils.random256()); + return cache.get(o); + } - @Override - public HashCode hashBytes(byte[] bytes) { - var key = Hex.toHexString(bytes); - cache.putIfAbsent(key, HashUtils.random256()); - return cache.get(key); - } + @Override + public HashCode hashBytes(byte[] bytes) { + var key = Hex.toHexString(bytes); + cache.putIfAbsent(key, HashUtils.random256()); + return cache.get(key); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/SerializerTestDataGenerator.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/SerializerTestDataGenerator.java index fc3b5a5e07..5eaeb09db6 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/SerializerTestDataGenerator.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/SerializerTestDataGenerator.java @@ -86,7 +86,6 @@ import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.ledger.AccumulatorState; - import java.math.BigInteger; import java.util.List; import java.util.Optional; @@ -94,88 +93,82 @@ public class SerializerTestDataGenerator { - private static final Random random = new Random(); - - private SerializerTestDataGenerator() { - // no-op - } - - public static QuorumCertificate randomQC() { - return new QuorumCertificate(randomVoteData(), randomTimestampedECDSASignatures()); - } - - public static Vote randomVote() { - return new Vote( - BFTNode.random(), - randomVoteData(), - Math.abs(random.nextLong()) + 1, - randomECDSASignature(), - randomHighQC(), - Optional.of(randomECDSASignature()) - ); - } - - public static Proposal randomProposal() { - var qc = randomQC(); - var txn = Txn.create(new byte[]{0, 1, 2, 3}); - var author = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - var vertex = UnverifiedVertex.create(qc, randomView(), List.of(txn), author); - return new Proposal(vertex, qc, ECDSASignature.zeroSignature(), Optional.empty()); - } - - public static VoteData randomVoteData() { - return new VoteData(randomBFTHeader(), randomBFTHeader(), randomBFTHeader()); - } - - public static BFTHeader randomBFTHeader() { - return new BFTHeader( - randomView(), - HashCode.fromLong(random.nextLong()), - LedgerHeader.create( - Math.abs(random.nextLong()), - randomView(), - new AccumulatorState(Math.abs(random.nextLong()) + 1, HashCode.fromLong(random.nextLong())), - Math.abs(random.nextLong()) + 1, - BFTValidatorSet.from( - ImmutableSet.builder() - .add(BFTValidator.from(BFTNode.random(), UInt256.from(random.nextLong()))) - .build()) - ) - ); - } - - public static TimestampedECDSASignatures randomTimestampedECDSASignatures() { - return new TimestampedECDSASignatures( - ImmutableMap.builder() - .put(BFTNode.random(), randomTimestampedECDSASignature()) - .build() - ); - } - - public static TimestampedECDSASignature randomTimestampedECDSASignature() { - return TimestampedECDSASignature.from( - Math.abs(random.nextLong()), - randomECDSASignature() - ); - } - - public static ECDSASignature randomECDSASignature() { - return ECDSASignature.create( - BigInteger.valueOf(Math.abs(random.nextLong())), - BigInteger.valueOf(Math.abs(random.nextLong())), - (random.nextInt() & 1) - ); - } - - public static HighQC randomHighQC() { - return HighQC.from(randomQC(), randomQC(), Optional.of(randomTimeoutCertificate())); - } - - public static TimeoutCertificate randomTimeoutCertificate() { - return new TimeoutCertificate(Math.abs(random.nextLong()), randomView(), randomTimestampedECDSASignatures()); - } - - public static View randomView() { - return View.of(Math.abs(random.nextLong())); - } + private static final Random random = new Random(); + + private SerializerTestDataGenerator() { + // no-op + } + + public static QuorumCertificate randomQC() { + return new QuorumCertificate(randomVoteData(), randomTimestampedECDSASignatures()); + } + + public static Vote randomVote() { + return new Vote( + BFTNode.random(), + randomVoteData(), + Math.abs(random.nextLong()) + 1, + randomECDSASignature(), + randomHighQC(), + Optional.of(randomECDSASignature())); + } + + public static Proposal randomProposal() { + var qc = randomQC(); + var txn = Txn.create(new byte[] {0, 1, 2, 3}); + var author = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + var vertex = UnverifiedVertex.create(qc, randomView(), List.of(txn), author); + return new Proposal(vertex, qc, ECDSASignature.zeroSignature(), Optional.empty()); + } + + public static VoteData randomVoteData() { + return new VoteData(randomBFTHeader(), randomBFTHeader(), randomBFTHeader()); + } + + public static BFTHeader randomBFTHeader() { + return new BFTHeader( + randomView(), + HashCode.fromLong(random.nextLong()), + LedgerHeader.create( + Math.abs(random.nextLong()), + randomView(), + new AccumulatorState( + Math.abs(random.nextLong()) + 1, HashCode.fromLong(random.nextLong())), + Math.abs(random.nextLong()) + 1, + BFTValidatorSet.from( + ImmutableSet.builder() + .add(BFTValidator.from(BFTNode.random(), UInt256.from(random.nextLong()))) + .build()))); + } + + public static TimestampedECDSASignatures randomTimestampedECDSASignatures() { + return new TimestampedECDSASignatures( + ImmutableMap.builder() + .put(BFTNode.random(), randomTimestampedECDSASignature()) + .build()); + } + + public static TimestampedECDSASignature randomTimestampedECDSASignature() { + return TimestampedECDSASignature.from(Math.abs(random.nextLong()), randomECDSASignature()); + } + + public static ECDSASignature randomECDSASignature() { + return ECDSASignature.create( + BigInteger.valueOf(Math.abs(random.nextLong())), + BigInteger.valueOf(Math.abs(random.nextLong())), + (random.nextInt() & 1)); + } + + public static HighQC randomHighQC() { + return HighQC.from(randomQC(), randomQC(), Optional.of(randomTimeoutCertificate())); + } + + public static TimeoutCertificate randomTimeoutCertificate() { + return new TimeoutCertificate( + Math.abs(random.nextLong()), randomView(), randomTimestampedECDSASignatures()); + } + + public static View randomView() { + return View.of(Math.abs(random.nextLong())); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/TypedMocks.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/TypedMocks.java index 4de27c4945..4453a6b8f8 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/TypedMocks.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/utils/TypedMocks.java @@ -64,45 +64,42 @@ package com.radixdlt.utils; -import com.google.inject.TypeLiteral; - import static org.mockito.Mockito.mock; -/** - * Typed mock wrappers to avoid compiler warnings where they are not - * warranted. - */ +import com.google.inject.TypeLiteral; + +/** Typed mock wrappers to avoid compiler warnings where they are not warranted. */ public final class TypedMocks { - private TypedMocks() { - throw new IllegalStateException("Can't construct"); - } + private TypedMocks() { + throw new IllegalStateException("Can't construct"); + } - /** - * Runtime checked typed mock, primarily for types with type arguments. - * - * @param The type of the mock without type arguments, eg {@code List} - * @param The type of the mock with type arguments, eg {@code List} - * @param cls The raw class for the mocked type - * @return the mock, cast to type {@code U}. If {@code U} is not assignable - * from {@code T}, then a {@code ClassCastException} will occur. - */ - public static U rmock(Class cls) { - @SuppressWarnings("unchecked") - U value = (U) mock(cls); - return value; - } + /** + * Runtime checked typed mock, primarily for types with type arguments. + * + * @param The type of the mock without type arguments, eg {@code List} + * @param The type of the mock with type arguments, eg {@code List} + * @param cls The raw class for the mocked type + * @return the mock, cast to type {@code U}. If {@code U} is not assignable from {@code T}, then a + * {@code ClassCastException} will occur. + */ + public static U rmock(Class cls) { + @SuppressWarnings("unchecked") + U value = (U) mock(cls); + return value; + } - /** - * Compile time checked typed mock, primarily for types with type arguments. - * - * @param The type of the mock without type arguments, eg {@code List} - * @param type The type literal describing the type of the mock - * @return the mock - */ - public static T cmock(TypeLiteral type) { - @SuppressWarnings("unchecked") - T value = (T) mock(type.getRawType()); - return value; - } + /** + * Compile time checked typed mock, primarily for types with type arguments. + * + * @param The type of the mock without type arguments, eg {@code List} + * @param type The type literal describing the type of the mock + * @return the mock + */ + public static T cmock(TypeLiteral type) { + @SuppressWarnings("unchecked") + T value = (T) mock(type.getRawType()); + return value; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/RadixVersionStringTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/RadixVersionStringTest.java index dc686f145c..19a2d4a522 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/RadixVersionStringTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/RadixVersionStringTest.java @@ -64,51 +64,50 @@ package org.radix; -import org.junit.Test; - -import java.util.Map; - import static org.junit.Assert.*; import static org.radix.Radix.calculateVersionString; -public class RadixVersionStringTest { - @Test - public void testCalculateVersionForCleanRepo() { - var details = Map.of( - "tag", "1.0-beta.35.1", - "last_tag", "1.0-beta.35.1" - ); +import java.util.Map; +import org.junit.Test; - var version = calculateVersionString(details); +public class RadixVersionStringTest { + @Test + public void testCalculateVersionForCleanRepo() { + var details = + Map.of( + "tag", "1.0-beta.35.1", + "last_tag", "1.0-beta.35.1"); - assertEquals("1.0-beta.35.1", version); - } + var version = calculateVersionString(details); - @Test - public void testCalculateVersionForDirtyRepo() { - var details = Map.of( - "tag", "", - "last_tag", "1.0-beta.35.1", - "build", "ed0717c", - "branch", "feature/rpnv1-1306-refactor-json-rpc-implementation" - ); + assertEquals("1.0-beta.35.1", version); + } - var version = calculateVersionString(details); + @Test + public void testCalculateVersionForDirtyRepo() { + var details = + Map.of( + "tag", "", + "last_tag", "1.0-beta.35.1", + "build", "ed0717c", + "branch", "feature/rpnv1-1306-refactor-json-rpc-implementation"); - assertEquals("1.0-beta.35.1-feature~rpnv1-1306-refactor-json-rpc-implementation-ed0717c", version); - } + var version = calculateVersionString(details); - @Test - public void testCalculateVersionForDetachedHead() { - var details = Map.of( - "tag", "", - "last_tag", "1.0-beta.35.1", - "build", "ed0717c" - ); + assertEquals( + "1.0-beta.35.1-feature~rpnv1-1306-refactor-json-rpc-implementation-ed0717c", version); + } - var version = calculateVersionString(details); + @Test + public void testCalculateVersionForDetachedHead() { + var details = + Map.of( + "tag", "", + "last_tag", "1.0-beta.35.1", + "build", "ed0717c"); - assertEquals("detached-head-ed0717c", version); - } + var version = calculateVersionString(details); -} \ No newline at end of file + assertEquals("detached-head-ed0717c", version); + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/containers/BasicContainerSubTypesEqualsVerifierTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/containers/BasicContainerSubTypesEqualsVerifierTest.java index 137665ff5e..acb4053604 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/containers/BasicContainerSubTypesEqualsVerifierTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/containers/BasicContainerSubTypesEqualsVerifierTest.java @@ -1,49 +1,113 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package org.radix.containers; import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; -import org.junit.Test; -import org.radix.network.messaging.Message; -import org.reflections.Reflections; - import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; +import org.junit.Test; +import org.radix.network.messaging.Message; +import org.reflections.Reflections; public class BasicContainerSubTypesEqualsVerifierTest { - @Test - public void verify_all_subtypes_correctly_override_equals_and_hash_code() { - final Set> subTypes = new HashSet<>(); - subTypes.addAll(findSubTypesInPkg("org.radix")); - subTypes.addAll(findSubTypesInPkg("com.radixdlt")); - - final Map, List> ignoredFieldsByClass = Map.of( - Message.class, List.of("instance")); - - subTypes.stream() - .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers())) - .forEach(clazz -> { - final String[] ignoredFields = - ignoredFieldsByClass.entrySet().stream() - .filter(e -> e.getKey().isAssignableFrom(clazz)) - .flatMap(e -> e.getValue().stream()) - .toArray(String[]::new); - - EqualsVerifier.forClass(clazz) - .withRedefinedSuperclass() - .suppress(Warning.NONFINAL_FIELDS) - .withIgnoredFields(ignoredFields) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - }); - } - - private Set> findSubTypesInPkg(String packagePrefix) { - return new Reflections(packagePrefix).getSubTypesOf(BasicContainer.class); - } + @Test + public void verify_all_subtypes_correctly_override_equals_and_hash_code() { + final Set> subTypes = new HashSet<>(); + subTypes.addAll(findSubTypesInPkg("org.radix")); + subTypes.addAll(findSubTypesInPkg("com.radixdlt")); + + final Map, List> ignoredFieldsByClass = + Map.of(Message.class, List.of("instance")); + + subTypes.stream() + .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers())) + .forEach( + clazz -> { + final String[] ignoredFields = + ignoredFieldsByClass.entrySet().stream() + .filter(e -> e.getKey().isAssignableFrom(clazz)) + .flatMap(e -> e.getValue().stream()) + .toArray(String[]::new); + + EqualsVerifier.forClass(clazz) + .withRedefinedSuperclass() + .suppress(Warning.NONFINAL_FIELDS) + .withIgnoredFields(ignoredFields) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + }); + } + + private Set> findSubTypesInPkg(String packagePrefix) { + return new Reflections(packagePrefix).getSubTypesOf(BasicContainer.class); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SSLFixTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SSLFixTest.java index 83214ba251..b28bb28b9b 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SSLFixTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SSLFixTest.java @@ -64,131 +64,129 @@ package org.radix.network.discovery; +import static org.junit.Assert.*; + import java.net.Socket; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSession; import javax.net.ssl.X509ExtendedTrustManager; - import org.junit.After; import org.junit.Test; -import static org.junit.Assert.*; - public class SSLFixTest { - // Dummy typed null values - private X509Certificate[] chain = null; - private String authType = null; - private Socket socket = null; - private SSLEngine engine = null; - private String hostname = null; - private SSLSession session = null; - - @After - public void resetExceptionStatus() { - SSLFix.standardVerifyResult = true; - } - - @Test - public void testSSLFix() throws CertificateException { - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - assertNotNull(tm.getAcceptedIssuers()); - assertEquals(0, tm.getAcceptedIssuers().length); - - // No exceptions for these methods, please - tm.checkClientTrusted(chain, authType); - tm.checkClientTrusted(chain, authType, socket); - tm.checkClientTrusted(chain, authType, engine); - - tm.checkServerTrusted(chain, authType); - tm.checkServerTrusted(chain, authType, socket); - tm.checkServerTrusted(chain, authType, engine); - - // Check hostname verifier is also good - HostnameVerifier hnv = HttpsURLConnection.getDefaultHostnameVerifier(); - assertTrue(hnv.verify(hostname, session)); - assertTrue(hnv.verify("radixdlt.com", session)); - assertTrue(hnv.verify("hjklas123789qsdfhj.com", session)); - } - - @Test(expected = CertificateException.class) - public void testSSLFailClient1() throws CertificateException { - SSLFix.standardVerifyResult = false; - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - tm.checkClientTrusted(chain, authType); - } - - @Test(expected = CertificateException.class) - public void testSSLFailClient2() throws CertificateException { - SSLFix.standardVerifyResult = false; - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - tm.checkClientTrusted(chain, authType, socket); - } - - @Test(expected = CertificateException.class) - public void testSSLFailClient3() throws CertificateException { - SSLFix.standardVerifyResult = false; - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - tm.checkClientTrusted(chain, authType, engine); - } - - @Test(expected = CertificateException.class) - public void testSSLFailServer1() throws CertificateException { - SSLFix.standardVerifyResult = false; - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - tm.checkServerTrusted(chain, authType); - } - - @Test(expected = CertificateException.class) - public void testSSLFailServer2() throws CertificateException { - SSLFix.standardVerifyResult = false; - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - tm.checkServerTrusted(chain, authType, socket); - } - - @Test(expected = CertificateException.class) - public void testSSLFailServer3() throws CertificateException { - SSLFix.standardVerifyResult = false; - X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); - - assertNotNull(tms); - assertEquals(1, tms.length); - - X509ExtendedTrustManager tm = tms[0]; - tm.checkServerTrusted(chain, authType, engine); - } + // Dummy typed null values + private X509Certificate[] chain = null; + private String authType = null; + private Socket socket = null; + private SSLEngine engine = null; + private String hostname = null; + private SSLSession session = null; + + @After + public void resetExceptionStatus() { + SSLFix.standardVerifyResult = true; + } + + @Test + public void testSSLFix() throws CertificateException { + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + assertNotNull(tm.getAcceptedIssuers()); + assertEquals(0, tm.getAcceptedIssuers().length); + + // No exceptions for these methods, please + tm.checkClientTrusted(chain, authType); + tm.checkClientTrusted(chain, authType, socket); + tm.checkClientTrusted(chain, authType, engine); + + tm.checkServerTrusted(chain, authType); + tm.checkServerTrusted(chain, authType, socket); + tm.checkServerTrusted(chain, authType, engine); + + // Check hostname verifier is also good + HostnameVerifier hnv = HttpsURLConnection.getDefaultHostnameVerifier(); + assertTrue(hnv.verify(hostname, session)); + assertTrue(hnv.verify("radixdlt.com", session)); + assertTrue(hnv.verify("hjklas123789qsdfhj.com", session)); + } + + @Test(expected = CertificateException.class) + public void testSSLFailClient1() throws CertificateException { + SSLFix.standardVerifyResult = false; + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + tm.checkClientTrusted(chain, authType); + } + + @Test(expected = CertificateException.class) + public void testSSLFailClient2() throws CertificateException { + SSLFix.standardVerifyResult = false; + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + tm.checkClientTrusted(chain, authType, socket); + } + + @Test(expected = CertificateException.class) + public void testSSLFailClient3() throws CertificateException { + SSLFix.standardVerifyResult = false; + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + tm.checkClientTrusted(chain, authType, engine); + } + + @Test(expected = CertificateException.class) + public void testSSLFailServer1() throws CertificateException { + SSLFix.standardVerifyResult = false; + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + tm.checkServerTrusted(chain, authType); + } + + @Test(expected = CertificateException.class) + public void testSSLFailServer2() throws CertificateException { + SSLFix.standardVerifyResult = false; + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + tm.checkServerTrusted(chain, authType, socket); + } + + @Test(expected = CertificateException.class) + public void testSSLFailServer3() throws CertificateException { + SSLFix.standardVerifyResult = false; + X509ExtendedTrustManager[] tms = SSLFix.trustAllHosts(); + + assertNotNull(tms); + assertEquals(1, tms.length); + + X509ExtendedTrustManager tm = tms[0]; + tm.checkServerTrusted(chain, authType, engine); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SeedNodesConfigParserTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SeedNodesConfigParserTest.java index b624f769d3..47e497f410 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SeedNodesConfigParserTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/network/discovery/SeedNodesConfigParserTest.java @@ -64,7 +64,10 @@ package org.radix.network.discovery; -import java.io.IOException; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.radixdlt.crypto.ECKeyPair; @@ -72,30 +75,32 @@ import com.radixdlt.network.p2p.P2PConfig; import com.radixdlt.networks.Addressing; import com.radixdlt.networks.Network; +import java.io.IOException; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class SeedNodesConfigParserTest { - private P2PConfig p2pConfig; + private P2PConfig p2pConfig; - @Before - public void setUp() throws IOException { - p2pConfig = mock(P2PConfig.class); - when(p2pConfig.defaultPort()).thenReturn(30000); - } + @Before + public void setUp() throws IOException { + p2pConfig = mock(P2PConfig.class); + when(p2pConfig.defaultPort()).thenReturn(30000); + } - @Test - public void parse_seeds_from_config() { - doReturn(ImmutableList.of(String.format( - "radix://%s@1.1.1.1", - NodeAddressing.of(Network.LOCALNET.getNodeHrp(), ECKeyPair.generateNew().getPublicKey()) - ))).when(p2pConfig).seedNodes(); - final var testSubject = new SeedNodesConfigParser(p2pConfig, Network.LOCALNET.getId(), Addressing.ofNetwork(Network.LOCALNET)); - assertEquals(1, testSubject.getResolvedSeedNodes().size()); - } + @Test + public void parse_seeds_from_config() { + doReturn( + ImmutableList.of( + String.format( + "radix://%s@1.1.1.1", + NodeAddressing.of( + Network.LOCALNET.getNodeHrp(), ECKeyPair.generateNew().getPublicKey())))) + .when(p2pConfig) + .seedNodes(); + final var testSubject = + new SeedNodesConfigParser( + p2pConfig, Network.LOCALNET.getId(), Addressing.ofNetwork(Network.LOCALNET)); + assertEquals(1, testSubject.getResolvedSeedNodes().size()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/GetPeersMessageTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/GetPeersMessageTest.java index 646dddcd29..0fc0ff6181 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/GetPeersMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/GetPeersMessageTest.java @@ -64,6 +64,8 @@ package org.radix.network.messages; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; @@ -71,27 +73,25 @@ import org.junit.Test; import org.radix.serialization.SerializeMessageObject; -import static org.assertj.core.api.Assertions.assertThat; - public class GetPeersMessageTest extends SerializeMessageObject { - public GetPeersMessageTest() { - super(GetPeersMessage.class, GetPeersMessage::new); - } + public GetPeersMessageTest() { + super(GetPeersMessage.class, GetPeersMessage::new); + } - @Test - public void sensibleToString() { - String s = new GetPeersMessage().toString(); + @Test + public void sensibleToString() { + String s = new GetPeersMessage().toString(); - assertThat(s).contains(GetPeersMessage.class.getSimpleName()); - } + assertThat(s).contains(GetPeersMessage.class.getSimpleName()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(GetPeersMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(GetPeersMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPingMessageTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPingMessageTest.java index 62252d469f..362b7e2405 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPingMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPingMessageTest.java @@ -71,13 +71,13 @@ import org.junit.Test; public class PeerPingMessageTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(PeerPingMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .withRedefinedSuperclass() - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(PeerPingMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .withRedefinedSuperclass() + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPongMessageTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPongMessageTest.java index 95ce3fd938..b404d9cf46 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPongMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeerPongMessageTest.java @@ -71,13 +71,13 @@ import org.junit.Test; public class PeerPongMessageTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(PeerPongMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .withRedefinedSuperclass() - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(PeerPongMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .withRedefinedSuperclass() + .verify(); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeersResponseMessageTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeersResponseMessageTest.java index 0dd922998c..07c08e7b45 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeersResponseMessageTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/network/messages/PeersResponseMessageTest.java @@ -64,6 +64,8 @@ package org.radix.network.messages; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.collect.ImmutableSet; import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; @@ -72,39 +74,37 @@ import org.junit.Test; import org.radix.serialization.SerializeMessageObject; -import static org.assertj.core.api.Assertions.assertThat; - public class PeersResponseMessageTest extends SerializeMessageObject { - public PeersResponseMessageTest() { - super(PeersResponseMessage.class, () -> new PeersResponseMessage(ImmutableSet.of())); - } + public PeersResponseMessageTest() { + super(PeersResponseMessage.class, () -> new PeersResponseMessage(ImmutableSet.of())); + } - @Test - public void sensibleToString() { - String s = new PeersResponseMessage(ImmutableSet.of()).toString(); + @Test + public void sensibleToString() { + String s = new PeersResponseMessage(ImmutableSet.of()).toString(); - assertThat(s).contains(PeersResponseMessage.class.getSimpleName()); - } + assertThat(s).contains(PeersResponseMessage.class.getSimpleName()); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(PeersResponseMessage.class) - .withIgnoredFields("instance") - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(PeersResponseMessage.class) + .withIgnoredFields("instance") + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - @Test - public void deserializationWithNullIsSafe() { - var peersResponseMessageWithNull = new PeersResponseMessage(null); + @Test + public void deserializationWithNullIsSafe() { + var peersResponseMessageWithNull = new PeersResponseMessage(null); - assertThat(peersResponseMessageWithNull.getPeers()).isEqualTo(ImmutableSet.of()); - } + assertThat(peersResponseMessageWithNull.getPeers()).isEqualTo(ImmutableSet.of()); + } - @Test(expected = NullPointerException.class) - public void deserializationWithNullElementsInSetThrowsException() { - new PeersResponseMessage(ImmutableSet.of(null)); - } + @Test(expected = NullPointerException.class) + public void deserializationWithNullElementsInSetThrowsException() { + new PeersResponseMessage(ImmutableSet.of(null)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/AIDSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/AIDSerializeTest.java index b3d8de9ba5..b2f4cf29bc 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/AIDSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/AIDSerializeTest.java @@ -1,86 +1,86 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.serialization; - -import com.radixdlt.identifiers.AID; - -public class AIDSerializeTest extends SerializeMessageObject { - public AIDSerializeTest() { - super(AID.class, AIDSerializeTest::getAID); - } - - private static AID getAID() { - byte[] bytes = new byte[AID.BYTES]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) i; - } - return AID.from(bytes); - } - - @Override - public void testNONEIsEmpty() { - // Not applicable to Atom IDs - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.serialization; + +import com.radixdlt.identifiers.AID; + +public class AIDSerializeTest extends SerializeMessageObject { + public AIDSerializeTest() { + super(AID.class, AIDSerializeTest::getAID); + } + + private static AID getAID() { + byte[] bytes = new byte[AID.BYTES]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) i; + } + return AID.from(bytes); + } + + @Override + public void testNONEIsEmpty() { + // Not applicable to Atom IDs + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/BFTHeaderSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/BFTHeaderSerializeTest.java index 9792e86b12..a510f8f9d1 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/BFTHeaderSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/BFTHeaderSerializeTest.java @@ -64,20 +64,19 @@ package org.radix.serialization; +import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.BFTHeader; import com.radixdlt.crypto.HashUtils; -import com.radixdlt.ledger.AccumulatorState; public class BFTHeaderSerializeTest extends SerializeObject { - public BFTHeaderSerializeTest() { - super(BFTHeader.class, BFTHeaderSerializeTest::get); - } + public BFTHeaderSerializeTest() { + super(BFTHeader.class, BFTHeaderSerializeTest::get); + } - private static BFTHeader get() { - View view = View.of(1234567890L); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - return new BFTHeader(view, HashUtils.random256(), ledgerHeader); - } + private static BFTHeader get() { + View view = View.of(1234567890L); + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + return new BFTHeader(view, HashUtils.random256(), ledgerHeader); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject.java index 4171347a61..f2e8f3392c 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject.java @@ -64,129 +64,132 @@ package org.radix.serialization; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.hash.HashCode; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.identifiers.EUID; import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; - +import com.radixdlt.utils.UInt128; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Random; - -import com.radixdlt.identifiers.EUID; import org.radix.containers.BasicContainer; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.utils.UInt128; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * A dummy object containing various primitives to test - * serialization / deserialization speed - */ +/** A dummy object containing various primitives to test serialization / deserialization speed */ @SerializerId2("test.dummy_test_object_1") public final class DummyTestObject extends BasicContainer { - private static final Random r = new Random(0); - private static final byte[] randomData; - - static { - randomData = new byte[0x40]; - r.nextBytes(randomData); - } - - @JsonProperty("btrue") - @DsonOutput(Output.ALL) - private boolean btrue; - - @JsonProperty("bfalse") - @DsonOutput(Output.ALL) - private boolean bfalse; - - @JsonProperty("num") - @DsonOutput(Output.ALL) - private long num; - - @JsonProperty("id") - @DsonOutput(Output.ALL) - private EUID id; - - @JsonProperty("theHash") - @DsonOutput(Output.ALL) - private HashCode theHash; - - @JsonProperty("bytes") - @DsonOutput(Output.ALL) - private byte[] bytes; - - @JsonProperty("string") - @DsonOutput(Output.ALL) - private String string; - - @JsonProperty("array") - @DsonOutput(Output.ALL) - private List array; - - @JsonProperty("object") - @DsonOutput(Output.ALL) - private DummyTestObject2 object; - - public DummyTestObject() { - this(false); - } - - public DummyTestObject(boolean initData) { - if (initData) { - this.btrue = true; - this.bfalse = false; - this.num = 0x123456789abcdefL; - this.id = new EUID(UInt128.from(r.nextLong(), r.nextLong())); - this.theHash = HashUtils.sha256(randomData); - this.bytes = randomData.clone(); - this.string = getClass().getName(); - this.array = new ArrayList<>(Collections.nCopies(10, id)); - this.object = new DummyTestObject2(); - } - } - - @Override - public short version() { - return 100; - } - - @Override - public int hashCode() { - return Objects.hash(btrue, bfalse, num, id, theHash, string, array, object) * 31 + Arrays.hashCode(bytes); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DummyTestObject) { - DummyTestObject other = (DummyTestObject) obj; - return - this.btrue == other.btrue && - this.bfalse == other.bfalse && - this.num == other.num && - Objects.equals(this.id, other.id) && - Objects.equals(this.theHash, other.theHash) && - Objects.equals(this.string, other.string) && - Arrays.equals(this.bytes, other.bytes) && - Objects.equals(this.array, other.array) && - Objects.equals(this.object, other.object); - } - return false; - } - - @Override - public String toString() { - return String.format( - "%s[btrue=%s, bfalse=%s, num=%s, id=%s, theHash=%s, bytes=%s, string=%s, array=%s, object=%s]", - getClass().getSimpleName(), btrue, bfalse, num, id, theHash, Arrays.toString(bytes), string, array, object); - } - + private static final Random r = new Random(0); + private static final byte[] randomData; + + static { + randomData = new byte[0x40]; + r.nextBytes(randomData); + } + + @JsonProperty("btrue") + @DsonOutput(Output.ALL) + private boolean btrue; + + @JsonProperty("bfalse") + @DsonOutput(Output.ALL) + private boolean bfalse; + + @JsonProperty("num") + @DsonOutput(Output.ALL) + private long num; + + @JsonProperty("id") + @DsonOutput(Output.ALL) + private EUID id; + + @JsonProperty("theHash") + @DsonOutput(Output.ALL) + private HashCode theHash; + + @JsonProperty("bytes") + @DsonOutput(Output.ALL) + private byte[] bytes; + + @JsonProperty("string") + @DsonOutput(Output.ALL) + private String string; + + @JsonProperty("array") + @DsonOutput(Output.ALL) + private List array; + + @JsonProperty("object") + @DsonOutput(Output.ALL) + private DummyTestObject2 object; + + public DummyTestObject() { + this(false); + } + + public DummyTestObject(boolean initData) { + if (initData) { + this.btrue = true; + this.bfalse = false; + this.num = 0x123456789abcdefL; + this.id = new EUID(UInt128.from(r.nextLong(), r.nextLong())); + this.theHash = HashUtils.sha256(randomData); + this.bytes = randomData.clone(); + this.string = getClass().getName(); + this.array = new ArrayList<>(Collections.nCopies(10, id)); + this.object = new DummyTestObject2(); + } + } + + @Override + public short version() { + return 100; + } + + @Override + public int hashCode() { + return Objects.hash(btrue, bfalse, num, id, theHash, string, array, object) * 31 + + Arrays.hashCode(bytes); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DummyTestObject) { + DummyTestObject other = (DummyTestObject) obj; + return this.btrue == other.btrue + && this.bfalse == other.bfalse + && this.num == other.num + && Objects.equals(this.id, other.id) + && Objects.equals(this.theHash, other.theHash) + && Objects.equals(this.string, other.string) + && Arrays.equals(this.bytes, other.bytes) + && Objects.equals(this.array, other.array) + && Objects.equals(this.object, other.object); + } + return false; + } + + @Override + public String toString() { + return String.format( + "%s[btrue=%s, bfalse=%s, num=%s, id=%s, theHash=%s, bytes=%s, string=%s, array=%s," + + " object=%s]", + getClass().getSimpleName(), + btrue, + bfalse, + num, + id, + theHash, + Arrays.toString(bytes), + string, + array, + object); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject2.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject2.java index 2f6cf231b2..576b1502e6 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject2.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/DummyTestObject2.java @@ -64,50 +64,48 @@ package org.radix.serialization; +import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerId2; import java.util.Objects; - import org.radix.containers.BasicContainer; -import com.radixdlt.serialization.DsonOutput.Output; - -import com.fasterxml.jackson.annotation.JsonProperty; @SerializerId2("test.dummy_test_object_2") public final class DummyTestObject2 extends BasicContainer { - @JsonProperty("name") - @DsonOutput(Output.ALL) - private String name = getClass().getName(); + @JsonProperty("name") + @DsonOutput(Output.ALL) + private String name = getClass().getName(); - public DummyTestObject2() { - // Nothing to do - } + public DummyTestObject2() { + // Nothing to do + } - @Override - public short version() { - return 100; - } + @Override + public short version() { + return 100; + } - @Override - public int hashCode() { - return Objects.hash(this.name); - } + @Override + public int hashCode() { + return Objects.hash(this.name); + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DummyTestObject2) { - DummyTestObject2 other = (DummyTestObject2) obj; - return Objects.equals(this.name, other.name); - } - return false; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DummyTestObject2) { + DummyTestObject2 other = (DummyTestObject2) obj; + return Objects.equals(this.name, other.name); + } + return false; + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), name); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), name); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/MempoolAddMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/MempoolAddMessageSerializeTest.java index ff0f32b079..367d2b530e 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/MempoolAddMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/MempoolAddMessageSerializeTest.java @@ -66,16 +66,15 @@ import com.radixdlt.atom.Txn; import com.radixdlt.middleware2.network.MempoolAddMessage; - import java.util.List; public class MempoolAddMessageSerializeTest extends SerializeMessageObject { - public MempoolAddMessageSerializeTest() { - super(MempoolAddMessage.class, MempoolAddMessageSerializeTest::get); - } + public MempoolAddMessageSerializeTest() { + super(MempoolAddMessage.class, MempoolAddMessageSerializeTest::get); + } - private static MempoolAddMessage get() { - final var txn = Txn.create(new byte[]{0, 1}); - return MempoolAddMessage.from(List.of(txn)); - } + private static MempoolAddMessage get() { + final var txn = Txn.create(new byte[] {0, 1}); + return MempoolAddMessage.from(List.of(txn)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/PeersResponseMessageSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/PeersResponseMessageSerializeTest.java index bf62148eac..295e5d03f1 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/PeersResponseMessageSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/PeersResponseMessageSerializeTest.java @@ -69,14 +69,15 @@ import com.radixdlt.network.p2p.RadixNodeUri; import org.radix.network.messages.PeersResponseMessage; -public class PeersResponseMessageSerializeTest extends SerializeMessageObject { - public PeersResponseMessageSerializeTest() { - super(PeersResponseMessage.class, PeersResponseMessageSerializeTest::get); - } +public class PeersResponseMessageSerializeTest + extends SerializeMessageObject { + public PeersResponseMessageSerializeTest() { + super(PeersResponseMessage.class, PeersResponseMessageSerializeTest::get); + } - private static PeersResponseMessage get() { - final var pubKey = ECKeyPair.generateNew().getPublicKey(); - final var uri = RadixNodeUri.fromPubKeyAndAddress(1, pubKey, "127.0.0.1", 30000); - return new PeersResponseMessage(ImmutableSet.of(uri)); - } + private static PeersResponseMessage get() { + final var pubKey = ECKeyPair.generateNew().getPublicKey(); + final var uri = RadixNodeUri.fromPubKeyAndAddress(1, pubKey, "127.0.0.1", 30000); + return new PeersResponseMessage(ImmutableSet.of(uri)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/ProposalSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/ProposalSerializeTest.java index 65bb126f73..36a3540597 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/ProposalSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/ProposalSerializeTest.java @@ -78,29 +78,28 @@ import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.HashUtils; - import java.util.List; import java.util.Optional; public class ProposalSerializeTest extends SerializeObject { - public ProposalSerializeTest() { - super(Proposal.class, ProposalSerializeTest::get); - } + public ProposalSerializeTest() { + super(Proposal.class, ProposalSerializeTest::get); + } - private static Proposal get() { - View view = View.of(1234567891L); - HashCode id = HashUtils.random256(); + private static Proposal get() { + View view = View.of(1234567891L); + HashCode id = HashUtils.random256(); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - BFTHeader header = new BFTHeader(view, id, ledgerHeader); - BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); - VoteData voteData = new VoteData(header, parent, null); - QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - var txn = Txn.create(new byte[]{0, 1, 2, 3}); + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + BFTHeader header = new BFTHeader(view, id, ledgerHeader); + BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); + VoteData voteData = new VoteData(header, parent, null); + QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + var txn = Txn.create(new byte[] {0, 1, 2, 3}); - // add a particle to ensure atom is valid and has at least one shard - BFTNode author = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - UnverifiedVertex vertex = UnverifiedVertex.create(qc, view, List.of(txn), author); - return new Proposal(vertex, qc, ECDSASignature.zeroSignature(), Optional.empty()); - } + // add a particle to ensure atom is valid and has at least one shard + BFTNode author = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + UnverifiedVertex vertex = UnverifiedVertex.create(qc, view, List.of(txn), author); + return new Proposal(vertex, qc, ECDSASignature.zeroSignature(), Optional.empty()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/REAddrTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/REAddrTest.java index f4b1be7cab..f11c21de34 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/REAddrTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/REAddrTest.java @@ -68,11 +68,11 @@ import com.radixdlt.identifiers.REAddr; public class REAddrTest extends SerializeValue { - public REAddrTest() { - super(REAddr.class, REAddrTest::get); - } + public REAddrTest() { + super(REAddr.class, REAddrTest::get); + } - private static REAddr get() { - return REAddr.ofHashedKey(ECKeyPair.generateNew().getPublicKey(), "jsh"); - } + private static REAddr get() { + return REAddr.ofHashedKey(ECKeyPair.generateNew().getPublicKey(), "jsh"); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/RadixTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/RadixTest.java index 0b48c200b9..8b94abf8e3 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/RadixTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/RadixTest.java @@ -64,6 +64,11 @@ package org.radix.serialization; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.radixdlt.DefaultSerialization; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.properties.RuntimeProperties; @@ -72,45 +77,41 @@ import org.mockito.stubbing.Answer; import org.radix.time.NtpService; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public abstract class RadixTest { - private static Serialization serialization; - private static NtpService ntpService; - private static RuntimeProperties properties; - private static ECKeyPair ecKeyPair; + private static Serialization serialization; + private static NtpService ntpService; + private static RuntimeProperties properties; + private static ECKeyPair ecKeyPair; - @BeforeClass - public static void startRadixTest() { - TestSetupUtils.installBouncyCastleProvider(); + @BeforeClass + public static void startRadixTest() { + TestSetupUtils.installBouncyCastleProvider(); - properties = mock(RuntimeProperties.class); - doAnswer(invocation -> invocation.getArgument(1)).when(properties).get(any(), any()); + properties = mock(RuntimeProperties.class); + doAnswer(invocation -> invocation.getArgument(1)).when(properties).get(any(), any()); - ntpService = mock(NtpService.class); - when(ntpService.getUTCTimeMS()).thenAnswer((Answer) invocation -> System.currentTimeMillis()); + ntpService = mock(NtpService.class); + when(ntpService.getUTCTimeMS()) + .thenAnswer((Answer) invocation -> System.currentTimeMillis()); - serialization = DefaultSerialization.getInstance(); + serialization = DefaultSerialization.getInstance(); - ecKeyPair = ECKeyPair.generateNew(); - } + ecKeyPair = ECKeyPair.generateNew(); + } - public static Serialization getSerialization() { - return serialization; - } + public static Serialization getSerialization() { + return serialization; + } - public static NtpService getNtpService() { - return ntpService; - } + public static NtpService getNtpService() { + return ntpService; + } - public static RuntimeProperties getProperties() { - return properties; - } + public static RuntimeProperties getProperties() { + return properties; + } - public static ECKeyPair getKeyPair() { - return ecKeyPair; - } + public static ECKeyPair getKeyPair() { + return ecKeyPair; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializationTestUtils.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializationTestUtils.java index 09e5d8e2b9..3aaecebd85 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializationTestUtils.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializationTestUtils.java @@ -64,42 +64,39 @@ package org.radix.serialization; +import static org.junit.Assert.fail; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.Serialization; - import java.io.IOException; -import static org.junit.Assert.fail; - -/** - * Utilities for test encoding and decoding with serializers. - */ +/** Utilities for test encoding and decoding with serializers. */ final class SerializationTestUtils { - private SerializationTestUtils() { - throw new IllegalStateException("Can't construct"); - } + private SerializationTestUtils() { + throw new IllegalStateException("Can't construct"); + } - static void testEncodeDecode(T target, Class cls, Serialization serialization, Output output) - throws Exception { - String json1 = serialization.toJson(target, output); - T newTarget = serialization.fromJson(json1, cls); - String json2 = serialization.toJson(newTarget, output); - compareJson(json1, json2); - } + static void testEncodeDecode( + T target, Class cls, Serialization serialization, Output output) throws Exception { + String json1 = serialization.toJson(target, output); + T newTarget = serialization.fromJson(json1, cls); + String json2 = serialization.toJson(newTarget, output); + compareJson(json1, json2); + } - static void compareJson(String s1Json, String s2Json) throws IOException { - ObjectMapper om = new ObjectMapper(); - om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); - om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); - JsonNode s1Tree = om.readTree(s1Json); - JsonNode s2Tree = om.readTree(s2Json); - if (!s1Tree.equals(s2Tree)) { - fail("Not equivalent JSON"); - } - } + static void compareJson(String s1Json, String s2Json) throws IOException { + ObjectMapper om = new ObjectMapper(); + om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); + JsonNode s1Tree = om.readTree(s1Json); + JsonNode s2Tree = om.readTree(s2Json); + if (!s1Tree.equals(s2Tree)) { + fail("Not equivalent JSON"); + } + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeMessageObject.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeMessageObject.java index 6e6a3f90d2..6a16338b66 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeMessageObject.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeMessageObject.java @@ -68,15 +68,14 @@ /** * Raft of tests for serialization of objects. - *

- * This class extends {@link SerializeObject} to set up databases - * required by most classes that extend {@link org.radix.network.messaging.Message}. + * + *

This class extends {@link SerializeObject} to set up databases required by most classes that + * extend {@link org.radix.network.messaging.Message}. * * @param The type under test. */ - public abstract class SerializeMessageObject extends SerializeObject { - protected SerializeMessageObject(Class cls, Supplier factory) { - super(cls, factory); - } + protected SerializeMessageObject(Class cls, Supplier factory) { + super(cls, factory); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeObject.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeObject.java index 7ae3d897a9..32c96eb87b 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeObject.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeObject.java @@ -64,117 +64,115 @@ package org.radix.serialization; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeFalse; +import static org.radix.serialization.SerializationTestUtils.testEncodeDecode; + import com.radixdlt.serialization.ClassScanningSerializerIds; import com.radixdlt.serialization.DeserializeException; +import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.Polymorphic; import com.radixdlt.serialization.Serialization; - import java.lang.reflect.Method; import java.util.function.Supplier; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.config.Configurator; import org.junit.BeforeClass; import org.junit.Test; -import com.radixdlt.serialization.DsonOutput.Output; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assume.assumeFalse; -import static org.radix.serialization.SerializationTestUtils.testEncodeDecode; - /** * Raft of tests for serialization of objects. - *

- * Note that the tests that round-trip types through the serializer - * are not run for {@link Polymorphic} types, as these types do not - * serialize to themselves, but to one of their superclasses. + * + *

Note that the tests that round-trip types through the serializer are not run for {@link + * Polymorphic} types, as these types do not serialize to themselves, but to one of their + * superclasses. * * @param The type under test. */ public abstract class SerializeObject extends RadixTest { - @BeforeClass - public static void serializeObjectBeforeClass() { - // Disable this output for now, as the serialiser is quite verbose when starting. - Configurator.setLevel(LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); - } - - private final Class cls; - private final Supplier factory; - - protected SerializeObject(Class cls, Supplier factory) { - this.cls = cls; - this.factory = factory; - } - - @Test - public void testObjectHasEquals() throws NoSuchMethodException { - Method method = factory.get().getClass().getMethod("equals", Object.class); - assertFalse(method.getDeclaringClass().equals(Object.class)); - } - - @Test - public void testObjectHasHashCode() throws NoSuchMethodException { - Method method = factory.get().getClass().getMethod("hashCode"); - assertFalse(method.getDeclaringClass().equals(Object.class)); - } - - @Test - public void testNONEIsEmpty() { - String s2Json = getSerialization().toJson(factory.get(), Output.NONE); - assertEquals("{}", s2Json); - } - - @Test - public void testRoundTripJsonSame() throws DeserializeException { - checkPolymorphic(); - Serialization s = getSerialization(); - T initialObj = factory.get(); - String initialJson = s.toJson(initialObj, Output.ALL); - T deserialisedObj = s.fromJson(initialJson, this.cls); - assertEquals(initialObj, deserialisedObj); - } - - @Test - public void testRoundTripDsonSame() throws DeserializeException { - checkPolymorphic(); - Serialization s = getSerialization(); - T initialObj = factory.get(); - byte[] initialDson = s.toDson(initialObj, Output.ALL); - T deserialisedObj = s.fromDson(initialDson, this.cls); - assertEquals(initialObj, deserialisedObj); - } - - @Test - public void testEncodeDecodeALL() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.ALL); - } - - @Test - public void testEncodeDecodeAPI() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.API); - } - - // Output.HASH does not serialize "serializers" and can't be deserialized - // Output.NONE does not serialize "serializers" and can't be deserialized - - @Test - public void testEncodeDecodePERSIST() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.PERSIST); - } - - @Test - public void testEncodeDecodeWIRE() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.WIRE); - } - - private void checkPolymorphic() { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - } + @BeforeClass + public static void serializeObjectBeforeClass() { + // Disable this output for now, as the serialiser is quite verbose when starting. + Configurator.setLevel( + LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); + } + + private final Class cls; + private final Supplier factory; + + protected SerializeObject(Class cls, Supplier factory) { + this.cls = cls; + this.factory = factory; + } + + @Test + public void testObjectHasEquals() throws NoSuchMethodException { + Method method = factory.get().getClass().getMethod("equals", Object.class); + assertFalse(method.getDeclaringClass().equals(Object.class)); + } + + @Test + public void testObjectHasHashCode() throws NoSuchMethodException { + Method method = factory.get().getClass().getMethod("hashCode"); + assertFalse(method.getDeclaringClass().equals(Object.class)); + } + + @Test + public void testNONEIsEmpty() { + String s2Json = getSerialization().toJson(factory.get(), Output.NONE); + assertEquals("{}", s2Json); + } + + @Test + public void testRoundTripJsonSame() throws DeserializeException { + checkPolymorphic(); + Serialization s = getSerialization(); + T initialObj = factory.get(); + String initialJson = s.toJson(initialObj, Output.ALL); + T deserialisedObj = s.fromJson(initialJson, this.cls); + assertEquals(initialObj, deserialisedObj); + } + + @Test + public void testRoundTripDsonSame() throws DeserializeException { + checkPolymorphic(); + Serialization s = getSerialization(); + T initialObj = factory.get(); + byte[] initialDson = s.toDson(initialObj, Output.ALL); + T deserialisedObj = s.fromDson(initialDson, this.cls); + assertEquals(initialObj, deserialisedObj); + } + + @Test + public void testEncodeDecodeALL() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.ALL); + } + + @Test + public void testEncodeDecodeAPI() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.API); + } + + // Output.HASH does not serialize "serializers" and can't be deserialized + // Output.NONE does not serialize "serializers" and can't be deserialized + + @Test + public void testEncodeDecodePERSIST() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.PERSIST); + } + + @Test + public void testEncodeDecodeWIRE() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.WIRE); + } + + private void checkPolymorphic() { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeValue.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeValue.java index 96d6a9025b..46362ba3b2 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeValue.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/SerializeValue.java @@ -64,61 +64,60 @@ package org.radix.serialization; -import com.radixdlt.serialization.DsonOutput.Output; +import static org.junit.Assume.assumeFalse; +import static org.radix.serialization.SerializationTestUtils.testEncodeDecode; + import com.radixdlt.serialization.ClassScanningSerializerIds; +import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.Polymorphic; - +import java.util.function.Supplier; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.config.Configurator; import org.junit.BeforeClass; import org.junit.Test; -import java.util.function.Supplier; - -import static org.junit.Assume.assumeFalse; -import static org.radix.serialization.SerializationTestUtils.testEncodeDecode; - public abstract class SerializeValue extends RadixTest { - @BeforeClass - public static void serializeObjectBeforeClass() { - // Disable this output for now, as the serialiser is quite verbose when starting. - Configurator.setLevel(LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); - } + @BeforeClass + public static void serializeObjectBeforeClass() { + // Disable this output for now, as the serialiser is quite verbose when starting. + Configurator.setLevel( + LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); + } - private final Class cls; - private final Supplier factory; + private final Class cls; + private final Supplier factory; - protected SerializeValue(Class cls, Supplier factory) { - this.cls = cls; - this.factory = factory; - } + protected SerializeValue(Class cls, Supplier factory) { + this.cls = cls; + this.factory = factory; + } - @Test - public void testEncodeDecodeALL() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.ALL); - } + @Test + public void testEncodeDecodeALL() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.ALL); + } - @Test - public void testEncodeDecodeAPI() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.API); - } + @Test + public void testEncodeDecodeAPI() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.API); + } - @Test - public void testEncodeDecodePERSIST() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.PERSIST); - } + @Test + public void testEncodeDecodePERSIST() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.PERSIST); + } - @Test - public void testEncodeDecodeWIRE() throws Exception { - checkPolymorphic(); - testEncodeDecode(factory.get(), cls, getSerialization(), Output.WIRE); - } + @Test + public void testEncodeDecodeWIRE() throws Exception { + checkPolymorphic(); + testEncodeDecode(factory.get(), cls, getSerialization(), Output.WIRE); + } - private void checkPolymorphic() { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - } + private void checkPolymorphic() { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/Serializer2Test.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/Serializer2Test.java index 59c27f872e..15749f5ca0 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/Serializer2Test.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/Serializer2Test.java @@ -64,104 +64,103 @@ package org.radix.serialization; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.radixdlt.DefaultSerialization; import com.radixdlt.serialization.ClassScanningSerializerIds; import com.radixdlt.serialization.DeserializeException; +import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.Serialization; import java.nio.charset.StandardCharsets; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.config.Configurator; import org.junit.BeforeClass; import org.junit.Test; -import com.radixdlt.serialization.DsonOutput.Output; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - /** - * Test that round-trips some objects through the serializer. - * Additional tests for the "serializer" field added. + * Test that round-trips some objects through the serializer. Additional tests for the "serializer" + * field added. */ public class Serializer2Test extends RadixTest { - private static Serialization serialization; - - private static DummyTestObject testObject; - - @BeforeClass - public static void beforeClass() throws DeserializeException { - // Disable this output for now, as the serialiser is quite verbose when starting. - Configurator.setLevel(LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); - - TestSetupUtils.installBouncyCastleProvider(); - - serialization = DefaultSerialization.getInstance(); - - testObject = new DummyTestObject(true); - - String jacksonJson = serialization.toJson(testObject, Output.ALL); - byte[] jacksonDson = serialization.toDson(testObject, Output.ALL); - - DummyTestObject jacksonJsonObj = serialization.fromJson(jacksonJson, DummyTestObject.class); - DummyTestObject jacksonCborObj = serialization.fromDson(jacksonDson, DummyTestObject.class); - - assertTrue(testObject.equals(jacksonJsonObj)); - assertTrue(testObject.equals(jacksonCborObj)); - } - - @Test - public void roundTripJacksonDsonTest() throws DeserializeException { - byte[] bytes = serialization.toDson(testObject, Output.ALL); - DummyTestObject newObject = serialization.fromDson(bytes, DummyTestObject.class); - assertEquals(testObject, newObject); - } - - @Test - public void roundTripJacksonJsonTest() throws DeserializeException { - String json = serialization.toJson(testObject, Output.ALL); - DummyTestObject newObject = serialization.fromJson(json, DummyTestObject.class); - assertEquals(testObject, newObject); - } - - @Test - public void checkJsonSerializerInclusion() { - String json = serialization.toJson(testObject, Output.HASH); - assertTrue(json.contains("sz")); - json = serialization.toJson(testObject, Output.WIRE); - assertTrue(json.contains("sz")); - } - - @Test - public void checkDsonSerializerInclusion() { - byte[] dson = serialization.toDson(testObject, Output.HASH); - assertTrue(contains(dson, "sz".getBytes(StandardCharsets.UTF_8))); - dson = serialization.toDson(testObject, Output.WIRE); - assertTrue(contains(dson, "sz".getBytes(StandardCharsets.UTF_8))); - } - - private static boolean contains(byte[] haystack, byte[] needle) { - if (needle.length > haystack.length) { - return false; - } - int length = needle.length; - int imax = haystack.length - length; - for (int i = 0; i <= imax; ++i) { - if (equals(haystack, i, needle, 0, length)) { - return true; - } - } - return false; - } - - private static boolean equals(byte[] a1, int offset1, byte[] a2, int offset2, int length) { - for (int i = 0; i < length; ++i) { - if (a1[offset1 + i] != a2[offset2 + i]) { - return false; - } - } - return true; - } + private static Serialization serialization; + + private static DummyTestObject testObject; + + @BeforeClass + public static void beforeClass() throws DeserializeException { + // Disable this output for now, as the serialiser is quite verbose when starting. + Configurator.setLevel( + LogManager.getLogger(ClassScanningSerializerIds.class).getName(), Level.INFO); + + TestSetupUtils.installBouncyCastleProvider(); + + serialization = DefaultSerialization.getInstance(); + + testObject = new DummyTestObject(true); + + String jacksonJson = serialization.toJson(testObject, Output.ALL); + byte[] jacksonDson = serialization.toDson(testObject, Output.ALL); + + DummyTestObject jacksonJsonObj = serialization.fromJson(jacksonJson, DummyTestObject.class); + DummyTestObject jacksonCborObj = serialization.fromDson(jacksonDson, DummyTestObject.class); + + assertTrue(testObject.equals(jacksonJsonObj)); + assertTrue(testObject.equals(jacksonCborObj)); + } + + @Test + public void roundTripJacksonDsonTest() throws DeserializeException { + byte[] bytes = serialization.toDson(testObject, Output.ALL); + DummyTestObject newObject = serialization.fromDson(bytes, DummyTestObject.class); + assertEquals(testObject, newObject); + } + + @Test + public void roundTripJacksonJsonTest() throws DeserializeException { + String json = serialization.toJson(testObject, Output.ALL); + DummyTestObject newObject = serialization.fromJson(json, DummyTestObject.class); + assertEquals(testObject, newObject); + } + + @Test + public void checkJsonSerializerInclusion() { + String json = serialization.toJson(testObject, Output.HASH); + assertTrue(json.contains("sz")); + json = serialization.toJson(testObject, Output.WIRE); + assertTrue(json.contains("sz")); + } + + @Test + public void checkDsonSerializerInclusion() { + byte[] dson = serialization.toDson(testObject, Output.HASH); + assertTrue(contains(dson, "sz".getBytes(StandardCharsets.UTF_8))); + dson = serialization.toDson(testObject, Output.WIRE); + assertTrue(contains(dson, "sz".getBytes(StandardCharsets.UTF_8))); + } + + private static boolean contains(byte[] haystack, byte[] needle) { + if (needle.length > haystack.length) { + return false; + } + int length = needle.length; + int imax = haystack.length - length; + for (int i = 0; i <= imax; ++i) { + if (equals(haystack, i, needle, 0, length)) { + return true; + } + } + return false; + } + + private static boolean equals(byte[] a1, int offset1, byte[] a2, int offset2, int length) { + for (int i = 0; i < length; ++i) { + if (a1[offset1 + i] != a2[offset2 + i]) { + return false; + } + } + return true; + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TestSetupUtils.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TestSetupUtils.java index 4d80570f7f..f29968de2a 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TestSetupUtils.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TestSetupUtils.java @@ -1,129 +1,125 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package org.radix.serialization; - -import java.security.Security; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -/** - * Some utilities to help with initialisation of other sub-systems - * that tests use. - */ -public final class TestSetupUtils { - private static final int HEXDUMP_LINESIZE = 0x10; - - private TestSetupUtils() { - throw new IllegalStateException("Can't construct"); - } - - /** - * Install the Bouncy Castle crypto provider used for various hashing - * and symmetric/asymmetric key functions. - */ - public static void installBouncyCastleProvider() { - Security.insertProviderAt(new BouncyCastleProvider(), 1); - } - - /** - * Useful method for discovering why things went wrong - outputs - * a hexdump to {@code System.out}. - * - * @param bytes bytes to dump - */ - public static void hexdump(byte[] bytes) { - for (int index = 0; index < bytes.length; index += HEXDUMP_LINESIZE) { - int thisLen = Math.min(HEXDUMP_LINESIZE, bytes.length - index); - System.out.format("%04X:", index); - int ofs = 0; - for (; ofs < thisLen; ++ofs) { - if (ofs == HEXDUMP_LINESIZE / 2) { - System.out.format("-%02X", bytes[index + ofs] & 0xFF); - } else { - System.out.format(" %02X", bytes[index + ofs] & 0xFF); - } - } - while (ofs < HEXDUMP_LINESIZE) { - System.out.print(" "); - ofs += 1; - } - System.out.print(" |"); - for (ofs = 0; ofs < thisLen; ++ofs) { - System.out.print(toPrintable(bytes[index + ofs])); - } - while (ofs < HEXDUMP_LINESIZE) { - System.out.print(' '); - ofs += 1; - } - System.out.println('|'); - } - } - - private static char toPrintable(byte b) { - if (b >= 0x20 && b < 0x7F) { - return (char)b; - } - return '.'; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package org.radix.serialization; + +import java.security.Security; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** Some utilities to help with initialisation of other sub-systems that tests use. */ +public final class TestSetupUtils { + private static final int HEXDUMP_LINESIZE = 0x10; + + private TestSetupUtils() { + throw new IllegalStateException("Can't construct"); + } + + /** + * Install the Bouncy Castle crypto provider used for various hashing and symmetric/asymmetric key + * functions. + */ + public static void installBouncyCastleProvider() { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + + /** + * Useful method for discovering why things went wrong - outputs a hexdump to {@code System.out}. + * + * @param bytes bytes to dump + */ + public static void hexdump(byte[] bytes) { + for (int index = 0; index < bytes.length; index += HEXDUMP_LINESIZE) { + int thisLen = Math.min(HEXDUMP_LINESIZE, bytes.length - index); + System.out.format("%04X:", index); + int ofs = 0; + for (; ofs < thisLen; ++ofs) { + if (ofs == HEXDUMP_LINESIZE / 2) { + System.out.format("-%02X", bytes[index + ofs] & 0xFF); + } else { + System.out.format(" %02X", bytes[index + ofs] & 0xFF); + } + } + while (ofs < HEXDUMP_LINESIZE) { + System.out.print(" "); + ofs += 1; + } + System.out.print(" |"); + for (ofs = 0; ofs < thisLen; ++ofs) { + System.out.print(toPrintable(bytes[index + ofs])); + } + while (ofs < HEXDUMP_LINESIZE) { + System.out.print(' '); + ofs += 1; + } + System.out.println('|'); + } + } + + private static char toPrintable(byte b) { + if (b >= 0x20 && b < 0x7F) { + return (char) b; + } + return '.'; + } +} diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/UnverifiedVertexSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/UnverifiedVertexSerializeTest.java index b84fcce7d0..e56b7a6fdb 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/UnverifiedVertexSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/UnverifiedVertexSerializeTest.java @@ -65,34 +65,33 @@ package org.radix.serialization; import com.radixdlt.atom.Txn; +import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.VoteData; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.UnverifiedVertex; -import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.VoteData; +import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.HashUtils; - import java.util.List; public class UnverifiedVertexSerializeTest extends SerializeObject { - public UnverifiedVertexSerializeTest() { - super(UnverifiedVertex.class, UnverifiedVertexSerializeTest::get); - } + public UnverifiedVertexSerializeTest() { + super(UnverifiedVertex.class, UnverifiedVertexSerializeTest::get); + } - private static UnverifiedVertex get() { - View view = View.of(1234567891L); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - BFTHeader header = new BFTHeader(view, HashUtils.random256(), ledgerHeader); - BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); - VoteData voteData = new VoteData(header, parent, null); + private static UnverifiedVertex get() { + View view = View.of(1234567891L); + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + BFTHeader header = new BFTHeader(view, HashUtils.random256(), ledgerHeader); + BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); + VoteData voteData = new VoteData(header, parent, null); - QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - var txn = Txn.create(new byte[]{0, 1, 2, 3}); + var txn = Txn.create(new byte[] {0, 1, 2, 3}); - return UnverifiedVertex.create(qc, view, List.of(txn), BFTNode.random()); - } + return UnverifiedVertex.create(qc, view, List.of(txn), BFTNode.random()); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteDataSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteDataSerializeTest.java index 5e17888ae2..3f867a4864 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteDataSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteDataSerializeTest.java @@ -64,24 +64,23 @@ package org.radix.serialization; +import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.VoteData; import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.BFTHeader; import com.radixdlt.crypto.HashUtils; -import com.radixdlt.ledger.AccumulatorState; public class VoteDataSerializeTest extends SerializeObject { - public VoteDataSerializeTest() { - super(VoteData.class, VoteDataSerializeTest::get); - } + public VoteDataSerializeTest() { + super(VoteData.class, VoteDataSerializeTest::get); + } - private static VoteData get() { - View view = View.of(1234567890L); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - BFTHeader committed = new BFTHeader(view, HashUtils.random256(), ledgerHeader); - BFTHeader parent = new BFTHeader(view.next(), HashUtils.random256(), ledgerHeader); - BFTHeader proposed = new BFTHeader(view.next().next(), HashUtils.random256(), ledgerHeader); - return new VoteData(proposed, parent, committed); - } + private static VoteData get() { + View view = View.of(1234567890L); + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + BFTHeader committed = new BFTHeader(view, HashUtils.random256(), ledgerHeader); + BFTHeader parent = new BFTHeader(view.next(), HashUtils.random256(), ledgerHeader); + BFTHeader proposed = new BFTHeader(view.next().next(), HashUtils.random256(), ledgerHeader); + return new VoteData(proposed, parent, committed); + } } diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteSerializeTest.java index 8ede6f51fc..a7099d9380 100644 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteSerializeTest.java +++ b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/VoteSerializeTest.java @@ -65,37 +65,37 @@ package org.radix.serialization; import com.google.common.hash.HashCode; +import com.radixdlt.consensus.BFTHeader; +import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.TimestampedECDSASignatures; +import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.VoteData; import com.radixdlt.consensus.bft.BFTNode; +import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.BFTHeader; -import com.radixdlt.consensus.HighQC; -import com.radixdlt.consensus.Vote; import com.radixdlt.crypto.HashUtils; - import java.util.Optional; public class VoteSerializeTest extends SerializeObject { - public VoteSerializeTest() { - super(Vote.class, VoteSerializeTest::get); - } + public VoteSerializeTest() { + super(Vote.class, VoteSerializeTest::get); + } - private static Vote get() { - View view = View.of(1234567891L); - HashCode id = HashUtils.random256(); + private static Vote get() { + View view = View.of(1234567891L); + HashCode id = HashUtils.random256(); - LedgerHeader ledgerHeader = LedgerHeader.mocked(); - BFTHeader header = new BFTHeader(view, id, ledgerHeader); - BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); - VoteData voteData = new VoteData(header, parent, null); - BFTNode author = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); - QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); - HighQC highQC = HighQC.from(qc, qc, Optional.empty()); - return new Vote(author, voteData, 123456L, ECDSASignature.zeroSignature(), highQC, Optional.empty()); - } + LedgerHeader ledgerHeader = LedgerHeader.mocked(); + BFTHeader header = new BFTHeader(view, id, ledgerHeader); + BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); + VoteData voteData = new VoteData(header, parent, null); + BFTNode author = BFTNode.create(ECKeyPair.generateNew().getPublicKey()); + QuorumCertificate qc = new QuorumCertificate(voteData, new TimestampedECDSASignatures()); + HighQC highQC = HighQC.from(qc, qc, Optional.empty()); + return new Vote( + author, voteData, 123456L, ECDSASignature.zeroSignature(), highQC, Optional.empty()); + } } diff --git a/radixdlt-engine/CHANGELOG.md b/radixdlt-engine/CHANGELOG.md index 94b5717c70..a4688a81b9 100644 --- a/radixdlt-engine/CHANGELOG.md +++ b/radixdlt-engine/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog All notable changes to this project will be documented in this file. - + This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - + ## [Unreleased](#) ## [1.0.0-beta.2] - 2019-08-28 diff --git a/radixdlt-engine/README.md b/radixdlt-engine/README.md index f0b8491c1d..97af905d89 100644 --- a/radixdlt-engine/README.md +++ b/radixdlt-engine/README.md @@ -11,10 +11,10 @@ Contributions are welcome, we simply ask to: * Submit a pull request for review When contributing to this repository, we recommend to discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. +email, or any other method with the owners of this repository before making a change. Please follow our [Code of Conduct](../CODE_OF_CONDUCT.md) in all your interactions with the project. ## License -The `radixdlt-engine` code is released under the [Radix License](../LICENSE). \ No newline at end of file +The `radixdlt-engine` code is released under the [Radix License](../LICENSE). diff --git a/radixdlt-engine/src/main/java/com/radixdlt/DefaultSerialization.java b/radixdlt-engine/src/main/java/com/radixdlt/DefaultSerialization.java index 516235a544..d5c4b4cb66 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/DefaultSerialization.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/DefaultSerialization.java @@ -70,22 +70,24 @@ public final class DefaultSerialization { - private DefaultSerialization() { - throw new IllegalStateException("Can't construct"); - } + private DefaultSerialization() { + throw new IllegalStateException("Can't construct"); + } - private static class LazyHolder { - static final Serialization INSTANCE = Serialization.create( - ClasspathScanningSerializerIds.create(), - ClasspathScanningSerializationPolicy.create() - ); - } + private static class LazyHolder { + static final Serialization INSTANCE = + Serialization.create( + ClasspathScanningSerializerIds.create(), ClasspathScanningSerializationPolicy.create()); + } - /** - * A singleton created using {@link ClasspathScanningSerializerIds} and {@link ClasspathScanningSerializationPolicy} - * @return A singleton created using {@link ClasspathScanningSerializerIds} and {@link ClasspathScanningSerializationPolicy}, - */ - public static Serialization getInstance() { - return LazyHolder.INSTANCE; - } + /** + * A singleton created using {@link ClasspathScanningSerializerIds} and {@link + * ClasspathScanningSerializationPolicy} + * + * @return A singleton created using {@link ClasspathScanningSerializerIds} and {@link + * ClasspathScanningSerializationPolicy}, + */ + public static Serialization getInstance() { + return LazyHolder.INSTANCE; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/accounting/REResourceAccounting.java b/radixdlt-engine/src/main/java/com/radixdlt/accounting/REResourceAccounting.java index 6edda1e63c..1a31952b17 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/accounting/REResourceAccounting.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/accounting/REResourceAccounting.java @@ -70,7 +70,6 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.math.BigInteger; import java.util.HashMap; import java.util.Map; @@ -78,69 +77,70 @@ import java.util.stream.Stream; public class REResourceAccounting { - private final Map bucketAccounting; - private final Map stakeOwnershipAccounting; - private final Map resourceAccounting; + private final Map bucketAccounting; + private final Map stakeOwnershipAccounting; + private final Map resourceAccounting; - private REResourceAccounting( - Map bucketAccounting, - Map stakeOwnershipAccounting, - Map resourceAccounting - ) { - this.bucketAccounting = bucketAccounting; - this.stakeOwnershipAccounting = stakeOwnershipAccounting; - this.resourceAccounting = resourceAccounting; - } + private REResourceAccounting( + Map bucketAccounting, + Map stakeOwnershipAccounting, + Map resourceAccounting) { + this.bucketAccounting = bucketAccounting; + this.stakeOwnershipAccounting = stakeOwnershipAccounting; + this.resourceAccounting = resourceAccounting; + } - public Map bucketAccounting() { - return bucketAccounting; - } + public Map bucketAccounting() { + return bucketAccounting; + } - public Map stakeOwnershipAccounting() { - return stakeOwnershipAccounting; - } + public Map stakeOwnershipAccounting() { + return stakeOwnershipAccounting; + } - public Map resourceAccounting() { - return resourceAccounting; - } + public Map resourceAccounting() { + return resourceAccounting; + } - private static BigInteger sumIfZeroThenNull(BigInteger a, BigInteger b) { - var sum = a.add(b); - return sum.equals(BigInteger.ZERO) ? null : sum; - } + private static BigInteger sumIfZeroThenNull(BigInteger a, BigInteger b) { + var sum = a.add(b); + return sum.equals(BigInteger.ZERO) ? null : sum; + } - public static REResourceAccounting compute(Stream updates) { - Map bucketAccounting = new HashMap<>(); - updates.forEach(update -> { - var substate = update.getParsed(); - if (substate instanceof ResourceInBucket resourceInBucket) { - bucketAccounting.merge( - resourceInBucket.bucket(), - new BigInteger(update.isBootUp() ? 1 : -1, resourceInBucket.getAmount().toByteArray(), 0, UInt256.BYTES), - REResourceAccounting::sumIfZeroThenNull - ); - } - }); + public static REResourceAccounting compute(Stream updates) { + Map bucketAccounting = new HashMap<>(); + updates.forEach( + update -> { + var substate = update.getParsed(); + if (substate instanceof ResourceInBucket resourceInBucket) { + bucketAccounting.merge( + resourceInBucket.bucket(), + new BigInteger( + update.isBootUp() ? 1 : -1, + resourceInBucket.getAmount().toByteArray(), + 0, + UInt256.BYTES), + REResourceAccounting::sumIfZeroThenNull); + } + }); - var stakeOwnershipAccounting = bucketAccounting.entrySet().stream() - .filter(e -> e.getKey().resourceAddr() == null) - .collect(Collectors.toMap( - e -> e.getKey().getValidatorKey(), - Map.Entry::getValue, - REResourceAccounting::sumIfZeroThenNull - )); - var resourceAccounting = bucketAccounting.entrySet().stream() - .filter(e -> e.getKey().resourceAddr() != null) - .collect(Collectors.toMap( - e -> e.getKey().resourceAddr(), - Map.Entry::getValue, - REResourceAccounting::sumIfZeroThenNull - )); + var stakeOwnershipAccounting = + bucketAccounting.entrySet().stream() + .filter(e -> e.getKey().resourceAddr() == null) + .collect( + Collectors.toMap( + e -> e.getKey().getValidatorKey(), + Map.Entry::getValue, + REResourceAccounting::sumIfZeroThenNull)); + var resourceAccounting = + bucketAccounting.entrySet().stream() + .filter(e -> e.getKey().resourceAddr() != null) + .collect( + Collectors.toMap( + e -> e.getKey().resourceAddr(), + Map.Entry::getValue, + REResourceAccounting::sumIfZeroThenNull)); - return new REResourceAccounting( - bucketAccounting, - stakeOwnershipAccounting, - resourceAccounting - ); - } + return new REResourceAccounting(bucketAccounting, stakeOwnershipAccounting, resourceAccounting); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/misc/SplitTokenConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/misc/SplitTokenConstructor.java index 21f9053948..361be32f22 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/misc/SplitTokenConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/misc/SplitTokenConstructor.java @@ -64,28 +64,29 @@ package com.radixdlt.application.misc; +import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.SplitToken; -import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.utils.UInt256; public final class SplitTokenConstructor implements ActionConstructor { - @Override - public void construct(SplitToken action, TxBuilder txBuilder) throws TxBuilderException { - var userAccount = action.userAcct(); - var tokens = txBuilder.downSubstate( - TokensInAccount.class, - p -> p.getResourceAddr().equals(action.rri()) - && p.getHoldingAddr().equals(userAccount) - && p.getAmount().compareTo(action.minSize()) > 0 - ); + @Override + public void construct(SplitToken action, TxBuilder txBuilder) throws TxBuilderException { + var userAccount = action.userAcct(); + var tokens = + txBuilder.downSubstate( + TokensInAccount.class, + p -> + p.getResourceAddr().equals(action.rri()) + && p.getHoldingAddr().equals(userAccount) + && p.getAmount().compareTo(action.minSize()) > 0); - var amt1 = tokens.getAmount().divide(UInt256.TWO); - var amt2 = tokens.getAmount().subtract(amt1); - txBuilder.up(new TokensInAccount(userAccount, action.rri(), amt1)); - txBuilder.up(new TokensInAccount(userAccount, action.rri(), amt2)); - txBuilder.end(); - } + var amt1 = tokens.getAmount().divide(UInt256.TWO); + var amt2 = tokens.getAmount().subtract(amt1); + txBuilder.up(new TokensInAccount(userAccount, action.rri(), amt1)); + txBuilder.up(new TokensInAccount(userAccount, action.rri(), amt2)); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/FeeTable.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/FeeTable.java index 7247c95ff7..3bf203224a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/FeeTable.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/FeeTable.java @@ -67,34 +67,34 @@ import com.radixdlt.application.tokens.Amount; import com.radixdlt.constraintmachine.Particle; import com.radixdlt.utils.UInt256; - import java.util.HashMap; import java.util.Map; public final class FeeTable { - private final Amount perByteFee; - private final Map, UInt256> perUpSubstateFee; + private final Amount perByteFee; + private final Map, UInt256> perUpSubstateFee; - private FeeTable(Amount perByteFee, Map, UInt256> perUpSubstateFee) { - this.perByteFee = perByteFee; - this.perUpSubstateFee = perUpSubstateFee; - } + private FeeTable(Amount perByteFee, Map, UInt256> perUpSubstateFee) { + this.perByteFee = perByteFee; + this.perUpSubstateFee = perUpSubstateFee; + } - public static FeeTable create(Amount perByteFee, Map, Amount> perUpSubstateFee) { - var map = new HashMap, UInt256>(); - perUpSubstateFee.forEach((k, v) -> map.put(k, v.toSubunits())); - return new FeeTable(perByteFee, map); - } + public static FeeTable create( + Amount perByteFee, Map, Amount> perUpSubstateFee) { + var map = new HashMap, UInt256>(); + perUpSubstateFee.forEach((k, v) -> map.put(k, v.toSubunits())); + return new FeeTable(perByteFee, map); + } - public static FeeTable noFees() { - return new FeeTable(Amount.zero(), Map.of()); - } + public static FeeTable noFees() { + return new FeeTable(Amount.zero(), Map.of()); + } - public UInt256 getPerByteFee() { - return perByteFee.toSubunits(); - } + public UInt256 getPerByteFee() { + return perByteFee.toSubunits(); + } - public Map, UInt256> getPerUpSubstateFee() { - return perUpSubstateFee; - } -} \ No newline at end of file + public Map, UInt256> getPerUpSubstateFee() { + return perUpSubstateFee; + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/NextValidatorSetEvent.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/NextValidatorSetEvent.java index 13868ca293..420719033a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/NextValidatorSetEvent.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/NextValidatorSetEvent.java @@ -66,21 +66,20 @@ import com.radixdlt.application.system.state.ValidatorStakeData; import com.radixdlt.constraintmachine.REEvent; - import java.util.List; public class NextValidatorSetEvent implements REEvent { - private final List nextValidators; + private final List nextValidators; - private NextValidatorSetEvent(List nextValidators) { - this.nextValidators = nextValidators; - } + private NextValidatorSetEvent(List nextValidators) { + this.nextValidators = nextValidators; + } - public static NextValidatorSetEvent create(List nextValidators) { - return new NextValidatorSetEvent(List.copyOf(nextValidators)); - } + public static NextValidatorSetEvent create(List nextValidators) { + return new NextValidatorSetEvent(List.copyOf(nextValidators)); + } - public List nextValidators() { - return nextValidators; - } + public List nextValidators() { + return nextValidators; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorBFTDataEvent.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorBFTDataEvent.java index 3ee97ac194..9c8d98c216 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorBFTDataEvent.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorBFTDataEvent.java @@ -68,29 +68,31 @@ import com.radixdlt.crypto.ECPublicKey; public final class ValidatorBFTDataEvent implements REEvent { - private final ECPublicKey validatorKey; - private final long completedProposals; - private final long missedProposals; + private final ECPublicKey validatorKey; + private final long completedProposals; + private final long missedProposals; - private ValidatorBFTDataEvent(ECPublicKey validatorKey, long completedProposals, long missedProposals) { - this.validatorKey = validatorKey; - this.completedProposals = completedProposals; - this.missedProposals = missedProposals; - } + private ValidatorBFTDataEvent( + ECPublicKey validatorKey, long completedProposals, long missedProposals) { + this.validatorKey = validatorKey; + this.completedProposals = completedProposals; + this.missedProposals = missedProposals; + } - public static ValidatorBFTDataEvent create(ECPublicKey validatorKey, long completedProposals, long missedProposals) { - return new ValidatorBFTDataEvent(validatorKey, completedProposals, missedProposals); - } + public static ValidatorBFTDataEvent create( + ECPublicKey validatorKey, long completedProposals, long missedProposals) { + return new ValidatorBFTDataEvent(validatorKey, completedProposals, missedProposals); + } - public ECPublicKey getValidatorKey() { - return validatorKey; - } + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public long getCompletedProposals() { - return completedProposals; - } + public long getCompletedProposals() { + return completedProposals; + } - public long getMissedProposals() { - return missedProposals; - } + public long getMissedProposals() { + return missedProposals; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorMissedProposalsEvent.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorMissedProposalsEvent.java index 621693cebc..683606ccfc 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorMissedProposalsEvent.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/ValidatorMissedProposalsEvent.java @@ -68,23 +68,24 @@ import com.radixdlt.crypto.ECPublicKey; public final class ValidatorMissedProposalsEvent implements REEvent { - private final ECPublicKey validatorKey; - private final long missedProposals; + private final ECPublicKey validatorKey; + private final long missedProposals; - private ValidatorMissedProposalsEvent(ECPublicKey validatorKey, long missedProposals) { - this.validatorKey = validatorKey; - this.missedProposals = missedProposals; - } + private ValidatorMissedProposalsEvent(ECPublicKey validatorKey, long missedProposals) { + this.validatorKey = validatorKey; + this.missedProposals = missedProposals; + } - public static ValidatorMissedProposalsEvent create(ECPublicKey validatorKey, long missedProposals) { - return new ValidatorMissedProposalsEvent(validatorKey, missedProposals); - } + public static ValidatorMissedProposalsEvent create( + ECPublicKey validatorKey, long missedProposals) { + return new ValidatorMissedProposalsEvent(validatorKey, missedProposals); + } - public ECPublicKey getValidatorKey() { - return validatorKey; - } + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public long getMissedProposals() { - return missedProposals; - } + public long getMissedProposals() { + return missedProposals; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/CreateSystemConstructorV2.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/CreateSystemConstructorV2.java index aff3454371..a6f40f9970 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/CreateSystemConstructorV2.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/CreateSystemConstructorV2.java @@ -65,35 +65,36 @@ package com.radixdlt.application.system.construction; import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.application.system.state.EpochData; +import com.radixdlt.application.system.state.RoundData; import com.radixdlt.application.system.state.VirtualParent; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.CreateSystem; -import com.radixdlt.application.system.state.EpochData; -import com.radixdlt.application.system.state.RoundData; import com.radixdlt.identifiers.REAddr; - import java.nio.charset.StandardCharsets; public class CreateSystemConstructorV2 implements ActionConstructor { - @Override - public void construct(CreateSystem action, TxBuilder builder) throws TxBuilderException { - builder.up(new VirtualParent(new byte[] {SubstateTypeId.UNCLAIMED_READDR.id()})); - builder.end(); + @Override + public void construct(CreateSystem action, TxBuilder builder) throws TxBuilderException { + builder.up(new VirtualParent(new byte[] {SubstateTypeId.UNCLAIMED_READDR.id()})); + builder.end(); - builder.toLowLevelBuilder().syscall(Syscall.READDR_CLAIM, "sys".getBytes(StandardCharsets.UTF_8)); - builder.downREAddr(REAddr.ofSystem()); - builder.up(new EpochData(0)); - builder.up(new RoundData(0, action.getTimestamp())); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_META_DATA.id()})); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_STAKE_DATA.id()})); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG.id()})); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id()})); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_RAKE_COPY.id()})); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_OWNER_COPY.id()})); - builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_SYSTEM_META_DATA.id()})); - builder.end(); - } + builder + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, "sys".getBytes(StandardCharsets.UTF_8)); + builder.downREAddr(REAddr.ofSystem()); + builder.up(new EpochData(0)); + builder.up(new RoundData(0, action.getTimestamp())); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_META_DATA.id()})); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_STAKE_DATA.id()})); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG.id()})); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id()})); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_RAKE_COPY.id()})); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_OWNER_COPY.id()})); + builder.up(new VirtualParent(new byte[] {SubstateTypeId.VALIDATOR_SYSTEM_META_DATA.id()})); + builder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteConstructor.java index e343a3ab22..614abb7794 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteConstructor.java @@ -64,88 +64,89 @@ package com.radixdlt.application.system.construction; +import com.radixdlt.application.system.FeeTable; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.FeeReserveComplete; import com.radixdlt.crypto.ECPublicKey; -import com.radixdlt.application.system.FeeTable; import com.radixdlt.utils.UInt256; - import java.util.Objects; import java.util.Optional; public class FeeReserveCompleteConstructor implements ActionConstructor { - private final FeeTable feeTable; - public FeeReserveCompleteConstructor(FeeTable feeTable) { - this.feeTable = feeTable; - } + private final FeeTable feeTable; - @Override - public void construct(FeeReserveComplete action, TxBuilder builder) throws TxBuilderException { - var feeReserve = Optional.ofNullable(builder.getFeeReserve()).orElse(UInt256.ZERO); - int curSize = builder.toLowLevelBuilder().size() + signatureInstructionSize(); - var perByteFee = feeTable.getPerByteFee(); - var upSubstateFeeTable = feeTable.getPerUpSubstateFee(); - var substateCost = builder.toLowLevelBuilder().localUpSubstate().stream() - .map(s -> s.getParticle().getClass()) - .map(upSubstateFeeTable::get) - .filter(Objects::nonNull) - .reduce(UInt256::add).orElse(UInt256.ZERO); + public FeeReserveCompleteConstructor(FeeTable feeTable) { + this.feeTable = feeTable; + } + @Override + public void construct(FeeReserveComplete action, TxBuilder builder) throws TxBuilderException { + var feeReserve = Optional.ofNullable(builder.getFeeReserve()).orElse(UInt256.ZERO); + int curSize = builder.toLowLevelBuilder().size() + signatureInstructionSize(); + var perByteFee = feeTable.getPerByteFee(); + var upSubstateFeeTable = feeTable.getPerUpSubstateFee(); + var substateCost = + builder.toLowLevelBuilder().localUpSubstate().stream() + .map(s -> s.getParticle().getClass()) + .map(upSubstateFeeTable::get) + .filter(Objects::nonNull) + .reduce(UInt256::add) + .orElse(UInt256.ZERO); - var txnBytesCost = perByteFee.multiply(UInt256.from(curSize)); - var expectedFee1 = txnBytesCost.add(substateCost); - if (feeReserve.compareTo(expectedFee1) < 0) { - throw new FeeReserveCompleteException(feeReserve, expectedFee1); - } + var txnBytesCost = perByteFee.multiply(UInt256.from(curSize)); + var expectedFee1 = txnBytesCost.add(substateCost); + if (feeReserve.compareTo(expectedFee1) < 0) { + throw new FeeReserveCompleteException(feeReserve, expectedFee1); + } - if (feeReserve.compareTo(expectedFee1) == 0) { - // Fees all accounted for, no need to add END - return; - } + if (feeReserve.compareTo(expectedFee1) == 0) { + // Fees all accounted for, no need to add END + return; + } - // TODO: Figure out cleaner way to do this which isn't so fragile - var expectedSizeWithNoReturn = curSize + syscallInstructionSize() + endInstructionSize(); - var expectedFee = perByteFee.multiply(UInt256.from(expectedSizeWithNoReturn)).add(substateCost); - if (!expectedFee.equals(feeReserve)) { - var expectedSizeWithReturn = expectedSizeWithNoReturn + returnedSubstateInstructionSize(); - expectedFee = perByteFee.multiply(UInt256.from(expectedSizeWithReturn)).add(substateCost); - } - if (feeReserve.compareTo(expectedFee) < 0) { - throw new FeeReserveCompleteException(feeReserve, expectedFee); - } - var leftover = feeReserve.subtract(expectedFee); - builder.takeFeeReserve(action.to(), leftover); - builder.end(); - } + // TODO: Figure out cleaner way to do this which isn't so fragile + var expectedSizeWithNoReturn = curSize + syscallInstructionSize() + endInstructionSize(); + var expectedFee = perByteFee.multiply(UInt256.from(expectedSizeWithNoReturn)).add(substateCost); + if (!expectedFee.equals(feeReserve)) { + var expectedSizeWithReturn = expectedSizeWithNoReturn + returnedSubstateInstructionSize(); + expectedFee = perByteFee.multiply(UInt256.from(expectedSizeWithReturn)).add(substateCost); + } + if (feeReserve.compareTo(expectedFee) < 0) { + throw new FeeReserveCompleteException(feeReserve, expectedFee); + } + var leftover = feeReserve.subtract(expectedFee); + builder.takeFeeReserve(action.to(), leftover); + builder.end(); + } - private static int endInstructionSize() { - return 1; - } + private static int endInstructionSize() { + return 1; + } - private static int signatureInstructionSize() { - return 1 + 32 + 32 + 1; - } + private static int signatureInstructionSize() { + return 1 + 32 + 32 + 1; + } - private static int syscallInstructionSize() { - var syscallSize = 0; - syscallSize++; // REInstruction - syscallSize += 2; // size - syscallSize++; // syscall id - syscallSize += UInt256.BYTES; - return syscallSize; - } + private static int syscallInstructionSize() { + var syscallSize = 0; + syscallSize++; // REInstruction + syscallSize += 2; // size + syscallSize++; // syscall id + syscallSize += UInt256.BYTES; + return syscallSize; + } - private static int returnedSubstateInstructionSize() { - var returnSubstateSize = 0; - returnSubstateSize++; // REInstruction - returnSubstateSize += 2; // Substate size - returnSubstateSize++; // Substate typeId - returnSubstateSize++; // Reserved - returnSubstateSize += ECPublicKey.COMPRESSED_BYTES + 1; // PubKey addr - returnSubstateSize++; // Native token addr - returnSubstateSize += UInt256.BYTES; // amount - return returnSubstateSize; - } + private static int returnedSubstateInstructionSize() { + var returnSubstateSize = 0; + returnSubstateSize++; // REInstruction + returnSubstateSize += 2; // Substate size + returnSubstateSize++; // Substate typeId + returnSubstateSize++; // Reserved + returnSubstateSize += ECPublicKey.COMPRESSED_BYTES + 1; // PubKey addr + returnSubstateSize++; // Native token addr + returnSubstateSize += UInt256.BYTES; // amount + return returnSubstateSize; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteException.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteException.java index b442169cdb..25912e8ea5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveCompleteException.java @@ -68,14 +68,14 @@ import com.radixdlt.utils.UInt256; public class FeeReserveCompleteException extends TxBuilderException { - private final UInt256 expectedFee; + private final UInt256 expectedFee; - public FeeReserveCompleteException(UInt256 feePaid, UInt256 expectedFee) { - super("Fee paid " + feePaid + " is not enough to cover fees " + expectedFee); - this.expectedFee = expectedFee; - } + public FeeReserveCompleteException(UInt256 feePaid, UInt256 expectedFee) { + super("Fee paid " + feePaid + " is not enough to cover fees " + expectedFee); + this.expectedFee = expectedFee; + } - public UInt256 getExpectedFee() { - return expectedFee; - } + public UInt256 getExpectedFee() { + return expectedFee; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReservePutConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReservePutConstructor.java index 3a07687552..9983e90047 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReservePutConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReservePutConstructor.java @@ -73,19 +73,18 @@ import com.radixdlt.identifiers.REAddr; public class FeeReservePutConstructor implements ActionConstructor { - @Override - public void construct(FeeReservePut action, TxBuilder txBuilder) throws TxBuilderException { - if (action.amount().isZero()) { - return; - } - txBuilder.putFeeReserve( - action.from(), - action.amount(), - available -> { - var from = AccountBucket.from(REAddr.ofNativeToken(), action.from()); - return new NotEnoughResourcesException(from, action.amount(), available); - } - ); - txBuilder.end(); - } + @Override + public void construct(FeeReservePut action, TxBuilder txBuilder) throws TxBuilderException { + if (action.amount().isZero()) { + return; + } + txBuilder.putFeeReserve( + action.from(), + action.amount(), + available -> { + var from = AccountBucket.from(REAddr.ofNativeToken(), action.from()); + return new NotEnoughResourcesException(from, action.amount(), available); + }); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveTakeConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveTakeConstructor.java index 05e51345e1..fbfd0fc791 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveTakeConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/FeeReserveTakeConstructor.java @@ -70,9 +70,9 @@ import com.radixdlt.atom.actions.FeeReserveTake; public class FeeReserveTakeConstructor implements ActionConstructor { - @Override - public void construct(FeeReserveTake action, TxBuilder builder) throws TxBuilderException { - builder.takeFeeReserve(action.to(), action.amount()); - builder.end(); - } + @Override + public void construct(FeeReserveTake action, TxBuilder builder) throws TxBuilderException { + builder.takeFeeReserve(action.to(), action.amount()); + builder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/InvalidRoundException.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/InvalidRoundException.java index f8b8174ca3..fc366ad0d3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/InvalidRoundException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/InvalidRoundException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,7 +67,7 @@ import com.radixdlt.atom.TxBuilderException; public class InvalidRoundException extends TxBuilderException { - public InvalidRoundException(long currentView, long view) { - super("Next view: " + view + " isn't higher than current view: " + currentView); - } + public InvalidRoundException(long currentView, long view) { + super("Next view: " + view + " isn't higher than current view: " + currentView); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextEpochConstructorV3.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextEpochConstructorV3.java index f6628b8c32..9f0e97047e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextEpochConstructorV3.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextEpochConstructorV3.java @@ -64,15 +64,11 @@ package com.radixdlt.application.system.construction; +import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; + import com.google.common.collect.Streams; import com.google.common.primitives.UnsignedBytes; import com.radixdlt.application.system.scrypt.ValidatorScratchPad; -import com.radixdlt.atom.ActionConstructor; -import com.radixdlt.atom.SubstateTypeId; -import com.radixdlt.atom.TxBuilder; -import com.radixdlt.atom.TxBuilderException; -import com.radixdlt.atom.actions.NextEpoch; - import com.radixdlt.application.system.state.EpochData; import com.radixdlt.application.system.state.RoundData; import com.radixdlt.application.system.state.ValidatorBFTData; @@ -81,15 +77,19 @@ import com.radixdlt.application.tokens.state.PreparedStake; import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.application.validators.state.ValidatorData; -import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.application.validators.state.ValidatorFeeCopy; +import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; +import com.radixdlt.atom.ActionConstructor; +import com.radixdlt.atom.SubstateTypeId; +import com.radixdlt.atom.TxBuilder; +import com.radixdlt.atom.TxBuilderException; +import com.radixdlt.atom.actions.NextEpoch; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.KeyComparator; import com.radixdlt.utils.UInt256; - import java.nio.ByteBuffer; import java.util.Comparator; import java.util.Iterator; @@ -98,235 +98,259 @@ import java.util.function.BiConsumer; import java.util.function.Function; -import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; - public final class NextEpochConstructorV3 implements ActionConstructor { - private final UInt256 rewardsPerProposal; - private final long unstakingEpochDelay; - private final long minimumCompletedProposalsPercentage; - private final int maxValidators; + private final UInt256 rewardsPerProposal; + private final long unstakingEpochDelay; + private final long minimumCompletedProposalsPercentage; + private final int maxValidators; - public NextEpochConstructorV3( - UInt256 rewardsPerProposal, - long minimumCompletedProposalsPercentage, - long unstakingEpochDelay, - int maxValidators - ) { - this.rewardsPerProposal = rewardsPerProposal; - this.unstakingEpochDelay = unstakingEpochDelay; - this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; - this.maxValidators = maxValidators; - } + public NextEpochConstructorV3( + UInt256 rewardsPerProposal, + long minimumCompletedProposalsPercentage, + long unstakingEpochDelay, + int maxValidators) { + this.rewardsPerProposal = rewardsPerProposal; + this.unstakingEpochDelay = unstakingEpochDelay; + this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; + this.maxValidators = maxValidators; + } - private static ValidatorScratchPad loadValidatorStakeData( - TxBuilder txBuilder, - ECPublicKey k, - TreeMap validatorsToUpdate - ) throws TxBuilderException { - var scratchPad = validatorsToUpdate.get(k); - if (scratchPad == null) { - var validatorData = txBuilder.down(ValidatorStakeData.class, k); - scratchPad = new ValidatorScratchPad(validatorData); - validatorsToUpdate.put(k, scratchPad); - } + private static ValidatorScratchPad loadValidatorStakeData( + TxBuilder txBuilder, + ECPublicKey k, + TreeMap validatorsToUpdate) + throws TxBuilderException { + var scratchPad = validatorsToUpdate.get(k); + if (scratchPad == null) { + var validatorData = txBuilder.down(ValidatorStakeData.class, k); + scratchPad = new ValidatorScratchPad(validatorData); + validatorsToUpdate.put(k, scratchPad); + } - return scratchPad; - } + return scratchPad; + } - private static void prepare( - TxBuilder txBuilder, - TreeMap validatorsToUpdate, - Class preparedClass, - byte typeId, - long epoch, - BiConsumer updater, - Function copy - ) throws TxBuilderException { - var preparing = new TreeMap(KeyComparator.instance()); - var buf = ByteBuffer.allocate(3 + Long.BYTES); - buf.put(typeId); - buf.put((byte) 0); // Reserved byte - buf.put((byte) 1); // Optional flag - buf.putLong(epoch); - var index = SubstateIndex.create(buf.array(), preparedClass); - txBuilder.shutdownAll(index, (Iterator i) -> { - i.forEachRemaining(update -> preparing.put(update.getValidatorKey(), update)); - return preparing; - }); - for (var e : preparing.entrySet()) { - var k = e.getKey(); - var update = e.getValue(); - var curValidator = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); - updater.accept(curValidator, update); - txBuilder.up(copy.apply(update)); - } - } + private static void prepare( + TxBuilder txBuilder, + TreeMap validatorsToUpdate, + Class preparedClass, + byte typeId, + long epoch, + BiConsumer updater, + Function copy) + throws TxBuilderException { + var preparing = new TreeMap(KeyComparator.instance()); + var buf = ByteBuffer.allocate(3 + Long.BYTES); + buf.put(typeId); + buf.put((byte) 0); // Reserved byte + buf.put((byte) 1); // Optional flag + buf.putLong(epoch); + var index = SubstateIndex.create(buf.array(), preparedClass); + txBuilder.shutdownAll( + index, + (Iterator i) -> { + i.forEachRemaining(update -> preparing.put(update.getValidatorKey(), update)); + return preparing; + }); + for (var e : preparing.entrySet()) { + var k = e.getKey(); + var update = e.getValue(); + var curValidator = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); + updater.accept(curValidator, update); + txBuilder.up(copy.apply(update)); + } + } - @Override - public void construct(NextEpoch action, TxBuilder txBuilder) throws TxBuilderException { - var closedRound = txBuilder.downSystem(RoundData.class); - var closingEpoch = txBuilder.downSystem(EpochData.class); + @Override + public void construct(NextEpoch action, TxBuilder txBuilder) throws TxBuilderException { + var closedRound = txBuilder.downSystem(RoundData.class); + var closingEpoch = txBuilder.downSystem(EpochData.class); - var unlockedStateIndexBuf = ByteBuffer.allocate(2 + Long.BYTES); - unlockedStateIndexBuf.put(SubstateTypeId.EXITING_STAKE.id()); - unlockedStateIndexBuf.put((byte) 0); - unlockedStateIndexBuf.putLong(closingEpoch.getEpoch() + 1); - var unlockedStakeIndex = SubstateIndex.create(unlockedStateIndexBuf.array(), ExitingStake.class); - var exitting = txBuilder.shutdownAll(unlockedStakeIndex, (Iterator i) -> { - final TreeSet exit = new TreeSet<>( - Comparator.comparing(ExitingStake::dataKey, UnsignedBytes.lexicographicalComparator()) - ); - i.forEachRemaining(exit::add); - return exit; - }); - for (var e : exitting) { - txBuilder.up(e.unlock()); - } + var unlockedStateIndexBuf = ByteBuffer.allocate(2 + Long.BYTES); + unlockedStateIndexBuf.put(SubstateTypeId.EXITING_STAKE.id()); + unlockedStateIndexBuf.put((byte) 0); + unlockedStateIndexBuf.putLong(closingEpoch.getEpoch() + 1); + var unlockedStakeIndex = + SubstateIndex.create(unlockedStateIndexBuf.array(), ExitingStake.class); + var exitting = + txBuilder.shutdownAll( + unlockedStakeIndex, + (Iterator i) -> { + final TreeSet exit = + new TreeSet<>( + Comparator.comparing( + ExitingStake::dataKey, UnsignedBytes.lexicographicalComparator())); + i.forEachRemaining(exit::add); + return exit; + }); + for (var e : exitting) { + txBuilder.up(e.unlock()); + } - var validatorsToUpdate = new TreeMap(KeyComparator.instance()); - var validatorBFTData = txBuilder.shutdownAll(ValidatorBFTData.class, i -> { - final TreeMap bftData = new TreeMap<>(KeyComparator.instance()); - i.forEachRemaining(e -> bftData.put(e.getValidatorKey(), e)); - return bftData; - }); - var preparingStake = new TreeMap>(KeyComparator.instance()); - for (var e : validatorBFTData.entrySet()) { - var k = e.getKey(); - var bftData = e.getValue(); - if (bftData.proposalsCompleted() + bftData.proposalsMissed() == 0) { - continue; - } - var percentageCompleted = bftData.proposalsCompleted() * 10000 - / (bftData.proposalsCompleted() + bftData.proposalsMissed()); + var validatorsToUpdate = + new TreeMap(KeyComparator.instance()); + var validatorBFTData = + txBuilder.shutdownAll( + ValidatorBFTData.class, + i -> { + final TreeMap bftData = + new TreeMap<>(KeyComparator.instance()); + i.forEachRemaining(e -> bftData.put(e.getValidatorKey(), e)); + return bftData; + }); + var preparingStake = + new TreeMap>(KeyComparator.instance()); + for (var e : validatorBFTData.entrySet()) { + var k = e.getKey(); + var bftData = e.getValue(); + if (bftData.proposalsCompleted() + bftData.proposalsMissed() == 0) { + continue; + } + var percentageCompleted = + bftData.proposalsCompleted() + * 10000 + / (bftData.proposalsCompleted() + bftData.proposalsMissed()); - // Didn't pass threshold, no rewards! - if (percentageCompleted < minimumCompletedProposalsPercentage) { - continue; - } + // Didn't pass threshold, no rewards! + if (percentageCompleted < minimumCompletedProposalsPercentage) { + continue; + } - var nodeRewards = rewardsPerProposal.multiply(UInt256.from(bftData.proposalsCompleted())); - if (nodeRewards.isZero()) { - continue; - } + var nodeRewards = rewardsPerProposal.multiply(UInt256.from(bftData.proposalsCompleted())); + if (nodeRewards.isZero()) { + continue; + } - var validatorStakeData = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); - int rakePercentage = validatorStakeData.getRakePercentage(); - final UInt256 rakedEmissions; - if (rakePercentage != 0) { - var rake = nodeRewards - .multiply(UInt256.from(rakePercentage)) - .divide(UInt256.from(RAKE_MAX)); - var validatorOwner = validatorStakeData.getOwnerAddr(); - var initStake = new TreeMap( - Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator()) - ); - initStake.put(validatorOwner, rake); - preparingStake.put(k, initStake); - rakedEmissions = nodeRewards.subtract(rake); - } else { - rakedEmissions = nodeRewards; - } - validatorStakeData.addEmission(rakedEmissions); - } + var validatorStakeData = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); + int rakePercentage = validatorStakeData.getRakePercentage(); + final UInt256 rakedEmissions; + if (rakePercentage != 0) { + var rake = + nodeRewards.multiply(UInt256.from(rakePercentage)).divide(UInt256.from(RAKE_MAX)); + var validatorOwner = validatorStakeData.getOwnerAddr(); + var initStake = + new TreeMap( + Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator())); + initStake.put(validatorOwner, rake); + preparingStake.put(k, initStake); + rakedEmissions = nodeRewards.subtract(rake); + } else { + rakedEmissions = nodeRewards; + } + validatorStakeData.addEmission(rakedEmissions); + } - var allPreparedUnstake = txBuilder.shutdownAll(PreparedUnstakeOwnership.class, i -> { - var map = new TreeMap>(KeyComparator.instance()); - i.forEachRemaining(preparedStake -> - map - .computeIfAbsent( - preparedStake.getDelegateKey(), - k -> new TreeMap<>(Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator())) - ) - .merge(preparedStake.getOwner(), preparedStake.getAmount(), UInt256::add) - ); - return map; - }); - for (var e : allPreparedUnstake.entrySet()) { - var k = e.getKey(); - var curValidator = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); - var unstakes = e.getValue(); - for (var entry : unstakes.entrySet()) { - var addr = entry.getKey(); - var amt = entry.getValue(); - var epochUnlocked = closingEpoch.getEpoch() + 1 + unstakingEpochDelay; - var exittingStake = curValidator.unstakeOwnership(addr, amt, epochUnlocked); - txBuilder.up(exittingStake); - } - validatorsToUpdate.put(k, curValidator); - } + var allPreparedUnstake = + txBuilder.shutdownAll( + PreparedUnstakeOwnership.class, + i -> { + var map = + new TreeMap>(KeyComparator.instance()); + i.forEachRemaining( + preparedStake -> + map.computeIfAbsent( + preparedStake.getDelegateKey(), + k -> + new TreeMap<>( + Comparator.comparing( + REAddr::getBytes, + UnsignedBytes.lexicographicalComparator()))) + .merge( + preparedStake.getOwner(), preparedStake.getAmount(), UInt256::add)); + return map; + }); + for (var e : allPreparedUnstake.entrySet()) { + var k = e.getKey(); + var curValidator = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); + var unstakes = e.getValue(); + for (var entry : unstakes.entrySet()) { + var addr = entry.getKey(); + var amt = entry.getValue(); + var epochUnlocked = closingEpoch.getEpoch() + 1 + unstakingEpochDelay; + var exittingStake = curValidator.unstakeOwnership(addr, amt, epochUnlocked); + txBuilder.up(exittingStake); + } + validatorsToUpdate.put(k, curValidator); + } - var allPreparedStake = txBuilder.shutdownAll(PreparedStake.class, i -> { - i.forEachRemaining(preparedStake -> - preparingStake - .computeIfAbsent( - preparedStake.getDelegateKey(), - k -> new TreeMap<>(Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator())) - ) - .merge(preparedStake.getOwner(), preparedStake.getAmount(), UInt256::add) - ); - return preparingStake; - }); - for (var e : allPreparedStake.entrySet()) { - var k = e.getKey(); - var stakes = e.getValue(); - var curValidator = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); - for (var entry : stakes.entrySet()) { - var addr = entry.getKey(); - var amt = entry.getValue(); - var stakeOwnership = curValidator.stake(addr, amt); - txBuilder.up(stakeOwnership); - } - validatorsToUpdate.put(k, curValidator); - } + var allPreparedStake = + txBuilder.shutdownAll( + PreparedStake.class, + i -> { + i.forEachRemaining( + preparedStake -> + preparingStake + .computeIfAbsent( + preparedStake.getDelegateKey(), + k -> + new TreeMap<>( + Comparator.comparing( + REAddr::getBytes, + UnsignedBytes.lexicographicalComparator()))) + .merge( + preparedStake.getOwner(), preparedStake.getAmount(), UInt256::add)); + return preparingStake; + }); + for (var e : allPreparedStake.entrySet()) { + var k = e.getKey(); + var stakes = e.getValue(); + var curValidator = loadValidatorStakeData(txBuilder, k, validatorsToUpdate); + for (var entry : stakes.entrySet()) { + var addr = entry.getKey(); + var amt = entry.getValue(); + var stakeOwnership = curValidator.stake(addr, amt); + txBuilder.up(stakeOwnership); + } + validatorsToUpdate.put(k, curValidator); + } - // Update rake - prepare( - txBuilder, - validatorsToUpdate, - ValidatorFeeCopy.class, - SubstateTypeId.VALIDATOR_RAKE_COPY.id(), - closingEpoch.getEpoch() + 1, - (v, u) -> v.setRakePercentage(u.getRakePercentage()), - u -> new ValidatorFeeCopy(u.getValidatorKey(), u.getRakePercentage()) - ); + // Update rake + prepare( + txBuilder, + validatorsToUpdate, + ValidatorFeeCopy.class, + SubstateTypeId.VALIDATOR_RAKE_COPY.id(), + closingEpoch.getEpoch() + 1, + (v, u) -> v.setRakePercentage(u.getRakePercentage()), + u -> new ValidatorFeeCopy(u.getValidatorKey(), u.getRakePercentage())); - // Update owners - prepare( - txBuilder, - validatorsToUpdate, - ValidatorOwnerCopy.class, - SubstateTypeId.VALIDATOR_OWNER_COPY.id(), - closingEpoch.getEpoch() + 1, - (v, u) -> v.setOwnerAddr(u.getOwner()), - u -> new ValidatorOwnerCopy(u.getValidatorKey(), u.getOwner()) - ); + // Update owners + prepare( + txBuilder, + validatorsToUpdate, + ValidatorOwnerCopy.class, + SubstateTypeId.VALIDATOR_OWNER_COPY.id(), + closingEpoch.getEpoch() + 1, + (v, u) -> v.setOwnerAddr(u.getOwner()), + u -> new ValidatorOwnerCopy(u.getValidatorKey(), u.getOwner())); - // Update registered flag - prepare( - txBuilder, - validatorsToUpdate, - ValidatorRegisteredCopy.class, - SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id(), - closingEpoch.getEpoch() + 1, - (v, u) -> v.setRegistered(u.isRegistered()), - u -> new ValidatorRegisteredCopy(u.getValidatorKey(), u.isRegistered()) - ); + // Update registered flag + prepare( + txBuilder, + validatorsToUpdate, + ValidatorRegisteredCopy.class, + SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id(), + closingEpoch.getEpoch() + 1, + (v, u) -> v.setRegistered(u.isRegistered()), + u -> new ValidatorRegisteredCopy(u.getValidatorKey(), u.isRegistered())); - validatorsToUpdate.forEach((k, v) -> txBuilder.up(v.toSubstate())); + validatorsToUpdate.forEach((k, v) -> txBuilder.up(v.toSubstate())); - try (var cursor = txBuilder.readIndex( - SubstateIndex.create(new byte[] {SubstateTypeId.VALIDATOR_STAKE_DATA.id(), 0, 1}, ValidatorStakeData.class), - true - )) { - // TODO: Explicitly specify next validatorset - Streams.stream(cursor) - .map(ValidatorStakeData.class::cast) - .limit(maxValidators) - .filter(s -> !s.getTotalStake().isZero()) - .forEach(v -> txBuilder.up(new ValidatorBFTData(v.getValidatorKey(), 0, 0))); - } - txBuilder.up(new EpochData(closingEpoch.getEpoch() + 1)); - txBuilder.up(new RoundData(0, closedRound.getTimestamp())); - txBuilder.end(); - } + try (var cursor = + txBuilder.readIndex( + SubstateIndex.create( + new byte[] {SubstateTypeId.VALIDATOR_STAKE_DATA.id(), 0, 1}, + ValidatorStakeData.class), + true)) { + // TODO: Explicitly specify next validatorset + Streams.stream(cursor) + .map(ValidatorStakeData.class::cast) + .limit(maxValidators) + .filter(s -> !s.getTotalStake().isZero()) + .forEach(v -> txBuilder.up(new ValidatorBFTData(v.getValidatorKey(), 0, 0))); + } + txBuilder.up(new EpochData(closingEpoch.getEpoch() + 1)); + txBuilder.up(new RoundData(0, closedRound.getTimestamp())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextViewConstructorV3.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextViewConstructorV3.java index 0a44f33510..51857a14ab 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextViewConstructorV3.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/construction/NextViewConstructorV3.java @@ -64,51 +64,51 @@ package com.radixdlt.application.system.construction; +import com.radixdlt.application.system.state.RoundData; +import com.radixdlt.application.system.state.ValidatorBFTData; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.NextRound; -import com.radixdlt.application.system.state.RoundData; -import com.radixdlt.application.system.state.ValidatorBFTData; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.utils.KeyComparator; - import java.util.TreeMap; public class NextViewConstructorV3 implements ActionConstructor { - @Override - public void construct(NextRound action, TxBuilder txBuilder) throws TxBuilderException { - var prevRound = txBuilder.downSystem(RoundData.class); - if (action.view() <= prevRound.getView()) { - throw new InvalidRoundException(prevRound.getView(), action.view()); - } + @Override + public void construct(NextRound action, TxBuilder txBuilder) throws TxBuilderException { + var prevRound = txBuilder.downSystem(RoundData.class); + if (action.view() <= prevRound.getView()) { + throw new InvalidRoundException(prevRound.getView(), action.view()); + } - var validatorsToUpdate = new TreeMap(KeyComparator.instance()); - for (long view = prevRound.getView() + 1; view < action.view(); view++) { - var missingLeader = action.leaderMapping().apply(view); - if (!validatorsToUpdate.containsKey(missingLeader)) { - var validatorData = txBuilder.down(ValidatorBFTData.class, missingLeader); - validatorsToUpdate.put(missingLeader, validatorData); - } - var nextData = validatorsToUpdate.get(missingLeader).incrementProposalsMissed(); - validatorsToUpdate.put(missingLeader, nextData); - } + var validatorsToUpdate = new TreeMap(KeyComparator.instance()); + for (long view = prevRound.getView() + 1; view < action.view(); view++) { + var missingLeader = action.leaderMapping().apply(view); + if (!validatorsToUpdate.containsKey(missingLeader)) { + var validatorData = txBuilder.down(ValidatorBFTData.class, missingLeader); + validatorsToUpdate.put(missingLeader, validatorData); + } + var nextData = validatorsToUpdate.get(missingLeader).incrementProposalsMissed(); + validatorsToUpdate.put(missingLeader, nextData); + } - var curLeader = action.leaderMapping().apply(action.view()); - if (!validatorsToUpdate.containsKey(curLeader)) { - var validatorData = txBuilder.down(ValidatorBFTData.class, curLeader); - validatorsToUpdate.put(curLeader, validatorData); - } - var nextData = action.isTimeout() - ? validatorsToUpdate.get(curLeader).incrementProposalsMissed() - : validatorsToUpdate.get(curLeader).incrementCompletedProposals(); - validatorsToUpdate.put(curLeader, nextData); + var curLeader = action.leaderMapping().apply(action.view()); + if (!validatorsToUpdate.containsKey(curLeader)) { + var validatorData = txBuilder.down(ValidatorBFTData.class, curLeader); + validatorsToUpdate.put(curLeader, validatorData); + } + var nextData = + action.isTimeout() + ? validatorsToUpdate.get(curLeader).incrementProposalsMissed() + : validatorsToUpdate.get(curLeader).incrementCompletedProposals(); + validatorsToUpdate.put(curLeader, nextData); - for (var e : validatorsToUpdate.entrySet()) { - txBuilder.up(e.getValue()); - } + for (var e : validatorsToUpdate.entrySet()) { + txBuilder.up(e.getValue()); + } - txBuilder.up(new RoundData(action.view(), action.timestamp())); - txBuilder.end(); - } + txBuilder.up(new RoundData(action.view(), action.timestamp())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EndPrevRound.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EndPrevRound.java index 95c77c60a5..0acd10b11d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EndPrevRound.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EndPrevRound.java @@ -68,13 +68,13 @@ import com.radixdlt.constraintmachine.ReducerState; public final class EndPrevRound implements ReducerState { - private final RoundData closed; + private final RoundData closed; - EndPrevRound(RoundData closed) { - this.closed = closed; - } + EndPrevRound(RoundData closed) { + this.closed = closed; + } - public RoundData getClosedRound() { - return closed; - } + public RoundData getClosedRound() { + return closed; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EpochUpdateConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EpochUpdateConstraintScrypt.java index 6d5a7fbb21..ba48c6317b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EpochUpdateConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/EpochUpdateConstraintScrypt.java @@ -64,12 +64,12 @@ package com.radixdlt.application.system.scrypt; +import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; + import com.google.common.collect.Streams; import com.google.common.primitives.UnsignedBytes; import com.radixdlt.application.system.NextValidatorSetEvent; import com.radixdlt.application.system.ValidatorBFTDataEvent; -import com.radixdlt.atom.REFieldSerialization; -import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.application.system.state.EpochData; import com.radixdlt.application.system.state.HasEpochData; import com.radixdlt.application.system.state.RoundData; @@ -80,30 +80,31 @@ import com.radixdlt.application.tokens.state.PreparedStake; import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.application.tokens.state.TokensInAccount; -import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.application.validators.state.ValidatorFeeCopy; +import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; +import com.radixdlt.atom.REFieldSerialization; +import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; import com.radixdlt.constraintmachine.Authorization; import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.ExecutionContext; +import com.radixdlt.constraintmachine.IndexedSubstateIterator; import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.ReadIndexProcedure; -import com.radixdlt.constraintmachine.exceptions.MismatchException; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.ReducerState; -import com.radixdlt.constraintmachine.IndexedSubstateIterator; import com.radixdlt.constraintmachine.ShutdownAllProcedure; import com.radixdlt.constraintmachine.UpProcedure; +import com.radixdlt.constraintmachine.exceptions.MismatchException; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.KeyComparator; import com.radixdlt.utils.Longs; import com.radixdlt.utils.UInt256; - import java.util.Comparator; import java.util.LinkedList; import java.util.Objects; @@ -113,861 +114,925 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; - public final class EpochUpdateConstraintScrypt implements ConstraintScrypt { - private final long maxRounds; - private final UInt256 rewardsPerProposal; - private final long unstakingEpochDelay; - private int minimumCompletedProposalsPercentage; - private int maxValidators; - - public EpochUpdateConstraintScrypt( - long maxRounds, - UInt256 rewardsPerProposal, - int minimumCompletedProposalsPercentage, - long unstakingEpochDelay, - int maxValidators - ) { - this.maxRounds = maxRounds; - this.rewardsPerProposal = rewardsPerProposal; - this.unstakingEpochDelay = unstakingEpochDelay; - this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; - this.maxValidators = maxValidators; - } - - public final class ProcessExittingStake implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeSet exitting = new TreeSet<>( - Comparator.comparing(ExitingStake::dataKey, UnsignedBytes.lexicographicalComparator()) - ); - - ProcessExittingStake(UpdatingEpoch updatingEpoch) { - this.updatingEpoch = updatingEpoch; - } - - public ReducerState process(IndexedSubstateIterator indexedSubstateIterator) throws ProcedureException { - var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; - var expectedPrefix = new byte[Long.BYTES + 1]; - Longs.copyTo(expectedEpoch, expectedPrefix, 1); - indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); - indexedSubstateIterator.iterator().forEachRemaining(e -> { - // Sanity check - if (e.getEpochUnlocked() != expectedEpoch) { - throw new IllegalStateException("Invalid shutdown of exitting stake update epoch expected " - + expectedEpoch + " but was " + e.getEpochUnlocked()); - } - exitting.add(e); - }); - return next(); - } - - public ReducerState unlock(TokensInAccount u) throws ProcedureException { - var exit = exitting.first(); - exitting.remove(exit); - if (exit.getEpochUnlocked() != updatingEpoch.prevEpoch.getEpoch() + 1) { - throw new ProcedureException("Stake must still be locked."); - } - var expected = exit.unlock(); - if (!expected.equals(u)) { - throw new ProcedureException("Expecting next state to be " + expected + " but was " + u); - } - - return next(); - } - - public ReducerState next() { - return exitting.isEmpty() ? new RewardingValidators(updatingEpoch) : this; - } - } - - private final class RewardingValidators implements ReducerState { - private final TreeMap updatingValidators = new TreeMap<>(KeyComparator.instance()); - private final TreeMap validatorBFTData = new TreeMap<>(KeyComparator.instance()); - private final TreeMap> preparingStake = new TreeMap<>(KeyComparator.instance()); - private final UpdatingEpoch updatingEpoch; - - RewardingValidators(UpdatingEpoch updatingEpoch) { - this.updatingEpoch = updatingEpoch; - } - - // TODO: Remove context - public ReducerState process(IndexedSubstateIterator i, ExecutionContext context) throws ProcedureException { - i.verifyPostTypePrefixIsEmpty(); - var iter = i.iterator(); - while (iter.hasNext()) { - var validatorEpochData = iter.next(); - if (validatorBFTData.containsKey(validatorEpochData.getValidatorKey())) { - throw new ProcedureException("Already inserted " + validatorEpochData.getValidatorKey()); - } - validatorBFTData.put(validatorEpochData.getValidatorKey(), validatorEpochData); - } - - return next(context); - } - - ReducerState next(ExecutionContext context) { - if (validatorBFTData.isEmpty()) { - return new PreparingUnstake(updatingEpoch, updatingValidators, preparingStake); - } - - var k = validatorBFTData.firstKey(); - if (updatingValidators.containsKey(k)) { - throw new IllegalStateException("Inconsistent data, there should only be a single substate per validator"); - } - var bftData = validatorBFTData.remove(k); - context.emitEvent(ValidatorBFTDataEvent.create(k, bftData.proposalsCompleted(), bftData.proposalsMissed())); - if (bftData.proposalsCompleted() + bftData.proposalsMissed() == 0) { - return next(context); - } - - var percentageCompleted = bftData.proposalsCompleted() * 10000 - / (bftData.proposalsCompleted() + bftData.proposalsMissed()); - - // Didn't pass threshold, no rewards! - if (percentageCompleted < minimumCompletedProposalsPercentage) { - return next(context); - } - - var nodeRewards = rewardsPerProposal.multiply(UInt256.from(bftData.proposalsCompleted())); - if (nodeRewards.isZero()) { - return next(context); - } - - return new LoadingStake(k, validatorStakeData -> { - int rakePercentage = validatorStakeData.getRakePercentage(); - final UInt256 rakedEmissions; - if (rakePercentage != 0) { - var rake = nodeRewards - .multiply(UInt256.from(rakePercentage)) - .divide(UInt256.from(RAKE_MAX)); - var validatorOwner = validatorStakeData.getOwnerAddr(); - var initStake = new TreeMap(Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator())); - initStake.put(validatorOwner, rake); - preparingStake.put(k, initStake); - rakedEmissions = nodeRewards.subtract(rake); - } else { - rakedEmissions = nodeRewards; - } - validatorStakeData.addEmission(rakedEmissions); - updatingValidators.put(k, validatorStakeData); - return next(context); - }); - } - } - - public static final class BootupValidator implements ReducerState { - private final ValidatorBFTData expected; - private final Supplier onDone; - - BootupValidator(ValidatorStakeData validator, Supplier onDone) { - this.expected = new ValidatorBFTData(validator.getValidatorKey(), 0, 0); - this.onDone = onDone; - } - - public ReducerState bootUp(ValidatorBFTData data) throws MismatchException { - if (!Objects.equals(this.expected, data)) { - throw new MismatchException(this.expected, data); - } - return this.onDone.get(); - } - } - - public static class StartingNextEpoch implements ReducerState { - private final HasEpochData prevEpoch; - - StartingNextEpoch(HasEpochData prevEpoch) { - this.prevEpoch = prevEpoch; - } - - ReducerState nextEpoch(EpochData u) throws ProcedureException { - if (u.getEpoch() != prevEpoch.getEpoch() + 1) { - throw new ProcedureException("Invalid next epoch: " + u.getEpoch() - + " Expected: " + (prevEpoch.getEpoch() + 1)); - } - return new StartingEpochRound(); - } - } - - public class CreatingNextValidatorSet implements ReducerState { - private LinkedList nextValidatorSet; - private final UpdatingEpoch updatingEpoch; - - CreatingNextValidatorSet(UpdatingEpoch updatingEpoch) { - this.updatingEpoch = updatingEpoch; - } - - ReducerState readIndex(IndexedSubstateIterator substateIterator, ExecutionContext context) throws ProcedureException { - substateIterator.verifyPostTypePrefixEquals(new byte[] {0, 1}); // registered validator - this.nextValidatorSet = Streams.stream(substateIterator.iterator()) - .sorted(Comparator.comparing(ValidatorStakeData::getAmount) - .thenComparing(ValidatorStakeData::getValidatorKey, KeyComparator.instance()) - .reversed() - ) - .limit(maxValidators) - .filter(v -> !v.getTotalStake().isZero()) - .collect(Collectors.toCollection(LinkedList::new)); - - context.emitEvent(NextValidatorSetEvent.create(this.nextValidatorSet)); - return next(); - } - - ReducerState next() { - if (this.nextValidatorSet.isEmpty()) { - return new StartingNextEpoch(updatingEpoch.prevEpoch); - } - - var nextValidator = this.nextValidatorSet.pop(); - return new BootupValidator(nextValidator, this::next); - } - } - - public static final class UpdatingEpoch implements ReducerState { - private final HasEpochData prevEpoch; - - UpdatingEpoch(HasEpochData prevEpoch) { - this.prevEpoch = prevEpoch; - } - } - - public static final class StartingEpochRound implements ReducerState { - } - - - private final class Unstaking implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap unstaking; - private final Supplier onDone; - private ValidatorScratchPad current; - - Unstaking( - UpdatingEpoch updatingEpoch, - ValidatorScratchPad current, - TreeMap unstaking, - Supplier onDone - ) { - this.updatingEpoch = updatingEpoch; - this.current = current; - this.unstaking = unstaking; - this.onDone = onDone; - } - - ReducerState exit(ExitingStake u) throws MismatchException { - var firstAddr = unstaking.firstKey(); - var ownershipUnstake = unstaking.remove(firstAddr); - var epochUnlocked = updatingEpoch.prevEpoch.getEpoch() + unstakingEpochDelay + 1; - var expectedExit = current.unstakeOwnership( - firstAddr, ownershipUnstake, epochUnlocked - ); - if (!u.equals(expectedExit)) { - throw new MismatchException(expectedExit, u); - } - - return unstaking.isEmpty() ? onDone.get() : this; - } - } - - private final class PreparingUnstake implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap> preparingUnstake = new TreeMap<>(KeyComparator.instance()); - private final TreeMap> preparingStake; - private final TreeMap updatingValidators; - - PreparingUnstake( - UpdatingEpoch updatingEpoch, - TreeMap updatingValidators, - TreeMap> preparingStake - ) { - this.updatingEpoch = updatingEpoch; - this.updatingValidators = updatingValidators; - this.preparingStake = preparingStake; - } - - ReducerState unstakes(IndexedSubstateIterator i) throws ProcedureException { - i.verifyPostTypePrefixIsEmpty(); - i.iterator().forEachRemaining(preparedUnstakeOwned -> - preparingUnstake - .computeIfAbsent( - preparedUnstakeOwned.getDelegateKey(), - k -> new TreeMap<>(Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator())) - ) - .merge(preparedUnstakeOwned.getOwner(), preparedUnstakeOwned.getAmount(), UInt256::add) - ); - return next(); - } - - ReducerState next() { - if (preparingUnstake.isEmpty()) { - return new PreparingStake(updatingEpoch, updatingValidators, preparingStake); - } - - var k = preparingUnstake.firstKey(); - var unstakes = preparingUnstake.remove(k); - - if (!updatingValidators.containsKey(k)) { - return new LoadingStake(k, validatorStake -> { - updatingValidators.put(k, validatorStake); - return new Unstaking(updatingEpoch, validatorStake, unstakes, this::next); - }); - } else { - var validatorStake = updatingValidators.get(k); - return new Unstaking(updatingEpoch, validatorStake, unstakes, this::next); - } - } - } - - private static final class Staking implements ReducerState { - private final ValidatorScratchPad validatorScratchPad; - private final TreeMap stakes; - private final Supplier onDone; - - Staking(ValidatorScratchPad validatorScratchPad, TreeMap stakes, Supplier onDone) { - this.validatorScratchPad = validatorScratchPad; - this.stakes = stakes; - this.onDone = onDone; - } - - ReducerState stake(StakeOwnership stakeOwnership) throws MismatchException { - var accountAddr = stakes.firstKey(); - var stakeAmt = stakes.remove(accountAddr); - var expectedOwnership = validatorScratchPad.stake(accountAddr, stakeAmt); - if (!Objects.equals(stakeOwnership, expectedOwnership)) { - throw new MismatchException(expectedOwnership, stakeOwnership); - } - return stakes.isEmpty() ? onDone.get() : this; - } - } - - private static final class LoadingStake implements ReducerState { - private final ECPublicKey key; - private final Function onDone; - - LoadingStake(ECPublicKey key, Function onDone) { - this.key = key; - this.onDone = onDone; - } - - ReducerState startUpdate(ValidatorStakeData stake) throws ProcedureException { - if (!stake.getValidatorKey().equals(key)) { - throw new ProcedureException("Invalid stake load"); - } - return onDone.apply(new ValidatorScratchPad(stake)); - } - - @Override - public String toString() { - return String.format("%s{onDone: %s}", this.getClass().getSimpleName(), onDone); - } - } - - private final class PreparingStake implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap validatorsScratchPad; - private final TreeMap> preparingStake; - - PreparingStake( - UpdatingEpoch updatingEpoch, - TreeMap validatorsScratchPad, - TreeMap> preparingStake - ) { - this.validatorsScratchPad = validatorsScratchPad; - this.updatingEpoch = updatingEpoch; - this.preparingStake = preparingStake; - } - - ReducerState prepareStakes(IndexedSubstateIterator i) throws ProcedureException { - i.verifyPostTypePrefixIsEmpty(); - i.iterator().forEachRemaining(preparedStake -> - preparingStake - .computeIfAbsent( - preparedStake.getDelegateKey(), - k -> new TreeMap<>(Comparator.comparing(REAddr::getBytes, UnsignedBytes.lexicographicalComparator())) - ) - .merge(preparedStake.getOwner(), preparedStake.getAmount(), UInt256::add) - ); - return next(); - } - - ReducerState next() { - if (preparingStake.isEmpty()) { - return new PreparingRakeUpdate(updatingEpoch, validatorsScratchPad); - } - - var k = preparingStake.firstKey(); - var stakes = preparingStake.remove(k); - if (!validatorsScratchPad.containsKey(k)) { - return new LoadingStake(k, validatorStake -> { - validatorsScratchPad.put(k, validatorStake); - return new Staking(validatorStake, stakes, this::next); - }); - } else { - return new Staking(validatorsScratchPad.get(k), stakes, this::next); - } - } - } - - private static final class ResetRakeUpdate implements ReducerState { - private final ValidatorFeeCopy update; - private final Supplier next; - - ResetRakeUpdate(ValidatorFeeCopy update, Supplier next) { - this.update = update; - this.next = next; - } - - ReducerState reset(ValidatorFeeCopy rakeCopy) throws ProcedureException { - if (!rakeCopy.getValidatorKey().equals(update.getValidatorKey())) { - throw new ProcedureException("Validator keys must match."); - } - - if (rakeCopy.getRakePercentage() != update.getRakePercentage()) { - throw new ProcedureException("Rake percentage must match."); - } - - if (rakeCopy.getEpochUpdate().isPresent()) { - throw new ProcedureException("Reset of rake update should not have an epoch."); - } - - return next.get(); - } - } - - private final class PreparingRakeUpdate implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap validatorsScratchPad; - private final TreeMap preparingRakeUpdates = new TreeMap<>(KeyComparator.instance()); - - PreparingRakeUpdate( - UpdatingEpoch updatingEpoch, - TreeMap validatorsScratchPad - ) { - this.updatingEpoch = updatingEpoch; - this.validatorsScratchPad = validatorsScratchPad; - } - - ReducerState prepareRakeUpdates(IndexedSubstateIterator indexedSubstateIterator) throws ProcedureException { - var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; - var expectedPrefix = new byte[2 + Long.BYTES]; - expectedPrefix[0] = 0; - expectedPrefix[1] = 1; - Longs.copyTo(expectedEpoch, expectedPrefix, 2); - indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); - var iter = indexedSubstateIterator.iterator(); - while (iter.hasNext()) { - var preparedRakeUpdate = iter.next(); - // Sanity check - var epochUpdate = preparedRakeUpdate.getEpochUpdate(); - if (epochUpdate.orElseThrow() != expectedEpoch) { - throw new IllegalStateException("Invalid rake update epoch expected " + expectedEpoch - + " but was " + epochUpdate); - } - preparingRakeUpdates.put(preparedRakeUpdate.getValidatorKey(), preparedRakeUpdate); - } - return next(); - } - - ReducerState next() { - if (preparingRakeUpdates.isEmpty()) { - return new PreparingOwnerUpdate(updatingEpoch, validatorsScratchPad); - } - - var k = preparingRakeUpdates.firstKey(); - var validatorUpdate = preparingRakeUpdates.remove(k); - if (!validatorsScratchPad.containsKey(k)) { - return new LoadingStake(k, validatorStake -> { - validatorsScratchPad.put(k, validatorStake); - validatorStake.setRakePercentage(validatorUpdate.getRakePercentage()); - return new ResetRakeUpdate(validatorUpdate, this::next); - }); - } else { - validatorsScratchPad.get(k).setRakePercentage(validatorUpdate.getRakePercentage()); - return new ResetRakeUpdate(validatorUpdate, this::next); - } - } - } - - private static final class ResetOwnerUpdate implements ReducerState { - private final ECPublicKey validatorKey; - private final Supplier next; - - ResetOwnerUpdate(ECPublicKey validatorKey, Supplier next) { - this.validatorKey = validatorKey; - this.next = next; - } - - ReducerState reset(ValidatorOwnerCopy update) throws ProcedureException { - if (!validatorKey.equals(update.getValidatorKey())) { - throw new ProcedureException("Validator keys must match."); - } - - if (update.getEpochUpdate().isPresent()) { - throw new ProcedureException("Epoch should not be present."); - } - - return next.get(); - } - } - - private final class PreparingOwnerUpdate implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap validatorsScratchPad; - private final TreeMap preparingOwnerUpdates = new TreeMap<>(KeyComparator.instance()); - - PreparingOwnerUpdate( - UpdatingEpoch updatingEpoch, - TreeMap validatorsScratchPad - ) { - this.updatingEpoch = updatingEpoch; - this.validatorsScratchPad = validatorsScratchPad; - } - - ReducerState prepareValidatorUpdate(IndexedSubstateIterator indexedSubstateIterator) throws ProcedureException { - var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; - var expectedPrefix = new byte[2 + Long.BYTES]; - expectedPrefix[0] = 0; - expectedPrefix[1] = 1; - Longs.copyTo(expectedEpoch, expectedPrefix, 2); - indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); - - var iter = indexedSubstateIterator.iterator(); - while (iter.hasNext()) { - var preparedValidatorUpdate = iter.next(); - preparingOwnerUpdates.put(preparedValidatorUpdate.getValidatorKey(), preparedValidatorUpdate); - } - return next(); - } - - ReducerState next() { - if (preparingOwnerUpdates.isEmpty()) { - return new PreparingRegisteredUpdate(updatingEpoch, validatorsScratchPad); - } - - var k = preparingOwnerUpdates.firstKey(); - var validatorUpdate = preparingOwnerUpdates.remove(k); - if (!validatorsScratchPad.containsKey(k)) { - return new LoadingStake(k, validatorStake -> { - validatorsScratchPad.put(k, validatorStake); - validatorStake.setOwnerAddr(validatorUpdate.getOwner()); - return new ResetOwnerUpdate(k, this::next); - }); - } else { - validatorsScratchPad.get(k).setOwnerAddr(validatorUpdate.getOwner()); - return new ResetOwnerUpdate(k, this::next); - } - } - - @Override - public String toString() { - return String.format("%s{preparingOwnerUpdates=%s}", this.getClass().getSimpleName(), preparingOwnerUpdates); - } - } - - private static final class ResetRegisteredUpdate implements ReducerState { - private final ValidatorRegisteredCopy update; - private final Supplier next; - - ResetRegisteredUpdate(ValidatorRegisteredCopy update, Supplier next) { - this.update = update; - this.next = next; - } - - ReducerState reset(ValidatorRegisteredCopy registeredCopy) throws ProcedureException { - if (!registeredCopy.getValidatorKey().equals(update.getValidatorKey())) { - throw new ProcedureException("Validator keys must match."); - } - - if (registeredCopy.isRegistered() != update.isRegistered()) { - throw new ProcedureException("Registered flags must match."); - } - - if (registeredCopy.getEpochUpdate().isPresent()) { - throw new ProcedureException("Should not have an epoch."); - } - - return next.get(); - } - } - - private final class PreparingRegisteredUpdate implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap validatorsScratchPad; - private final TreeMap preparingRegisteredUpdates = new TreeMap<>(KeyComparator.instance()); - - PreparingRegisteredUpdate( - UpdatingEpoch updatingEpoch, - TreeMap validatorsScratchPad - ) { - this.updatingEpoch = updatingEpoch; - this.validatorsScratchPad = validatorsScratchPad; - } - - ReducerState prepareRegisterUpdates(IndexedSubstateIterator indexedSubstateIterator) throws ProcedureException { - var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; - var expectedPrefix = new byte[2 + Long.BYTES]; - expectedPrefix[0] = 0; - expectedPrefix[1] = 1; - Longs.copyTo(expectedEpoch, expectedPrefix, 2); - indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); - var iter = indexedSubstateIterator.iterator(); - while (iter.hasNext()) { - var preparedRegisteredUpdate = iter.next(); - preparingRegisteredUpdates.put(preparedRegisteredUpdate.getValidatorKey(), preparedRegisteredUpdate); - } - return next(); - } - - ReducerState next() { - if (preparingRegisteredUpdates.isEmpty()) { - return validatorsScratchPad.isEmpty() - ? new CreatingNextValidatorSet(updatingEpoch) - : new UpdatingValidatorStakes(updatingEpoch, validatorsScratchPad); - } - - var k = preparingRegisteredUpdates.firstKey(); - var validatorUpdate = preparingRegisteredUpdates.remove(k); - if (!validatorsScratchPad.containsKey(k)) { - return new LoadingStake(k, validatorStake -> { - validatorsScratchPad.put(k, validatorStake); - validatorStake.setRegistered(validatorUpdate.isRegistered()); - return new ResetRegisteredUpdate(validatorUpdate, this::next); - }); - } else { - validatorsScratchPad.get(k).setRegistered(validatorUpdate.isRegistered()); - return new ResetRegisteredUpdate(validatorUpdate, this::next); - } - } - } - - private final class UpdatingValidatorStakes implements ReducerState { - private final UpdatingEpoch updatingEpoch; - private final TreeMap validatorsScratchPad; - UpdatingValidatorStakes(UpdatingEpoch updatingEpoch, TreeMap validatorsScratchPad) { - this.updatingEpoch = updatingEpoch; - this.validatorsScratchPad = validatorsScratchPad; - } - - ReducerState updateStake(ValidatorStakeData stake) throws MismatchException { - var k = validatorsScratchPad.firstKey(); - var expectedValidatorData = validatorsScratchPad.remove(k).toSubstate(); - if (!stake.equals(expectedValidatorData)) { - throw new MismatchException(expectedValidatorData, stake); - } - - return validatorsScratchPad.isEmpty() ? new CreatingNextValidatorSet(updatingEpoch) : this; - } - } - - - private void registerGenesisTransitions(Loader os) { - - } - - private void epochUpdate(Loader os) { - // Epoch Update - os.procedure(new DownProcedure<>( - EndPrevRound.class, EpochData.class, - d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (d, s, r, c) -> { - // TODO: Should move this authorization instead of checking epoch > 0 - if (d.getEpoch() > 0 && s.getClosedRound().getView() != maxRounds) { - throw new ProcedureException("Must execute epoch update on end of round " + maxRounds - + " but is " + s.getClosedRound().getView()); - } - - return ReducerResult.incomplete(new UpdatingEpoch(d)); - } - )); - - os.procedure(new ShutdownAllProcedure<>( - ExitingStake.class, UpdatingEpoch.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> { - var exittingStake = new ProcessExittingStake(s); - return ReducerResult.incomplete(exittingStake.process(d)); - } - )); - os.procedure(new UpProcedure<>( - ProcessExittingStake.class, TokensInAccount.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.unlock(u)) - )); - - os.procedure(new ShutdownAllProcedure<>( - ValidatorBFTData.class, RewardingValidators.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.process(d, c)) - )); - - os.procedure(new ShutdownAllProcedure<>( - PreparedUnstakeOwnership.class, PreparingUnstake.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.unstakes(d)) - )); - os.procedure(new DownProcedure<>( - LoadingStake.class, ValidatorStakeData.class, - d -> d.bucket().withdrawAuthorization(), - (d, s, r, c) -> ReducerResult.incomplete(s.startUpdate(d)) - )); - os.procedure(new UpProcedure<>( - Unstaking.class, ExitingStake.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.exit(u)) - )); - os.procedure(new ShutdownAllProcedure<>( - PreparedStake.class, PreparingStake.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.prepareStakes(d)) - )); - os.procedure(new ShutdownAllProcedure<>( - ValidatorFeeCopy.class, PreparingRakeUpdate.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.prepareRakeUpdates(d)) - )); - os.procedure(new UpProcedure<>( - ResetRakeUpdate.class, ValidatorFeeCopy.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.reset(u)) - )); - - os.procedure(new ShutdownAllProcedure<>( - ValidatorOwnerCopy.class, PreparingOwnerUpdate.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.prepareValidatorUpdate(d)) - )); - os.procedure(new UpProcedure<>( - ResetOwnerUpdate.class, ValidatorOwnerCopy.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.reset(u)) - )); - - os.procedure(new ShutdownAllProcedure<>( - ValidatorRegisteredCopy.class, PreparingRegisteredUpdate.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.prepareRegisterUpdates(d)) - )); - os.procedure(new UpProcedure<>( - ResetRegisteredUpdate.class, ValidatorRegisteredCopy.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.reset(u)) - )); - - os.procedure(new UpProcedure<>( - Staking.class, StakeOwnership.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.stake(u)) - )); - os.procedure(new UpProcedure<>( - UpdatingValidatorStakes.class, ValidatorStakeData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.updateStake(u)) - )); - - os.procedure(new ReadIndexProcedure<>( - CreatingNextValidatorSet.class, ValidatorStakeData.class, - () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, d, c, r) -> ReducerResult.incomplete(s.readIndex(d, c)) - )); - - os.procedure(new UpProcedure<>( - BootupValidator.class, ValidatorBFTData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.bootUp(u)) - )); - - os.procedure(new UpProcedure<>( - StartingNextEpoch.class, EpochData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.nextEpoch(u)) - )); - - os.procedure(new UpProcedure<>( - StartingEpochRound.class, RoundData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> { - if (u.getView() != 0) { - throw new ProcedureException("Epoch must start with view 0"); - } - - return ReducerResult.complete(); - } - )); - } - - @Override - public void main(Loader os) { - - os.substate( - new SubstateDefinition<>( - ValidatorStakeData.class, - SubstateTypeId.VALIDATOR_STAKE_DATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var isRegistered = REFieldSerialization.deserializeBoolean(buf); - var amount = REFieldSerialization.deserializeUInt256(buf); - var delegate = REFieldSerialization.deserializeKey(buf); - var ownership = REFieldSerialization.deserializeUInt256(buf); - var rakePercentage = REFieldSerialization.deserializeInt(buf); - var ownerAddress = REFieldSerialization.deserializeAccountREAddr(buf); - return ValidatorStakeData.create(delegate, amount, ownership, rakePercentage, ownerAddress, isRegistered); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeBoolean(buf, s.isRegistered()); - buf.put(s.getAmount().toByteArray()); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - buf.put(s.getTotalOwnership().toByteArray()); - buf.putInt(s.getRakePercentage()); - REFieldSerialization.serializeREAddr(buf, s.getOwnerAddr()); - }, - buf -> REFieldSerialization.deserializeKey(buf), - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> ValidatorStakeData.createVirtual((ECPublicKey) k) - ) - ); - os.substate( - new SubstateDefinition<>( - StakeOwnership.class, - SubstateTypeId.STAKE_OWNERSHIP.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var delegate = REFieldSerialization.deserializeKey(buf); - var owner = REFieldSerialization.deserializeAccountREAddr(buf); - var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); - return new StakeOwnership(delegate, owner, amount); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getDelegateKey()); - REFieldSerialization.serializeREAddr(buf, s.getOwner()); - buf.put(s.getAmount().toByteArray()); - } - ) - ); - os.substate( - new SubstateDefinition<>( - ExitingStake.class, - SubstateTypeId.EXITING_STAKE.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var epochUnlocked = REFieldSerialization.deserializeNonNegativeLong(buf); - var delegate = REFieldSerialization.deserializeKey(buf); - var owner = REFieldSerialization.deserializeAccountREAddr(buf); - var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); - return new ExitingStake(epochUnlocked, delegate, owner, amount); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - buf.putLong(s.getEpochUnlocked()); - REFieldSerialization.serializeKey(buf, s.getDelegateKey()); - REFieldSerialization.serializeREAddr(buf, s.getOwner()); - buf.put(s.getAmount().toByteArray()); - } - ) - ); - - registerGenesisTransitions(os); - - // Epoch update - epochUpdate(os); - } + private final long maxRounds; + private final UInt256 rewardsPerProposal; + private final long unstakingEpochDelay; + private int minimumCompletedProposalsPercentage; + private int maxValidators; + + public EpochUpdateConstraintScrypt( + long maxRounds, + UInt256 rewardsPerProposal, + int minimumCompletedProposalsPercentage, + long unstakingEpochDelay, + int maxValidators) { + this.maxRounds = maxRounds; + this.rewardsPerProposal = rewardsPerProposal; + this.unstakingEpochDelay = unstakingEpochDelay; + this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; + this.maxValidators = maxValidators; + } + + public final class ProcessExittingStake implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeSet exitting = + new TreeSet<>( + Comparator.comparing(ExitingStake::dataKey, UnsignedBytes.lexicographicalComparator())); + + ProcessExittingStake(UpdatingEpoch updatingEpoch) { + this.updatingEpoch = updatingEpoch; + } + + public ReducerState process(IndexedSubstateIterator indexedSubstateIterator) + throws ProcedureException { + var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; + var expectedPrefix = new byte[Long.BYTES + 1]; + Longs.copyTo(expectedEpoch, expectedPrefix, 1); + indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); + indexedSubstateIterator + .iterator() + .forEachRemaining( + e -> { + // Sanity check + if (e.getEpochUnlocked() != expectedEpoch) { + throw new IllegalStateException( + "Invalid shutdown of exitting stake update epoch expected " + + expectedEpoch + + " but was " + + e.getEpochUnlocked()); + } + exitting.add(e); + }); + return next(); + } + + public ReducerState unlock(TokensInAccount u) throws ProcedureException { + var exit = exitting.first(); + exitting.remove(exit); + if (exit.getEpochUnlocked() != updatingEpoch.prevEpoch.getEpoch() + 1) { + throw new ProcedureException("Stake must still be locked."); + } + var expected = exit.unlock(); + if (!expected.equals(u)) { + throw new ProcedureException("Expecting next state to be " + expected + " but was " + u); + } + + return next(); + } + + public ReducerState next() { + return exitting.isEmpty() ? new RewardingValidators(updatingEpoch) : this; + } + } + + private final class RewardingValidators implements ReducerState { + private final TreeMap updatingValidators = + new TreeMap<>(KeyComparator.instance()); + private final TreeMap validatorBFTData = + new TreeMap<>(KeyComparator.instance()); + private final TreeMap> preparingStake = + new TreeMap<>(KeyComparator.instance()); + private final UpdatingEpoch updatingEpoch; + + RewardingValidators(UpdatingEpoch updatingEpoch) { + this.updatingEpoch = updatingEpoch; + } + + // TODO: Remove context + public ReducerState process( + IndexedSubstateIterator i, ExecutionContext context) + throws ProcedureException { + i.verifyPostTypePrefixIsEmpty(); + var iter = i.iterator(); + while (iter.hasNext()) { + var validatorEpochData = iter.next(); + if (validatorBFTData.containsKey(validatorEpochData.getValidatorKey())) { + throw new ProcedureException("Already inserted " + validatorEpochData.getValidatorKey()); + } + validatorBFTData.put(validatorEpochData.getValidatorKey(), validatorEpochData); + } + + return next(context); + } + + ReducerState next(ExecutionContext context) { + if (validatorBFTData.isEmpty()) { + return new PreparingUnstake(updatingEpoch, updatingValidators, preparingStake); + } + + var k = validatorBFTData.firstKey(); + if (updatingValidators.containsKey(k)) { + throw new IllegalStateException( + "Inconsistent data, there should only be a single substate per validator"); + } + var bftData = validatorBFTData.remove(k); + context.emitEvent( + ValidatorBFTDataEvent.create(k, bftData.proposalsCompleted(), bftData.proposalsMissed())); + if (bftData.proposalsCompleted() + bftData.proposalsMissed() == 0) { + return next(context); + } + + var percentageCompleted = + bftData.proposalsCompleted() + * 10000 + / (bftData.proposalsCompleted() + bftData.proposalsMissed()); + + // Didn't pass threshold, no rewards! + if (percentageCompleted < minimumCompletedProposalsPercentage) { + return next(context); + } + + var nodeRewards = rewardsPerProposal.multiply(UInt256.from(bftData.proposalsCompleted())); + if (nodeRewards.isZero()) { + return next(context); + } + + return new LoadingStake( + k, + validatorStakeData -> { + int rakePercentage = validatorStakeData.getRakePercentage(); + final UInt256 rakedEmissions; + if (rakePercentage != 0) { + var rake = + nodeRewards.multiply(UInt256.from(rakePercentage)).divide(UInt256.from(RAKE_MAX)); + var validatorOwner = validatorStakeData.getOwnerAddr(); + var initStake = + new TreeMap( + Comparator.comparing( + REAddr::getBytes, UnsignedBytes.lexicographicalComparator())); + initStake.put(validatorOwner, rake); + preparingStake.put(k, initStake); + rakedEmissions = nodeRewards.subtract(rake); + } else { + rakedEmissions = nodeRewards; + } + validatorStakeData.addEmission(rakedEmissions); + updatingValidators.put(k, validatorStakeData); + return next(context); + }); + } + } + + public static final class BootupValidator implements ReducerState { + private final ValidatorBFTData expected; + private final Supplier onDone; + + BootupValidator(ValidatorStakeData validator, Supplier onDone) { + this.expected = new ValidatorBFTData(validator.getValidatorKey(), 0, 0); + this.onDone = onDone; + } + + public ReducerState bootUp(ValidatorBFTData data) throws MismatchException { + if (!Objects.equals(this.expected, data)) { + throw new MismatchException(this.expected, data); + } + return this.onDone.get(); + } + } + + public static class StartingNextEpoch implements ReducerState { + private final HasEpochData prevEpoch; + + StartingNextEpoch(HasEpochData prevEpoch) { + this.prevEpoch = prevEpoch; + } + + ReducerState nextEpoch(EpochData u) throws ProcedureException { + if (u.getEpoch() != prevEpoch.getEpoch() + 1) { + throw new ProcedureException( + "Invalid next epoch: " + u.getEpoch() + " Expected: " + (prevEpoch.getEpoch() + 1)); + } + return new StartingEpochRound(); + } + } + + public class CreatingNextValidatorSet implements ReducerState { + private LinkedList nextValidatorSet; + private final UpdatingEpoch updatingEpoch; + + CreatingNextValidatorSet(UpdatingEpoch updatingEpoch) { + this.updatingEpoch = updatingEpoch; + } + + ReducerState readIndex( + IndexedSubstateIterator substateIterator, ExecutionContext context) + throws ProcedureException { + substateIterator.verifyPostTypePrefixEquals(new byte[] {0, 1}); // registered validator + this.nextValidatorSet = + Streams.stream(substateIterator.iterator()) + .sorted( + Comparator.comparing(ValidatorStakeData::getAmount) + .thenComparing(ValidatorStakeData::getValidatorKey, KeyComparator.instance()) + .reversed()) + .limit(maxValidators) + .filter(v -> !v.getTotalStake().isZero()) + .collect(Collectors.toCollection(LinkedList::new)); + + context.emitEvent(NextValidatorSetEvent.create(this.nextValidatorSet)); + return next(); + } + + ReducerState next() { + if (this.nextValidatorSet.isEmpty()) { + return new StartingNextEpoch(updatingEpoch.prevEpoch); + } + + var nextValidator = this.nextValidatorSet.pop(); + return new BootupValidator(nextValidator, this::next); + } + } + + public static final class UpdatingEpoch implements ReducerState { + private final HasEpochData prevEpoch; + + UpdatingEpoch(HasEpochData prevEpoch) { + this.prevEpoch = prevEpoch; + } + } + + public static final class StartingEpochRound implements ReducerState {} + + private final class Unstaking implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap unstaking; + private final Supplier onDone; + private ValidatorScratchPad current; + + Unstaking( + UpdatingEpoch updatingEpoch, + ValidatorScratchPad current, + TreeMap unstaking, + Supplier onDone) { + this.updatingEpoch = updatingEpoch; + this.current = current; + this.unstaking = unstaking; + this.onDone = onDone; + } + + ReducerState exit(ExitingStake u) throws MismatchException { + var firstAddr = unstaking.firstKey(); + var ownershipUnstake = unstaking.remove(firstAddr); + var epochUnlocked = updatingEpoch.prevEpoch.getEpoch() + unstakingEpochDelay + 1; + var expectedExit = current.unstakeOwnership(firstAddr, ownershipUnstake, epochUnlocked); + if (!u.equals(expectedExit)) { + throw new MismatchException(expectedExit, u); + } + + return unstaking.isEmpty() ? onDone.get() : this; + } + } + + private final class PreparingUnstake implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap> preparingUnstake = + new TreeMap<>(KeyComparator.instance()); + private final TreeMap> preparingStake; + private final TreeMap updatingValidators; + + PreparingUnstake( + UpdatingEpoch updatingEpoch, + TreeMap updatingValidators, + TreeMap> preparingStake) { + this.updatingEpoch = updatingEpoch; + this.updatingValidators = updatingValidators; + this.preparingStake = preparingStake; + } + + ReducerState unstakes(IndexedSubstateIterator i) + throws ProcedureException { + i.verifyPostTypePrefixIsEmpty(); + i.iterator() + .forEachRemaining( + preparedUnstakeOwned -> + preparingUnstake + .computeIfAbsent( + preparedUnstakeOwned.getDelegateKey(), + k -> + new TreeMap<>( + Comparator.comparing( + REAddr::getBytes, UnsignedBytes.lexicographicalComparator()))) + .merge( + preparedUnstakeOwned.getOwner(), + preparedUnstakeOwned.getAmount(), + UInt256::add)); + return next(); + } + + ReducerState next() { + if (preparingUnstake.isEmpty()) { + return new PreparingStake(updatingEpoch, updatingValidators, preparingStake); + } + + var k = preparingUnstake.firstKey(); + var unstakes = preparingUnstake.remove(k); + + if (!updatingValidators.containsKey(k)) { + return new LoadingStake( + k, + validatorStake -> { + updatingValidators.put(k, validatorStake); + return new Unstaking(updatingEpoch, validatorStake, unstakes, this::next); + }); + } else { + var validatorStake = updatingValidators.get(k); + return new Unstaking(updatingEpoch, validatorStake, unstakes, this::next); + } + } + } + + private static final class Staking implements ReducerState { + private final ValidatorScratchPad validatorScratchPad; + private final TreeMap stakes; + private final Supplier onDone; + + Staking( + ValidatorScratchPad validatorScratchPad, + TreeMap stakes, + Supplier onDone) { + this.validatorScratchPad = validatorScratchPad; + this.stakes = stakes; + this.onDone = onDone; + } + + ReducerState stake(StakeOwnership stakeOwnership) throws MismatchException { + var accountAddr = stakes.firstKey(); + var stakeAmt = stakes.remove(accountAddr); + var expectedOwnership = validatorScratchPad.stake(accountAddr, stakeAmt); + if (!Objects.equals(stakeOwnership, expectedOwnership)) { + throw new MismatchException(expectedOwnership, stakeOwnership); + } + return stakes.isEmpty() ? onDone.get() : this; + } + } + + private static final class LoadingStake implements ReducerState { + private final ECPublicKey key; + private final Function onDone; + + LoadingStake(ECPublicKey key, Function onDone) { + this.key = key; + this.onDone = onDone; + } + + ReducerState startUpdate(ValidatorStakeData stake) throws ProcedureException { + if (!stake.getValidatorKey().equals(key)) { + throw new ProcedureException("Invalid stake load"); + } + return onDone.apply(new ValidatorScratchPad(stake)); + } + + @Override + public String toString() { + return String.format("%s{onDone: %s}", this.getClass().getSimpleName(), onDone); + } + } + + private final class PreparingStake implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap validatorsScratchPad; + private final TreeMap> preparingStake; + + PreparingStake( + UpdatingEpoch updatingEpoch, + TreeMap validatorsScratchPad, + TreeMap> preparingStake) { + this.validatorsScratchPad = validatorsScratchPad; + this.updatingEpoch = updatingEpoch; + this.preparingStake = preparingStake; + } + + ReducerState prepareStakes(IndexedSubstateIterator i) throws ProcedureException { + i.verifyPostTypePrefixIsEmpty(); + i.iterator() + .forEachRemaining( + preparedStake -> + preparingStake + .computeIfAbsent( + preparedStake.getDelegateKey(), + k -> + new TreeMap<>( + Comparator.comparing( + REAddr::getBytes, UnsignedBytes.lexicographicalComparator()))) + .merge(preparedStake.getOwner(), preparedStake.getAmount(), UInt256::add)); + return next(); + } + + ReducerState next() { + if (preparingStake.isEmpty()) { + return new PreparingRakeUpdate(updatingEpoch, validatorsScratchPad); + } + + var k = preparingStake.firstKey(); + var stakes = preparingStake.remove(k); + if (!validatorsScratchPad.containsKey(k)) { + return new LoadingStake( + k, + validatorStake -> { + validatorsScratchPad.put(k, validatorStake); + return new Staking(validatorStake, stakes, this::next); + }); + } else { + return new Staking(validatorsScratchPad.get(k), stakes, this::next); + } + } + } + + private static final class ResetRakeUpdate implements ReducerState { + private final ValidatorFeeCopy update; + private final Supplier next; + + ResetRakeUpdate(ValidatorFeeCopy update, Supplier next) { + this.update = update; + this.next = next; + } + + ReducerState reset(ValidatorFeeCopy rakeCopy) throws ProcedureException { + if (!rakeCopy.getValidatorKey().equals(update.getValidatorKey())) { + throw new ProcedureException("Validator keys must match."); + } + + if (rakeCopy.getRakePercentage() != update.getRakePercentage()) { + throw new ProcedureException("Rake percentage must match."); + } + + if (rakeCopy.getEpochUpdate().isPresent()) { + throw new ProcedureException("Reset of rake update should not have an epoch."); + } + + return next.get(); + } + } + + private final class PreparingRakeUpdate implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap validatorsScratchPad; + private final TreeMap preparingRakeUpdates = + new TreeMap<>(KeyComparator.instance()); + + PreparingRakeUpdate( + UpdatingEpoch updatingEpoch, + TreeMap validatorsScratchPad) { + this.updatingEpoch = updatingEpoch; + this.validatorsScratchPad = validatorsScratchPad; + } + + ReducerState prepareRakeUpdates( + IndexedSubstateIterator indexedSubstateIterator) + throws ProcedureException { + var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; + var expectedPrefix = new byte[2 + Long.BYTES]; + expectedPrefix[0] = 0; + expectedPrefix[1] = 1; + Longs.copyTo(expectedEpoch, expectedPrefix, 2); + indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); + var iter = indexedSubstateIterator.iterator(); + while (iter.hasNext()) { + var preparedRakeUpdate = iter.next(); + // Sanity check + var epochUpdate = preparedRakeUpdate.getEpochUpdate(); + if (epochUpdate.orElseThrow() != expectedEpoch) { + throw new IllegalStateException( + "Invalid rake update epoch expected " + expectedEpoch + " but was " + epochUpdate); + } + preparingRakeUpdates.put(preparedRakeUpdate.getValidatorKey(), preparedRakeUpdate); + } + return next(); + } + + ReducerState next() { + if (preparingRakeUpdates.isEmpty()) { + return new PreparingOwnerUpdate(updatingEpoch, validatorsScratchPad); + } + + var k = preparingRakeUpdates.firstKey(); + var validatorUpdate = preparingRakeUpdates.remove(k); + if (!validatorsScratchPad.containsKey(k)) { + return new LoadingStake( + k, + validatorStake -> { + validatorsScratchPad.put(k, validatorStake); + validatorStake.setRakePercentage(validatorUpdate.getRakePercentage()); + return new ResetRakeUpdate(validatorUpdate, this::next); + }); + } else { + validatorsScratchPad.get(k).setRakePercentage(validatorUpdate.getRakePercentage()); + return new ResetRakeUpdate(validatorUpdate, this::next); + } + } + } + + private static final class ResetOwnerUpdate implements ReducerState { + private final ECPublicKey validatorKey; + private final Supplier next; + + ResetOwnerUpdate(ECPublicKey validatorKey, Supplier next) { + this.validatorKey = validatorKey; + this.next = next; + } + + ReducerState reset(ValidatorOwnerCopy update) throws ProcedureException { + if (!validatorKey.equals(update.getValidatorKey())) { + throw new ProcedureException("Validator keys must match."); + } + + if (update.getEpochUpdate().isPresent()) { + throw new ProcedureException("Epoch should not be present."); + } + + return next.get(); + } + } + + private final class PreparingOwnerUpdate implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap validatorsScratchPad; + private final TreeMap preparingOwnerUpdates = + new TreeMap<>(KeyComparator.instance()); + + PreparingOwnerUpdate( + UpdatingEpoch updatingEpoch, + TreeMap validatorsScratchPad) { + this.updatingEpoch = updatingEpoch; + this.validatorsScratchPad = validatorsScratchPad; + } + + ReducerState prepareValidatorUpdate( + IndexedSubstateIterator indexedSubstateIterator) + throws ProcedureException { + var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; + var expectedPrefix = new byte[2 + Long.BYTES]; + expectedPrefix[0] = 0; + expectedPrefix[1] = 1; + Longs.copyTo(expectedEpoch, expectedPrefix, 2); + indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); + + var iter = indexedSubstateIterator.iterator(); + while (iter.hasNext()) { + var preparedValidatorUpdate = iter.next(); + preparingOwnerUpdates.put( + preparedValidatorUpdate.getValidatorKey(), preparedValidatorUpdate); + } + return next(); + } + + ReducerState next() { + if (preparingOwnerUpdates.isEmpty()) { + return new PreparingRegisteredUpdate(updatingEpoch, validatorsScratchPad); + } + + var k = preparingOwnerUpdates.firstKey(); + var validatorUpdate = preparingOwnerUpdates.remove(k); + if (!validatorsScratchPad.containsKey(k)) { + return new LoadingStake( + k, + validatorStake -> { + validatorsScratchPad.put(k, validatorStake); + validatorStake.setOwnerAddr(validatorUpdate.getOwner()); + return new ResetOwnerUpdate(k, this::next); + }); + } else { + validatorsScratchPad.get(k).setOwnerAddr(validatorUpdate.getOwner()); + return new ResetOwnerUpdate(k, this::next); + } + } + + @Override + public String toString() { + return String.format( + "%s{preparingOwnerUpdates=%s}", this.getClass().getSimpleName(), preparingOwnerUpdates); + } + } + + private static final class ResetRegisteredUpdate implements ReducerState { + private final ValidatorRegisteredCopy update; + private final Supplier next; + + ResetRegisteredUpdate(ValidatorRegisteredCopy update, Supplier next) { + this.update = update; + this.next = next; + } + + ReducerState reset(ValidatorRegisteredCopy registeredCopy) throws ProcedureException { + if (!registeredCopy.getValidatorKey().equals(update.getValidatorKey())) { + throw new ProcedureException("Validator keys must match."); + } + + if (registeredCopy.isRegistered() != update.isRegistered()) { + throw new ProcedureException("Registered flags must match."); + } + + if (registeredCopy.getEpochUpdate().isPresent()) { + throw new ProcedureException("Should not have an epoch."); + } + + return next.get(); + } + } + + private final class PreparingRegisteredUpdate implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap validatorsScratchPad; + private final TreeMap preparingRegisteredUpdates = + new TreeMap<>(KeyComparator.instance()); + + PreparingRegisteredUpdate( + UpdatingEpoch updatingEpoch, + TreeMap validatorsScratchPad) { + this.updatingEpoch = updatingEpoch; + this.validatorsScratchPad = validatorsScratchPad; + } + + ReducerState prepareRegisterUpdates( + IndexedSubstateIterator indexedSubstateIterator) + throws ProcedureException { + var expectedEpoch = updatingEpoch.prevEpoch.getEpoch() + 1; + var expectedPrefix = new byte[2 + Long.BYTES]; + expectedPrefix[0] = 0; + expectedPrefix[1] = 1; + Longs.copyTo(expectedEpoch, expectedPrefix, 2); + indexedSubstateIterator.verifyPostTypePrefixEquals(expectedPrefix); + var iter = indexedSubstateIterator.iterator(); + while (iter.hasNext()) { + var preparedRegisteredUpdate = iter.next(); + preparingRegisteredUpdates.put( + preparedRegisteredUpdate.getValidatorKey(), preparedRegisteredUpdate); + } + return next(); + } + + ReducerState next() { + if (preparingRegisteredUpdates.isEmpty()) { + return validatorsScratchPad.isEmpty() + ? new CreatingNextValidatorSet(updatingEpoch) + : new UpdatingValidatorStakes(updatingEpoch, validatorsScratchPad); + } + + var k = preparingRegisteredUpdates.firstKey(); + var validatorUpdate = preparingRegisteredUpdates.remove(k); + if (!validatorsScratchPad.containsKey(k)) { + return new LoadingStake( + k, + validatorStake -> { + validatorsScratchPad.put(k, validatorStake); + validatorStake.setRegistered(validatorUpdate.isRegistered()); + return new ResetRegisteredUpdate(validatorUpdate, this::next); + }); + } else { + validatorsScratchPad.get(k).setRegistered(validatorUpdate.isRegistered()); + return new ResetRegisteredUpdate(validatorUpdate, this::next); + } + } + } + + private final class UpdatingValidatorStakes implements ReducerState { + private final UpdatingEpoch updatingEpoch; + private final TreeMap validatorsScratchPad; + + UpdatingValidatorStakes( + UpdatingEpoch updatingEpoch, + TreeMap validatorsScratchPad) { + this.updatingEpoch = updatingEpoch; + this.validatorsScratchPad = validatorsScratchPad; + } + + ReducerState updateStake(ValidatorStakeData stake) throws MismatchException { + var k = validatorsScratchPad.firstKey(); + var expectedValidatorData = validatorsScratchPad.remove(k).toSubstate(); + if (!stake.equals(expectedValidatorData)) { + throw new MismatchException(expectedValidatorData, stake); + } + + return validatorsScratchPad.isEmpty() ? new CreatingNextValidatorSet(updatingEpoch) : this; + } + } + + private void registerGenesisTransitions(Loader os) {} + + private void epochUpdate(Loader os) { + // Epoch Update + os.procedure( + new DownProcedure<>( + EndPrevRound.class, + EpochData.class, + d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (d, s, r, c) -> { + // TODO: Should move this authorization instead of checking epoch > 0 + if (d.getEpoch() > 0 && s.getClosedRound().getView() != maxRounds) { + throw new ProcedureException( + "Must execute epoch update on end of round " + + maxRounds + + " but is " + + s.getClosedRound().getView()); + } + + return ReducerResult.incomplete(new UpdatingEpoch(d)); + })); + + os.procedure( + new ShutdownAllProcedure<>( + ExitingStake.class, + UpdatingEpoch.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> { + var exittingStake = new ProcessExittingStake(s); + return ReducerResult.incomplete(exittingStake.process(d)); + })); + os.procedure( + new UpProcedure<>( + ProcessExittingStake.class, + TokensInAccount.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.unlock(u)))); + + os.procedure( + new ShutdownAllProcedure<>( + ValidatorBFTData.class, + RewardingValidators.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.process(d, c)))); + + os.procedure( + new ShutdownAllProcedure<>( + PreparedUnstakeOwnership.class, + PreparingUnstake.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.unstakes(d)))); + os.procedure( + new DownProcedure<>( + LoadingStake.class, + ValidatorStakeData.class, + d -> d.bucket().withdrawAuthorization(), + (d, s, r, c) -> ReducerResult.incomplete(s.startUpdate(d)))); + os.procedure( + new UpProcedure<>( + Unstaking.class, + ExitingStake.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.exit(u)))); + os.procedure( + new ShutdownAllProcedure<>( + PreparedStake.class, + PreparingStake.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.prepareStakes(d)))); + os.procedure( + new ShutdownAllProcedure<>( + ValidatorFeeCopy.class, + PreparingRakeUpdate.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.prepareRakeUpdates(d)))); + os.procedure( + new UpProcedure<>( + ResetRakeUpdate.class, + ValidatorFeeCopy.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.reset(u)))); + + os.procedure( + new ShutdownAllProcedure<>( + ValidatorOwnerCopy.class, + PreparingOwnerUpdate.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.prepareValidatorUpdate(d)))); + os.procedure( + new UpProcedure<>( + ResetOwnerUpdate.class, + ValidatorOwnerCopy.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.reset(u)))); + + os.procedure( + new ShutdownAllProcedure<>( + ValidatorRegisteredCopy.class, + PreparingRegisteredUpdate.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.prepareRegisterUpdates(d)))); + os.procedure( + new UpProcedure<>( + ResetRegisteredUpdate.class, + ValidatorRegisteredCopy.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.reset(u)))); + + os.procedure( + new UpProcedure<>( + Staking.class, + StakeOwnership.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.stake(u)))); + os.procedure( + new UpProcedure<>( + UpdatingValidatorStakes.class, + ValidatorStakeData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.updateStake(u)))); + + os.procedure( + new ReadIndexProcedure<>( + CreatingNextValidatorSet.class, + ValidatorStakeData.class, + () -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, d, c, r) -> ReducerResult.incomplete(s.readIndex(d, c)))); + + os.procedure( + new UpProcedure<>( + BootupValidator.class, + ValidatorBFTData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.bootUp(u)))); + + os.procedure( + new UpProcedure<>( + StartingNextEpoch.class, + EpochData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.nextEpoch(u)))); + + os.procedure( + new UpProcedure<>( + StartingEpochRound.class, + RoundData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> { + if (u.getView() != 0) { + throw new ProcedureException("Epoch must start with view 0"); + } + + return ReducerResult.complete(); + })); + } + + @Override + public void main(Loader os) { + + os.substate( + new SubstateDefinition<>( + ValidatorStakeData.class, + SubstateTypeId.VALIDATOR_STAKE_DATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var isRegistered = REFieldSerialization.deserializeBoolean(buf); + var amount = REFieldSerialization.deserializeUInt256(buf); + var delegate = REFieldSerialization.deserializeKey(buf); + var ownership = REFieldSerialization.deserializeUInt256(buf); + var rakePercentage = REFieldSerialization.deserializeInt(buf); + var ownerAddress = REFieldSerialization.deserializeAccountREAddr(buf); + return ValidatorStakeData.create( + delegate, amount, ownership, rakePercentage, ownerAddress, isRegistered); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeBoolean(buf, s.isRegistered()); + buf.put(s.getAmount().toByteArray()); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + buf.put(s.getTotalOwnership().toByteArray()); + buf.putInt(s.getRakePercentage()); + REFieldSerialization.serializeREAddr(buf, s.getOwnerAddr()); + }, + buf -> REFieldSerialization.deserializeKey(buf), + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> ValidatorStakeData.createVirtual((ECPublicKey) k))); + os.substate( + new SubstateDefinition<>( + StakeOwnership.class, + SubstateTypeId.STAKE_OWNERSHIP.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var delegate = REFieldSerialization.deserializeKey(buf); + var owner = REFieldSerialization.deserializeAccountREAddr(buf); + var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); + return new StakeOwnership(delegate, owner, amount); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getDelegateKey()); + REFieldSerialization.serializeREAddr(buf, s.getOwner()); + buf.put(s.getAmount().toByteArray()); + })); + os.substate( + new SubstateDefinition<>( + ExitingStake.class, + SubstateTypeId.EXITING_STAKE.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var epochUnlocked = REFieldSerialization.deserializeNonNegativeLong(buf); + var delegate = REFieldSerialization.deserializeKey(buf); + var owner = REFieldSerialization.deserializeAccountREAddr(buf); + var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); + return new ExitingStake(epochUnlocked, delegate, owner, amount); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + buf.putLong(s.getEpochUnlocked()); + REFieldSerialization.serializeKey(buf, s.getDelegateKey()); + REFieldSerialization.serializeREAddr(buf, s.getOwner()); + buf.put(s.getAmount().toByteArray()); + })); + + registerGenesisTransitions(os); + + // Epoch update + epochUpdate(os); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/RoundUpdateConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/RoundUpdateConstraintScrypt.java index fc3a5e0d31..bfd0c6141f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/RoundUpdateConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/RoundUpdateConstraintScrypt.java @@ -64,124 +64,128 @@ package com.radixdlt.application.system.scrypt; -import com.radixdlt.atom.REFieldSerialization; -import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.application.system.state.RoundData; import com.radixdlt.application.system.state.ValidatorBFTData; +import com.radixdlt.atom.REFieldSerialization; +import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; import com.radixdlt.constraintmachine.Authorization; import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.PermissionLevel; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.constraintmachine.UpProcedure; import com.radixdlt.constraintmachine.VoidReducerState; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.utils.KeyComparator; - import java.util.TreeMap; public class RoundUpdateConstraintScrypt implements ConstraintScrypt { - private final long maxRounds; - - public RoundUpdateConstraintScrypt(long maxRounds) { - this.maxRounds = maxRounds; - } - - private class StartValidatorBFTUpdate implements ReducerState { - private final long closedRound; - private final TreeMap validatorsToUpdate = new TreeMap<>(KeyComparator.instance()); - - StartValidatorBFTUpdate(long closedRound) { - this.closedRound = closedRound; - } - - public ReducerState beginUpdate(ValidatorBFTData validatorBFTData) throws ProcedureException { - if (validatorsToUpdate.containsKey(validatorBFTData.getValidatorKey())) { - throw new ProcedureException("Validator already started to update."); - } - - validatorsToUpdate.put(validatorBFTData.getValidatorKey(), validatorBFTData); - return this; - } - - public UpdatingValidatorBFTData exit() { - return new UpdatingValidatorBFTData(maxRounds, closedRound, validatorsToUpdate); - } - } - - @Override - public void main(Loader os) { - - os.substate( - new SubstateDefinition<>( - ValidatorBFTData.class, - SubstateTypeId.VALIDATOR_BFT_DATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var key = REFieldSerialization.deserializeKey(buf); - var proposalsCompleted = REFieldSerialization.deserializeNonNegativeLong(buf); - var proposalsMissed = REFieldSerialization.deserializeNonNegativeLong(buf); - return new ValidatorBFTData(key, proposalsCompleted, proposalsMissed); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - buf.putLong(s.proposalsCompleted()); - buf.putLong(s.proposalsMissed()); - }, - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k) - ) - ); - - os.procedure(new DownProcedure<>( - VoidReducerState.class, RoundData.class, - d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (d, s, r, c) -> ReducerResult.incomplete(new EndPrevRound(d)) - )); - - os.procedure(new DownProcedure<>( - EndPrevRound.class, ValidatorBFTData.class, - d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (d, s, r, c) -> { - var closedRound = s.getClosedRound().getView(); - var next = new StartValidatorBFTUpdate(closedRound); - next.beginUpdate(d); - return ReducerResult.incomplete(next); - } - )); - - os.procedure(new DownProcedure<>( - StartValidatorBFTUpdate.class, ValidatorBFTData.class, - d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (d, s, r, c) -> ReducerResult.incomplete(s.beginUpdate(d)) - )); - - os.procedure(new UpProcedure<>( - StartValidatorBFTUpdate.class, ValidatorBFTData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> { - var next = s.exit(); - return ReducerResult.incomplete(next.update(u, c)); - } - )); - - os.procedure(new UpProcedure<>( - UpdatingValidatorBFTData.class, ValidatorBFTData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> ReducerResult.incomplete(s.update(u, c)) - )); - - os.procedure(new UpProcedure<>( - StartNextRound.class, RoundData.class, - u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }), - (s, u, c, r) -> { - s.update(u); - return ReducerResult.complete(); - } - )); - } + private final long maxRounds; + + public RoundUpdateConstraintScrypt(long maxRounds) { + this.maxRounds = maxRounds; + } + + private class StartValidatorBFTUpdate implements ReducerState { + private final long closedRound; + private final TreeMap validatorsToUpdate = + new TreeMap<>(KeyComparator.instance()); + + StartValidatorBFTUpdate(long closedRound) { + this.closedRound = closedRound; + } + + public ReducerState beginUpdate(ValidatorBFTData validatorBFTData) throws ProcedureException { + if (validatorsToUpdate.containsKey(validatorBFTData.getValidatorKey())) { + throw new ProcedureException("Validator already started to update."); + } + + validatorsToUpdate.put(validatorBFTData.getValidatorKey(), validatorBFTData); + return this; + } + + public UpdatingValidatorBFTData exit() { + return new UpdatingValidatorBFTData(maxRounds, closedRound, validatorsToUpdate); + } + } + + @Override + public void main(Loader os) { + + os.substate( + new SubstateDefinition<>( + ValidatorBFTData.class, + SubstateTypeId.VALIDATOR_BFT_DATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var key = REFieldSerialization.deserializeKey(buf); + var proposalsCompleted = REFieldSerialization.deserializeNonNegativeLong(buf); + var proposalsMissed = REFieldSerialization.deserializeNonNegativeLong(buf); + return new ValidatorBFTData(key, proposalsCompleted, proposalsMissed); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + buf.putLong(s.proposalsCompleted()); + buf.putLong(s.proposalsMissed()); + }, + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k))); + + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + RoundData.class, + d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (d, s, r, c) -> ReducerResult.incomplete(new EndPrevRound(d)))); + + os.procedure( + new DownProcedure<>( + EndPrevRound.class, + ValidatorBFTData.class, + d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (d, s, r, c) -> { + var closedRound = s.getClosedRound().getView(); + var next = new StartValidatorBFTUpdate(closedRound); + next.beginUpdate(d); + return ReducerResult.incomplete(next); + })); + + os.procedure( + new DownProcedure<>( + StartValidatorBFTUpdate.class, + ValidatorBFTData.class, + d -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (d, s, r, c) -> ReducerResult.incomplete(s.beginUpdate(d)))); + + os.procedure( + new UpProcedure<>( + StartValidatorBFTUpdate.class, + ValidatorBFTData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> { + var next = s.exit(); + return ReducerResult.incomplete(next.update(u, c)); + })); + + os.procedure( + new UpProcedure<>( + UpdatingValidatorBFTData.class, + ValidatorBFTData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> ReducerResult.incomplete(s.update(u, c)))); + + os.procedure( + new UpProcedure<>( + StartNextRound.class, + RoundData.class, + u -> new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}), + (s, u, c, r) -> { + s.update(u); + return ReducerResult.complete(); + })); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/StartNextRound.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/StartNextRound.java index 1bcff23eb1..170fa4bee5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/StartNextRound.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/StartNextRound.java @@ -65,18 +65,20 @@ package com.radixdlt.application.system.scrypt; import com.radixdlt.application.system.state.RoundData; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.ReducerState; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; public class StartNextRound implements ReducerState { - private final long expectedView; - public StartNextRound(long expectedView) { - this.expectedView = expectedView; - } + private final long expectedView; + + public StartNextRound(long expectedView) { + this.expectedView = expectedView; + } - public void update(RoundData next) throws ProcedureException { - if (this.expectedView != next.getView()) { - throw new ProcedureException("Expected view " + this.expectedView + " but was " + next.getView()); - } - } + public void update(RoundData next) throws ProcedureException { + if (this.expectedView != next.getView()) { + throw new ProcedureException( + "Expected view " + this.expectedView + " but was " + next.getView()); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/Syscall.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/Syscall.java index b26ea1b0df..5125bf405d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/Syscall.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/Syscall.java @@ -67,27 +67,27 @@ import java.util.Optional; public enum Syscall { - FEE_RESERVE_PUT((byte) 0), - FEE_RESERVE_TAKE((byte) 1), - READDR_CLAIM((byte) 2); + FEE_RESERVE_PUT((byte) 0), + FEE_RESERVE_TAKE((byte) 1), + READDR_CLAIM((byte) 2); - private final byte id; + private final byte id; - Syscall(byte id) { - this.id = id; - } + Syscall(byte id) { + this.id = id; + } - public byte id() { - return id; - } + public byte id() { + return id; + } - public static Optional of(byte id) { - for (var s : Syscall.values()) { - if (s.id == id) { - return Optional.of(s); - } - } + public static Optional of(byte id) { + for (var s : Syscall.values()) { + if (s.id == id) { + return Optional.of(s); + } + } - return Optional.empty(); - } + return Optional.empty(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/SystemConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/SystemConstraintScrypt.java index e2f4287017..42feb9d126 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/SystemConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/SystemConstraintScrypt.java @@ -66,6 +66,7 @@ import com.radixdlt.application.system.state.EpochData; import com.radixdlt.application.system.state.RoundData; +import com.radixdlt.application.system.state.UnclaimedREAddr; import com.radixdlt.application.system.state.VirtualParent; import com.radixdlt.application.tokens.scrypt.TokenHoldingBucket; import com.radixdlt.atom.REFieldSerialization; @@ -73,261 +74,263 @@ import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; -import com.radixdlt.application.system.state.UnclaimedREAddr; import com.radixdlt.constraintmachine.Authorization; import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.ExecutionContext; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.ReducerState; +import com.radixdlt.constraintmachine.SystemCallProcedure; import com.radixdlt.constraintmachine.UpProcedure; import com.radixdlt.constraintmachine.VoidReducerState; import com.radixdlt.constraintmachine.exceptions.InvalidHashedKeyException; import com.radixdlt.constraintmachine.exceptions.ProcedureException; -import com.radixdlt.constraintmachine.ReducerResult; -import com.radixdlt.constraintmachine.SystemCallProcedure; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.Bytes; - import java.util.Arrays; import java.util.EnumSet; import java.util.LinkedList; public final class SystemConstraintScrypt implements ConstraintScrypt { - public static final int MAX_SYMBOL_LENGTH = 32; + public static final int MAX_SYMBOL_LENGTH = 32; + + private static class AllocatingSystem implements ReducerState {} - private static class AllocatingSystem implements ReducerState { - } + private static class AllocatingVirtualState implements ReducerState { + private final LinkedList substatesToVirtualize = new LinkedList<>(); - private static class AllocatingVirtualState implements ReducerState { - private final LinkedList substatesToVirtualize = new LinkedList<>(); - AllocatingVirtualState() { - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_META_DATA); - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_STAKE_DATA); - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG); - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY); - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_RAKE_COPY); - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_OWNER_COPY); - substatesToVirtualize.add(SubstateTypeId.VALIDATOR_SYSTEM_META_DATA); - } + AllocatingVirtualState() { + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_META_DATA); + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_STAKE_DATA); + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG); + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY); + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_RAKE_COPY); + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_OWNER_COPY); + substatesToVirtualize.add(SubstateTypeId.VALIDATOR_SYSTEM_META_DATA); + } - public ReducerState createVirtualSubstate(VirtualParent virtualParent) throws ProcedureException { - var typeId = substatesToVirtualize.remove(0); - if (!Arrays.equals(virtualParent.getData(), new byte[] {typeId.id()})) { - throw new ProcedureException("Expected " + typeId + " but was " + Bytes.toHexString(virtualParent.getData())); - } - return substatesToVirtualize.isEmpty() ? null : this; - } - } + public ReducerState createVirtualSubstate(VirtualParent virtualParent) + throws ProcedureException { + var typeId = substatesToVirtualize.remove(0); + if (!Arrays.equals(virtualParent.getData(), new byte[] {typeId.id()})) { + throw new ProcedureException( + "Expected " + typeId + " but was " + Bytes.toHexString(virtualParent.getData())); + } + return substatesToVirtualize.isEmpty() ? null : this; + } + } + public static class REAddrClaim implements ReducerState { + private final byte[] arg; + private final UnclaimedREAddr unclaimedREAddr; - public static class REAddrClaim implements ReducerState { - private final byte[] arg; - private final UnclaimedREAddr unclaimedREAddr; + public REAddrClaim(UnclaimedREAddr unclaimedREAddr, byte[] arg) { + this.unclaimedREAddr = unclaimedREAddr; + this.arg = arg; + } - public REAddrClaim(UnclaimedREAddr unclaimedREAddr, byte[] arg) { - this.unclaimedREAddr = unclaimedREAddr; - this.arg = arg; - } + public byte[] getArg() { + return arg; + } - public byte[] getArg() { - return arg; - } + public REAddr getAddr() { + return unclaimedREAddr.getAddr(); + } + } - public REAddr getAddr() { - return unclaimedREAddr.getAddr(); - } - } + public static class REAddrClaimStart implements ReducerState { + private final byte[] arg; - public static class REAddrClaimStart implements ReducerState { - private final byte[] arg; - public REAddrClaimStart(byte[] arg) { - this.arg = arg; - } + public REAddrClaimStart(byte[] arg) { + this.arg = arg; + } - public ReducerState claim(UnclaimedREAddr unclaimedREAddr, ExecutionContext ctx) throws ProcedureException, InvalidHashedKeyException { - if (ctx.permissionLevel() != PermissionLevel.SYSTEM && !ctx.skipAuthorization()) { - var key = ctx.key().orElseThrow(() -> new ProcedureException("Missing key")); - unclaimedREAddr.verifyHashedKey(key, arg); - } - return new REAddrClaim(unclaimedREAddr, arg); - } - } + public ReducerState claim(UnclaimedREAddr unclaimedREAddr, ExecutionContext ctx) + throws ProcedureException, InvalidHashedKeyException { + if (ctx.permissionLevel() != PermissionLevel.SYSTEM && !ctx.skipAuthorization()) { + var key = ctx.key().orElseThrow(() -> new ProcedureException("Missing key")); + unclaimedREAddr.verifyHashedKey(key, arg); + } + return new REAddrClaim(unclaimedREAddr, arg); + } + } - @Override - public void main(Loader os) { - os.substate( - new SubstateDefinition<>( - VirtualParent.class, - SubstateTypeId.VIRTUAL_PARENT.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var data = new byte[buf.remaining()]; - buf.get(data); - return new VirtualParent(data); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - buf.put(s.getData()); - } - ) - ); + @Override + public void main(Loader os) { + os.substate( + new SubstateDefinition<>( + VirtualParent.class, + SubstateTypeId.VIRTUAL_PARENT.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var data = new byte[buf.remaining()]; + buf.get(data); + return new VirtualParent(data); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + buf.put(s.getData()); + })); - // TODO: Down singleton - os.procedure(new UpProcedure<>( - VoidReducerState.class, VirtualParent.class, - u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> { }), - (s, u, c, r) -> { - if (u.getData().length != 1) { - throw new ProcedureException("Invalid data: " + Bytes.toHexString(u.getData())); - } - if (u.getData()[0] != SubstateTypeId.UNCLAIMED_READDR.id()) { - throw new ProcedureException("Invalid data: " + Bytes.toHexString(u.getData())); - } - return ReducerResult.complete(); - } - )); + // TODO: Down singleton + os.procedure( + new UpProcedure<>( + VoidReducerState.class, + VirtualParent.class, + u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> {}), + (s, u, c, r) -> { + if (u.getData().length != 1) { + throw new ProcedureException("Invalid data: " + Bytes.toHexString(u.getData())); + } + if (u.getData()[0] != SubstateTypeId.UNCLAIMED_READDR.id()) { + throw new ProcedureException("Invalid data: " + Bytes.toHexString(u.getData())); + } + return ReducerResult.complete(); + })); - os.substate( - new SubstateDefinition<>( - EpochData.class, - SubstateTypeId.EPOCH_DATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var epoch = REFieldSerialization.deserializeNonNegativeLong(buf); - return new EpochData(epoch); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - buf.putLong(s.getEpoch()); - } - ) - ); + os.substate( + new SubstateDefinition<>( + EpochData.class, + SubstateTypeId.EPOCH_DATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var epoch = REFieldSerialization.deserializeNonNegativeLong(buf); + return new EpochData(epoch); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + buf.putLong(s.getEpoch()); + })); - os.substate( - new SubstateDefinition<>( - RoundData.class, - SubstateTypeId.ROUND_DATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var view = REFieldSerialization.deserializeNonNegativeLong(buf); - var timestamp = REFieldSerialization.deserializeNonNegativeLong(buf); - return new RoundData(view, timestamp); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - buf.putLong(s.getView()); - buf.putLong(s.getTimestamp()); - } - ) - ); + os.substate( + new SubstateDefinition<>( + RoundData.class, + SubstateTypeId.ROUND_DATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var view = REFieldSerialization.deserializeNonNegativeLong(buf); + var timestamp = REFieldSerialization.deserializeNonNegativeLong(buf); + return new RoundData(view, timestamp); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + buf.putLong(s.getView()); + buf.putLong(s.getTimestamp()); + })); - os.procedure( - new SystemCallProcedure<>( - TokenHoldingBucket.class, REAddr.ofSystem(), - () -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, d, c) -> { - var id = d.get(0); - var syscall = Syscall.of(id).orElseThrow(() -> new ProcedureException("Invalid call type " + id)); - if (syscall != Syscall.FEE_RESERVE_PUT) { - throw new ProcedureException("Invalid call type: " + syscall); - } + os.procedure( + new SystemCallProcedure<>( + TokenHoldingBucket.class, + REAddr.ofSystem(), + () -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, d, c) -> { + var id = d.get(0); + var syscall = + Syscall.of(id) + .orElseThrow(() -> new ProcedureException("Invalid call type " + id)); + if (syscall != Syscall.FEE_RESERVE_PUT) { + throw new ProcedureException("Invalid call type: " + syscall); + } - var amt = d.getUInt256(1); - var tokens = s.withdraw(REAddr.ofNativeToken(), amt); - c.depositFeeReserve(tokens); - return ReducerResult.incomplete(s); - } - ) - ); + var amt = d.getUInt256(1); + var tokens = s.withdraw(REAddr.ofNativeToken(), amt); + c.depositFeeReserve(tokens); + return ReducerResult.incomplete(s); + })); - os.procedure( - new SystemCallProcedure<>( - VoidReducerState.class, REAddr.ofSystem(), - () -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, d, c) -> { - var id = d.get(0); - var syscall = Syscall.of(id).orElseThrow(() -> new ProcedureException("Invalid call type " + id)); - if (syscall == Syscall.FEE_RESERVE_TAKE) { - var amt = d.getUInt256(1); - var tokens = c.withdrawFeeReserve(amt); - return ReducerResult.incomplete(new TokenHoldingBucket(tokens)); - } else if (syscall == Syscall.READDR_CLAIM) { - var bytes = d.getRemainingBytes(1); - if (bytes.length > MAX_SYMBOL_LENGTH) { - throw new ProcedureException("Address claim too large."); - } - return ReducerResult.incomplete(new REAddrClaimStart(bytes)); - } else { - throw new ProcedureException("Invalid call type: " + syscall); - } - } - ) - ); + os.procedure( + new SystemCallProcedure<>( + VoidReducerState.class, + REAddr.ofSystem(), + () -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, d, c) -> { + var id = d.get(0); + var syscall = + Syscall.of(id) + .orElseThrow(() -> new ProcedureException("Invalid call type " + id)); + if (syscall == Syscall.FEE_RESERVE_TAKE) { + var amt = d.getUInt256(1); + var tokens = c.withdrawFeeReserve(amt); + return ReducerResult.incomplete(new TokenHoldingBucket(tokens)); + } else if (syscall == Syscall.READDR_CLAIM) { + var bytes = d.getRemainingBytes(1); + if (bytes.length > MAX_SYMBOL_LENGTH) { + throw new ProcedureException("Address claim too large."); + } + return ReducerResult.incomplete(new REAddrClaimStart(bytes)); + } else { + throw new ProcedureException("Invalid call type: " + syscall); + } + })); - // PUB_KEY type is already claimed by accounts - var claimableAddrTypes = - EnumSet.of(REAddr.REAddrType.NATIVE_TOKEN, REAddr.REAddrType.HASHED_KEY, REAddr.REAddrType.SYSTEM); - os.substate( - new SubstateDefinition<>( - UnclaimedREAddr.class, - SubstateTypeId.UNCLAIMED_READDR.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var addr = REFieldSerialization.deserializeREAddr(buf, claimableAddrTypes); - return new UnclaimedREAddr(addr); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeREAddr(buf, s.getAddr()); - }, - buf -> REFieldSerialization.deserializeREAddr(buf, claimableAddrTypes), - (a, buf) -> REFieldSerialization.serializeREAddr(buf, (REAddr) a), - k -> new UnclaimedREAddr((REAddr) k) - )); + // PUB_KEY type is already claimed by accounts + var claimableAddrTypes = + EnumSet.of( + REAddr.REAddrType.NATIVE_TOKEN, REAddr.REAddrType.HASHED_KEY, REAddr.REAddrType.SYSTEM); + os.substate( + new SubstateDefinition<>( + UnclaimedREAddr.class, + SubstateTypeId.UNCLAIMED_READDR.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var addr = REFieldSerialization.deserializeREAddr(buf, claimableAddrTypes); + return new UnclaimedREAddr(addr); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeREAddr(buf, s.getAddr()); + }, + buf -> REFieldSerialization.deserializeREAddr(buf, claimableAddrTypes), + (a, buf) -> REFieldSerialization.serializeREAddr(buf, (REAddr) a), + k -> new UnclaimedREAddr((REAddr) k))); - os.procedure(new DownProcedure<>( - REAddrClaimStart.class, UnclaimedREAddr.class, - d -> { - final PermissionLevel permissionLevel; - if (d.getAddr().isNativeToken() || d.getAddr().isSystem()) { - permissionLevel = PermissionLevel.SYSTEM; - } else { - permissionLevel = PermissionLevel.USER; - } - return new Authorization(permissionLevel, (r, ctx) -> { }); - }, - (d, s, r, c) -> ReducerResult.incomplete(s.claim(d, c)) - )); + os.procedure( + new DownProcedure<>( + REAddrClaimStart.class, + UnclaimedREAddr.class, + d -> { + final PermissionLevel permissionLevel; + if (d.getAddr().isNativeToken() || d.getAddr().isSystem()) { + permissionLevel = PermissionLevel.SYSTEM; + } else { + permissionLevel = PermissionLevel.USER; + } + return new Authorization(permissionLevel, (r, ctx) -> {}); + }, + (d, s, r, c) -> ReducerResult.incomplete(s.claim(d, c)))); - // For Mainnet Genesis - os.procedure(new UpProcedure<>( - SystemConstraintScrypt.REAddrClaim.class, EpochData.class, - u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> { }), - (s, u, c, r) -> { - if (u.getEpoch() != 0) { - throw new ProcedureException("First epoch must be 0."); - } + // For Mainnet Genesis + os.procedure( + new UpProcedure<>( + SystemConstraintScrypt.REAddrClaim.class, + EpochData.class, + u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> {}), + (s, u, c, r) -> { + if (u.getEpoch() != 0) { + throw new ProcedureException("First epoch must be 0."); + } - return ReducerResult.incomplete(new AllocatingSystem()); - } - )); - os.procedure(new UpProcedure<>( - AllocatingSystem.class, RoundData.class, - u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> { }), - (s, u, c, r) -> { - if (u.getView() != 0) { - throw new ProcedureException("First view must be 0."); - } - return ReducerResult.incomplete(new AllocatingVirtualState()); - } - )); - os.procedure(new UpProcedure<>( - AllocatingVirtualState.class, VirtualParent.class, - u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> { }), - (s, u, c, r) -> { - var next = s.createVirtualSubstate(u); - return next == null ? ReducerResult.complete() : ReducerResult.incomplete(next); - } - )); - } + return ReducerResult.incomplete(new AllocatingSystem()); + })); + os.procedure( + new UpProcedure<>( + AllocatingSystem.class, + RoundData.class, + u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> {}), + (s, u, c, r) -> { + if (u.getView() != 0) { + throw new ProcedureException("First view must be 0."); + } + return ReducerResult.incomplete(new AllocatingVirtualState()); + })); + os.procedure( + new UpProcedure<>( + AllocatingVirtualState.class, + VirtualParent.class, + u -> new Authorization(PermissionLevel.SYSTEM, (r, c) -> {}), + (s, u, c, r) -> { + var next = s.createVirtualSubstate(u); + return next == null ? ReducerResult.complete() : ReducerResult.incomplete(next); + })); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/UpdatingValidatorBFTData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/UpdatingValidatorBFTData.java index f127d9e002..7f412f6b4d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/UpdatingValidatorBFTData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/UpdatingValidatorBFTData.java @@ -67,64 +67,70 @@ import com.radixdlt.application.system.ValidatorMissedProposalsEvent; import com.radixdlt.application.system.state.ValidatorBFTData; import com.radixdlt.constraintmachine.ExecutionContext; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.ReducerState; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; - import java.util.TreeMap; public class UpdatingValidatorBFTData implements ReducerState { - private final long maxRounds; - private final TreeMap validatorsToUpdate; - private long expectedNextView; + private final long maxRounds; + private final TreeMap validatorsToUpdate; + private long expectedNextView; - UpdatingValidatorBFTData(long maxRounds, long view, TreeMap validatorsToUpdate) { - this.maxRounds = maxRounds; - this.expectedNextView = view; - this.validatorsToUpdate = validatorsToUpdate; - } + UpdatingValidatorBFTData( + long maxRounds, long view, TreeMap validatorsToUpdate) { + this.maxRounds = maxRounds; + this.expectedNextView = view; + this.validatorsToUpdate = validatorsToUpdate; + } - private void incrementViews(long count) throws ProcedureException { - if (this.expectedNextView + count < this.expectedNextView) { - throw new ProcedureException("View overflow"); - } + private void incrementViews(long count) throws ProcedureException { + if (this.expectedNextView + count < this.expectedNextView) { + throw new ProcedureException("View overflow"); + } - if (this.expectedNextView + count > maxRounds) { - throw new ProcedureException("Max rounds is " + maxRounds + " but attempting to execute " - + (this.expectedNextView + count)); - } + if (this.expectedNextView + count > maxRounds) { + throw new ProcedureException( + "Max rounds is " + + maxRounds + + " but attempting to execute " + + (this.expectedNextView + count)); + } - this.expectedNextView += count; - } + this.expectedNextView += count; + } - public ReducerState update(ValidatorBFTData next, ExecutionContext context) throws ProcedureException { - var first = validatorsToUpdate.firstKey(); - if (!next.getValidatorKey().equals(first)) { - throw new ProcedureException("Invalid key for validator bft data update"); - } - var old = validatorsToUpdate.remove(first); - if (old.proposalsCompleted() > next.proposalsCompleted() - || old.proposalsMissed() > next.proposalsMissed()) { - throw new ProcedureException("Invalid data for validator bft data update"); - } + public ReducerState update(ValidatorBFTData next, ExecutionContext context) + throws ProcedureException { + var first = validatorsToUpdate.firstKey(); + if (!next.getValidatorKey().equals(first)) { + throw new ProcedureException("Invalid key for validator bft data update"); + } + var old = validatorsToUpdate.remove(first); + if (old.proposalsCompleted() > next.proposalsCompleted() + || old.proposalsMissed() > next.proposalsMissed()) { + throw new ProcedureException("Invalid data for validator bft data update"); + } - if (old.proposalsCompleted() == next.proposalsCompleted() && old.proposalsMissed() == next.proposalsMissed()) { - throw new ProcedureException("No update to Validator BFT data"); - } + if (old.proposalsCompleted() == next.proposalsCompleted() + && old.proposalsMissed() == next.proposalsMissed()) { + throw new ProcedureException("No update to Validator BFT data"); + } - var additionalProposalsCompleted = next.proposalsCompleted() - old.proposalsCompleted(); - var additionalProposalsMissed = next.proposalsMissed() - old.proposalsMissed(); - if (additionalProposalsMissed > 0) { - context.emitEvent(ValidatorMissedProposalsEvent.create(next.getValidatorKey(), additionalProposalsMissed)); - } + var additionalProposalsCompleted = next.proposalsCompleted() - old.proposalsCompleted(); + var additionalProposalsMissed = next.proposalsMissed() - old.proposalsMissed(); + if (additionalProposalsMissed > 0) { + context.emitEvent( + ValidatorMissedProposalsEvent.create(next.getValidatorKey(), additionalProposalsMissed)); + } - incrementViews(additionalProposalsCompleted); - incrementViews(additionalProposalsMissed); + incrementViews(additionalProposalsCompleted); + incrementViews(additionalProposalsMissed); - if (!validatorsToUpdate.isEmpty()) { - return this; - } + if (!validatorsToUpdate.isEmpty()) { + return this; + } - return new StartNextRound(this.expectedNextView); - } + return new StartNextRound(this.expectedNextView); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/ValidatorScratchPad.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/ValidatorScratchPad.java index f795703382..bfabb1c059 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/ValidatorScratchPad.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/scrypt/ValidatorScratchPad.java @@ -73,96 +73,95 @@ import com.radixdlt.utils.UInt384; public final class ValidatorScratchPad { - private final ECPublicKey validatorKey; - private UInt384 totalStake; - private UInt384 totalOwnership; - private int rakePercentage; - private REAddr ownerAddr; - private boolean isRegistered; - - public ValidatorScratchPad(ValidatorStakeData validatorStakeData) { - this.totalStake = UInt384.from(validatorStakeData.getTotalStake()); - this.totalOwnership = UInt384.from(validatorStakeData.getTotalOwnership()); - this.rakePercentage = validatorStakeData.getRakePercentage(); - this.ownerAddr = validatorStakeData.getOwnerAddr(); - this.isRegistered = validatorStakeData.isRegistered(); - this.validatorKey = validatorStakeData.getValidatorKey(); - } - - public ECPublicKey getValidatorKey() { - return validatorKey; - } - - public REAddr getOwnerAddr() { - return ownerAddr; - } - - public int getRakePercentage() { - return rakePercentage; - } - - public void setRegistered(boolean isRegistered) { - this.isRegistered = isRegistered; - } - - public void setRakePercentage(int rakePercentage) { - this.rakePercentage = rakePercentage; - } - - public void setOwnerAddr(REAddr ownerAddr) { - this.ownerAddr = ownerAddr; - } - - public void addEmission(UInt256 amount) { - this.totalStake = verifyNoOverflow(this.totalStake.add(amount)); - } - - private static UInt384 verifyNoOverflow(UInt384 i) { - if (!i.getHigh().isZero()) { - throw new IllegalStateException("Unexpected overflow occurred " + i); - } - return i; - } - - private static UInt256 toSafeLow(UInt384 i) { - return verifyNoOverflow(i).getLow(); - } - - public StakeOwnership stake(REAddr owner, UInt256 stake) { - if (totalStake.isZero()) { - this.totalStake = UInt384.from(stake); - this.totalOwnership = this.totalStake; - return new StakeOwnership(validatorKey, owner, this.totalOwnership.getLow()); - } - - var ownership384 = totalOwnership.multiply(stake).divide(totalStake); - var ownershipAmt = toSafeLow(ownership384); - this.totalStake = verifyNoOverflow(this.totalStake.add(stake)); - this.totalOwnership = verifyNoOverflow(this.totalOwnership.add(ownershipAmt)); - - return new StakeOwnership(validatorKey, owner, ownershipAmt); - } - - public ExitingStake unstakeOwnership(REAddr owner, UInt256 unstakeOwnership, long epochUnlocked) { - if (totalOwnership.getLow().compareTo(unstakeOwnership) < 0) { - throw new IllegalStateException("Not enough ownership"); - } - - var unstaked384 = totalStake.multiply(unstakeOwnership).divide(totalOwnership); - var unstaked = toSafeLow(unstaked384); - this.totalStake = this.totalStake.subtract(unstaked); - this.totalOwnership = this.totalOwnership.subtract(unstakeOwnership); - return new ExitingStake(epochUnlocked, validatorKey, owner, unstaked); - } - - public ValidatorStakeData toSubstate() { - return ValidatorStakeData.create( - validatorKey, - totalStake.getLow(), - totalOwnership.getLow(), - rakePercentage, - ownerAddr, - isRegistered - ); - } + private final ECPublicKey validatorKey; + private UInt384 totalStake; + private UInt384 totalOwnership; + private int rakePercentage; + private REAddr ownerAddr; + private boolean isRegistered; + + public ValidatorScratchPad(ValidatorStakeData validatorStakeData) { + this.totalStake = UInt384.from(validatorStakeData.getTotalStake()); + this.totalOwnership = UInt384.from(validatorStakeData.getTotalOwnership()); + this.rakePercentage = validatorStakeData.getRakePercentage(); + this.ownerAddr = validatorStakeData.getOwnerAddr(); + this.isRegistered = validatorStakeData.isRegistered(); + this.validatorKey = validatorStakeData.getValidatorKey(); + } + + public ECPublicKey getValidatorKey() { + return validatorKey; + } + + public REAddr getOwnerAddr() { + return ownerAddr; + } + + public int getRakePercentage() { + return rakePercentage; + } + + public void setRegistered(boolean isRegistered) { + this.isRegistered = isRegistered; + } + + public void setRakePercentage(int rakePercentage) { + this.rakePercentage = rakePercentage; + } + + public void setOwnerAddr(REAddr ownerAddr) { + this.ownerAddr = ownerAddr; + } + + public void addEmission(UInt256 amount) { + this.totalStake = verifyNoOverflow(this.totalStake.add(amount)); + } + + private static UInt384 verifyNoOverflow(UInt384 i) { + if (!i.getHigh().isZero()) { + throw new IllegalStateException("Unexpected overflow occurred " + i); + } + return i; + } + + private static UInt256 toSafeLow(UInt384 i) { + return verifyNoOverflow(i).getLow(); + } + + public StakeOwnership stake(REAddr owner, UInt256 stake) { + if (totalStake.isZero()) { + this.totalStake = UInt384.from(stake); + this.totalOwnership = this.totalStake; + return new StakeOwnership(validatorKey, owner, this.totalOwnership.getLow()); + } + + var ownership384 = totalOwnership.multiply(stake).divide(totalStake); + var ownershipAmt = toSafeLow(ownership384); + this.totalStake = verifyNoOverflow(this.totalStake.add(stake)); + this.totalOwnership = verifyNoOverflow(this.totalOwnership.add(ownershipAmt)); + + return new StakeOwnership(validatorKey, owner, ownershipAmt); + } + + public ExitingStake unstakeOwnership(REAddr owner, UInt256 unstakeOwnership, long epochUnlocked) { + if (totalOwnership.getLow().compareTo(unstakeOwnership) < 0) { + throw new IllegalStateException("Not enough ownership"); + } + + var unstaked384 = totalStake.multiply(unstakeOwnership).divide(totalOwnership); + var unstaked = toSafeLow(unstaked384); + this.totalStake = this.totalStake.subtract(unstaked); + this.totalOwnership = this.totalOwnership.subtract(unstakeOwnership); + return new ExitingStake(epochUnlocked, validatorKey, owner, unstaked); + } + + public ValidatorStakeData toSubstate() { + return ValidatorStakeData.create( + validatorKey, + totalStake.getLow(), + totalOwnership.getLow(), + rakePercentage, + ownerAddr, + isRegistered); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/EpochData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/EpochData.java index aca41a176e..8aa0d5ccf5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/EpochData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/EpochData.java @@ -67,35 +67,35 @@ import com.google.common.base.Objects; public final class EpochData implements SystemData, HasEpochData { - private final long epoch; + private final long epoch; - public EpochData(long epoch) { - this.epoch = epoch; - } + public EpochData(long epoch) { + this.epoch = epoch; + } - @Override - public long getEpoch() { - return epoch; - } + @Override + public long getEpoch() { + return epoch; + } - @Override - public int hashCode() { - return Objects.hashCode(epoch); - } + @Override + public int hashCode() { + return Objects.hashCode(epoch); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof EpochData)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof EpochData)) { + return false; + } - var other = (EpochData) o; + var other = (EpochData) o; - return this.epoch == other.epoch; - } + return this.epoch == other.epoch; + } - @Override - public String toString() { - return String.format("%s{epoch=%s}", this.getClass().getSimpleName(), epoch); - } + @Override + public String toString() { + return String.format("%s{epoch=%s}", this.getClass().getSimpleName(), epoch); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/HasEpochData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/HasEpochData.java index 49c732a198..fe19a8c1c5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/HasEpochData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/HasEpochData.java @@ -65,5 +65,5 @@ package com.radixdlt.application.system.state; public interface HasEpochData { - long getEpoch(); + long getEpoch(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/RoundData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/RoundData.java index 5609a718fb..8d7feb371e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/RoundData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/RoundData.java @@ -65,49 +65,48 @@ package com.radixdlt.application.system.state; import com.google.common.base.Objects; - import java.time.Instant; public final class RoundData implements SystemData { - private final long view; - private final long timestamp; + private final long view; + private final long timestamp; - public RoundData(long view, long timestamp) { - this.view = view; - this.timestamp = timestamp; - } + public RoundData(long view, long timestamp) { + this.view = view; + this.timestamp = timestamp; + } - public long getView() { - return view; - } + public long getView() { + return view; + } - public long getTimestamp() { - return timestamp; - } + public long getTimestamp() { + return timestamp; + } - public Instant asInstant() { - return Instant.ofEpochMilli(timestamp); - } + public Instant asInstant() { + return Instant.ofEpochMilli(timestamp); + } - @Override - public int hashCode() { - return Objects.hashCode(view, timestamp); - } + @Override + public int hashCode() { + return Objects.hashCode(view, timestamp); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof RoundData)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof RoundData)) { + return false; + } - var other = (RoundData) o; + var other = (RoundData) o; - return this.view == other.view - && this.timestamp == other.timestamp; - } + return this.view == other.view && this.timestamp == other.timestamp; + } - @Override - public String toString() { - return String.format("%s{view=%s timestamp=%s}", this.getClass().getSimpleName(), view, timestamp); - } + @Override + public String toString() { + return String.format( + "%s{view=%s timestamp=%s}", this.getClass().getSimpleName(), view, timestamp); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeBucket.java index 013a9cb0e7..57908cf68c 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeBucket.java @@ -69,53 +69,52 @@ import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class StakeBucket implements Bucket { - private final ECPublicKey validatorKey; + private final ECPublicKey validatorKey; - public StakeBucket(ECPublicKey validatorKey) { - this.validatorKey = validatorKey; - } + public StakeBucket(ECPublicKey validatorKey) { + this.validatorKey = validatorKey; + } - @Override - public Authorization withdrawAuthorization() { - return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }); - } + @Override + public Authorization withdrawAuthorization() { + return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}); + } - @Override - public REAddr resourceAddr() { - return REAddr.ofNativeToken(); - } + @Override + public REAddr resourceAddr() { + return REAddr.ofNativeToken(); + } - @Override - public REAddr getOwner() { - return null; - } + @Override + public REAddr getOwner() { + return null; + } - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } - @Override - public Long getEpochUnlock() { - return null; - } + @Override + public Long getEpochUnlock() { + return null; + } - @Override - public int hashCode() { - return Objects.hash(validatorKey); - } + @Override + public int hashCode() { + return Objects.hash(validatorKey); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof StakeBucket)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof StakeBucket)) { + return false; + } - var other = (StakeBucket) o; - return Objects.equals(this.validatorKey, other.validatorKey); - } + var other = (StakeBucket) o; + return Objects.equals(this.validatorKey, other.validatorKey); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnership.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnership.java index 26a1869a00..01b70f21e5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnership.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnership.java @@ -64,81 +64,68 @@ package com.radixdlt.application.system.state; -import com.radixdlt.application.tokens.ResourceInBucket; import com.radixdlt.application.tokens.Bucket; +import com.radixdlt.application.tokens.ResourceInBucket; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class StakeOwnership implements ResourceInBucket { - private final UInt256 amount; + private final UInt256 amount; - // Bucket keys - private final REAddr owner; - private final ECPublicKey delegateKey; + // Bucket keys + private final REAddr owner; + private final ECPublicKey delegateKey; - public StakeOwnership( - ECPublicKey delegateKey, - REAddr owner, - UInt256 amount - ) { - if (amount.isZero()) { - throw new IllegalArgumentException("Stake ownership should not be zero"); - } - this.delegateKey = Objects.requireNonNull(delegateKey); - this.owner = Objects.requireNonNull(owner); - this.amount = Objects.requireNonNull(amount); - } + public StakeOwnership(ECPublicKey delegateKey, REAddr owner, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Stake ownership should not be zero"); + } + this.delegateKey = Objects.requireNonNull(delegateKey); + this.owner = Objects.requireNonNull(owner); + this.amount = Objects.requireNonNull(amount); + } - @Override - public UInt256 getAmount() { - return this.amount; - } + @Override + public UInt256 getAmount() { + return this.amount; + } - public Bucket bucket() { - return StakeOwnershipBucket.from(delegateKey, owner); - } + public Bucket bucket() { + return StakeOwnershipBucket.from(delegateKey, owner); + } - public ECPublicKey getDelegateKey() { - return delegateKey; - } + public ECPublicKey getDelegateKey() { + return delegateKey; + } - public REAddr getOwner() { - return this.owner; - } + public REAddr getOwner() { + return this.owner; + } - @Override - public String toString() { - return String.format("%s{delegate=%s owner=%s amt=%s}", - getClass().getSimpleName(), - delegateKey, - owner, - amount - ); - } + @Override + public String toString() { + return String.format( + "%s{delegate=%s owner=%s amt=%s}", getClass().getSimpleName(), delegateKey, owner, amount); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof StakeOwnership)) { - return false; - } - StakeOwnership that = (StakeOwnership) o; - return Objects.equals(delegateKey, that.delegateKey) - && Objects.equals(owner, that.owner) - && Objects.equals(amount, that.amount); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof StakeOwnership)) { + return false; + } + StakeOwnership that = (StakeOwnership) o; + return Objects.equals(delegateKey, that.delegateKey) + && Objects.equals(owner, that.owner) + && Objects.equals(amount, that.amount); + } - @Override - public int hashCode() { - return Objects.hash( - delegateKey, - owner, - amount - ); - } + @Override + public int hashCode() { + return Objects.hash(delegateKey, owner, amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnershipBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnershipBucket.java index fa56a25d0f..d4ddddc625 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnershipBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/StakeOwnershipBucket.java @@ -65,77 +65,72 @@ package com.radixdlt.application.system.state; import com.radixdlt.application.tokens.Bucket; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.Authorization; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class StakeOwnershipBucket implements Bucket { - private final REAddr owner; - private final ECPublicKey delegateKey; + private final REAddr owner; + private final ECPublicKey delegateKey; - private StakeOwnershipBucket( - ECPublicKey delegateKey, - REAddr owner - ) { - this.delegateKey = Objects.requireNonNull(delegateKey); - this.owner = Objects.requireNonNull(owner); - } + private StakeOwnershipBucket(ECPublicKey delegateKey, REAddr owner) { + this.delegateKey = Objects.requireNonNull(delegateKey); + this.owner = Objects.requireNonNull(owner); + } - public static StakeOwnershipBucket from(ECPublicKey validator, REAddr owner) { - return new StakeOwnershipBucket(validator, owner); - } + public static StakeOwnershipBucket from(ECPublicKey validator, REAddr owner) { + return new StakeOwnershipBucket(validator, owner); + } - @Override - public Authorization withdrawAuthorization() { - return new Authorization( - PermissionLevel.USER, - (r, c) -> { - try { - owner.verifyWithdrawAuthorization(c.key()); - } catch (REAddr.BucketWithdrawAuthorizationException e) { - throw new AuthorizationException(e.getMessage()); - } - } - ); - } + @Override + public Authorization withdrawAuthorization() { + return new Authorization( + PermissionLevel.USER, + (r, c) -> { + try { + owner.verifyWithdrawAuthorization(c.key()); + } catch (REAddr.BucketWithdrawAuthorizationException e) { + throw new AuthorizationException(e.getMessage()); + } + }); + } - @Override - public REAddr resourceAddr() { - return null; - } + @Override + public REAddr resourceAddr() { + return null; + } - @Override - public REAddr getOwner() { - return owner; - } + @Override + public REAddr getOwner() { + return owner; + } - @Override - public ECPublicKey getValidatorKey() { - return delegateKey; - } + @Override + public ECPublicKey getValidatorKey() { + return delegateKey; + } - @Override - public Long getEpochUnlock() { - return null; - } + @Override + public Long getEpochUnlock() { + return null; + } - @Override - public int hashCode() { - return Objects.hash(owner, delegateKey); - } + @Override + public int hashCode() { + return Objects.hash(owner, delegateKey); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof StakeOwnershipBucket)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof StakeOwnershipBucket)) { + return false; + } - var other = (StakeOwnershipBucket) o; - return Objects.equals(this.owner, other.owner) - && Objects.equals(this.delegateKey, other.delegateKey); - } + var other = (StakeOwnershipBucket) o; + return Objects.equals(this.owner, other.owner) + && Objects.equals(this.delegateKey, other.delegateKey); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/SystemData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/SystemData.java index 04ac2850b4..db673c2401 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/SystemData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/SystemData.java @@ -66,5 +66,4 @@ import com.radixdlt.constraintmachine.Particle; -public interface SystemData extends Particle { -} +public interface SystemData extends Particle {} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/UnclaimedREAddr.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/UnclaimedREAddr.java index 6604e6a416..8b3c07f071 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/UnclaimedREAddr.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/UnclaimedREAddr.java @@ -64,54 +64,54 @@ package com.radixdlt.application.system.state; -import java.util.Arrays; -import java.util.Objects; +import static com.radixdlt.identifiers.REAddr.HASHED_KEY_BYTES; import com.radixdlt.constraintmachine.Particle; import com.radixdlt.constraintmachine.exceptions.InvalidHashedKeyException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - -import static com.radixdlt.identifiers.REAddr.HASHED_KEY_BYTES; +import java.util.Arrays; +import java.util.Objects; public final class UnclaimedREAddr implements Particle { - private final REAddr addr; + private final REAddr addr; - public UnclaimedREAddr(REAddr addr) { - this.addr = addr; - } + public UnclaimedREAddr(REAddr addr) { + this.addr = addr; + } - public REAddr getAddr() { - return addr; - } + public REAddr getAddr() { + return addr; + } - public void verifyHashedKey(ECPublicKey publicKey, byte[] arg) throws InvalidHashedKeyException { - if (addr.getType() != REAddr.REAddrType.HASHED_KEY) { - throw new InvalidHashedKeyException("Expected address to be " + REAddr.REAddrType.HASHED_KEY + " but was " + addr.getType()); - } - var str = new String(arg); - var hash = REAddr.pkToHash(new String(arg), publicKey); - if (!Arrays.equals(addr.getBytes(), 1, HASHED_KEY_BYTES + 1, hash, 0, HASHED_KEY_BYTES)) { - throw new InvalidHashedKeyException("Hashed key does not match {arg=\"" + str + "\"}"); - } - } + public void verifyHashedKey(ECPublicKey publicKey, byte[] arg) throws InvalidHashedKeyException { + if (addr.getType() != REAddr.REAddrType.HASHED_KEY) { + throw new InvalidHashedKeyException( + "Expected address to be " + REAddr.REAddrType.HASHED_KEY + " but was " + addr.getType()); + } + var str = new String(arg); + var hash = REAddr.pkToHash(new String(arg), publicKey); + if (!Arrays.equals(addr.getBytes(), 1, HASHED_KEY_BYTES + 1, hash, 0, HASHED_KEY_BYTES)) { + throw new InvalidHashedKeyException("Hashed key does not match {arg=\"" + str + "\"}"); + } + } - @Override - public int hashCode() { - return Objects.hash(this.addr); - } + @Override + public int hashCode() { + return Objects.hash(this.addr); + } - @Override - public boolean equals(Object obj) { - if (!(obj instanceof UnclaimedREAddr)) { - return false; - } - final var that = (UnclaimedREAddr) obj; - return Objects.equals(this.addr, that.addr); - } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof UnclaimedREAddr)) { + return false; + } + final var that = (UnclaimedREAddr) obj; + return Objects.equals(this.addr, that.addr); + } - @Override - public String toString() { - return String.format("%s[(%s)]", getClass().getSimpleName(), addr); - } + @Override + public String toString() { + return String.format("%s[(%s)]", getClass().getSimpleName(), addr); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorBFTData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorBFTData.java index 496ab132b8..f4b5d57f14 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorBFTData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorBFTData.java @@ -66,66 +66,61 @@ import com.radixdlt.application.validators.state.ValidatorData; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; public final class ValidatorBFTData implements ValidatorData { - private final ECPublicKey validatorKey; - private final long proposalsCompleted; - private final long proposalsMissed; + private final ECPublicKey validatorKey; + private final long proposalsCompleted; + private final long proposalsMissed; - public ValidatorBFTData( - ECPublicKey validatorKey, - long proposalsCompleted, - long proposalsMissed - ) { - this.validatorKey = validatorKey; - this.proposalsCompleted = proposalsCompleted; - this.proposalsMissed = proposalsMissed; - } + public ValidatorBFTData(ECPublicKey validatorKey, long proposalsCompleted, long proposalsMissed) { + this.validatorKey = validatorKey; + this.proposalsCompleted = proposalsCompleted; + this.proposalsMissed = proposalsMissed; + } - public long proposalsMissed() { - return proposalsMissed; - } + public long proposalsMissed() { + return proposalsMissed; + } - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public ValidatorBFTData incrementCompletedProposals() { - return new ValidatorBFTData(validatorKey, proposalsCompleted + 1, proposalsMissed); - } + public ValidatorBFTData incrementCompletedProposals() { + return new ValidatorBFTData(validatorKey, proposalsCompleted + 1, proposalsMissed); + } - public ValidatorBFTData incrementProposalsMissed() { - return new ValidatorBFTData(validatorKey, proposalsCompleted, proposalsMissed + 1); - } + public ValidatorBFTData incrementProposalsMissed() { + return new ValidatorBFTData(validatorKey, proposalsCompleted, proposalsMissed + 1); + } - public long proposalsCompleted() { - return proposalsCompleted; - } + public long proposalsCompleted() { + return proposalsCompleted; + } - @Override - public int hashCode() { - return Objects.hash(validatorKey, proposalsCompleted, proposalsMissed); - } + @Override + public int hashCode() { + return Objects.hash(validatorKey, proposalsCompleted, proposalsMissed); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ValidatorBFTData)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ValidatorBFTData)) { + return false; + } - var other = (ValidatorBFTData) o; - return Objects.equals(this.validatorKey, other.validatorKey) - && this.proposalsCompleted == other.proposalsCompleted - && Objects.equals(this.proposalsMissed, other.proposalsMissed); - } + var other = (ValidatorBFTData) o; + return Objects.equals(this.validatorKey, other.validatorKey) + && this.proposalsCompleted == other.proposalsCompleted + && Objects.equals(this.proposalsMissed, other.proposalsMissed); + } - @Override - public String toString() { - return String.format( - "%s{validatorKey=%s proposalsCompleted=%s}", this.getClass().getSimpleName(), validatorKey, proposalsCompleted - ); - } + @Override + public String toString() { + return String.format( + "%s{validatorKey=%s proposalsCompleted=%s}", + this.getClass().getSimpleName(), validatorKey, proposalsCompleted); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorStakeData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorStakeData.java index 9ebc9d91ac..f03277b76e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorStakeData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/ValidatorStakeData.java @@ -64,144 +64,137 @@ package com.radixdlt.application.system.state; -import com.radixdlt.application.tokens.ResourceInBucket; +import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; + import com.radixdlt.application.tokens.Bucket; +import com.radixdlt.application.tokens.ResourceInBucket; import com.radixdlt.application.validators.state.ValidatorData; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.Objects; -import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; - public final class ValidatorStakeData implements ResourceInBucket, ValidatorData { - private final UInt256 totalStake; - private final UInt256 totalOwnership; - private final int rakePercentage; - private final REAddr ownerAddr; - private final boolean isRegistered; - - // Bucket keys - private final ECPublicKey validatorKey; - - private ValidatorStakeData( - ECPublicKey validatorKey, - UInt256 totalStake, - UInt256 totalOwnership, - int rakePercentage, - REAddr ownerAddr, - boolean isRegistered - ) { - if (totalStake.isZero() != totalOwnership.isZero()) { - throw new IllegalArgumentException( - "Zero must be equivalent between stake and ownership: " + totalStake + " " + totalOwnership - ); - } - this.validatorKey = Objects.requireNonNull(validatorKey); - this.totalStake = totalStake; - this.totalOwnership = totalOwnership; - this.rakePercentage = rakePercentage; - this.ownerAddr = ownerAddr; - this.isRegistered = isRegistered; - } - - public static ValidatorStakeData createVirtual(ECPublicKey validatorKey) { - return new ValidatorStakeData(validatorKey, UInt256.ZERO, UInt256.ZERO, RAKE_MAX, REAddr.ofPubKeyAccount(validatorKey), false); - } - - public static ValidatorStakeData create( - ECPublicKey validatorKey, - UInt256 totalStake, - UInt256 totalOwnership, - int rakePercentage, - REAddr ownerAddress, - boolean isRegistered - ) { - return new ValidatorStakeData( - validatorKey, - totalStake, - totalOwnership, - rakePercentage, - ownerAddress, - isRegistered - ); - } - - public UInt256 getTotalStake() { - return totalStake; - } - - public boolean isRegistered() { - return isRegistered; - } - - public REAddr getOwnerAddr() { - return ownerAddr; - } - - public int getRakePercentage() { - return rakePercentage; - } - - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } - - public UInt256 getTotalOwnership() { - return this.totalOwnership; - } - - @Override - public String toString() { - return String.format("%s{registered=%s stake=%s validator=%s ownership=%s rake=%s owner=%s}", - getClass().getSimpleName(), - isRegistered, - totalStake, - validatorKey.toHex().substring(0, 11), - totalOwnership, - rakePercentage, - ownerAddr - ); - } - - @Override - public UInt256 getAmount() { - return this.totalStake; - } - - @Override - public Bucket bucket() { - return new StakeBucket(validatorKey); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ValidatorStakeData)) { - return false; - } - var that = (ValidatorStakeData) o; - return Objects.equals(validatorKey, that.validatorKey) - && Objects.equals(totalOwnership, that.totalOwnership) - && Objects.equals(totalStake, that.totalStake) - && Objects.equals(rakePercentage, that.rakePercentage) - && Objects.equals(ownerAddr, that.ownerAddr) - && this.isRegistered == that.isRegistered; - } - - @Override - public int hashCode() { - return Objects.hash( - validatorKey, - totalOwnership, - totalStake, - rakePercentage, - ownerAddr, - isRegistered - ); - } + private final UInt256 totalStake; + private final UInt256 totalOwnership; + private final int rakePercentage; + private final REAddr ownerAddr; + private final boolean isRegistered; + + // Bucket keys + private final ECPublicKey validatorKey; + + private ValidatorStakeData( + ECPublicKey validatorKey, + UInt256 totalStake, + UInt256 totalOwnership, + int rakePercentage, + REAddr ownerAddr, + boolean isRegistered) { + if (totalStake.isZero() != totalOwnership.isZero()) { + throw new IllegalArgumentException( + "Zero must be equivalent between stake and ownership: " + + totalStake + + " " + + totalOwnership); + } + this.validatorKey = Objects.requireNonNull(validatorKey); + this.totalStake = totalStake; + this.totalOwnership = totalOwnership; + this.rakePercentage = rakePercentage; + this.ownerAddr = ownerAddr; + this.isRegistered = isRegistered; + } + + public static ValidatorStakeData createVirtual(ECPublicKey validatorKey) { + return new ValidatorStakeData( + validatorKey, + UInt256.ZERO, + UInt256.ZERO, + RAKE_MAX, + REAddr.ofPubKeyAccount(validatorKey), + false); + } + + public static ValidatorStakeData create( + ECPublicKey validatorKey, + UInt256 totalStake, + UInt256 totalOwnership, + int rakePercentage, + REAddr ownerAddress, + boolean isRegistered) { + return new ValidatorStakeData( + validatorKey, totalStake, totalOwnership, rakePercentage, ownerAddress, isRegistered); + } + + public UInt256 getTotalStake() { + return totalStake; + } + + public boolean isRegistered() { + return isRegistered; + } + + public REAddr getOwnerAddr() { + return ownerAddr; + } + + public int getRakePercentage() { + return rakePercentage; + } + + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } + + public UInt256 getTotalOwnership() { + return this.totalOwnership; + } + + @Override + public String toString() { + return String.format( + "%s{registered=%s stake=%s validator=%s ownership=%s rake=%s owner=%s}", + getClass().getSimpleName(), + isRegistered, + totalStake, + validatorKey.toHex().substring(0, 11), + totalOwnership, + rakePercentage, + ownerAddr); + } + + @Override + public UInt256 getAmount() { + return this.totalStake; + } + + @Override + public Bucket bucket() { + return new StakeBucket(validatorKey); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ValidatorStakeData)) { + return false; + } + var that = (ValidatorStakeData) o; + return Objects.equals(validatorKey, that.validatorKey) + && Objects.equals(totalOwnership, that.totalOwnership) + && Objects.equals(totalStake, that.totalStake) + && Objects.equals(rakePercentage, that.rakePercentage) + && Objects.equals(ownerAddr, that.ownerAddr) + && this.isRegistered == that.isRegistered; + } + + @Override + public int hashCode() { + return Objects.hash( + validatorKey, totalOwnership, totalStake, rakePercentage, ownerAddr, isRegistered); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/VirtualParent.java b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/VirtualParent.java index ad3dd817e2..4ecd135f38 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/VirtualParent.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/system/state/VirtualParent.java @@ -66,37 +66,36 @@ import com.radixdlt.constraintmachine.Particle; import com.radixdlt.utils.Bytes; - import java.util.Arrays; public final class VirtualParent implements Particle { - private final byte[] data; + private final byte[] data; - public VirtualParent(byte[] data) { - this.data = data; - } + public VirtualParent(byte[] data) { + this.data = data; + } - public byte[] getData() { - return data; - } + public byte[] getData() { + return data; + } - @Override - public int hashCode() { - return Arrays.hashCode(data); - } + @Override + public int hashCode() { + return Arrays.hashCode(data); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof VirtualParent)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof VirtualParent)) { + return false; + } - var other = (VirtualParent) o; - return Arrays.equals(this.data, other.data); - } + var other = (VirtualParent) o; + return Arrays.equals(this.data, other.data); + } - @Override - public String toString() { - return String.format("%s{%s}", this.getClass().getSimpleName(), Bytes.toHexString(data)); - } + @Override + public String toString() { + return String.format("%s{%s}", this.getClass().getSimpleName(), Bytes.toHexString(data)); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Amount.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Amount.java index f3525cb7d8..68b2eb5b5f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Amount.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Amount.java @@ -65,51 +65,56 @@ package com.radixdlt.application.tokens; import com.radixdlt.utils.UInt256; - import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; public final class Amount { - private final UInt256 subunits; + private final UInt256 subunits; - private Amount(UInt256 subunits) { - this.subunits = subunits; - } + private Amount(UInt256 subunits) { + this.subunits = subunits; + } - public static Amount zero() { - return new Amount(UInt256.ZERO); - } + public static Amount zero() { + return new Amount(UInt256.ZERO); + } - public static Amount ofMilliTokens(long units) { - return new Amount(UInt256.from(units).multiply(UInt256.TEN.pow(TokenUtils.SUB_UNITS_POW_10 - 3))); - } + public static Amount ofMilliTokens(long units) { + return new Amount( + UInt256.from(units).multiply(UInt256.TEN.pow(TokenUtils.SUB_UNITS_POW_10 - 3))); + } - public static Amount ofMicroTokens(long units) { - return new Amount(UInt256.from(units).multiply(UInt256.TEN.pow(TokenUtils.SUB_UNITS_POW_10 - 6))); - } + public static Amount ofMicroTokens(long units) { + return new Amount( + UInt256.from(units).multiply(UInt256.TEN.pow(TokenUtils.SUB_UNITS_POW_10 - 6))); + } - public static Amount ofTokens(long units) { - return new Amount(UInt256.from(units).multiply(TokenUtils.SUB_UNITS)); - } + public static Amount ofTokens(long units) { + return new Amount(UInt256.from(units).multiply(TokenUtils.SUB_UNITS)); + } - public static Amount ofSubunits(UInt256 subunits) { - return new Amount(subunits); - } + public static Amount ofSubunits(UInt256 subunits) { + return new Amount(subunits); + } - public UInt256 toSubunits() { - return subunits; - } + public UInt256 toSubunits() { + return subunits; + } - public Amount times(long i) { - return new Amount(subunits.multiply(UInt256.from(i))); - } + public Amount times(long i) { + return new Amount(subunits.multiply(UInt256.from(i))); + } - @Override - public String toString() { - var i = new BigInteger(1, subunits.toByteArray()); - var d = new BigDecimal(i); - var amt = d.divide(new BigDecimal(10).pow(TokenUtils.SUB_UNITS_POW_10), TokenUtils.SUB_UNITS_POW_10, RoundingMode.DOWN); - return amt.stripTrailingZeros().toPlainString(); - } + @Override + public String toString() { + var i = new BigInteger(1, subunits.toByteArray()); + var d = new BigDecimal(i); + var amt = + d.divide( + new BigDecimal(10).pow(TokenUtils.SUB_UNITS_POW_10), + TokenUtils.SUB_UNITS_POW_10, + RoundingMode.DOWN); + return amt.stripTrailingZeros().toPlainString(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Bucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Bucket.java index 201b0abb29..cdac8a37b5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Bucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/Bucket.java @@ -71,10 +71,13 @@ // TODO: these methods are really here for client api, // TODO: fix to be more in line with scrypto abstractions public interface Bucket { - Authorization withdrawAuthorization(); + Authorization withdrawAuthorization(); - REAddr resourceAddr(); - REAddr getOwner(); - ECPublicKey getValidatorKey(); - Long getEpochUnlock(); + REAddr resourceAddr(); + + REAddr getOwner(); + + ECPublicKey getValidatorKey(); + + Long getEpochUnlock(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceCreatedEvent.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceCreatedEvent.java index 3a6f69d9ee..385cad5d03 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceCreatedEvent.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceCreatedEvent.java @@ -69,25 +69,26 @@ import com.radixdlt.constraintmachine.REEvent; public final class ResourceCreatedEvent implements REEvent { - private final String symbol; - private final TokenResource tokenResource; - private final TokenResourceMetadata metadata; + private final String symbol; + private final TokenResource tokenResource; + private final TokenResourceMetadata metadata; - public ResourceCreatedEvent(String symbol, TokenResource tokenResource, TokenResourceMetadata metadata) { - this.symbol = symbol; - this.tokenResource = tokenResource; - this.metadata = metadata; - } + public ResourceCreatedEvent( + String symbol, TokenResource tokenResource, TokenResourceMetadata metadata) { + this.symbol = symbol; + this.tokenResource = tokenResource; + this.metadata = metadata; + } - public String getSymbol() { - return symbol; - } + public String getSymbol() { + return symbol; + } - public TokenResource getTokenResource() { - return tokenResource; - } + public TokenResource getTokenResource() { + return tokenResource; + } - public TokenResourceMetadata getMetadata() { - return metadata; - } + public TokenResourceMetadata getMetadata() { + return metadata; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceInBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceInBucket.java index bb1def63bc..68bfe038f1 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceInBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/ResourceInBucket.java @@ -68,6 +68,7 @@ import com.radixdlt.utils.UInt256; public interface ResourceInBucket extends Particle { - UInt256 getAmount(); - Bucket bucket(); + UInt256 getAmount(); + + Bucket bucket(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/TokenUtils.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/TokenUtils.java index e93d178c75..01b324df22 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/TokenUtils.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/TokenUtils.java @@ -66,22 +66,15 @@ import com.radixdlt.utils.UInt256; -/** - * Utility values and methods for tokens. - */ +/** Utility values and methods for tokens. */ public final class TokenUtils { - /** - * Power of 10 number of subunits to be used by every token. - * Follows EIP-777 model. - */ - public static final int SUB_UNITS_POW_10 = 18; + /** Power of 10 number of subunits to be used by every token. Follows EIP-777 model. */ + public static final int SUB_UNITS_POW_10 = 18; - /** - * Implicit number of subunits to be used by every token. Follows EIP-777 model. - */ - public static final UInt256 SUB_UNITS = UInt256.TEN.pow(SUB_UNITS_POW_10); + /** Implicit number of subunits to be used by every token. Follows EIP-777 model. */ + public static final UInt256 SUB_UNITS = UInt256.TEN.pow(SUB_UNITS_POW_10); - private TokenUtils() { - throw new IllegalStateException("Cannot instantiate."); - } + private TokenUtils() { + throw new IllegalStateException("Cannot instantiate."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/BurnTokenConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/BurnTokenConstructor.java index c3a13b32dd..49ad14126c 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/BurnTokenConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/BurnTokenConstructor.java @@ -65,41 +65,41 @@ package com.radixdlt.application.tokens.construction; import com.radixdlt.application.tokens.state.AccountBucket; +import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.NotEnoughResourcesException; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.BurnToken; -import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; - import java.nio.ByteBuffer; public final class BurnTokenConstructor implements ActionConstructor { - @Override - public void construct(BurnToken action, TxBuilder txBuilder) throws TxBuilderException { - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(action.from().getBytes()); + @Override + public void construct(BurnToken action, TxBuilder txBuilder) throws TxBuilderException { + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(action.from().getBytes()); - var index = SubstateIndex.create(buf.array(), TokensInAccount.class); - var change = txBuilder.downFungible( - index, - p -> p.getResourceAddr().equals(action.resourceAddr()) - && p.getHoldingAddr().equals(action.from()), - action.amount(), - available -> { - var from = AccountBucket.from(action.resourceAddr(), action.from()); - return new NotEnoughResourcesException(from, action.amount(), available); - } - ); - if (!change.isZero()) { - txBuilder.up(new TokensInAccount(action.from(), action.resourceAddr(), change)); - } - txBuilder.end(); - } + var index = SubstateIndex.create(buf.array(), TokensInAccount.class); + var change = + txBuilder.downFungible( + index, + p -> + p.getResourceAddr().equals(action.resourceAddr()) + && p.getHoldingAddr().equals(action.from()), + action.amount(), + available -> { + var from = AccountBucket.from(action.resourceAddr(), action.from()); + return new NotEnoughResourcesException(from, action.amount(), available); + }); + if (!change.isZero()) { + txBuilder.up(new TokensInAccount(action.from(), action.resourceAddr(), change)); + } + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateFixedTokenConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateFixedTokenConstructor.java index 26c4b26aec..2ff18bca9d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateFixedTokenConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateFixedTokenConstructor.java @@ -65,40 +65,42 @@ package com.radixdlt.application.tokens.construction; import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.tokens.state.TokenResourceMetadata; +import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.CreateFixedToken; -import com.radixdlt.application.tokens.state.TokenResource; -import com.radixdlt.application.tokens.state.TokensInAccount; - import java.nio.charset.StandardCharsets; public final class CreateFixedTokenConstructor implements ActionConstructor { - private final int maxSymbolLength; + private final int maxSymbolLength; - public CreateFixedTokenConstructor(int maxSymbolLength) { - this.maxSymbolLength = maxSymbolLength; - } + public CreateFixedTokenConstructor(int maxSymbolLength) { + this.maxSymbolLength = maxSymbolLength; + } - @Override - public void construct(CreateFixedToken action, TxBuilder txBuilder) throws TxBuilderException { - if (action.getSymbol().length() > maxSymbolLength) { - throw new SymbolLengthException(maxSymbolLength, action.getSymbol().length()); - } - txBuilder.toLowLevelBuilder().syscall(Syscall.READDR_CLAIM, action.getSymbol().getBytes(StandardCharsets.UTF_8)); - txBuilder.downREAddr(action.getResourceAddr()); - txBuilder.up(TokenResource.createFixedSupplyResource(action.getResourceAddr())); - txBuilder.up(new TokensInAccount(action.getAccountAddr(), action.getResourceAddr(), action.getSupply())); - txBuilder.up(new TokenResourceMetadata( - action.getResourceAddr(), - action.getSymbol(), - action.getName(), - action.getDescription(), - action.getIconUrl(), - action.getTokenUrl() - )); - txBuilder.end(); - } + @Override + public void construct(CreateFixedToken action, TxBuilder txBuilder) throws TxBuilderException { + if (action.getSymbol().length() > maxSymbolLength) { + throw new SymbolLengthException(maxSymbolLength, action.getSymbol().length()); + } + txBuilder + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, action.getSymbol().getBytes(StandardCharsets.UTF_8)); + txBuilder.downREAddr(action.getResourceAddr()); + txBuilder.up(TokenResource.createFixedSupplyResource(action.getResourceAddr())); + txBuilder.up( + new TokensInAccount(action.getAccountAddr(), action.getResourceAddr(), action.getSupply())); + txBuilder.up( + new TokenResourceMetadata( + action.getResourceAddr(), + action.getSymbol(), + action.getName(), + action.getDescription(), + action.getIconUrl(), + action.getTokenUrl())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateMutableTokenConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateMutableTokenConstructor.java index a6c3303c11..d1dc672d63 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateMutableTokenConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/CreateMutableTokenConstructor.java @@ -65,40 +65,41 @@ package com.radixdlt.application.tokens.construction; import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.tokens.state.TokenResourceMetadata; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.CreateMutableToken; -import com.radixdlt.application.tokens.state.TokenResource; - import java.nio.charset.StandardCharsets; public final class CreateMutableTokenConstructor implements ActionConstructor { - private final int maxSymbolLength; + private final int maxSymbolLength; - public CreateMutableTokenConstructor(int maxSymbolLength) { - this.maxSymbolLength = maxSymbolLength; - } + public CreateMutableTokenConstructor(int maxSymbolLength) { + this.maxSymbolLength = maxSymbolLength; + } - @Override - public void construct(CreateMutableToken action, TxBuilder txBuilder) throws TxBuilderException { - if (action.getSymbol().length() > maxSymbolLength) { - throw new SymbolLengthException(maxSymbolLength, action.getSymbol().length()); - } + @Override + public void construct(CreateMutableToken action, TxBuilder txBuilder) throws TxBuilderException { + if (action.getSymbol().length() > maxSymbolLength) { + throw new SymbolLengthException(maxSymbolLength, action.getSymbol().length()); + } - final var reAddress = action.getResourceAddress(); - txBuilder.toLowLevelBuilder().syscall(Syscall.READDR_CLAIM, action.getSymbol().getBytes(StandardCharsets.UTF_8)); - txBuilder.downREAddr(reAddress); - txBuilder.up(TokenResource.createMutableSupplyResource(reAddress, action.getOwner())); - txBuilder.up(new TokenResourceMetadata( - reAddress, - action.getSymbol(), - action.getName(), - action.getDescription(), - action.getIconUrl(), - action.getTokenUrl() - )); - txBuilder.end(); - } + final var reAddress = action.getResourceAddress(); + txBuilder + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, action.getSymbol().getBytes(StandardCharsets.UTF_8)); + txBuilder.downREAddr(reAddress); + txBuilder.up(TokenResource.createMutableSupplyResource(reAddress, action.getOwner())); + txBuilder.up( + new TokenResourceMetadata( + reAddress, + action.getSymbol(), + action.getName(), + action.getDescription(), + action.getIconUrl(), + action.getTokenUrl())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/DelegateStakePermissionException.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/DelegateStakePermissionException.java index 0bf9ad7e6a..b2bcc62004 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/DelegateStakePermissionException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/DelegateStakePermissionException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,20 +68,20 @@ import com.radixdlt.identifiers.REAddr; public final class DelegateStakePermissionException extends TxBuilderException { - private final REAddr owner; - private final REAddr user; + private final REAddr owner; + private final REAddr user; - public DelegateStakePermissionException(REAddr owner, REAddr user) { - super("Delegation flag is false with owner " + owner + " but attempting to stake as " + user); - this.owner = owner; - this.user = user; - } + public DelegateStakePermissionException(REAddr owner, REAddr user) { + super("Delegation flag is false with owner " + owner + " but attempting to stake as " + user); + this.owner = owner; + this.user = user; + } - public REAddr getOwner() { - return owner; - } + public REAddr getOwner() { + return owner; + } - public REAddr getUser() { - return user; - } + public REAddr getUser() { + return user; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MinimumStakeException.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MinimumStakeException.java index c9eb184ab8..2d98de199a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MinimumStakeException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MinimumStakeException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,20 +68,20 @@ import com.radixdlt.utils.UInt256; public class MinimumStakeException extends TxBuilderException { - private final UInt256 minimumStake; - private final UInt256 attempt; + private final UInt256 minimumStake; + private final UInt256 attempt; - public MinimumStakeException(UInt256 minimumStake, UInt256 attempt) { - super("Minimum to stake is " + minimumStake + " but trying to stake " + attempt); - this.minimumStake = minimumStake; - this.attempt = attempt; - } + public MinimumStakeException(UInt256 minimumStake, UInt256 attempt) { + super("Minimum to stake is " + minimumStake + " but trying to stake " + attempt); + this.minimumStake = minimumStake; + this.attempt = attempt; + } - public UInt256 getMinimumStake() { - return minimumStake; - } + public UInt256 getMinimumStake() { + return minimumStake; + } - public UInt256 getAttempt() { - return attempt; - } + public UInt256 getAttempt() { + return attempt; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MintTokenConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MintTokenConstructor.java index 81075d4ee4..cc65d7b21b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MintTokenConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/MintTokenConstructor.java @@ -64,16 +64,16 @@ package com.radixdlt.application.tokens.construction; +import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.application.tokens.state.TokensInAccount; public final class MintTokenConstructor implements ActionConstructor { - @Override - public void construct(MintToken action, TxBuilder txBuilder) throws TxBuilderException { - txBuilder.up(new TokensInAccount(action.to(), action.resourceAddr(), action.amount())); - txBuilder.end(); - } + @Override + public void construct(MintToken action, TxBuilder txBuilder) throws TxBuilderException { + txBuilder.up(new TokensInAccount(action.to(), action.resourceAddr(), action.amount())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/StakeTokensConstructorV3.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/StakeTokensConstructorV3.java index 2d0912e3e7..03325c5df7 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/StakeTokensConstructorV3.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/StakeTokensConstructorV3.java @@ -65,66 +65,64 @@ package com.radixdlt.application.tokens.construction; import com.radixdlt.application.tokens.state.AccountBucket; +import com.radixdlt.application.tokens.state.PreparedStake; +import com.radixdlt.application.tokens.state.TokensInAccount; +import com.radixdlt.application.validators.state.AllowDelegationFlag; +import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.NotEnoughResourcesException; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.StakeTokens; -import com.radixdlt.application.tokens.state.PreparedStake; -import com.radixdlt.application.tokens.state.TokensInAccount; -import com.radixdlt.application.validators.state.AllowDelegationFlag; -import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.nio.ByteBuffer; public class StakeTokensConstructorV3 implements ActionConstructor { - private final UInt256 minimumStake; + private final UInt256 minimumStake; - public StakeTokensConstructorV3(UInt256 minimumStake) { - this.minimumStake = minimumStake; - } + public StakeTokensConstructorV3(UInt256 minimumStake) { + this.minimumStake = minimumStake; + } - @Override - public void construct(StakeTokens action, TxBuilder builder) throws TxBuilderException { - if (action.amount().compareTo(minimumStake) < 0) { - throw new MinimumStakeException(minimumStake, action.amount()); - } + @Override + public void construct(StakeTokens action, TxBuilder builder) throws TxBuilderException { + if (action.amount().compareTo(minimumStake) < 0) { + throw new MinimumStakeException(minimumStake, action.amount()); + } - // TODO: construct this based on substate definition - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(action.from().getBytes()); + // TODO: construct this based on substate definition + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(action.from().getBytes()); - var index = SubstateIndex.create(buf.array(), TokensInAccount.class); - var change = builder.downFungible( - index, - p -> p.getResourceAddr().isNativeToken() - && p.getHoldingAddr().equals(action.from()), - action.amount(), - available -> { - var from = AccountBucket.from(REAddr.ofNativeToken(), action.from()); - return new NotEnoughResourcesException(from, action.amount(), available); - } - ); - if (!change.isZero()) { - builder.up(new TokensInAccount(action.from(), REAddr.ofNativeToken(), change)); - } + var index = SubstateIndex.create(buf.array(), TokensInAccount.class); + var change = + builder.downFungible( + index, + p -> p.getResourceAddr().isNativeToken() && p.getHoldingAddr().equals(action.from()), + action.amount(), + available -> { + var from = AccountBucket.from(REAddr.ofNativeToken(), action.from()); + return new NotEnoughResourcesException(from, action.amount(), available); + }); + if (!change.isZero()) { + builder.up(new TokensInAccount(action.from(), REAddr.ofNativeToken(), change)); + } - var flag = builder.read(AllowDelegationFlag.class, action.to()); - if (!flag.allowsDelegation()) { - var validator = builder.read(ValidatorOwnerCopy.class, action.to()); - var owner = validator.getOwner(); - if (!action.from().equals(owner)) { - throw new DelegateStakePermissionException(owner, action.from()); - } - } - builder.up(new PreparedStake(action.amount(), action.from(), action.to())); - builder.end(); - } + var flag = builder.read(AllowDelegationFlag.class, action.to()); + if (!flag.allowsDelegation()) { + var validator = builder.read(ValidatorOwnerCopy.class, action.to()); + var owner = validator.getOwner(); + if (!action.from().equals(owner)) { + throw new DelegateStakePermissionException(owner, action.from()); + } + } + builder.up(new PreparedStake(action.amount(), action.from(), action.to())); + builder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/SymbolLengthException.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/SymbolLengthException.java index e06e14de31..24a9d6b115 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/SymbolLengthException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/SymbolLengthException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,7 +67,7 @@ import com.radixdlt.atom.TxBuilderException; public class SymbolLengthException extends TxBuilderException { - public SymbolLengthException(int maxSymbolLength, int attempt) { - super("Symbol must have a length <= " + maxSymbolLength + " but was " + attempt); - } + public SymbolLengthException(int maxSymbolLength, int attempt) { + super("Symbol must have a length <= " + maxSymbolLength + " but was " + attempt); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/TransferTokensConstructorV2.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/TransferTokensConstructorV2.java index d26094c7e9..53a53a597d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/TransferTokensConstructorV2.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/TransferTokensConstructorV2.java @@ -65,40 +65,40 @@ package com.radixdlt.application.tokens.construction; import com.radixdlt.application.tokens.state.AccountBucket; +import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.NotEnoughResourcesException; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.TransferToken; -import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; - import java.nio.ByteBuffer; public class TransferTokensConstructorV2 implements ActionConstructor { - @Override - public void construct(TransferToken action, TxBuilder txBuilder) throws TxBuilderException { - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(action.from().getBytes()); - var index = SubstateIndex.create(buf.array(), TokensInAccount.class); - var change = txBuilder.downFungible( - index, - p -> p.getResourceAddr().equals(action.resourceAddr()) - && p.getHoldingAddr().equals(action.from()), - action.amount(), - available -> { - var from = AccountBucket.from(action.resourceAddr(), action.from()); - return new NotEnoughResourcesException(from, action.amount(), available); - } - ); - if (!change.isZero()) { - txBuilder.up(new TokensInAccount(action.from(), action.resourceAddr(), change)); - } - txBuilder.up(new TokensInAccount(action.to(), action.resourceAddr(), action.amount())); - txBuilder.end(); - } + @Override + public void construct(TransferToken action, TxBuilder txBuilder) throws TxBuilderException { + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(action.from().getBytes()); + var index = SubstateIndex.create(buf.array(), TokensInAccount.class); + var change = + txBuilder.downFungible( + index, + p -> + p.getResourceAddr().equals(action.resourceAddr()) + && p.getHoldingAddr().equals(action.from()), + action.amount(), + available -> { + var from = AccountBucket.from(action.resourceAddr(), action.from()); + return new NotEnoughResourcesException(from, action.amount(), available); + }); + if (!change.isZero()) { + txBuilder.up(new TokensInAccount(action.from(), action.resourceAddr(), change)); + } + txBuilder.up(new TokensInAccount(action.to(), action.resourceAddr(), action.amount())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeOwnershipConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeOwnershipConstructor.java index 9cbad98611..e7405938ee 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeOwnershipConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeOwnershipConstructor.java @@ -64,47 +64,50 @@ package com.radixdlt.application.tokens.construction; +import com.radixdlt.application.system.state.StakeOwnership; import com.radixdlt.application.system.state.StakeOwnershipBucket; +import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.NotEnoughResourcesException; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UnstakeOwnership; -import com.radixdlt.application.system.state.StakeOwnership; -import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; - import java.nio.ByteBuffer; public class UnstakeOwnershipConstructor implements ActionConstructor { - @Override - public void construct(UnstakeOwnership action, TxBuilder txBuilder) throws TxBuilderException { - var buf = ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES)); - buf.put(SubstateTypeId.STAKE_OWNERSHIP.id()); - buf.put((byte) 0); - buf.put(action.from().getCompressedBytes()); - buf.put(action.accountAddr().getBytes()); - if (buf.hasRemaining()) { - // Sanity - throw new IllegalStateException(); - } + @Override + public void construct(UnstakeOwnership action, TxBuilder txBuilder) throws TxBuilderException { + var buf = + ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES)); + buf.put(SubstateTypeId.STAKE_OWNERSHIP.id()); + buf.put((byte) 0); + buf.put(action.from().getCompressedBytes()); + buf.put(action.accountAddr().getBytes()); + if (buf.hasRemaining()) { + // Sanity + throw new IllegalStateException(); + } - var index = SubstateIndex.create(buf.array(), StakeOwnership.class); - var change = txBuilder.downFungible( - index, - p -> p.getOwner().equals(action.accountAddr()) && p.getDelegateKey().equals(action.from()), - action.amount(), - available -> { - var from = StakeOwnershipBucket.from(action.from(), action.accountAddr()); - return new NotEnoughResourcesException(from, action.amount(), available); - } - ); - if (!change.isZero()) { - txBuilder.up(new StakeOwnership(action.from(), action.accountAddr(), change)); - } - txBuilder.up(new PreparedUnstakeOwnership(action.from(), action.accountAddr(), action.amount())); - txBuilder.end(); - } + var index = SubstateIndex.create(buf.array(), StakeOwnership.class); + var change = + txBuilder.downFungible( + index, + p -> + p.getOwner().equals(action.accountAddr()) + && p.getDelegateKey().equals(action.from()), + action.amount(), + available -> { + var from = StakeOwnershipBucket.from(action.from(), action.accountAddr()); + return new NotEnoughResourcesException(from, action.amount(), available); + }); + if (!change.isZero()) { + txBuilder.up(new StakeOwnership(action.from(), action.accountAddr(), change)); + } + txBuilder.up( + new PreparedUnstakeOwnership(action.from(), action.accountAddr(), action.amount())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeTokensConstructorV2.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeTokensConstructorV2.java index c77dc563ee..9a20b5dd55 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeTokensConstructorV2.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/construction/UnstakeTokensConstructorV2.java @@ -64,54 +64,58 @@ package com.radixdlt.application.tokens.construction; +import com.radixdlt.application.system.state.StakeOwnership; import com.radixdlt.application.system.state.StakeOwnershipBucket; +import com.radixdlt.application.system.state.ValidatorStakeData; +import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.NotEnoughResourcesException; import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UnstakeTokens; -import com.radixdlt.application.system.state.StakeOwnership; -import com.radixdlt.application.system.state.ValidatorStakeData; -import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.crypto.ECPublicKey; - import java.nio.ByteBuffer; public class UnstakeTokensConstructorV2 implements ActionConstructor { - @Override - public void construct(UnstakeTokens action, TxBuilder txBuilder) throws TxBuilderException { - var validatorStake = txBuilder.find(ValidatorStakeData.class, action.from()); - var ownershipAmt = action.amount() - .multiply(validatorStake.getTotalOwnership()) - .divide(validatorStake.getAmount()); + @Override + public void construct(UnstakeTokens action, TxBuilder txBuilder) throws TxBuilderException { + var validatorStake = txBuilder.find(ValidatorStakeData.class, action.from()); + var ownershipAmt = + action + .amount() + .multiply(validatorStake.getTotalOwnership()) + .divide(validatorStake.getAmount()); - // TODO: construct this in substate definition - var buf = ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES)); - buf.put(SubstateTypeId.STAKE_OWNERSHIP.id()); - buf.put((byte) 0); - buf.put(action.from().getCompressedBytes()); - buf.put(action.accountAddr().getBytes()); - if (buf.hasRemaining()) { - // Sanity - throw new IllegalStateException(); - } + // TODO: construct this in substate definition + var buf = + ByteBuffer.allocate(2 + ECPublicKey.COMPRESSED_BYTES + (1 + ECPublicKey.COMPRESSED_BYTES)); + buf.put(SubstateTypeId.STAKE_OWNERSHIP.id()); + buf.put((byte) 0); + buf.put(action.from().getCompressedBytes()); + buf.put(action.accountAddr().getBytes()); + if (buf.hasRemaining()) { + // Sanity + throw new IllegalStateException(); + } - var index = SubstateIndex.create(buf.array(), StakeOwnership.class); - var change = txBuilder.downFungible( - index, - p -> p.getOwner().equals(action.accountAddr()) && p.getDelegateKey().equals(action.from()), - ownershipAmt, - available -> { - var from = StakeOwnershipBucket.from(action.from(), action.accountAddr()); - return new NotEnoughResourcesException(from, action.amount(), available); - } - ); - if (!change.isZero()) { - txBuilder.up(new StakeOwnership(action.from(), action.accountAddr(), change)); - } - txBuilder.up(new PreparedUnstakeOwnership(action.from(), action.accountAddr(), ownershipAmt)); - txBuilder.end(); - } + var index = SubstateIndex.create(buf.array(), StakeOwnership.class); + var change = + txBuilder.downFungible( + index, + p -> + p.getOwner().equals(action.accountAddr()) + && p.getDelegateKey().equals(action.from()), + ownershipAmt, + available -> { + var from = StakeOwnershipBucket.from(action.from(), action.accountAddr()); + return new NotEnoughResourcesException(from, action.amount(), available); + }); + if (!change.isZero()) { + txBuilder.up(new StakeOwnership(action.from(), action.accountAddr(), change)); + } + txBuilder.up(new PreparedUnstakeOwnership(action.from(), action.accountAddr(), ownershipAmt)); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakeOwnershipHoldingBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakeOwnershipHoldingBucket.java index 54a7ab60de..b2c50b6b50 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakeOwnershipHoldingBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakeOwnershipHoldingBucket.java @@ -66,70 +66,72 @@ import com.radixdlt.application.system.state.StakeOwnership; import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; +import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.constraintmachine.exceptions.MismatchException; import com.radixdlt.constraintmachine.exceptions.NotEnoughResourcesException; import com.radixdlt.constraintmachine.exceptions.ProcedureException; -import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt384; public final class StakeOwnershipHoldingBucket implements ReducerState { - private final ECPublicKey delegate; - private final REAddr accountAddr; - private UInt384 ownershipAmount; + private final ECPublicKey delegate; + private final REAddr accountAddr; + private UInt384 ownershipAmount; - public StakeOwnershipHoldingBucket(StakeOwnership stakeOwnership) { - this(stakeOwnership.getDelegateKey(), stakeOwnership.getOwner(), UInt384.from(stakeOwnership.getAmount())); - } + public StakeOwnershipHoldingBucket(StakeOwnership stakeOwnership) { + this( + stakeOwnership.getDelegateKey(), + stakeOwnership.getOwner(), + UInt384.from(stakeOwnership.getAmount())); + } - public StakeOwnershipHoldingBucket( - ECPublicKey delegate, - REAddr accountAddr, - UInt384 amount - ) { - this.delegate = delegate; - this.accountAddr = accountAddr; - this.ownershipAmount = amount; - } + public StakeOwnershipHoldingBucket(ECPublicKey delegate, REAddr accountAddr, UInt384 amount) { + this.delegate = delegate; + this.accountAddr = accountAddr; + this.ownershipAmount = amount; + } - public StakeOwnership withdrawOwnership(UInt256 amount) throws NotEnoughResourcesException { - var withdraw384 = UInt384.from(amount); - if (ownershipAmount.compareTo(withdraw384) < 0) { - throw new NotEnoughResourcesException(amount, ownershipAmount.getLow()); - } - ownershipAmount = ownershipAmount.subtract(withdraw384); - return new StakeOwnership(delegate, accountAddr, amount); - } + public StakeOwnership withdrawOwnership(UInt256 amount) throws NotEnoughResourcesException { + var withdraw384 = UInt384.from(amount); + if (ownershipAmount.compareTo(withdraw384) < 0) { + throw new NotEnoughResourcesException(amount, ownershipAmount.getLow()); + } + ownershipAmount = ownershipAmount.subtract(withdraw384); + return new StakeOwnership(delegate, accountAddr, amount); + } - public void depositOwnership(StakeOwnership stakeOwnership) throws MismatchException { - if (!delegate.equals(stakeOwnership.getDelegateKey())) { - throw new MismatchException("Shares must be from same delegate"); - } - if (!stakeOwnership.getOwner().equals(accountAddr)) { - throw new MismatchException("Shares must be for same account"); - } - ownershipAmount = UInt384.from(stakeOwnership.getAmount()).add(ownershipAmount); - } + public void depositOwnership(StakeOwnership stakeOwnership) throws MismatchException { + if (!delegate.equals(stakeOwnership.getDelegateKey())) { + throw new MismatchException("Shares must be from same delegate"); + } + if (!stakeOwnership.getOwner().equals(accountAddr)) { + throw new MismatchException("Shares must be for same account"); + } + ownershipAmount = UInt384.from(stakeOwnership.getAmount()).add(ownershipAmount); + } - public PreparedUnstakeOwnership unstake(UInt256 amount) throws NotEnoughResourcesException, MismatchException { - var unstakeAmount = UInt384.from(amount); - if (ownershipAmount.compareTo(unstakeAmount) < 0) { - throw new NotEnoughResourcesException(amount, ownershipAmount.getLow()); - } - ownershipAmount = ownershipAmount.subtract(unstakeAmount); - return new PreparedUnstakeOwnership(delegate, accountAddr, amount); - } + public PreparedUnstakeOwnership unstake(UInt256 amount) + throws NotEnoughResourcesException, MismatchException { + var unstakeAmount = UInt384.from(amount); + if (ownershipAmount.compareTo(unstakeAmount) < 0) { + throw new NotEnoughResourcesException(amount, ownershipAmount.getLow()); + } + ownershipAmount = ownershipAmount.subtract(unstakeAmount); + return new PreparedUnstakeOwnership(delegate, accountAddr, amount); + } - public void destroy() throws ProcedureException { - if (!ownershipAmount.isZero()) { - throw new ProcedureException("Shares cannot be burnt."); - } - } + public void destroy() throws ProcedureException { + if (!ownershipAmount.isZero()) { + throw new ProcedureException("Shares cannot be burnt."); + } + } - @Override - public String toString() { - return String.format("%s{delegate=%s owner=%s amount=%s}", this.getClass().getSimpleName(), delegate, accountAddr, ownershipAmount); - } + @Override + public String toString() { + return String.format( + "%s{delegate=%s owner=%s amount=%s}", + this.getClass().getSimpleName(), delegate, accountAddr, ownershipAmount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakingConstraintScryptV4.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakingConstraintScryptV4.java index bfeb0902e9..067fd9aa5b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakingConstraintScryptV4.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/StakingConstraintScryptV4.java @@ -64,13 +64,13 @@ package com.radixdlt.application.tokens.scrypt; -import com.radixdlt.atom.REFieldSerialization; -import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.application.system.state.StakeOwnership; import com.radixdlt.application.tokens.state.PreparedStake; import com.radixdlt.application.tokens.state.PreparedUnstakeOwnership; import com.radixdlt.application.validators.state.AllowDelegationFlag; import com.radixdlt.application.validators.state.ValidatorOwnerCopy; +import com.radixdlt.atom.REFieldSerialization; +import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; @@ -78,201 +78,206 @@ import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.EndProcedure; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.constraintmachine.ReadProcedure; +import com.radixdlt.constraintmachine.ReducerResult; +import com.radixdlt.constraintmachine.ReducerState; +import com.radixdlt.constraintmachine.UpProcedure; +import com.radixdlt.constraintmachine.VoidReducerState; import com.radixdlt.constraintmachine.exceptions.InvalidDelegationException; import com.radixdlt.constraintmachine.exceptions.InvalidResourceException; import com.radixdlt.constraintmachine.exceptions.MinimumStakeException; import com.radixdlt.constraintmachine.exceptions.MismatchException; import com.radixdlt.constraintmachine.exceptions.NotEnoughResourcesException; import com.radixdlt.constraintmachine.exceptions.ProcedureException; -import com.radixdlt.constraintmachine.ReadProcedure; -import com.radixdlt.constraintmachine.ReducerResult; -import com.radixdlt.constraintmachine.ReducerState; -import com.radixdlt.constraintmachine.UpProcedure; -import com.radixdlt.constraintmachine.VoidReducerState; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.function.Predicate; public final class StakingConstraintScryptV4 implements ConstraintScrypt { - private final UInt256 minimumStake; + private final UInt256 minimumStake; - public StakingConstraintScryptV4(UInt256 minimumStake) { - this.minimumStake = minimumStake; - } + public StakingConstraintScryptV4(UInt256 minimumStake) { + this.minimumStake = minimumStake; + } - @Override - public void main(Loader os) { - os.substate( - new SubstateDefinition<>( - PreparedStake.class, - SubstateTypeId.PREPARED_STAKE.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var delegate = REFieldSerialization.deserializeKey(buf); - var owner = REFieldSerialization.deserializeAccountREAddr(buf); - var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); - return new PreparedStake(amount, owner, delegate); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getDelegateKey()); - REFieldSerialization.serializeREAddr(buf, s.getOwner()); - buf.put(s.getAmount().toByteArray()); - } - ) - ); + @Override + public void main(Loader os) { + os.substate( + new SubstateDefinition<>( + PreparedStake.class, + SubstateTypeId.PREPARED_STAKE.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var delegate = REFieldSerialization.deserializeKey(buf); + var owner = REFieldSerialization.deserializeAccountREAddr(buf); + var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); + return new PreparedStake(amount, owner, delegate); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getDelegateKey()); + REFieldSerialization.serializeREAddr(buf, s.getOwner()); + buf.put(s.getAmount().toByteArray()); + })); - os.substate( - new SubstateDefinition<>( - PreparedUnstakeOwnership.class, - SubstateTypeId.PREPARED_UNSTAKE.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var delegate = REFieldSerialization.deserializeKey(buf); - var owner = REFieldSerialization.deserializeAccountREAddr(buf); - var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); - return new PreparedUnstakeOwnership(delegate, owner, amount); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getDelegateKey()); - REFieldSerialization.serializeREAddr(buf, s.getOwner()); - buf.put(s.getAmount().toByteArray()); - } - ) - ); + os.substate( + new SubstateDefinition<>( + PreparedUnstakeOwnership.class, + SubstateTypeId.PREPARED_UNSTAKE.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var delegate = REFieldSerialization.deserializeKey(buf); + var owner = REFieldSerialization.deserializeAccountREAddr(buf); + var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); + return new PreparedUnstakeOwnership(delegate, owner, amount); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getDelegateKey()); + REFieldSerialization.serializeREAddr(buf, s.getOwner()); + buf.put(s.getAmount().toByteArray()); + })); - defineStaking(os); - } + defineStaking(os); + } - private final class OwnerStakePrepare implements ReducerState { - private final TokenHoldingBucket tokenHoldingBucket; - private final AllowDelegationFlag allowDelegationFlag; + private final class OwnerStakePrepare implements ReducerState { + private final TokenHoldingBucket tokenHoldingBucket; + private final AllowDelegationFlag allowDelegationFlag; - OwnerStakePrepare(TokenHoldingBucket tokenHoldingBucket, AllowDelegationFlag allowDelegationFlag) { - this.tokenHoldingBucket = tokenHoldingBucket; - this.allowDelegationFlag = allowDelegationFlag; - } + OwnerStakePrepare( + TokenHoldingBucket tokenHoldingBucket, AllowDelegationFlag allowDelegationFlag) { + this.tokenHoldingBucket = tokenHoldingBucket; + this.allowDelegationFlag = allowDelegationFlag; + } - ReducerState readOwner(ValidatorOwnerCopy ownerCopy) throws ProcedureException { - if (!allowDelegationFlag.getValidatorKey().equals(ownerCopy.getValidatorKey())) { - throw new ProcedureException("Not matching validator keys"); - } - var owner = ownerCopy.getOwner(); - return new StakePrepare( - tokenHoldingBucket, - allowDelegationFlag.getValidatorKey(), - owner::equals - ); - } - } + ReducerState readOwner(ValidatorOwnerCopy ownerCopy) throws ProcedureException { + if (!allowDelegationFlag.getValidatorKey().equals(ownerCopy.getValidatorKey())) { + throw new ProcedureException("Not matching validator keys"); + } + var owner = ownerCopy.getOwner(); + return new StakePrepare( + tokenHoldingBucket, allowDelegationFlag.getValidatorKey(), owner::equals); + } + } - private final class StakePrepare implements ReducerState { - private final TokenHoldingBucket tokenHoldingBucket; - private final ECPublicKey validatorKey; - private final Predicate delegateAllowed; + private final class StakePrepare implements ReducerState { + private final TokenHoldingBucket tokenHoldingBucket; + private final ECPublicKey validatorKey; + private final Predicate delegateAllowed; - StakePrepare(TokenHoldingBucket tokenHoldingBucket, ECPublicKey validatorKey, Predicate delegateAllowed) { - this.tokenHoldingBucket = tokenHoldingBucket; - this.validatorKey = validatorKey; - this.delegateAllowed = delegateAllowed; - } + StakePrepare( + TokenHoldingBucket tokenHoldingBucket, + ECPublicKey validatorKey, + Predicate delegateAllowed) { + this.tokenHoldingBucket = tokenHoldingBucket; + this.validatorKey = validatorKey; + this.delegateAllowed = delegateAllowed; + } - ReducerState withdrawTo(PreparedStake preparedStake) throws MinimumStakeException, NotEnoughResourcesException, - InvalidResourceException, InvalidDelegationException, MismatchException { + ReducerState withdrawTo(PreparedStake preparedStake) + throws MinimumStakeException, NotEnoughResourcesException, InvalidResourceException, + InvalidDelegationException, MismatchException { - tokenHoldingBucket.withdraw(preparedStake.getResourceAddr(), preparedStake.getAmount()); + tokenHoldingBucket.withdraw(preparedStake.getResourceAddr(), preparedStake.getAmount()); - if (preparedStake.getAmount().compareTo(minimumStake) < 0) { - throw new MinimumStakeException(minimumStake, preparedStake.getAmount()); - } - if (!preparedStake.getDelegateKey().equals(validatorKey)) { - throw new MismatchException("Not matching validator keys"); - } + if (preparedStake.getAmount().compareTo(minimumStake) < 0) { + throw new MinimumStakeException(minimumStake, preparedStake.getAmount()); + } + if (!preparedStake.getDelegateKey().equals(validatorKey)) { + throw new MismatchException("Not matching validator keys"); + } - if (!delegateAllowed.test(preparedStake.getOwner())) { - throw new InvalidDelegationException(); - } + if (!delegateAllowed.test(preparedStake.getOwner())) { + throw new InvalidDelegationException(); + } - return tokenHoldingBucket; - } - } + return tokenHoldingBucket; + } + } - private void defineStaking(Loader os) { - // Stake - os.procedure(new ReadProcedure<>( - TokenHoldingBucket.class, AllowDelegationFlag.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, d, r) -> { - var nextState = (!d.allowsDelegation()) - ? new OwnerStakePrepare(s, d) - : new StakePrepare(s, d.getValidatorKey(), p -> true); - return ReducerResult.incomplete(nextState); - } - )); - os.procedure(new ReadProcedure<>( - OwnerStakePrepare.class, ValidatorOwnerCopy.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, d, r) -> { - var nextState = s.readOwner(d); - return ReducerResult.incomplete(nextState); - } - )); - os.procedure(new UpProcedure<>( - StakePrepare.class, PreparedStake.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - var nextState = s.withdrawTo(u); - return ReducerResult.incomplete(nextState); - } - )); + private void defineStaking(Loader os) { + // Stake + os.procedure( + new ReadProcedure<>( + TokenHoldingBucket.class, + AllowDelegationFlag.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, d, r) -> { + var nextState = + (!d.allowsDelegation()) + ? new OwnerStakePrepare(s, d) + : new StakePrepare(s, d.getValidatorKey(), p -> true); + return ReducerResult.incomplete(nextState); + })); + os.procedure( + new ReadProcedure<>( + OwnerStakePrepare.class, + ValidatorOwnerCopy.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, d, r) -> { + var nextState = s.readOwner(d); + return ReducerResult.incomplete(nextState); + })); + os.procedure( + new UpProcedure<>( + StakePrepare.class, + PreparedStake.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + var nextState = s.withdrawTo(u); + return ReducerResult.incomplete(nextState); + })); - // Unstake - os.procedure(new DownProcedure<>( - VoidReducerState.class, StakeOwnership.class, - d -> d.bucket().withdrawAuthorization(), - (d, s, r, c) -> ReducerResult.incomplete(new StakeOwnershipHoldingBucket(d)) - )); - // Additional Unstake - os.procedure(new DownProcedure<>( - StakeOwnershipHoldingBucket.class, StakeOwnership.class, - d -> d.bucket().withdrawAuthorization(), - (d, s, r, c) -> { - s.depositOwnership(d); - return ReducerResult.incomplete(s); - } - )); - // Change - os.procedure(new UpProcedure<>( - StakeOwnershipHoldingBucket.class, StakeOwnership.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - var ownership = s.withdrawOwnership(u.getAmount()); - if (!ownership.equals(u)) { - throw new MismatchException(ownership, u); - } - return ReducerResult.incomplete(s); - } - )); - os.procedure(new UpProcedure<>( - StakeOwnershipHoldingBucket.class, PreparedUnstakeOwnership.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - var unstake = s.unstake(u.getAmount()); - if (!unstake.equals(u)) { - throw new MismatchException(unstake, u); - } - return ReducerResult.incomplete(s); - } - )); + // Unstake + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + StakeOwnership.class, + d -> d.bucket().withdrawAuthorization(), + (d, s, r, c) -> ReducerResult.incomplete(new StakeOwnershipHoldingBucket(d)))); + // Additional Unstake + os.procedure( + new DownProcedure<>( + StakeOwnershipHoldingBucket.class, + StakeOwnership.class, + d -> d.bucket().withdrawAuthorization(), + (d, s, r, c) -> { + s.depositOwnership(d); + return ReducerResult.incomplete(s); + })); + // Change + os.procedure( + new UpProcedure<>( + StakeOwnershipHoldingBucket.class, + StakeOwnership.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + var ownership = s.withdrawOwnership(u.getAmount()); + if (!ownership.equals(u)) { + throw new MismatchException(ownership, u); + } + return ReducerResult.incomplete(s); + })); + os.procedure( + new UpProcedure<>( + StakeOwnershipHoldingBucket.class, + PreparedUnstakeOwnership.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + var unstake = s.unstake(u.getAmount()); + if (!unstake.equals(u)) { + throw new MismatchException(unstake, u); + } + return ReducerResult.incomplete(s); + })); - // Deallocate Stake Holding Bucket - os.procedure(new EndProcedure<>( - StakeOwnershipHoldingBucket.class, - s -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, c, r) -> s.destroy() - )); - } + // Deallocate Stake Holding Bucket + os.procedure( + new EndProcedure<>( + StakeOwnershipHoldingBucket.class, + s -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, c, r) -> s.destroy())); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokenHoldingBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokenHoldingBucket.java index 696ce09e2a..1908e39b27 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokenHoldingBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokenHoldingBucket.java @@ -65,62 +65,64 @@ package com.radixdlt.application.tokens.scrypt; import com.radixdlt.constraintmachine.ExecutionContext; +import com.radixdlt.constraintmachine.ReducerState; +import com.radixdlt.constraintmachine.Resources; import com.radixdlt.constraintmachine.exceptions.InvalidResourceException; import com.radixdlt.constraintmachine.exceptions.NotAResourceException; import com.radixdlt.constraintmachine.exceptions.NotEnoughResourcesException; import com.radixdlt.constraintmachine.exceptions.ProcedureException; -import com.radixdlt.constraintmachine.Resources; -import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.constraintmachine.exceptions.ResourceAllocationAndDestructionException; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; public final class TokenHoldingBucket implements ReducerState { - private Tokens tokens; + private Tokens tokens; - public TokenHoldingBucket(Tokens tokens) { - this.tokens = tokens; - } + public TokenHoldingBucket(Tokens tokens) { + this.tokens = tokens; + } - public boolean isEmpty() { - return tokens.isZero(); - } + public boolean isEmpty() { + return tokens.isZero(); + } - public REAddr getResourceAddr() { - return tokens.getResourceAddr(); - } + public REAddr getResourceAddr() { + return tokens.getResourceAddr(); + } - public void deposit(Tokens tokens) throws InvalidResourceException { - this.tokens = this.tokens.merge(tokens); - } + public void deposit(Tokens tokens) throws InvalidResourceException { + this.tokens = this.tokens.merge(tokens); + } - public Tokens withdraw(REAddr resourceAddr, UInt256 amountToWithdraw) throws InvalidResourceException, NotEnoughResourcesException { - if (!this.tokens.getResourceAddr().equals(resourceAddr)) { - throw new InvalidResourceException(resourceAddr, this.tokens.getResourceAddr()); - } + public Tokens withdraw(REAddr resourceAddr, UInt256 amountToWithdraw) + throws InvalidResourceException, NotEnoughResourcesException { + if (!this.tokens.getResourceAddr().equals(resourceAddr)) { + throw new InvalidResourceException(resourceAddr, this.tokens.getResourceAddr()); + } - if (amountToWithdraw.isZero()) { - return Tokens.zero(resourceAddr); - } + if (amountToWithdraw.isZero()) { + return Tokens.zero(resourceAddr); + } - var p = this.tokens.split(amountToWithdraw); - this.tokens = p.getSecond(); - return p.getFirst(); - } + var p = this.tokens.split(amountToWithdraw); + this.tokens = p.getSecond(); + return p.getFirst(); + } - public void destroy(ExecutionContext c, Resources r) throws ResourceAllocationAndDestructionException, NotAResourceException, ProcedureException { - if (!tokens.isZero()) { - c.verifyCanAllocAndDestroyResources(); + public void destroy(ExecutionContext c, Resources r) + throws ResourceAllocationAndDestructionException, NotAResourceException, ProcedureException { + if (!tokens.isZero()) { + c.verifyCanAllocAndDestroyResources(); - var tokenResource = r.loadResource(tokens.getResourceAddr()); - if (!tokenResource.isMutable()) { - throw new ProcedureException("Can only burn mutable tokens."); - } - } - } + var tokenResource = r.loadResource(tokens.getResourceAddr()); + if (!tokenResource.isMutable()) { + throw new ProcedureException("Can only burn mutable tokens."); + } + } + } - @Override - public String toString() { - return String.format("%s{tokens=%s}", this.getClass().getSimpleName(), tokens); - } + @Override + public String toString() { + return String.format("%s{tokens=%s}", this.getClass().getSimpleName(), tokens); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/Tokens.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/Tokens.java index 5f15ee5f89..181173ec82 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/Tokens.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/Tokens.java @@ -70,83 +70,81 @@ import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt384; - import java.util.Objects; /** - * Execution layer representation of tokens. We use UInt384 as substates are limited - * to use of UInt256 amounts and we make the assumption of 2^128 max total transactions - * in the network so UInt384 should be enough to handle this. + * Execution layer representation of tokens. We use UInt384 as substates are limited to use of + * UInt256 amounts and we make the assumption of 2^128 max total transactions in the network so + * UInt384 should be enough to handle this. */ public final class Tokens { - private final REAddr resourceAddr; - private final UInt384 amount; - - private Tokens( - REAddr resourceAddr, - UInt384 amount - ) { - this.resourceAddr = resourceAddr; - this.amount = amount; - } - - public static Tokens create(REAddr resourceAddr, UInt256 amount) { - return new Tokens(resourceAddr, UInt384.from(amount)); - } - - public static Tokens zero(REAddr resourceAddr) { - return new Tokens(resourceAddr, UInt384.ZERO); - } - - public UInt384 getAmount() { - return amount; - } - - public REAddr getResourceAddr() { - return resourceAddr; - } - - Pair split(UInt256 first) throws NotEnoughResourcesException { - var first384 = UInt384.from(first); - if (amount.compareTo(first384) < 0) { - throw new NotEnoughResourcesException(first, amount.getLow()); - } - - var second384 = this.amount.subtract(first384); - return Pair.of(new Tokens(resourceAddr, first384), new Tokens(resourceAddr, second384)); - } - - Tokens merge(Tokens tokens) throws InvalidResourceException { - if (!this.resourceAddr.equals(tokens.resourceAddr)) { - throw new InvalidResourceException(this.resourceAddr, tokens.resourceAddr); - } - - var amount = tokens.amount.add(this.amount); - return new Tokens(resourceAddr, amount); - } - - public boolean isZero() { - return amount.isZero(); - } - - @Override - public int hashCode() { - return Objects.hash(resourceAddr, amount); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Tokens)) { - return false; - } - - var other = (Tokens) o; - return Objects.equals(this.amount, other.amount) - && Objects.equals(this.resourceAddr, other.resourceAddr); - } - - @Override - public String toString() { - return String.format("%s{resource=%s amount=%s}", this.getClass().getSimpleName(), this.resourceAddr, this.amount); - } + private final REAddr resourceAddr; + private final UInt384 amount; + + private Tokens(REAddr resourceAddr, UInt384 amount) { + this.resourceAddr = resourceAddr; + this.amount = amount; + } + + public static Tokens create(REAddr resourceAddr, UInt256 amount) { + return new Tokens(resourceAddr, UInt384.from(amount)); + } + + public static Tokens zero(REAddr resourceAddr) { + return new Tokens(resourceAddr, UInt384.ZERO); + } + + public UInt384 getAmount() { + return amount; + } + + public REAddr getResourceAddr() { + return resourceAddr; + } + + Pair split(UInt256 first) throws NotEnoughResourcesException { + var first384 = UInt384.from(first); + if (amount.compareTo(first384) < 0) { + throw new NotEnoughResourcesException(first, amount.getLow()); + } + + var second384 = this.amount.subtract(first384); + return Pair.of(new Tokens(resourceAddr, first384), new Tokens(resourceAddr, second384)); + } + + Tokens merge(Tokens tokens) throws InvalidResourceException { + if (!this.resourceAddr.equals(tokens.resourceAddr)) { + throw new InvalidResourceException(this.resourceAddr, tokens.resourceAddr); + } + + var amount = tokens.amount.add(this.amount); + return new Tokens(resourceAddr, amount); + } + + public boolean isZero() { + return amount.isZero(); + } + + @Override + public int hashCode() { + return Objects.hash(resourceAddr, amount); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Tokens)) { + return false; + } + + var other = (Tokens) o; + return Objects.equals(this.amount, other.amount) + && Objects.equals(this.resourceAddr, other.resourceAddr); + } + + @Override + public String toString() { + return String.format( + "%s{resource=%s amount=%s}", + this.getClass().getSimpleName(), this.resourceAddr, this.amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokensConstraintScryptV3.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokensConstraintScryptV3.java index 31b884e440..e1cbeef8ba 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokensConstraintScryptV3.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/scrypt/TokensConstraintScryptV3.java @@ -66,262 +66,270 @@ import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; import com.radixdlt.application.tokens.ResourceCreatedEvent; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.tokens.state.TokenResourceMetadata; +import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atom.REFieldSerialization; import com.radixdlt.atom.SubstateTypeId; -import com.radixdlt.application.tokens.state.TokenResource; -import com.radixdlt.application.tokens.state.TokensInAccount; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; import com.radixdlt.constraintmachine.Authorization; -import com.radixdlt.constraintmachine.ExecutionContext; import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.EndProcedure; +import com.radixdlt.constraintmachine.ExecutionContext; import com.radixdlt.constraintmachine.PermissionLevel; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.constraintmachine.UpProcedure; import com.radixdlt.constraintmachine.VoidReducerState; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.exceptions.ReservedSymbolException; import com.radixdlt.serialization.DeserializeException; import com.radixdlt.utils.UInt256; - import java.nio.charset.StandardCharsets; import java.util.Set; import java.util.regex.Pattern; public final class TokensConstraintScryptV3 implements ConstraintScrypt { - private final Set reservedSymbols; - private final Pattern tokenSymbolPattern; + private final Set reservedSymbols; + private final Pattern tokenSymbolPattern; + + public TokensConstraintScryptV3(Set reservedSymbols, Pattern tokenSymbolPattern) { + this.reservedSymbols = reservedSymbols; + this.tokenSymbolPattern = tokenSymbolPattern; + } + + @Override + public void main(Loader os) { + registerParticles(os); + defineTokenCreation(os); + defineMintTransferBurn(os); + } - public TokensConstraintScryptV3(Set reservedSymbols, Pattern tokenSymbolPattern) { - this.reservedSymbols = reservedSymbols; - this.tokenSymbolPattern = tokenSymbolPattern; - } + private void registerParticles(Loader os) { + os.substate( + new SubstateDefinition<>( + TokenResource.class, + SubstateTypeId.TOKEN_RESOURCE.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var addr = REFieldSerialization.deserializeResourceAddr(buf); + var granularity = REFieldSerialization.deserializeNonZeroUInt256(buf); + if (!granularity.equals(UInt256.ONE)) { + throw new DeserializeException("Granularity must be one."); + } + var isMutable = REFieldSerialization.deserializeBoolean(buf); + var minter = REFieldSerialization.deserializeOptionalKey(buf); + return new TokenResource(addr, granularity, isMutable, minter.orElse(null)); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeREAddr(buf, s.getAddr()); + REFieldSerialization.serializeUInt256(buf, UInt256.ONE); + REFieldSerialization.serializeBoolean(buf, s.isMutable()); + REFieldSerialization.serializeOptionalKey(buf, s.getOwner()); + })); - @Override - public void main(Loader os) { - registerParticles(os); - defineTokenCreation(os); - defineMintTransferBurn(os); - } + os.substate( + new SubstateDefinition<>( + TokenResourceMetadata.class, + SubstateTypeId.TOKEN_RESOURCE_METADATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var addr = REFieldSerialization.deserializeResourceAddr(buf); + var symbol = REFieldSerialization.deserializeString(buf); + var name = REFieldSerialization.deserializeString(buf); + var description = REFieldSerialization.deserializeString(buf); + var url = REFieldSerialization.deserializeUrl(buf); + var iconUrl = REFieldSerialization.deserializeUrl(buf); + return new TokenResourceMetadata(addr, symbol, name, description, iconUrl, url); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeREAddr(buf, s.getAddr()); + REFieldSerialization.serializeString(buf, s.getSymbol()); + REFieldSerialization.serializeString(buf, s.getName()); + REFieldSerialization.serializeString(buf, s.getDescription()); + REFieldSerialization.serializeString(buf, s.getUrl()); + REFieldSerialization.serializeString(buf, s.getIconUrl()); + })); - private void registerParticles(Loader os) { - os.substate( - new SubstateDefinition<>( - TokenResource.class, - SubstateTypeId.TOKEN_RESOURCE.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var addr = REFieldSerialization.deserializeResourceAddr(buf); - var granularity = REFieldSerialization.deserializeNonZeroUInt256(buf); - if (!granularity.equals(UInt256.ONE)) { - throw new DeserializeException("Granularity must be one."); - } - var isMutable = REFieldSerialization.deserializeBoolean(buf); - var minter = REFieldSerialization.deserializeOptionalKey(buf); - return new TokenResource(addr, granularity, isMutable, minter.orElse(null)); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeREAddr(buf, s.getAddr()); - REFieldSerialization.serializeUInt256(buf, UInt256.ONE); - REFieldSerialization.serializeBoolean(buf, s.isMutable()); - REFieldSerialization.serializeOptionalKey(buf, s.getOwner()); - } - ) - ); + os.substate( + new SubstateDefinition<>( + TokensInAccount.class, + SubstateTypeId.TOKENS.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var holdingAddr = REFieldSerialization.deserializeAccountREAddr(buf); + var addr = REFieldSerialization.deserializeResourceAddr(buf); + var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); + return new TokensInAccount(holdingAddr, addr, amount); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeREAddr(buf, s.getHoldingAddr()); + REFieldSerialization.serializeREAddr(buf, s.getResourceAddr()); + buf.put(s.getAmount().toByteArray()); + })); + } - os.substate( - new SubstateDefinition<>( - TokenResourceMetadata.class, - SubstateTypeId.TOKEN_RESOURCE_METADATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var addr = REFieldSerialization.deserializeResourceAddr(buf); - var symbol = REFieldSerialization.deserializeString(buf); - var name = REFieldSerialization.deserializeString(buf); - var description = REFieldSerialization.deserializeString(buf); - var url = REFieldSerialization.deserializeUrl(buf); - var iconUrl = REFieldSerialization.deserializeUrl(buf); - return new TokenResourceMetadata(addr, symbol, name, description, iconUrl, url); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeREAddr(buf, s.getAddr()); - REFieldSerialization.serializeString(buf, s.getSymbol()); - REFieldSerialization.serializeString(buf, s.getName()); - REFieldSerialization.serializeString(buf, s.getDescription()); - REFieldSerialization.serializeString(buf, s.getUrl()); - REFieldSerialization.serializeString(buf, s.getIconUrl()); - } - ) - ); + private static class NeedFixedTokenSupply implements ReducerState { + private final byte[] arg; + private final TokenResource tokenResource; - os.substate( - new SubstateDefinition<>( - TokensInAccount.class, - SubstateTypeId.TOKENS.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var holdingAddr = REFieldSerialization.deserializeAccountREAddr(buf); - var addr = REFieldSerialization.deserializeResourceAddr(buf); - var amount = REFieldSerialization.deserializeNonZeroUInt256(buf); - return new TokensInAccount(holdingAddr, addr, amount); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeREAddr(buf, s.getHoldingAddr()); - REFieldSerialization.serializeREAddr(buf, s.getResourceAddr()); - buf.put(s.getAmount().toByteArray()); - } - ) - ); - } + private NeedFixedTokenSupply(byte[] arg, TokenResource tokenResource) { + this.arg = arg; + this.tokenResource = tokenResource; + } + } - private static class NeedFixedTokenSupply implements ReducerState { - private final byte[] arg; - private final TokenResource tokenResource; - private NeedFixedTokenSupply(byte[] arg, TokenResource tokenResource) { - this.arg = arg; - this.tokenResource = tokenResource; - } - } + private static class NeedMetadata implements ReducerState { + private final TokenResource tokenResource; + private final byte[] arg; - private static class NeedMetadata implements ReducerState { - private final TokenResource tokenResource; - private final byte[] arg; - private NeedMetadata(byte[] arg, TokenResource tokenResource) { - this.arg = arg; - this.tokenResource = tokenResource; - } + private NeedMetadata(byte[] arg, TokenResource tokenResource) { + this.arg = arg; + this.tokenResource = tokenResource; + } - void metadata(TokenResourceMetadata metadata, ExecutionContext context) throws ProcedureException { - if (!metadata.getAddr().equals(tokenResource.getAddr())) { - throw new ProcedureException("Addresses don't match."); - } + void metadata(TokenResourceMetadata metadata, ExecutionContext context) + throws ProcedureException { + if (!metadata.getAddr().equals(tokenResource.getAddr())) { + throw new ProcedureException("Addresses don't match."); + } - var symbol = new String(arg, StandardCharsets.UTF_8); - if (!symbol.equals(metadata.getSymbol())) { - throw new ProcedureException("Symbols don't match."); - } - context.emitEvent(new ResourceCreatedEvent(symbol, tokenResource, metadata)); - } - } + var symbol = new String(arg, StandardCharsets.UTF_8); + if (!symbol.equals(metadata.getSymbol())) { + throw new ProcedureException("Symbols don't match."); + } + context.emitEvent(new ResourceCreatedEvent(symbol, tokenResource, metadata)); + } + } - private void defineTokenCreation(Loader os) { - os.procedure(new UpProcedure<>( - SystemConstraintScrypt.REAddrClaim.class, TokenResource.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - if (!u.getAddr().equals(s.getAddr())) { - throw new ProcedureException("Addresses don't match"); - } + private void defineTokenCreation(Loader os) { + os.procedure( + new UpProcedure<>( + SystemConstraintScrypt.REAddrClaim.class, + TokenResource.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + if (!u.getAddr().equals(s.getAddr())) { + throw new ProcedureException("Addresses don't match"); + } - var str = new String(s.getArg()); - if (reservedSymbols.contains(str) && c.permissionLevel() != PermissionLevel.SYSTEM) { - throw new ReservedSymbolException(str); - } - if (!tokenSymbolPattern.matcher(str).matches()) { - throw new ProcedureException("invalid token symbol: " + str); - } + var str = new String(s.getArg()); + if (reservedSymbols.contains(str) && c.permissionLevel() != PermissionLevel.SYSTEM) { + throw new ReservedSymbolException(str); + } + if (!tokenSymbolPattern.matcher(str).matches()) { + throw new ProcedureException("invalid token symbol: " + str); + } - if (u.isMutable()) { - return ReducerResult.incomplete(new NeedMetadata(s.getArg(), u)); - } + if (u.isMutable()) { + return ReducerResult.incomplete(new NeedMetadata(s.getArg(), u)); + } - if (!u.getGranularity().equals(UInt256.ONE)) { - throw new ProcedureException("Granularity must be one."); - } + if (!u.getGranularity().equals(UInt256.ONE)) { + throw new ProcedureException("Granularity must be one."); + } - return ReducerResult.incomplete(new NeedFixedTokenSupply(s.getArg(), u)); - } - )); + return ReducerResult.incomplete(new NeedFixedTokenSupply(s.getArg(), u)); + })); - os.procedure(new UpProcedure<>( - NeedFixedTokenSupply.class, TokensInAccount.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - if (!u.getResourceAddr().equals(s.tokenResource.getAddr())) { - throw new ProcedureException("Addresses don't match."); - } - return ReducerResult.incomplete(new NeedMetadata(s.arg, s.tokenResource)); - } - )); + os.procedure( + new UpProcedure<>( + NeedFixedTokenSupply.class, + TokensInAccount.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + if (!u.getResourceAddr().equals(s.tokenResource.getAddr())) { + throw new ProcedureException("Addresses don't match."); + } + return ReducerResult.incomplete(new NeedMetadata(s.arg, s.tokenResource)); + })); - os.procedure(new UpProcedure<>( - NeedMetadata.class, TokenResourceMetadata.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.metadata(u, c); - return ReducerResult.complete(); - } - )); - } + os.procedure( + new UpProcedure<>( + NeedMetadata.class, + TokenResourceMetadata.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.metadata(u, c); + return ReducerResult.complete(); + })); + } - private void defineMintTransferBurn(Loader os) { - // Mint - os.procedure(new UpProcedure<>( - VoidReducerState.class, TokensInAccount.class, - u -> { - if (u.getResourceAddr().isNativeToken()) { - return new Authorization(PermissionLevel.SYSTEM, (r, c) -> { }); - } + private void defineMintTransferBurn(Loader os) { + // Mint + os.procedure( + new UpProcedure<>( + VoidReducerState.class, + TokensInAccount.class, + u -> { + if (u.getResourceAddr().isNativeToken()) { + return new Authorization(PermissionLevel.SYSTEM, (r, c) -> {}); + } - return new Authorization(PermissionLevel.USER, (r, c) -> { - var tokenResource = r.loadResource(u.getResourceAddr()); - tokenResource.verifyMintAuthorization(c.key()); - }); - }, - (s, u, c, r) -> { - c.verifyCanAllocAndDestroyResources(); - return ReducerResult.complete(); - } - )); + return new Authorization( + PermissionLevel.USER, + (r, c) -> { + var tokenResource = r.loadResource(u.getResourceAddr()); + tokenResource.verifyMintAuthorization(c.key()); + }); + }, + (s, u, c, r) -> { + c.verifyCanAllocAndDestroyResources(); + return ReducerResult.complete(); + })); - // Burn - os.procedure(new EndProcedure<>( - TokenHoldingBucket.class, - s -> new Authorization(PermissionLevel.USER, (r, c) -> { - if (s.isEmpty()) { - return; - } - var tokenResource = r.loadResource(s.getResourceAddr()); - tokenResource.verifyBurnAuthorization(c.key()); - }), - TokenHoldingBucket::destroy - )); + // Burn + os.procedure( + new EndProcedure<>( + TokenHoldingBucket.class, + s -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (s.isEmpty()) { + return; + } + var tokenResource = r.loadResource(s.getResourceAddr()); + tokenResource.verifyBurnAuthorization(c.key()); + }), + TokenHoldingBucket::destroy)); - // Initial Withdraw - os.procedure(new DownProcedure<>( - VoidReducerState.class, TokensInAccount.class, - d -> d.bucket().withdrawAuthorization(), - (d, s, r, c) -> { - var state = new TokenHoldingBucket(d.toTokens()); - return ReducerResult.incomplete(state); - } - )); + // Initial Withdraw + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + TokensInAccount.class, + d -> d.bucket().withdrawAuthorization(), + (d, s, r, c) -> { + var state = new TokenHoldingBucket(d.toTokens()); + return ReducerResult.incomplete(state); + })); - // More Withdraws - os.procedure(new DownProcedure<>( - TokenHoldingBucket.class, TokensInAccount.class, - d -> d.bucket().withdrawAuthorization(), - (d, s, r, c) -> { - s.deposit(d.toTokens()); - return ReducerResult.incomplete(s); - } - )); + // More Withdraws + os.procedure( + new DownProcedure<>( + TokenHoldingBucket.class, + TokensInAccount.class, + d -> d.bucket().withdrawAuthorization(), + (d, s, r, c) -> { + s.deposit(d.toTokens()); + return ReducerResult.incomplete(s); + })); - // Deposit - os.procedure(new UpProcedure<>( - TokenHoldingBucket.class, TokensInAccount.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.withdraw(u.getResourceAddr(), u.getAmount()); - return ReducerResult.incomplete(s); - } - )); - } -} \ No newline at end of file + // Deposit + os.procedure( + new UpProcedure<>( + TokenHoldingBucket.class, + TokensInAccount.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.withdraw(u.getResourceAddr(), u.getAmount()); + return ReducerResult.incomplete(s); + })); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/AccountBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/AccountBucket.java index 8e2ef498f1..aae5cf3d1b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/AccountBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/AccountBucket.java @@ -65,80 +65,79 @@ package com.radixdlt.application.tokens.state; import com.radixdlt.application.tokens.Bucket; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.Authorization; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class AccountBucket implements Bucket { - private final REAddr resourceAddress; - private final REAddr holdingAddress; + private final REAddr resourceAddress; + private final REAddr holdingAddress; - private AccountBucket(REAddr resourceAddress, REAddr holdingAddress) { - this.resourceAddress = resourceAddress; - this.holdingAddress = holdingAddress; - } + private AccountBucket(REAddr resourceAddress, REAddr holdingAddress) { + this.resourceAddress = resourceAddress; + this.holdingAddress = holdingAddress; + } - public static AccountBucket from(REAddr resourceAddress, REAddr holdingAddress) { - return new AccountBucket(resourceAddress, holdingAddress); - } + public static AccountBucket from(REAddr resourceAddress, REAddr holdingAddress) { + return new AccountBucket(resourceAddress, holdingAddress); + } - @Override - public Authorization withdrawAuthorization() { - return new Authorization( - PermissionLevel.USER, - (r, c) -> { - try { - holdingAddress.verifyWithdrawAuthorization(c.key()); - } catch (REAddr.BucketWithdrawAuthorizationException e) { - throw new AuthorizationException(e.getMessage()); - } - } - ); - } + @Override + public Authorization withdrawAuthorization() { + return new Authorization( + PermissionLevel.USER, + (r, c) -> { + try { + holdingAddress.verifyWithdrawAuthorization(c.key()); + } catch (REAddr.BucketWithdrawAuthorizationException e) { + throw new AuthorizationException(e.getMessage()); + } + }); + } - @Override - public REAddr resourceAddr() { - return resourceAddress; - } + @Override + public REAddr resourceAddr() { + return resourceAddress; + } - @Override - public REAddr getOwner() { - return holdingAddress; - } + @Override + public REAddr getOwner() { + return holdingAddress; + } - @Override - public ECPublicKey getValidatorKey() { - return null; - } + @Override + public ECPublicKey getValidatorKey() { + return null; + } - @Override - public Long getEpochUnlock() { - // This should still be null as its a different epoch unlock - return null; - } + @Override + public Long getEpochUnlock() { + // This should still be null as its a different epoch unlock + return null; + } - @Override - public String toString() { - return String.format("%s{res=%s owner=%s}", this.getClass().getSimpleName(), resourceAddress, holdingAddress); - } + @Override + public String toString() { + return String.format( + "%s{res=%s owner=%s}", this.getClass().getSimpleName(), resourceAddress, holdingAddress); + } - @Override - public int hashCode() { - return Objects.hash(holdingAddress, resourceAddress); - } + @Override + public int hashCode() { + return Objects.hash(holdingAddress, resourceAddress); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof AccountBucket)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof AccountBucket)) { + return false; + } - var other = (AccountBucket) o; - return Objects.equals(this.holdingAddress, other.holdingAddress) - && Objects.equals(this.resourceAddress, other.resourceAddress); - } + var other = (AccountBucket) o; + return Objects.equals(this.holdingAddress, other.holdingAddress) + && Objects.equals(this.resourceAddress, other.resourceAddress); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/DeprecatedResourceInBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/DeprecatedResourceInBucket.java index f17dbd61c7..cf55b3034b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/DeprecatedResourceInBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/DeprecatedResourceInBucket.java @@ -65,35 +65,35 @@ package com.radixdlt.application.tokens.state; import com.radixdlt.identifiers.REAddr; - import java.util.Optional; public class DeprecatedResourceInBucket { - private final REAddr resourceAddr; - // Bucket - private final REAddr holdingAddress; - // Bucket properties - private final Long epochUnlocked; + private final REAddr resourceAddr; + // Bucket + private final REAddr holdingAddress; + // Bucket properties + private final Long epochUnlocked; - public DeprecatedResourceInBucket(REAddr resourceAddr, REAddr holdingAddress, Long epochUnlocked) { - this.resourceAddr = resourceAddr; - this.holdingAddress = holdingAddress; - this.epochUnlocked = epochUnlocked; - } + public DeprecatedResourceInBucket( + REAddr resourceAddr, REAddr holdingAddress, Long epochUnlocked) { + this.resourceAddr = resourceAddr; + this.holdingAddress = holdingAddress; + this.epochUnlocked = epochUnlocked; + } - public boolean isNativeToken() { - return resourceAddr.isNativeToken(); - } + public boolean isNativeToken() { + return resourceAddr.isNativeToken(); + } - public REAddr resourceAddr() { - return resourceAddr; - } + public REAddr resourceAddr() { + return resourceAddr; + } - public REAddr holdingAddress() { - return holdingAddress; - } + public REAddr holdingAddress() { + return holdingAddress; + } - public Optional epochUnlocked() { - return Optional.ofNullable(epochUnlocked); - } + public Optional epochUnlocked() { + return Optional.ofNullable(epochUnlocked); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExitingStake.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExitingStake.java index 5e5d695efb..454d918712 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExitingStake.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExitingStake.java @@ -69,101 +69,83 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.nio.ByteBuffer; import java.util.Objects; public final class ExitingStake implements ResourceInBucket { - private final UInt256 amount; - - // Bucket keys - private final REAddr owner; - private final ECPublicKey delegateKey; - private final long epochUnlocked; - - public ExitingStake( - long epochUnlocked, - ECPublicKey delegateKey, - REAddr owner, - UInt256 amount - ) { - this.delegateKey = Objects.requireNonNull(delegateKey); - this.owner = Objects.requireNonNull(owner); - this.amount = Objects.requireNonNull(amount); - this.epochUnlocked = epochUnlocked; - } - - public byte[] dataKey() { - var dataSize = ECPublicKey.COMPRESSED_BYTES + (ECPublicKey.COMPRESSED_BYTES + 1) + Long.BYTES; - var bytes = new byte[dataSize]; - var byteBuffer = ByteBuffer.wrap(bytes); - byteBuffer.putLong(epochUnlocked); - byteBuffer.put(delegateKey.getCompressedBytes()); - byteBuffer.put(owner.getBytes()); - return bytes; - } - - public long getEpochUnlocked() { - return epochUnlocked; - } - - public TokensInAccount unlock() { - return new TokensInAccount( - owner, REAddr.ofNativeToken(), amount - ); - } - - @Override - public UInt256 getAmount() { - return this.amount; - } - - @Override - public Bucket bucket() { - return new ExittingStakeBucket(owner, delegateKey, epochUnlocked); - } - - public ECPublicKey getDelegateKey() { - return delegateKey; - } - - public REAddr getOwner() { - return this.owner; - } - - @Override - public String toString() { - return String.format("%s[%s:%s:%s:%s]", - getClass().getSimpleName(), - amount, - owner, - delegateKey, - epochUnlocked - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ExitingStake)) { - return false; - } - var that = (ExitingStake) o; - return Objects.equals(delegateKey, that.delegateKey) - && Objects.equals(owner, that.owner) - && Objects.equals(amount, that.amount) - && this.epochUnlocked == that.epochUnlocked; - } - - @Override - public int hashCode() { - return Objects.hash( - delegateKey, - owner, - amount, - epochUnlocked - ); - } + private final UInt256 amount; + + // Bucket keys + private final REAddr owner; + private final ECPublicKey delegateKey; + private final long epochUnlocked; + + public ExitingStake(long epochUnlocked, ECPublicKey delegateKey, REAddr owner, UInt256 amount) { + this.delegateKey = Objects.requireNonNull(delegateKey); + this.owner = Objects.requireNonNull(owner); + this.amount = Objects.requireNonNull(amount); + this.epochUnlocked = epochUnlocked; + } + + public byte[] dataKey() { + var dataSize = ECPublicKey.COMPRESSED_BYTES + (ECPublicKey.COMPRESSED_BYTES + 1) + Long.BYTES; + var bytes = new byte[dataSize]; + var byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.putLong(epochUnlocked); + byteBuffer.put(delegateKey.getCompressedBytes()); + byteBuffer.put(owner.getBytes()); + return bytes; + } + + public long getEpochUnlocked() { + return epochUnlocked; + } + + public TokensInAccount unlock() { + return new TokensInAccount(owner, REAddr.ofNativeToken(), amount); + } + + @Override + public UInt256 getAmount() { + return this.amount; + } + + @Override + public Bucket bucket() { + return new ExittingStakeBucket(owner, delegateKey, epochUnlocked); + } + + public ECPublicKey getDelegateKey() { + return delegateKey; + } + + public REAddr getOwner() { + return this.owner; + } + + @Override + public String toString() { + return String.format( + "%s[%s:%s:%s:%s]", getClass().getSimpleName(), amount, owner, delegateKey, epochUnlocked); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ExitingStake)) { + return false; + } + var that = (ExitingStake) o; + return Objects.equals(delegateKey, that.delegateKey) + && Objects.equals(owner, that.owner) + && Objects.equals(amount, that.amount) + && this.epochUnlocked == that.epochUnlocked; + } + + @Override + public int hashCode() { + return Objects.hash(delegateKey, owner, amount, epochUnlocked); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingOwnershipBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingOwnershipBucket.java index 116554c9d8..98a7f0ab1a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingOwnershipBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingOwnershipBucket.java @@ -69,55 +69,53 @@ import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class ExittingOwnershipBucket implements Bucket { - private final REAddr owner; - private final ECPublicKey delegate; + private final REAddr owner; + private final ECPublicKey delegate; - public ExittingOwnershipBucket(REAddr owner, ECPublicKey delegate) { - this.owner = owner; - this.delegate = delegate; - } + public ExittingOwnershipBucket(REAddr owner, ECPublicKey delegate) { + this.owner = owner; + this.delegate = delegate; + } - @Override - public Authorization withdrawAuthorization() { - return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }); - } + @Override + public Authorization withdrawAuthorization() { + return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}); + } - @Override - public REAddr resourceAddr() { - return null; - } + @Override + public REAddr resourceAddr() { + return null; + } - @Override - public REAddr getOwner() { - return owner; - } + @Override + public REAddr getOwner() { + return owner; + } - @Override - public ECPublicKey getValidatorKey() { - return delegate; - } + @Override + public ECPublicKey getValidatorKey() { + return delegate; + } - @Override - public Long getEpochUnlock() { - return 0L; - } + @Override + public Long getEpochUnlock() { + return 0L; + } - @Override - public int hashCode() { - return Objects.hash(owner, delegate); - } + @Override + public int hashCode() { + return Objects.hash(owner, delegate); + } - public boolean equals(Object o) { - if (!(o instanceof ExittingOwnershipBucket)) { - return false; - } + public boolean equals(Object o) { + if (!(o instanceof ExittingOwnershipBucket)) { + return false; + } - var other = (ExittingOwnershipBucket) o; - return Objects.equals(this.owner, other.owner) - && Objects.equals(this.delegate, other.delegate); - } + var other = (ExittingOwnershipBucket) o; + return Objects.equals(this.owner, other.owner) && Objects.equals(this.delegate, other.delegate); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingStakeBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingStakeBucket.java index bbefaceb6c..0b50081173 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingStakeBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ExittingStakeBucket.java @@ -69,59 +69,58 @@ import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class ExittingStakeBucket implements Bucket { - private final REAddr owner; - private final ECPublicKey delegateKey; - private final Long epochUnlocked; + private final REAddr owner; + private final ECPublicKey delegateKey; + private final Long epochUnlocked; - public ExittingStakeBucket(REAddr owner, ECPublicKey delegateKey, Long epochUnlocked) { - this.owner = owner; - this.delegateKey = delegateKey; - this.epochUnlocked = epochUnlocked; - } + public ExittingStakeBucket(REAddr owner, ECPublicKey delegateKey, Long epochUnlocked) { + this.owner = owner; + this.delegateKey = delegateKey; + this.epochUnlocked = epochUnlocked; + } - @Override - public Authorization withdrawAuthorization() { - return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }); - } + @Override + public Authorization withdrawAuthorization() { + return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}); + } - @Override - public REAddr resourceAddr() { - return REAddr.ofNativeToken(); - } + @Override + public REAddr resourceAddr() { + return REAddr.ofNativeToken(); + } - @Override - public REAddr getOwner() { - return owner; - } + @Override + public REAddr getOwner() { + return owner; + } - @Override - public ECPublicKey getValidatorKey() { - return delegateKey; - } + @Override + public ECPublicKey getValidatorKey() { + return delegateKey; + } - @Override - public Long getEpochUnlock() { - return epochUnlocked; - } + @Override + public Long getEpochUnlock() { + return epochUnlocked; + } - @Override - public int hashCode() { - return Objects.hash(owner, delegateKey, epochUnlocked); - } + @Override + public int hashCode() { + return Objects.hash(owner, delegateKey, epochUnlocked); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ExittingStakeBucket)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ExittingStakeBucket)) { + return false; + } - var other = (ExittingStakeBucket) o; - return Objects.equals(this.owner, other.owner) - && Objects.equals(this.delegateKey, other.delegateKey) - && Objects.equals(this.epochUnlocked, other.epochUnlocked); - } -} \ No newline at end of file + var other = (ExittingStakeBucket) o; + return Objects.equals(this.owner, other.owner) + && Objects.equals(this.delegateKey, other.delegateKey) + && Objects.equals(this.epochUnlocked, other.epochUnlocked); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStake.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStake.java index f4b896fe30..48fcf17cb4 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStake.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStake.java @@ -69,82 +69,68 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.Objects; /** - * A particle which represents an amount of staked fungible tokens - * owned by some key owner, stored in an account and staked to a delegate address. + * A particle which represents an amount of staked fungible tokens owned by some key owner, stored + * in an account and staked to a delegate address. */ public final class PreparedStake implements ResourceInBucket { - private final UInt256 amount; + private final UInt256 amount; - // Bucket keys - private final REAddr owner; - private final ECPublicKey delegateKey; + // Bucket keys + private final REAddr owner; + private final ECPublicKey delegateKey; - public PreparedStake( - UInt256 amount, - REAddr owner, - ECPublicKey delegateKey - ) { - this.delegateKey = Objects.requireNonNull(delegateKey); - this.owner = Objects.requireNonNull(owner); - this.amount = Objects.requireNonNull(amount); - } + public PreparedStake(UInt256 amount, REAddr owner, ECPublicKey delegateKey) { + this.delegateKey = Objects.requireNonNull(delegateKey); + this.owner = Objects.requireNonNull(owner); + this.amount = Objects.requireNonNull(amount); + } - @Override - public UInt256 getAmount() { - return this.amount; - } + @Override + public UInt256 getAmount() { + return this.amount; + } - @Override - public Bucket bucket() { - return new PreparedStakeBucket(owner, delegateKey); - } + @Override + public Bucket bucket() { + return new PreparedStakeBucket(owner, delegateKey); + } - public REAddr getResourceAddr() { - return REAddr.ofNativeToken(); - } + public REAddr getResourceAddr() { + return REAddr.ofNativeToken(); + } - public ECPublicKey getDelegateKey() { - return delegateKey; - } + public ECPublicKey getDelegateKey() { + return delegateKey; + } - public REAddr getOwner() { - return this.owner; - } + public REAddr getOwner() { + return this.owner; + } - @Override - public String toString() { - return String.format("%s[%s:%s:%s]", - getClass().getSimpleName(), - amount, - owner, - delegateKey - ); - } + @Override + public String toString() { + return String.format("%s[%s:%s:%s]", getClass().getSimpleName(), amount, owner, delegateKey); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof PreparedStake)) { - return false; - } - PreparedStake that = (PreparedStake) o; - return Objects.equals(delegateKey, that.delegateKey) - && Objects.equals(owner, that.owner) - && Objects.equals(amount, that.amount); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PreparedStake)) { + return false; + } + PreparedStake that = (PreparedStake) o; + return Objects.equals(delegateKey, that.delegateKey) + && Objects.equals(owner, that.owner) + && Objects.equals(amount, that.amount); + } - @Override - public int hashCode() { - return Objects.hash( - delegateKey, - owner, - amount - ); - } + @Override + public int hashCode() { + return Objects.hash(delegateKey, owner, amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStakeBucket.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStakeBucket.java index 8bfed9f168..892794968b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStakeBucket.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedStakeBucket.java @@ -69,56 +69,55 @@ import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class PreparedStakeBucket implements Bucket { - private final REAddr owner; - private final ECPublicKey delegateKey; + private final REAddr owner; + private final ECPublicKey delegateKey; - public PreparedStakeBucket(REAddr owner, ECPublicKey delegateKey) { - this.owner = owner; - this.delegateKey = delegateKey; - } + public PreparedStakeBucket(REAddr owner, ECPublicKey delegateKey) { + this.owner = owner; + this.delegateKey = delegateKey; + } - @Override - public Authorization withdrawAuthorization() { - return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> { }); - } + @Override + public Authorization withdrawAuthorization() { + return new Authorization(PermissionLevel.SUPER_USER, (r, c) -> {}); + } - @Override - public REAddr resourceAddr() { - return REAddr.ofNativeToken(); - } + @Override + public REAddr resourceAddr() { + return REAddr.ofNativeToken(); + } - @Override - public REAddr getOwner() { - return owner; - } + @Override + public REAddr getOwner() { + return owner; + } - @Override - public ECPublicKey getValidatorKey() { - return delegateKey; - } + @Override + public ECPublicKey getValidatorKey() { + return delegateKey; + } - @Override - public Long getEpochUnlock() { - return null; - } + @Override + public Long getEpochUnlock() { + return null; + } - @Override - public int hashCode() { - return Objects.hash(owner, delegateKey); - } + @Override + public int hashCode() { + return Objects.hash(owner, delegateKey); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof PreparedStakeBucket)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof PreparedStakeBucket)) { + return false; + } - var other = (PreparedStakeBucket) o; - return Objects.equals(this.owner, other.owner) - && Objects.equals(this.delegateKey, other.delegateKey); - } + var other = (PreparedStakeBucket) o; + return Objects.equals(this.owner, other.owner) + && Objects.equals(this.delegateKey, other.delegateKey); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedUnstakeOwnership.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedUnstakeOwnership.java index 48a87742c5..26521ed3d0 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedUnstakeOwnership.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/PreparedUnstakeOwnership.java @@ -69,73 +69,59 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class PreparedUnstakeOwnership implements ResourceInBucket { - private final UInt256 amount; + private final UInt256 amount; - // Bucket keys - private final REAddr owner; - private final ECPublicKey delegateKey; + // Bucket keys + private final REAddr owner; + private final ECPublicKey delegateKey; - public PreparedUnstakeOwnership( - ECPublicKey delegateKey, - REAddr owner, - UInt256 amount - ) { - this.delegateKey = Objects.requireNonNull(delegateKey); - this.owner = Objects.requireNonNull(owner); - this.amount = Objects.requireNonNull(amount); - } + public PreparedUnstakeOwnership(ECPublicKey delegateKey, REAddr owner, UInt256 amount) { + this.delegateKey = Objects.requireNonNull(delegateKey); + this.owner = Objects.requireNonNull(owner); + this.amount = Objects.requireNonNull(amount); + } - public ECPublicKey getDelegateKey() { - return delegateKey; - } + public ECPublicKey getDelegateKey() { + return delegateKey; + } - public REAddr getOwner() { - return this.owner; - } + public REAddr getOwner() { + return this.owner; + } - @Override - public UInt256 getAmount() { - return this.amount; - } + @Override + public UInt256 getAmount() { + return this.amount; + } - public Bucket bucket() { - return new ExittingOwnershipBucket(owner, delegateKey); - } + public Bucket bucket() { + return new ExittingOwnershipBucket(owner, delegateKey); + } - @Override - public String toString() { - return String.format("%s[%s:%s:%s]", - getClass().getSimpleName(), - amount, - owner, - delegateKey - ); - } + @Override + public String toString() { + return String.format("%s[%s:%s:%s]", getClass().getSimpleName(), amount, owner, delegateKey); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof PreparedUnstakeOwnership)) { - return false; - } - var that = (PreparedUnstakeOwnership) o; - return Objects.equals(delegateKey, that.delegateKey) - && Objects.equals(owner, that.owner) - && Objects.equals(amount, that.amount); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PreparedUnstakeOwnership)) { + return false; + } + var that = (PreparedUnstakeOwnership) o; + return Objects.equals(delegateKey, that.delegateKey) + && Objects.equals(owner, that.owner) + && Objects.equals(amount, that.amount); + } - @Override - public int hashCode() { - return Objects.hash( - delegateKey, - owner, - amount - ); - } + @Override + public int hashCode() { + return Objects.hash(delegateKey, owner, amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ResourceData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ResourceData.java index 67f343d168..86c8df0880 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ResourceData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/ResourceData.java @@ -68,5 +68,5 @@ import com.radixdlt.identifiers.REAddr; public interface ResourceData extends Particle { - REAddr getAddr(); + REAddr getAddr(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResource.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResource.java index ee1a3b575f..db14f55bc0 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResource.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResource.java @@ -68,94 +68,87 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.Objects; import java.util.Optional; -/** - * Particle representing a fixed supply token definition - */ +/** Particle representing a fixed supply token definition */ public final class TokenResource implements ResourceData { - private final REAddr addr; - private final UInt256 granularity; - private final boolean isMutable; - private final ECPublicKey owner; - - public TokenResource( - REAddr addr, - UInt256 granularity, - boolean isMutable, - ECPublicKey owner - ) { - if (!isMutable && owner != null) { - throw new IllegalArgumentException("Can't have fixed supply and minter"); - } - this.addr = Objects.requireNonNull(addr); - this.granularity = granularity; - this.isMutable = isMutable; - this.owner = owner; - } - - public static TokenResource createFixedSupplyResource(REAddr addr) { - return new TokenResource(addr, UInt256.ONE, false, null); - } - - public static TokenResource createMutableSupplyResource(REAddr addr, ECPublicKey owner) { - return new TokenResource(addr, UInt256.ONE, true, owner); - } - - public void verifyMintAuthorization(Optional key) throws AuthorizationException { - if (!key.flatMap(p -> getOwner().map(p::equals)).orElse(false)) { - throw new AuthorizationException("Key not authorized: " + key); - } - } - - public void verifyBurnAuthorization(Optional key) throws AuthorizationException { - if (!key.flatMap(p -> getOwner().map(p::equals)).orElse(false)) { - throw new AuthorizationException("Key not authorized: " + key); - } - } - - public UInt256 getGranularity() { - return granularity; - } - - public Optional getOwner() { - return Optional.ofNullable(owner); - } - - public boolean isMutable() { - return isMutable; - } - - @Override - public REAddr getAddr() { - return addr; - } - - @Override - public String toString() { - return String.format("%s{addr=%s granularity=%s isMutable=%s owner=%s}", getClass().getSimpleName(), - this.addr, this.granularity, isMutable, owner); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TokenResource)) { - return false; - } - TokenResource that = (TokenResource) o; - return Objects.equals(addr, that.addr) - && Objects.equals(this.granularity, that.granularity) - && this.isMutable == that.isMutable - && Objects.equals(owner, that.owner); - } - - @Override - public int hashCode() { - return Objects.hash(addr, granularity, isMutable, owner); - } + private final REAddr addr; + private final UInt256 granularity; + private final boolean isMutable; + private final ECPublicKey owner; + + public TokenResource(REAddr addr, UInt256 granularity, boolean isMutable, ECPublicKey owner) { + if (!isMutable && owner != null) { + throw new IllegalArgumentException("Can't have fixed supply and minter"); + } + this.addr = Objects.requireNonNull(addr); + this.granularity = granularity; + this.isMutable = isMutable; + this.owner = owner; + } + + public static TokenResource createFixedSupplyResource(REAddr addr) { + return new TokenResource(addr, UInt256.ONE, false, null); + } + + public static TokenResource createMutableSupplyResource(REAddr addr, ECPublicKey owner) { + return new TokenResource(addr, UInt256.ONE, true, owner); + } + + public void verifyMintAuthorization(Optional key) throws AuthorizationException { + if (!key.flatMap(p -> getOwner().map(p::equals)).orElse(false)) { + throw new AuthorizationException("Key not authorized: " + key); + } + } + + public void verifyBurnAuthorization(Optional key) throws AuthorizationException { + if (!key.flatMap(p -> getOwner().map(p::equals)).orElse(false)) { + throw new AuthorizationException("Key not authorized: " + key); + } + } + + public UInt256 getGranularity() { + return granularity; + } + + public Optional getOwner() { + return Optional.ofNullable(owner); + } + + public boolean isMutable() { + return isMutable; + } + + @Override + public REAddr getAddr() { + return addr; + } + + @Override + public String toString() { + return String.format( + "%s{addr=%s granularity=%s isMutable=%s owner=%s}", + getClass().getSimpleName(), this.addr, this.granularity, isMutable, owner); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TokenResource)) { + return false; + } + TokenResource that = (TokenResource) o; + return Objects.equals(addr, that.addr) + && Objects.equals(this.granularity, that.granularity) + && this.isMutable == that.isMutable + && Objects.equals(owner, that.owner); + } + + @Override + public int hashCode() { + return Objects.hash(addr, granularity, isMutable, owner); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResourceMetadata.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResourceMetadata.java index 99e9f2567b..599f5e24cf 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResourceMetadata.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokenResourceMetadata.java @@ -66,91 +66,84 @@ import com.radixdlt.atom.REFieldSerialization; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class TokenResourceMetadata implements ResourceData { - private final REAddr addr; - private final String name; - private final String symbol; - private final String description; - private final String iconUrl; - private final String url; + private final REAddr addr; + private final String name; + private final String symbol; + private final String description; + private final String iconUrl; + private final String url; - public TokenResourceMetadata( - REAddr addr, - String symbol, - String name, - String description, - String iconUrl, - String url - ) { - this.addr = Objects.requireNonNull(addr); - this.name = Objects.requireNonNull(name); - this.symbol = Objects.requireNonNull(symbol); - this.description = Objects.requireNonNull(description); - this.iconUrl = REFieldSerialization.requireValidUrl(iconUrl); - this.url = REFieldSerialization.requireValidUrl(url); - } + public TokenResourceMetadata( + REAddr addr, String symbol, String name, String description, String iconUrl, String url) { + this.addr = Objects.requireNonNull(addr); + this.name = Objects.requireNonNull(name); + this.symbol = Objects.requireNonNull(symbol); + this.description = Objects.requireNonNull(description); + this.iconUrl = REFieldSerialization.requireValidUrl(iconUrl); + this.url = REFieldSerialization.requireValidUrl(url); + } - @Override - public REAddr getAddr() { - return addr; - } + @Override + public REAddr getAddr() { + return addr; + } - public String getSymbol() { - return symbol; - } + public String getSymbol() { + return symbol; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - public String getIconUrl() { - return iconUrl; - } + public String getIconUrl() { + return iconUrl; + } - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public static TokenResourceMetadata empty(REAddr addr, String symbol) { - return new TokenResourceMetadata(addr, symbol, "", "", "", ""); - } + public static TokenResourceMetadata empty(REAddr addr, String symbol) { + return new TokenResourceMetadata(addr, symbol, "", "", "", ""); + } - @Override - public int hashCode() { - return Objects.hash(addr, symbol, name, description, iconUrl, url); - } + @Override + public int hashCode() { + return Objects.hash(addr, symbol, name, description, iconUrl, url); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof TokenResourceMetadata)) { - return false; - } - var other = (TokenResourceMetadata) o; - return Objects.equals(this.addr, other.addr) - && Objects.equals(this.symbol, other.symbol) - && Objects.equals(this.name, other.name) - && Objects.equals(this.description, other.description) - && Objects.equals(this.iconUrl, other.iconUrl) - && Objects.equals(this.url, other.url); - } + @Override + public boolean equals(Object o) { + if (!(o instanceof TokenResourceMetadata)) { + return false; + } + var other = (TokenResourceMetadata) o; + return Objects.equals(this.addr, other.addr) + && Objects.equals(this.symbol, other.symbol) + && Objects.equals(this.name, other.name) + && Objects.equals(this.description, other.description) + && Objects.equals(this.iconUrl, other.iconUrl) + && Objects.equals(this.url, other.url); + } - @Override - public String toString() { - return String.format("%s{addr=%s symbol=%s name=%s desc=%s iconUrl=%s url=%s}", - this.getClass().getSimpleName(), - this.addr, - this.symbol, - this.name, - this.description, - this.iconUrl, - this.url - ); - } + @Override + public String toString() { + return String.format( + "%s{addr=%s symbol=%s name=%s desc=%s iconUrl=%s url=%s}", + this.getClass().getSimpleName(), + this.addr, + this.symbol, + this.name, + this.description, + this.iconUrl, + this.url); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokensInAccount.java b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokensInAccount.java index 7604b50c73..f6167a7389 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokensInAccount.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/tokens/state/TokensInAccount.java @@ -72,72 +72,65 @@ import java.util.Objects; /** - * A particle which represents an amount of transferrable fungible tokens - * owned by some key owner and stored in an account. + * A particle which represents an amount of transferrable fungible tokens owned by some key owner + * and stored in an account. */ public final class TokensInAccount implements ResourceInBucket { - private final REAddr holdingAddress; - private final REAddr resourceAddr; - private final UInt256 amount; + private final REAddr holdingAddress; + private final REAddr resourceAddr; + private final UInt256 amount; - public TokensInAccount( - REAddr holdingAddress, - REAddr resourceAddr, - UInt256 amount - ) { - this.holdingAddress = Objects.requireNonNull(holdingAddress); - this.resourceAddr = Objects.requireNonNull(resourceAddr); - this.amount = Objects.requireNonNull(amount); - } + public TokensInAccount(REAddr holdingAddress, REAddr resourceAddr, UInt256 amount) { + this.holdingAddress = Objects.requireNonNull(holdingAddress); + this.resourceAddr = Objects.requireNonNull(resourceAddr); + this.amount = Objects.requireNonNull(amount); + } - public Tokens toTokens() { - return Tokens.create(resourceAddr, amount); - } + public Tokens toTokens() { + return Tokens.create(resourceAddr, amount); + } - @Override - public UInt256 getAmount() { - return this.amount; - } + @Override + public UInt256 getAmount() { + return this.amount; + } - @Override - public Bucket bucket() { - return AccountBucket.from(resourceAddr, holdingAddress); - } + @Override + public Bucket bucket() { + return AccountBucket.from(resourceAddr, holdingAddress); + } - public REAddr getHoldingAddr() { - return this.holdingAddress; - } + public REAddr getHoldingAddr() { + return this.holdingAddress; + } - public REAddr getResourceAddr() { - return this.resourceAddr; - } + public REAddr getResourceAddr() { + return this.resourceAddr; + } - @Override - public String toString() { - return String.format("%s{resourceAddr=%s holder=%s amount=%s}", - getClass().getSimpleName(), - resourceAddr, - holdingAddress, - amount - ); - } + @Override + public String toString() { + return String.format( + "%s{resourceAddr=%s holder=%s amount=%s}", + getClass().getSimpleName(), resourceAddr, holdingAddress, amount); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TokensInAccount)) { - return false; - } - TokensInAccount that = (TokensInAccount) o; - return Objects.equals(holdingAddress, that.holdingAddress) - && Objects.equals(resourceAddr, that.resourceAddr) - && Objects.equals(amount, that.amount); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TokensInAccount)) { + return false; + } + TokensInAccount that = (TokensInAccount) o; + return Objects.equals(holdingAddress, that.holdingAddress) + && Objects.equals(resourceAddr, that.resourceAddr) + && Objects.equals(amount, that.amount); + } - @Override - public int hashCode() { - return Objects.hash(holdingAddress, resourceAddr, amount); - } + @Override + public int hashCode() { + return Objects.hash(holdingAddress, resourceAddr, amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/unique/scrypt/MutexConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/unique/scrypt/MutexConstraintScrypt.java index e77201b3da..edae9d71d2 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/unique/scrypt/MutexConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/unique/scrypt/MutexConstraintScrypt.java @@ -65,19 +65,19 @@ package com.radixdlt.application.unique.scrypt; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; -import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.ConstraintScrypt; +import com.radixdlt.atomos.Loader; import com.radixdlt.constraintmachine.Authorization; import com.radixdlt.constraintmachine.EndProcedure; import com.radixdlt.constraintmachine.PermissionLevel; public class MutexConstraintScrypt implements ConstraintScrypt { - @Override - public void main(Loader os) { - os.procedure(new EndProcedure<>( - SystemConstraintScrypt.REAddrClaim.class, - s -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, c, r) -> { } - )); - } + @Override + public void main(Loader os) { + os.procedure( + new EndProcedure<>( + SystemConstraintScrypt.REAddrClaim.class, + s -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, c, r) -> {})); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/InvalidRakeIncreaseException.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/InvalidRakeIncreaseException.java index ef37acf1e3..bced482766 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/InvalidRakeIncreaseException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/InvalidRakeIncreaseException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,20 +67,20 @@ import com.radixdlt.atom.TxBuilderException; public final class InvalidRakeIncreaseException extends TxBuilderException { - private final int maxRakeIncrease; - private final int increaseAttempt; + private final int maxRakeIncrease; + private final int increaseAttempt; - public InvalidRakeIncreaseException(int maxRakeIncrease, int increaseAttempt) { - super("Max rake increase is " + maxRakeIncrease + " but trying to increase " + increaseAttempt); - this.maxRakeIncrease = maxRakeIncrease; - this.increaseAttempt = increaseAttempt; - } + public InvalidRakeIncreaseException(int maxRakeIncrease, int increaseAttempt) { + super("Max rake increase is " + maxRakeIncrease + " but trying to increase " + increaseAttempt); + this.maxRakeIncrease = maxRakeIncrease; + this.increaseAttempt = increaseAttempt; + } - public int getMaxRakeIncrease() { - return maxRakeIncrease; - } + public int getMaxRakeIncrease() { + return maxRakeIncrease; + } - public int getIncreaseAttempt() { - return increaseAttempt; - } + public int getIncreaseAttempt() { + return increaseAttempt; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/RegisterValidatorConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/RegisterValidatorConstructor.java index 597c20094a..7b33f768a6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/RegisterValidatorConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/RegisterValidatorConstructor.java @@ -65,20 +65,21 @@ package com.radixdlt.application.validators.construction; import com.radixdlt.application.system.state.EpochData; +import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.RegisterValidator; -import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; - import java.util.OptionalLong; public class RegisterValidatorConstructor implements ActionConstructor { - @Override - public void construct(RegisterValidator action, TxBuilder txBuilder) throws TxBuilderException { - txBuilder.down(ValidatorRegisteredCopy.class, action.validatorKey()); - var curEpoch = txBuilder.readSystem(EpochData.class); - txBuilder.up(new ValidatorRegisteredCopy(OptionalLong.of(curEpoch.getEpoch() + 1), action.validatorKey(), true)); - txBuilder.end(); - } + @Override + public void construct(RegisterValidator action, TxBuilder txBuilder) throws TxBuilderException { + txBuilder.down(ValidatorRegisteredCopy.class, action.validatorKey()); + var curEpoch = txBuilder.readSystem(EpochData.class); + txBuilder.up( + new ValidatorRegisteredCopy( + OptionalLong.of(curEpoch.getEpoch() + 1), action.validatorKey(), true)); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UnregisterValidatorConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UnregisterValidatorConstructor.java index f4c9a4827a..8fc2f1ddee 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UnregisterValidatorConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UnregisterValidatorConstructor.java @@ -65,20 +65,22 @@ package com.radixdlt.application.validators.construction; import com.radixdlt.application.system.state.EpochData; +import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UnregisterValidator; -import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; - import java.util.OptionalLong; -public final class UnregisterValidatorConstructor implements ActionConstructor { - @Override - public void construct(UnregisterValidator action, TxBuilder txBuilder) throws TxBuilderException { - txBuilder.down(ValidatorRegisteredCopy.class, action.validatorKey()); - var curEpoch = txBuilder.readSystem(EpochData.class); - txBuilder.up(new ValidatorRegisteredCopy(OptionalLong.of(curEpoch.getEpoch() + 1), action.validatorKey(), false)); - txBuilder.end(); - } +public final class UnregisterValidatorConstructor + implements ActionConstructor { + @Override + public void construct(UnregisterValidator action, TxBuilder txBuilder) throws TxBuilderException { + txBuilder.down(ValidatorRegisteredCopy.class, action.validatorKey()); + var curEpoch = txBuilder.readSystem(EpochData.class); + txBuilder.up( + new ValidatorRegisteredCopy( + OptionalLong.of(curEpoch.getEpoch() + 1), action.validatorKey(), false)); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateAllowDelegationFlagConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateAllowDelegationFlagConstructor.java index f13092aa11..3dd4daea32 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateAllowDelegationFlagConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateAllowDelegationFlagConstructor.java @@ -64,20 +64,19 @@ package com.radixdlt.application.validators.construction; +import com.radixdlt.application.validators.state.AllowDelegationFlag; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UpdateAllowDelegationFlag; -import com.radixdlt.application.validators.state.AllowDelegationFlag; -public class UpdateAllowDelegationFlagConstructor implements ActionConstructor { - @Override - public void construct(UpdateAllowDelegationFlag action, TxBuilder builder) throws TxBuilderException { - builder.down(AllowDelegationFlag.class, action.validatorKey()); - builder.up(new AllowDelegationFlag( - action.validatorKey(), - action.allowDelegation() - )); - builder.end(); - } +public class UpdateAllowDelegationFlagConstructor + implements ActionConstructor { + @Override + public void construct(UpdateAllowDelegationFlag action, TxBuilder builder) + throws TxBuilderException { + builder.down(AllowDelegationFlag.class, action.validatorKey()); + builder.up(new AllowDelegationFlag(action.validatorKey(), action.allowDelegation())); + builder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateRakeConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateRakeConstructor.java index 1ade507d77..a8f2a60382 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateRakeConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateRakeConstructor.java @@ -64,51 +64,45 @@ package com.radixdlt.application.validators.construction; +import com.radixdlt.application.system.state.EpochData; import com.radixdlt.application.system.state.ValidatorStakeData; +import com.radixdlt.application.validators.state.ValidatorFeeCopy; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UpdateValidatorFee; -import com.radixdlt.application.system.state.EpochData; -import com.radixdlt.application.validators.state.ValidatorFeeCopy; - import java.util.OptionalLong; public final class UpdateRakeConstructor implements ActionConstructor { - private final long rakeIncreaseDebounceEpochLength; - private final int maxRakeIncrease; + private final long rakeIncreaseDebounceEpochLength; + private final int maxRakeIncrease; - public UpdateRakeConstructor( - long rakeIncreaseDebounceEpochLength, - int maxRakeIncrease - ) { - if (rakeIncreaseDebounceEpochLength < 0 || maxRakeIncrease < 0) { - throw new IllegalArgumentException(); - } - this.rakeIncreaseDebounceEpochLength = rakeIncreaseDebounceEpochLength; - this.maxRakeIncrease = maxRakeIncrease; - } + public UpdateRakeConstructor(long rakeIncreaseDebounceEpochLength, int maxRakeIncrease) { + if (rakeIncreaseDebounceEpochLength < 0 || maxRakeIncrease < 0) { + throw new IllegalArgumentException(); + } + this.rakeIncreaseDebounceEpochLength = rakeIncreaseDebounceEpochLength; + this.maxRakeIncrease = maxRakeIncrease; + } - @Override - public void construct(UpdateValidatorFee action, TxBuilder builder) throws TxBuilderException { - builder.down(ValidatorFeeCopy.class, action.validatorKey()); - var curRakePercentage = builder.read(ValidatorStakeData.class, action.validatorKey()) - .getRakePercentage(); + @Override + public void construct(UpdateValidatorFee action, TxBuilder builder) throws TxBuilderException { + builder.down(ValidatorFeeCopy.class, action.validatorKey()); + var curRakePercentage = + builder.read(ValidatorStakeData.class, action.validatorKey()).getRakePercentage(); - var isIncrease = action.getFeePercentage() > curRakePercentage; - var rakeIncrease = action.getFeePercentage() - curRakePercentage; - if (isIncrease && rakeIncrease >= maxRakeIncrease) { - throw new InvalidRakeIncreaseException(maxRakeIncrease, rakeIncrease); - } + var isIncrease = action.getFeePercentage() > curRakePercentage; + var rakeIncrease = action.getFeePercentage() - curRakePercentage; + if (isIncrease && rakeIncrease >= maxRakeIncrease) { + throw new InvalidRakeIncreaseException(maxRakeIncrease, rakeIncrease); + } - var epochDiff = isIncrease ? (1 + rakeIncreaseDebounceEpochLength) : 1; - var curEpoch = builder.readSystem(EpochData.class); - var epoch = curEpoch.getEpoch() + epochDiff; - builder.up(new ValidatorFeeCopy( - OptionalLong.of(epoch), - action.validatorKey(), - action.getFeePercentage() - )); - builder.end(); - } + var epochDiff = isIncrease ? (1 + rakeIncreaseDebounceEpochLength) : 1; + var curEpoch = builder.readSystem(EpochData.class); + var epoch = curEpoch.getEpoch() + epochDiff; + builder.up( + new ValidatorFeeCopy( + OptionalLong.of(epoch), action.validatorKey(), action.getFeePercentage())); + builder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorMetadataConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorMetadataConstructor.java index 94331e755a..f6ee58f0d9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorMetadataConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorMetadataConstructor.java @@ -64,21 +64,23 @@ package com.radixdlt.application.validators.construction; +import com.radixdlt.application.validators.state.ValidatorMetaData; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UpdateValidatorMetadata; -import com.radixdlt.application.validators.state.ValidatorMetaData; -public final class UpdateValidatorMetadataConstructor implements ActionConstructor { - @Override - public void construct(UpdateValidatorMetadata action, TxBuilder txBuilder) throws TxBuilderException { - var substateDown = txBuilder.down(ValidatorMetaData.class, action.validatorKey()); - txBuilder.up(new ValidatorMetaData( - action.validatorKey(), - action.name() == null ? substateDown.getName() : action.name(), - action.name() == null ? substateDown.getUrl() : action.url() - )); - txBuilder.end(); - } +public final class UpdateValidatorMetadataConstructor + implements ActionConstructor { + @Override + public void construct(UpdateValidatorMetadata action, TxBuilder txBuilder) + throws TxBuilderException { + var substateDown = txBuilder.down(ValidatorMetaData.class, action.validatorKey()); + txBuilder.up( + new ValidatorMetaData( + action.validatorKey(), + action.name() == null ? substateDown.getName() : action.name(), + action.name() == null ? substateDown.getUrl() : action.url())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorOwnerConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorOwnerConstructor.java index 27e634abb5..b27853f5aa 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorOwnerConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorOwnerConstructor.java @@ -65,22 +65,24 @@ package com.radixdlt.application.validators.construction; import com.radixdlt.application.system.state.EpochData; +import com.radixdlt.application.validators.state.ValidatorOwnerCopy; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UpdateValidatorOwner; -import com.radixdlt.application.validators.state.ValidatorOwnerCopy; - import java.util.OptionalLong; public class UpdateValidatorOwnerConstructor implements ActionConstructor { - @Override - public void construct(UpdateValidatorOwner action, TxBuilder txBuilder) throws TxBuilderException { - txBuilder.down(ValidatorOwnerCopy.class, action.validatorKey()); - var curEpoch = txBuilder.readSystem(EpochData.class); - txBuilder.up(new ValidatorOwnerCopy( - OptionalLong.of(curEpoch.getEpoch() + 1), action.validatorKey(), action.getOwnerAddress() - )); - txBuilder.end(); - } + @Override + public void construct(UpdateValidatorOwner action, TxBuilder txBuilder) + throws TxBuilderException { + txBuilder.down(ValidatorOwnerCopy.class, action.validatorKey()); + var curEpoch = txBuilder.readSystem(EpochData.class); + txBuilder.up( + new ValidatorOwnerCopy( + OptionalLong.of(curEpoch.getEpoch() + 1), + action.validatorKey(), + action.getOwnerAddress())); + txBuilder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorSystemMetadataConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorSystemMetadataConstructor.java index 36292e008d..689337efe6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorSystemMetadataConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/construction/UpdateValidatorSystemMetadataConstructor.java @@ -70,14 +70,13 @@ import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atom.actions.UpdateValidatorSystemMetadata; -public class UpdateValidatorSystemMetadataConstructor implements ActionConstructor { - @Override - public void construct(UpdateValidatorSystemMetadata action, TxBuilder builder) throws TxBuilderException { - builder.down(ValidatorSystemMetadata.class, action.validatorKey()); - builder.up(new ValidatorSystemMetadata( - action.validatorKey(), - action.bytes() - )); - builder.end(); - } +public class UpdateValidatorSystemMetadataConstructor + implements ActionConstructor { + @Override + public void construct(UpdateValidatorSystemMetadata action, TxBuilder builder) + throws TxBuilderException { + builder.down(ValidatorSystemMetadata.class, action.validatorKey()); + builder.up(new ValidatorSystemMetadata(action.validatorKey(), action.bytes())); + builder.end(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorConstraintScryptV2.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorConstraintScryptV2.java index fdc9473f16..112d09a700 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorConstraintScryptV2.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorConstraintScryptV2.java @@ -64,204 +64,204 @@ package com.radixdlt.application.validators.scrypt; +import com.radixdlt.application.validators.state.AllowDelegationFlag; +import com.radixdlt.application.validators.state.ValidatorMetaData; import com.radixdlt.application.validators.state.ValidatorSystemMetadata; import com.radixdlt.atom.REFieldSerialization; import com.radixdlt.atom.SubstateTypeId; -import com.radixdlt.application.validators.state.AllowDelegationFlag; -import com.radixdlt.application.validators.state.ValidatorMetaData; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; import com.radixdlt.constraintmachine.Authorization; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.PermissionLevel; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.constraintmachine.UpProcedure; import com.radixdlt.constraintmachine.VoidReducerState; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.HashUtils; - import java.util.Objects; - public class ValidatorConstraintScryptV2 implements ConstraintScrypt { - private static class UpdatingValidatorHashMetadata implements ReducerState { - private final ValidatorSystemMetadata prevState; + private static class UpdatingValidatorHashMetadata implements ReducerState { + private final ValidatorSystemMetadata prevState; - private UpdatingValidatorHashMetadata(ValidatorSystemMetadata prevState) { - this.prevState = prevState; - } + private UpdatingValidatorHashMetadata(ValidatorSystemMetadata prevState) { + this.prevState = prevState; + } - void update(ValidatorSystemMetadata next) throws ProcedureException { - if (!prevState.getValidatorKey().equals(next.getValidatorKey())) { - throw new ProcedureException("Invalid key"); - } - } - } + void update(ValidatorSystemMetadata next) throws ProcedureException { + if (!prevState.getValidatorKey().equals(next.getValidatorKey())) { + throw new ProcedureException("Invalid key"); + } + } + } - private static class UpdatingValidatorInfo implements ReducerState { - private final ValidatorMetaData prevState; + private static class UpdatingValidatorInfo implements ReducerState { + private final ValidatorMetaData prevState; - private UpdatingValidatorInfo(ValidatorMetaData prevState) { - this.prevState = prevState; - } - } + private UpdatingValidatorInfo(ValidatorMetaData prevState) { + this.prevState = prevState; + } + } - private static class UpdatingDelegationFlag implements ReducerState { - private final AllowDelegationFlag current; + private static class UpdatingDelegationFlag implements ReducerState { + private final AllowDelegationFlag current; - private UpdatingDelegationFlag(AllowDelegationFlag current) { - this.current = current; - } + private UpdatingDelegationFlag(AllowDelegationFlag current) { + this.current = current; + } - void update(AllowDelegationFlag next) throws ProcedureException { - if (!current.getValidatorKey().equals(next.getValidatorKey())) { - throw new ProcedureException("Invalid key update"); - } - } - } + void update(AllowDelegationFlag next) throws ProcedureException { + if (!current.getValidatorKey().equals(next.getValidatorKey())) { + throw new ProcedureException("Invalid key update"); + } + } + } - @Override - public void main(Loader os) { - os.substate( - new SubstateDefinition<>( - ValidatorSystemMetadata.class, - SubstateTypeId.VALIDATOR_SYSTEM_META_DATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var key = REFieldSerialization.deserializeKey(buf); - var bytes = REFieldSerialization.deserializeFixedLengthBytes(buf, 32); - return new ValidatorSystemMetadata(key, bytes); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - REFieldSerialization.serializeFixedLengthBytes(buf, s.getData()); - }, - REFieldSerialization::deserializeKey, - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> new ValidatorSystemMetadata((ECPublicKey) k, HashUtils.zero256().asBytes()) - ) - ); + @Override + public void main(Loader os) { + os.substate( + new SubstateDefinition<>( + ValidatorSystemMetadata.class, + SubstateTypeId.VALIDATOR_SYSTEM_META_DATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var key = REFieldSerialization.deserializeKey(buf); + var bytes = REFieldSerialization.deserializeFixedLengthBytes(buf, 32); + return new ValidatorSystemMetadata(key, bytes); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + REFieldSerialization.serializeFixedLengthBytes(buf, s.getData()); + }, + REFieldSerialization::deserializeKey, + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> new ValidatorSystemMetadata((ECPublicKey) k, HashUtils.zero256().asBytes()))); - os.procedure(new DownProcedure<>( - VoidReducerState.class, ValidatorSystemMetadata.class, - d -> new Authorization( - PermissionLevel.USER, - (r, c) -> { - if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { - throw new AuthorizationException("Key does not match."); - } - } - ), - (d, s, r, c) -> ReducerResult.incomplete(new UpdatingValidatorHashMetadata(d)) - )); - os.procedure(new UpProcedure<>( - UpdatingValidatorHashMetadata.class, ValidatorSystemMetadata.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.update(u); - return ReducerResult.complete(); - } - )); + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + ValidatorSystemMetadata.class, + d -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { + throw new AuthorizationException("Key does not match."); + } + }), + (d, s, r, c) -> ReducerResult.incomplete(new UpdatingValidatorHashMetadata(d)))); + os.procedure( + new UpProcedure<>( + UpdatingValidatorHashMetadata.class, + ValidatorSystemMetadata.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.update(u); + return ReducerResult.complete(); + })); - os.substate( - new SubstateDefinition<>( - ValidatorMetaData.class, - SubstateTypeId.VALIDATOR_META_DATA.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var key = REFieldSerialization.deserializeKey(buf); - var name = REFieldSerialization.deserializeString(buf); - var url = REFieldSerialization.deserializeUrl(buf); - return new ValidatorMetaData(key, name, url); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - REFieldSerialization.serializeString(buf, s.getName()); - REFieldSerialization.serializeString(buf, s.getUrl()); - }, - buf -> REFieldSerialization.deserializeKey(buf), - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> ValidatorMetaData.createVirtual((ECPublicKey) k) - ) - ); + os.substate( + new SubstateDefinition<>( + ValidatorMetaData.class, + SubstateTypeId.VALIDATOR_META_DATA.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var key = REFieldSerialization.deserializeKey(buf); + var name = REFieldSerialization.deserializeString(buf); + var url = REFieldSerialization.deserializeUrl(buf); + return new ValidatorMetaData(key, name, url); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + REFieldSerialization.serializeString(buf, s.getName()); + REFieldSerialization.serializeString(buf, s.getUrl()); + }, + buf -> REFieldSerialization.deserializeKey(buf), + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> ValidatorMetaData.createVirtual((ECPublicKey) k))); - os.procedure(new DownProcedure<>( - VoidReducerState.class, ValidatorMetaData.class, - d -> new Authorization( - PermissionLevel.USER, - (r, c) -> { - if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { - throw new AuthorizationException("Key does not match."); - } - } - ), - (d, s, r, c) -> ReducerResult.incomplete(new UpdatingValidatorInfo(d)) - )); + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + ValidatorMetaData.class, + d -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { + throw new AuthorizationException("Key does not match."); + } + }), + (d, s, r, c) -> ReducerResult.incomplete(new UpdatingValidatorInfo(d)))); - os.procedure(new UpProcedure<>( - UpdatingValidatorInfo.class, ValidatorMetaData.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - if (!Objects.equals(s.prevState.getValidatorKey(), u.getValidatorKey())) { - throw new ProcedureException(String.format( - "validator addresses do not match: %s != %s", - s.prevState.getValidatorKey(), u.getValidatorKey() - )); - } - return ReducerResult.complete(); - } - )); + os.procedure( + new UpProcedure<>( + UpdatingValidatorInfo.class, + ValidatorMetaData.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + if (!Objects.equals(s.prevState.getValidatorKey(), u.getValidatorKey())) { + throw new ProcedureException( + String.format( + "validator addresses do not match: %s != %s", + s.prevState.getValidatorKey(), u.getValidatorKey())); + } + return ReducerResult.complete(); + })); - registerRakeUpdates(os); - } + registerRakeUpdates(os); + } - public void registerRakeUpdates(Loader os) { - os.substate(new SubstateDefinition<>( - AllowDelegationFlag.class, - SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var key = REFieldSerialization.deserializeKey(buf); - var flag = REFieldSerialization.deserializeBoolean(buf); - return new AllowDelegationFlag(key, flag); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - buf.put((byte) (s.allowsDelegation() ? 1 : 0)); - }, - buf -> REFieldSerialization.deserializeKey(buf), - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> new AllowDelegationFlag((ECPublicKey) k, false) - )); + public void registerRakeUpdates(Loader os) { + os.substate( + new SubstateDefinition<>( + AllowDelegationFlag.class, + SubstateTypeId.VALIDATOR_ALLOW_DELEGATION_FLAG.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var key = REFieldSerialization.deserializeKey(buf); + var flag = REFieldSerialization.deserializeBoolean(buf); + return new AllowDelegationFlag(key, flag); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + buf.put((byte) (s.allowsDelegation() ? 1 : 0)); + }, + buf -> REFieldSerialization.deserializeKey(buf), + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> new AllowDelegationFlag((ECPublicKey) k, false))); - os.procedure(new DownProcedure<>( - VoidReducerState.class, AllowDelegationFlag.class, - d -> new Authorization( - PermissionLevel.USER, - (r, c) -> { - if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { - throw new AuthorizationException("Key does not match."); - } - } - ), - (d, s, r, c) -> ReducerResult.incomplete(new UpdatingDelegationFlag(d)) - )); + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + AllowDelegationFlag.class, + d -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { + throw new AuthorizationException("Key does not match."); + } + }), + (d, s, r, c) -> ReducerResult.incomplete(new UpdatingDelegationFlag(d)))); - os.procedure(new UpProcedure<>( - UpdatingDelegationFlag.class, AllowDelegationFlag.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.update(u); - return ReducerResult.complete(); - } - )); - } + os.procedure( + new UpProcedure<>( + UpdatingDelegationFlag.class, + AllowDelegationFlag.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.update(u); + return ReducerResult.complete(); + })); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorRegisterConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorRegisterConstraintScrypt.java index aeea242ea7..7ffec3a479 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorRegisterConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorRegisterConstraintScrypt.java @@ -65,109 +65,114 @@ package com.radixdlt.application.validators.scrypt; import com.radixdlt.application.system.state.EpochData; +import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; import com.radixdlt.atom.REFieldSerialization; import com.radixdlt.atom.SubstateTypeId; -import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.atomos.Loader; import com.radixdlt.atomos.SubstateDefinition; import com.radixdlt.constraintmachine.Authorization; -import com.radixdlt.constraintmachine.ReadProcedure; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.DownProcedure; import com.radixdlt.constraintmachine.PermissionLevel; -import com.radixdlt.constraintmachine.exceptions.ProcedureException; +import com.radixdlt.constraintmachine.ReadProcedure; import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.ReducerState; import com.radixdlt.constraintmachine.UpProcedure; import com.radixdlt.constraintmachine.VoidReducerState; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; +import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; public class ValidatorRegisterConstraintScrypt implements ConstraintScrypt { - private static class UpdatingRegistered implements ReducerState { - private final ECPublicKey validatorKey; - private final EpochData epochData; + private static class UpdatingRegistered implements ReducerState { + private final ECPublicKey validatorKey; + private final EpochData epochData; - UpdatingRegistered(ECPublicKey validatorKey, EpochData epochData) { - this.validatorKey = validatorKey; - this.epochData = epochData; - } + UpdatingRegistered(ECPublicKey validatorKey, EpochData epochData) { + this.validatorKey = validatorKey; + this.epochData = epochData; + } - void update(ValidatorRegisteredCopy update) throws ProcedureException { - if (!update.getValidatorKey().equals(validatorKey)) { - throw new ProcedureException("Cannot update validator"); - } + void update(ValidatorRegisteredCopy update) throws ProcedureException { + if (!update.getValidatorKey().equals(validatorKey)) { + throw new ProcedureException("Cannot update validator"); + } - var expectedEpoch = epochData.getEpoch() + 1; - if (update.getEpochUpdate().orElseThrow() != expectedEpoch) { - throw new ProcedureException("Expected epoch to be " + expectedEpoch + " but is " + update.getEpochUpdate()); - } - } - } + var expectedEpoch = epochData.getEpoch() + 1; + if (update.getEpochUpdate().orElseThrow() != expectedEpoch) { + throw new ProcedureException( + "Expected epoch to be " + expectedEpoch + " but is " + update.getEpochUpdate()); + } + } + } - private static class UpdatingRegisteredNeedToReadEpoch implements ReducerState { - private final ECPublicKey validatorKey; + private static class UpdatingRegisteredNeedToReadEpoch implements ReducerState { + private final ECPublicKey validatorKey; - UpdatingRegisteredNeedToReadEpoch(ECPublicKey validatorKey) { - this.validatorKey = validatorKey; - } + UpdatingRegisteredNeedToReadEpoch(ECPublicKey validatorKey) { + this.validatorKey = validatorKey; + } - ReducerState readEpoch(EpochData epochData) { - return new UpdatingRegistered(validatorKey, epochData); - } - } + ReducerState readEpoch(EpochData epochData) { + return new UpdatingRegistered(validatorKey, epochData); + } + } - @Override - public void main(Loader os) { - os.substate(new SubstateDefinition<>( - ValidatorRegisteredCopy.class, - SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - var epochUpdate = REFieldSerialization.deserializeOptionalNonNegativeLong(buf); - var key = REFieldSerialization.deserializeKey(buf); - var flag = REFieldSerialization.deserializeBoolean(buf); - return new ValidatorRegisteredCopy(epochUpdate, key, flag); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeOptionalLong(buf, s.getEpochUpdate()); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - buf.put((byte) (s.isRegistered() ? 1 : 0)); - }, - buf -> REFieldSerialization.deserializeKey(buf), - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> new ValidatorRegisteredCopy((ECPublicKey) k, false) - )); + @Override + public void main(Loader os) { + os.substate( + new SubstateDefinition<>( + ValidatorRegisteredCopy.class, + SubstateTypeId.VALIDATOR_REGISTERED_FLAG_COPY.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + var epochUpdate = REFieldSerialization.deserializeOptionalNonNegativeLong(buf); + var key = REFieldSerialization.deserializeKey(buf); + var flag = REFieldSerialization.deserializeBoolean(buf); + return new ValidatorRegisteredCopy(epochUpdate, key, flag); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeOptionalLong(buf, s.getEpochUpdate()); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + buf.put((byte) (s.isRegistered() ? 1 : 0)); + }, + buf -> REFieldSerialization.deserializeKey(buf), + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> new ValidatorRegisteredCopy((ECPublicKey) k, false))); - os.procedure(new DownProcedure<>( - VoidReducerState.class, ValidatorRegisteredCopy.class, - d -> new Authorization( - PermissionLevel.USER, - (r, c) -> { - if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { - throw new AuthorizationException("Key does not match."); - } - } - ), - (d, s, r, c) -> { - return ReducerResult.incomplete(new UpdatingRegisteredNeedToReadEpoch(d.getValidatorKey())); - } - )); + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + ValidatorRegisteredCopy.class, + d -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { + throw new AuthorizationException("Key does not match."); + } + }), + (d, s, r, c) -> { + return ReducerResult.incomplete( + new UpdatingRegisteredNeedToReadEpoch(d.getValidatorKey())); + })); - os.procedure(new ReadProcedure<>( - UpdatingRegisteredNeedToReadEpoch.class, EpochData.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, r) -> ReducerResult.incomplete(s.readEpoch(u)) - )); + os.procedure( + new ReadProcedure<>( + UpdatingRegisteredNeedToReadEpoch.class, + EpochData.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, r) -> ReducerResult.incomplete(s.readEpoch(u)))); - os.procedure(new UpProcedure<>( - UpdatingRegistered.class, ValidatorRegisteredCopy.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.update(u); - return ReducerResult.complete(); - } - )); - } + os.procedure( + new UpProcedure<>( + UpdatingRegistered.class, + ValidatorRegisteredCopy.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.update(u); + return ReducerResult.complete(); + })); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateOwnerConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateOwnerConstraintScrypt.java index fa45f0d97a..8278aba676 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateOwnerConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateOwnerConstraintScrypt.java @@ -82,95 +82,98 @@ import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; - import java.util.OptionalLong; public class ValidatorUpdateOwnerConstraintScrypt implements ConstraintScrypt { - private static class UpdatingValidatorOwner implements ReducerState { - private final ECPublicKey validatorKey; - private final EpochData epochData; - - UpdatingValidatorOwner(ECPublicKey validatorKey, EpochData epochData) { - this.validatorKey = validatorKey; - this.epochData = epochData; - } - - void update(ValidatorOwnerCopy update) throws ProcedureException { - if (!update.getValidatorKey().equals(validatorKey)) { - throw new ProcedureException("Invalid key update"); - } - - var expectedEpoch = epochData.getEpoch() + 1; - if (update.getEpochUpdate().orElseThrow() != expectedEpoch) { - throw new ProcedureException("Expected epoch to be " + expectedEpoch + " but is " + update.getEpochUpdate()); - } - } - } - - - private static class UpdatingOwnerNeedToReadEpoch implements ReducerState { - private final ECPublicKey validatorKey; - - UpdatingOwnerNeedToReadEpoch(ECPublicKey validatorKey) { - this.validatorKey = validatorKey; - } - - ReducerState readEpoch(EpochData epochData) { - return new UpdatingValidatorOwner(validatorKey, epochData); - } - } - - @Override - public void main(Loader os) { - os.substate(new SubstateDefinition<>( - ValidatorOwnerCopy.class, - SubstateTypeId.VALIDATOR_OWNER_COPY.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - OptionalLong epochUpdate = REFieldSerialization.deserializeOptionalNonNegativeLong(buf); - var key = REFieldSerialization.deserializeKey(buf); - var owner = REFieldSerialization.deserializeAccountREAddr(buf); - return new ValidatorOwnerCopy(epochUpdate, key, owner); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeOptionalLong(buf, s.getEpochUpdate()); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - REFieldSerialization.serializeREAddr(buf, s.getOwner()); - }, - buf -> REFieldSerialization.deserializeKey(buf), - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> ValidatorOwnerCopy.createVirtual((ECPublicKey) k) - )); - - os.procedure(new DownProcedure<>( - VoidReducerState.class, ValidatorOwnerCopy.class, - d -> new Authorization( - PermissionLevel.USER, - (r, c) -> { - if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { - throw new AuthorizationException("Key does not match."); - } - } - ), - (d, s, r, c) -> ReducerResult.incomplete(new UpdatingOwnerNeedToReadEpoch(d.getValidatorKey())) - )); - - - os.procedure(new ReadProcedure<>( - UpdatingOwnerNeedToReadEpoch.class, EpochData.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, r) -> ReducerResult.incomplete(s.readEpoch(u)) - )); - - os.procedure(new UpProcedure<>( - UpdatingValidatorOwner.class, ValidatorOwnerCopy.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.update(u); - return ReducerResult.complete(); - } - )); - } + private static class UpdatingValidatorOwner implements ReducerState { + private final ECPublicKey validatorKey; + private final EpochData epochData; + + UpdatingValidatorOwner(ECPublicKey validatorKey, EpochData epochData) { + this.validatorKey = validatorKey; + this.epochData = epochData; + } + + void update(ValidatorOwnerCopy update) throws ProcedureException { + if (!update.getValidatorKey().equals(validatorKey)) { + throw new ProcedureException("Invalid key update"); + } + + var expectedEpoch = epochData.getEpoch() + 1; + if (update.getEpochUpdate().orElseThrow() != expectedEpoch) { + throw new ProcedureException( + "Expected epoch to be " + expectedEpoch + " but is " + update.getEpochUpdate()); + } + } + } + + private static class UpdatingOwnerNeedToReadEpoch implements ReducerState { + private final ECPublicKey validatorKey; + + UpdatingOwnerNeedToReadEpoch(ECPublicKey validatorKey) { + this.validatorKey = validatorKey; + } + + ReducerState readEpoch(EpochData epochData) { + return new UpdatingValidatorOwner(validatorKey, epochData); + } + } + + @Override + public void main(Loader os) { + os.substate( + new SubstateDefinition<>( + ValidatorOwnerCopy.class, + SubstateTypeId.VALIDATOR_OWNER_COPY.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + OptionalLong epochUpdate = + REFieldSerialization.deserializeOptionalNonNegativeLong(buf); + var key = REFieldSerialization.deserializeKey(buf); + var owner = REFieldSerialization.deserializeAccountREAddr(buf); + return new ValidatorOwnerCopy(epochUpdate, key, owner); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeOptionalLong(buf, s.getEpochUpdate()); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + REFieldSerialization.serializeREAddr(buf, s.getOwner()); + }, + buf -> REFieldSerialization.deserializeKey(buf), + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> ValidatorOwnerCopy.createVirtual((ECPublicKey) k))); + + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + ValidatorOwnerCopy.class, + d -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { + throw new AuthorizationException("Key does not match."); + } + }), + (d, s, r, c) -> + ReducerResult.incomplete(new UpdatingOwnerNeedToReadEpoch(d.getValidatorKey())))); + + os.procedure( + new ReadProcedure<>( + UpdatingOwnerNeedToReadEpoch.class, + EpochData.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, r) -> ReducerResult.incomplete(s.readEpoch(u)))); + + os.procedure( + new UpProcedure<>( + UpdatingValidatorOwner.class, + ValidatorOwnerCopy.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.update(u); + return ReducerResult.complete(); + })); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateRakeConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateRakeConstraintScrypt.java index d8fa5f6f99..d0c5a7cec8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateRakeConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/scrypt/ValidatorUpdateRakeConstraintScrypt.java @@ -84,145 +84,158 @@ import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.serialization.DeserializeException; - import java.util.Objects; import java.util.OptionalLong; public final class ValidatorUpdateRakeConstraintScrypt implements ConstraintScrypt { - public static final int RAKE_PERCENTAGE_GRANULARITY = 10 * 10; // 100 == 1.00%, 1 == 0.01% - public static final int RAKE_MAX = 100 * RAKE_PERCENTAGE_GRANULARITY; - public static final int RAKE_MIN = 0; - public static final int MAX_RAKE_INCREASE = 10 * RAKE_PERCENTAGE_GRANULARITY; // 10% - - private final long rakeIncreaseDebounceEpochLength; - - public ValidatorUpdateRakeConstraintScrypt(long rakeIncreaseDebounceEpochLength) { - this.rakeIncreaseDebounceEpochLength = rakeIncreaseDebounceEpochLength; - } - - private class UpdatingRakeReady implements ReducerState { - private final EpochData epochData; - private final ValidatorStakeData stakeData; - - UpdatingRakeReady(EpochData epochData, ValidatorStakeData stakeData) { - this.epochData = epochData; - this.stakeData = stakeData; - } - - void update(ValidatorFeeCopy update) throws ProcedureException { - if (!Objects.equals(stakeData.getValidatorKey(), update.getValidatorKey())) { - throw new ProcedureException("Must update same key"); - } - - var rakeIncrease = update.getRakePercentage() - stakeData.getRakePercentage(); - if (rakeIncrease > MAX_RAKE_INCREASE) { - throw new ProcedureException("Max rake increase is " + MAX_RAKE_INCREASE + " but trying to increase " + rakeIncrease); - } - - var epoch = update.getEpochUpdate().orElseThrow(() -> new ProcedureException("Must contain epoch update")); - if (rakeIncrease > 0) { - var expectedEpoch = epochData.getEpoch() + 1 + rakeIncreaseDebounceEpochLength; - if (epoch != expectedEpoch) { - throw new ProcedureException("Increasing rake requires epoch delay to " + expectedEpoch + " but was " + epoch); - } - } else { - var expectedEpoch = epochData.getEpoch() + 1; - if (epoch != expectedEpoch) { - throw new ProcedureException("Decreasing rake requires epoch delay to " + expectedEpoch + " but was " + epoch); - } - } - } - } - - private class UpdatingRakeNeedToReadCurrentRake implements ReducerState { - private final ECPublicKey validatorKey; - - UpdatingRakeNeedToReadCurrentRake(ECPublicKey validatorKey) { - this.validatorKey = validatorKey; - } - - public ReducerState readValidatorStakeState(ValidatorStakeData validatorStakeData) throws ProcedureException { - if (!validatorStakeData.getValidatorKey().equals(validatorKey)) { - throw new ProcedureException("Invalid key update"); - } - - return new UpdatingRakeNeedToReadEpoch(validatorStakeData); - } - } - - private class UpdatingRakeNeedToReadEpoch implements ReducerState { - private final ValidatorStakeData validatorStakeData; - - private UpdatingRakeNeedToReadEpoch(ValidatorStakeData validatorStakeData) { - this.validatorStakeData = validatorStakeData; - } - - ReducerState readEpoch(EpochData epochData) { - return new UpdatingRakeReady(epochData, validatorStakeData); - } - } - - - @Override - public void main(Loader os) { - os.substate(new SubstateDefinition<>( - ValidatorFeeCopy.class, - SubstateTypeId.VALIDATOR_RAKE_COPY.id(), - buf -> { - REFieldSerialization.deserializeReservedByte(buf); - OptionalLong epochUpdate = REFieldSerialization.deserializeOptionalNonNegativeLong(buf); - var key = REFieldSerialization.deserializeKey(buf); - var curRakePercentage = REFieldSerialization.deserializeInt(buf); - if (curRakePercentage < RAKE_MIN || curRakePercentage > RAKE_MAX) { - throw new DeserializeException("Invalid rake percentage " + curRakePercentage); - } - - return new ValidatorFeeCopy(epochUpdate, key, curRakePercentage); - }, - (s, buf) -> { - REFieldSerialization.serializeReservedByte(buf); - REFieldSerialization.serializeOptionalLong(buf, s.getEpochUpdate()); - REFieldSerialization.serializeKey(buf, s.getValidatorKey()); - buf.putInt(s.getRakePercentage()); - }, - buf -> REFieldSerialization.deserializeKey(buf), - (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), - k -> ValidatorFeeCopy.createVirtual((ECPublicKey) k) - )); - - os.procedure(new DownProcedure<>( - VoidReducerState.class, ValidatorFeeCopy.class, - d -> new Authorization( - PermissionLevel.USER, - (r, c) -> { - if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { - throw new AuthorizationException("Key does not match."); - } - } - ), - (d, s, r, c) -> { - return ReducerResult.incomplete(new UpdatingRakeNeedToReadCurrentRake(d.getValidatorKey())); - } - )); - os.procedure(new ReadProcedure<>( - UpdatingRakeNeedToReadEpoch.class, EpochData.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, r) -> ReducerResult.incomplete(s.readEpoch(u)) - )); - os.procedure(new ReadProcedure<>( - UpdatingRakeNeedToReadCurrentRake.class, ValidatorStakeData.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, r) -> ReducerResult.incomplete(s.readValidatorStakeState(u)) - )); - - os.procedure(new UpProcedure<>( - UpdatingRakeReady.class, ValidatorFeeCopy.class, - u -> new Authorization(PermissionLevel.USER, (r, c) -> { }), - (s, u, c, r) -> { - s.update(u); - return ReducerResult.complete(); - } - )); - - } + public static final int RAKE_PERCENTAGE_GRANULARITY = 10 * 10; // 100 == 1.00%, 1 == 0.01% + public static final int RAKE_MAX = 100 * RAKE_PERCENTAGE_GRANULARITY; + public static final int RAKE_MIN = 0; + public static final int MAX_RAKE_INCREASE = 10 * RAKE_PERCENTAGE_GRANULARITY; // 10% + + private final long rakeIncreaseDebounceEpochLength; + + public ValidatorUpdateRakeConstraintScrypt(long rakeIncreaseDebounceEpochLength) { + this.rakeIncreaseDebounceEpochLength = rakeIncreaseDebounceEpochLength; + } + + private class UpdatingRakeReady implements ReducerState { + private final EpochData epochData; + private final ValidatorStakeData stakeData; + + UpdatingRakeReady(EpochData epochData, ValidatorStakeData stakeData) { + this.epochData = epochData; + this.stakeData = stakeData; + } + + void update(ValidatorFeeCopy update) throws ProcedureException { + if (!Objects.equals(stakeData.getValidatorKey(), update.getValidatorKey())) { + throw new ProcedureException("Must update same key"); + } + + var rakeIncrease = update.getRakePercentage() - stakeData.getRakePercentage(); + if (rakeIncrease > MAX_RAKE_INCREASE) { + throw new ProcedureException( + "Max rake increase is " + + MAX_RAKE_INCREASE + + " but trying to increase " + + rakeIncrease); + } + + var epoch = + update + .getEpochUpdate() + .orElseThrow(() -> new ProcedureException("Must contain epoch update")); + if (rakeIncrease > 0) { + var expectedEpoch = epochData.getEpoch() + 1 + rakeIncreaseDebounceEpochLength; + if (epoch != expectedEpoch) { + throw new ProcedureException( + "Increasing rake requires epoch delay to " + expectedEpoch + " but was " + epoch); + } + } else { + var expectedEpoch = epochData.getEpoch() + 1; + if (epoch != expectedEpoch) { + throw new ProcedureException( + "Decreasing rake requires epoch delay to " + expectedEpoch + " but was " + epoch); + } + } + } + } + + private class UpdatingRakeNeedToReadCurrentRake implements ReducerState { + private final ECPublicKey validatorKey; + + UpdatingRakeNeedToReadCurrentRake(ECPublicKey validatorKey) { + this.validatorKey = validatorKey; + } + + public ReducerState readValidatorStakeState(ValidatorStakeData validatorStakeData) + throws ProcedureException { + if (!validatorStakeData.getValidatorKey().equals(validatorKey)) { + throw new ProcedureException("Invalid key update"); + } + + return new UpdatingRakeNeedToReadEpoch(validatorStakeData); + } + } + + private class UpdatingRakeNeedToReadEpoch implements ReducerState { + private final ValidatorStakeData validatorStakeData; + + private UpdatingRakeNeedToReadEpoch(ValidatorStakeData validatorStakeData) { + this.validatorStakeData = validatorStakeData; + } + + ReducerState readEpoch(EpochData epochData) { + return new UpdatingRakeReady(epochData, validatorStakeData); + } + } + + @Override + public void main(Loader os) { + os.substate( + new SubstateDefinition<>( + ValidatorFeeCopy.class, + SubstateTypeId.VALIDATOR_RAKE_COPY.id(), + buf -> { + REFieldSerialization.deserializeReservedByte(buf); + OptionalLong epochUpdate = + REFieldSerialization.deserializeOptionalNonNegativeLong(buf); + var key = REFieldSerialization.deserializeKey(buf); + var curRakePercentage = REFieldSerialization.deserializeInt(buf); + if (curRakePercentage < RAKE_MIN || curRakePercentage > RAKE_MAX) { + throw new DeserializeException("Invalid rake percentage " + curRakePercentage); + } + + return new ValidatorFeeCopy(epochUpdate, key, curRakePercentage); + }, + (s, buf) -> { + REFieldSerialization.serializeReservedByte(buf); + REFieldSerialization.serializeOptionalLong(buf, s.getEpochUpdate()); + REFieldSerialization.serializeKey(buf, s.getValidatorKey()); + buf.putInt(s.getRakePercentage()); + }, + buf -> REFieldSerialization.deserializeKey(buf), + (k, buf) -> REFieldSerialization.serializeKey(buf, (ECPublicKey) k), + k -> ValidatorFeeCopy.createVirtual((ECPublicKey) k))); + + os.procedure( + new DownProcedure<>( + VoidReducerState.class, + ValidatorFeeCopy.class, + d -> + new Authorization( + PermissionLevel.USER, + (r, c) -> { + if (!c.key().map(d.getValidatorKey()::equals).orElse(false)) { + throw new AuthorizationException("Key does not match."); + } + }), + (d, s, r, c) -> { + return ReducerResult.incomplete( + new UpdatingRakeNeedToReadCurrentRake(d.getValidatorKey())); + })); + os.procedure( + new ReadProcedure<>( + UpdatingRakeNeedToReadEpoch.class, + EpochData.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, r) -> ReducerResult.incomplete(s.readEpoch(u)))); + os.procedure( + new ReadProcedure<>( + UpdatingRakeNeedToReadCurrentRake.class, + ValidatorStakeData.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, r) -> ReducerResult.incomplete(s.readValidatorStakeState(u)))); + + os.procedure( + new UpProcedure<>( + UpdatingRakeReady.class, + ValidatorFeeCopy.class, + u -> new Authorization(PermissionLevel.USER, (r, c) -> {}), + (s, u, c, r) -> { + s.update(u); + return ReducerResult.complete(); + })); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/AllowDelegationFlag.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/AllowDelegationFlag.java index 076630520d..ca6a520e60 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/AllowDelegationFlag.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/AllowDelegationFlag.java @@ -65,44 +65,43 @@ package com.radixdlt.application.validators.state; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; public final class AllowDelegationFlag implements ValidatorData { - private final ECPublicKey validatorKey; - private final boolean allowDelegation; + private final ECPublicKey validatorKey; + private final boolean allowDelegation; - public AllowDelegationFlag(ECPublicKey validatorKey, boolean allowDelegation) { - this.validatorKey = validatorKey; - this.allowDelegation = allowDelegation; - } + public AllowDelegationFlag(ECPublicKey validatorKey, boolean allowDelegation) { + this.validatorKey = validatorKey; + this.allowDelegation = allowDelegation; + } - public static AllowDelegationFlag createVirtual(ECPublicKey validatorKey) { - return new AllowDelegationFlag(validatorKey, false); - } + public static AllowDelegationFlag createVirtual(ECPublicKey validatorKey) { + return new AllowDelegationFlag(validatorKey, false); + } - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public boolean allowsDelegation() { - return allowDelegation; - } + public boolean allowsDelegation() { + return allowDelegation; + } - @Override - public int hashCode() { - return Objects.hash(validatorKey, allowDelegation); - } + @Override + public int hashCode() { + return Objects.hash(validatorKey, allowDelegation); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof AllowDelegationFlag)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof AllowDelegationFlag)) { + return false; + } - var other = (AllowDelegationFlag) o; - return Objects.equals(this.validatorKey, other.validatorKey) - && this.allowDelegation == other.allowDelegation; - } + var other = (AllowDelegationFlag) o; + return Objects.equals(this.validatorKey, other.validatorKey) + && this.allowDelegation == other.allowDelegation; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorData.java index c39c3d7ddf..c2ae26d7f7 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorData.java @@ -68,5 +68,5 @@ import com.radixdlt.crypto.ECPublicKey; public interface ValidatorData extends Particle { - ECPublicKey getValidatorKey(); + ECPublicKey getValidatorKey(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorFeeCopy.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorFeeCopy.java index f2ed5698f1..bde600f5fd 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorFeeCopy.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorFeeCopy.java @@ -64,71 +64,68 @@ package com.radixdlt.application.validators.state; -import com.radixdlt.crypto.ECPublicKey; +import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; +import com.radixdlt.crypto.ECPublicKey; import java.util.Objects; import java.util.OptionalLong; -import static com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt.RAKE_MAX; - public final class ValidatorFeeCopy implements ValidatorUpdatingData { - private final ECPublicKey validatorKey; - private final int curRakePercentage; - private final OptionalLong epochUpdate; + private final ECPublicKey validatorKey; + private final int curRakePercentage; + private final OptionalLong epochUpdate; - public ValidatorFeeCopy(OptionalLong epochUpdate, ECPublicKey validatorKey, int curRakePercentage) { - this.epochUpdate = epochUpdate; - this.validatorKey = Objects.requireNonNull(validatorKey); - this.curRakePercentage = curRakePercentage; - } + public ValidatorFeeCopy( + OptionalLong epochUpdate, ECPublicKey validatorKey, int curRakePercentage) { + this.epochUpdate = epochUpdate; + this.validatorKey = Objects.requireNonNull(validatorKey); + this.curRakePercentage = curRakePercentage; + } - public ValidatorFeeCopy(ECPublicKey validatorKey, int curRakePercentage) { - this.epochUpdate = OptionalLong.empty(); - this.validatorKey = Objects.requireNonNull(validatorKey); - this.curRakePercentage = curRakePercentage; - } + public ValidatorFeeCopy(ECPublicKey validatorKey, int curRakePercentage) { + this.epochUpdate = OptionalLong.empty(); + this.validatorKey = Objects.requireNonNull(validatorKey); + this.curRakePercentage = curRakePercentage; + } - public static ValidatorFeeCopy createVirtual(ECPublicKey validatorKey) { - return new ValidatorFeeCopy(validatorKey, RAKE_MAX); - } + public static ValidatorFeeCopy createVirtual(ECPublicKey validatorKey) { + return new ValidatorFeeCopy(validatorKey, RAKE_MAX); + } - @Override - public OptionalLong getEpochUpdate() { - return epochUpdate; - } + @Override + public OptionalLong getEpochUpdate() { + return epochUpdate; + } - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public int getRakePercentage() { - return curRakePercentage; - } + public int getRakePercentage() { + return curRakePercentage; + } - @Override - public int hashCode() { - return Objects.hash(epochUpdate, validatorKey, curRakePercentage); - } + @Override + public int hashCode() { + return Objects.hash(epochUpdate, validatorKey, curRakePercentage); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ValidatorFeeCopy)) { - return false; - } - var other = (ValidatorFeeCopy) o; - return Objects.equals(this.epochUpdate, other.epochUpdate) - && Objects.equals(this.validatorKey, other.validatorKey) - && this.curRakePercentage == other.curRakePercentage; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ValidatorFeeCopy)) { + return false; + } + var other = (ValidatorFeeCopy) o; + return Objects.equals(this.epochUpdate, other.epochUpdate) + && Objects.equals(this.validatorKey, other.validatorKey) + && this.curRakePercentage == other.curRakePercentage; + } - @Override - public String toString() { - return String.format("%s{epoch=%s validator=%s feePercentage=%s}", - this.getClass().getSimpleName(), - epochUpdate, - validatorKey.toHex(), - curRakePercentage - ); - } -} \ No newline at end of file + @Override + public String toString() { + return String.format( + "%s{epoch=%s validator=%s feePercentage=%s}", + this.getClass().getSimpleName(), epochUpdate, validatorKey.toHex(), curRakePercentage); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorMetaData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorMetaData.java index cb5807b2fc..1bda9fad63 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorMetaData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorMetaData.java @@ -1,130 +1,122 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.application.validators.state; - -import com.radixdlt.atom.REFieldSerialization; -import com.radixdlt.crypto.ECPublicKey; - -import java.util.Objects; - -public final class ValidatorMetaData implements ValidatorData { - private final ECPublicKey validatorKey; - private final String name; - private final String url; - - public ValidatorMetaData( - ECPublicKey validatorKey, - String name, - String url - ) { - this.validatorKey = Objects.requireNonNull(validatorKey); - this.name = Objects.requireNonNull(name); - this.url = REFieldSerialization.requireValidUrl(url); - } - - public static ValidatorMetaData createVirtual(ECPublicKey validatorKey) { - return new ValidatorMetaData(validatorKey, "", ""); - } - - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } - - public String getName() { - return name; - } - - public String getUrl() { - return url; - } - - @Override - public int hashCode() { - return Objects.hash(this.validatorKey, this.name, this.url); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ValidatorMetaData)) { - return false; - } - final var that = (ValidatorMetaData) obj; - return Objects.equals(this.validatorKey, that.validatorKey) - && Objects.equals(this.name, that.name) - && Objects.equals(this.url, that.url); - } - - @Override - public String toString() { - return String.format( - "%s[%s, %s]", - getClass().getSimpleName(), getValidatorKey(), getUrl() - ); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.application.validators.state; + +import com.radixdlt.atom.REFieldSerialization; +import com.radixdlt.crypto.ECPublicKey; +import java.util.Objects; + +public final class ValidatorMetaData implements ValidatorData { + private final ECPublicKey validatorKey; + private final String name; + private final String url; + + public ValidatorMetaData(ECPublicKey validatorKey, String name, String url) { + this.validatorKey = Objects.requireNonNull(validatorKey); + this.name = Objects.requireNonNull(name); + this.url = REFieldSerialization.requireValidUrl(url); + } + + public static ValidatorMetaData createVirtual(ECPublicKey validatorKey) { + return new ValidatorMetaData(validatorKey, "", ""); + } + + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + + @Override + public int hashCode() { + return Objects.hash(this.validatorKey, this.name, this.url); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ValidatorMetaData)) { + return false; + } + final var that = (ValidatorMetaData) obj; + return Objects.equals(this.validatorKey, that.validatorKey) + && Objects.equals(this.name, that.name) + && Objects.equals(this.url, that.url); + } + + @Override + public String toString() { + return String.format("%s[%s, %s]", getClass().getSimpleName(), getValidatorKey(), getUrl()); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorOwnerCopy.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorOwnerCopy.java index eb04c4b475..4110d418e7 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorOwnerCopy.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorOwnerCopy.java @@ -66,67 +66,58 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; import java.util.OptionalLong; public final class ValidatorOwnerCopy implements ValidatorUpdatingData { - private final ECPublicKey validatorKey; - private final REAddr owner; - private final OptionalLong epochUpdate; + private final ECPublicKey validatorKey; + private final REAddr owner; + private final OptionalLong epochUpdate; - public ValidatorOwnerCopy( - OptionalLong epochUpdate, - ECPublicKey validatorKey, - REAddr owner - ) { - this.epochUpdate = epochUpdate; - this.validatorKey = validatorKey; - this.owner = owner; - } + public ValidatorOwnerCopy(OptionalLong epochUpdate, ECPublicKey validatorKey, REAddr owner) { + this.epochUpdate = epochUpdate; + this.validatorKey = validatorKey; + this.owner = owner; + } - public ValidatorOwnerCopy( - ECPublicKey validatorKey, - REAddr owner - ) { - this.epochUpdate = OptionalLong.empty(); - this.validatorKey = validatorKey; - this.owner = owner; - } + public ValidatorOwnerCopy(ECPublicKey validatorKey, REAddr owner) { + this.epochUpdate = OptionalLong.empty(); + this.validatorKey = validatorKey; + this.owner = owner; + } - public static ValidatorOwnerCopy createVirtual(ECPublicKey validatorKey) { - return new ValidatorOwnerCopy(validatorKey, REAddr.ofPubKeyAccount(validatorKey)); - } + public static ValidatorOwnerCopy createVirtual(ECPublicKey validatorKey) { + return new ValidatorOwnerCopy(validatorKey, REAddr.ofPubKeyAccount(validatorKey)); + } - @Override - public OptionalLong getEpochUpdate() { - return epochUpdate; - } + @Override + public OptionalLong getEpochUpdate() { + return epochUpdate; + } - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public REAddr getOwner() { - return this.owner; - } + public REAddr getOwner() { + return this.owner; + } - @Override - public int hashCode() { - return Objects.hash(epochUpdate, validatorKey, owner); - } + @Override + public int hashCode() { + return Objects.hash(epochUpdate, validatorKey, owner); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ValidatorOwnerCopy)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ValidatorOwnerCopy)) { + return false; + } - var other = (ValidatorOwnerCopy) o; - return - Objects.equals(this.epochUpdate, other.epochUpdate) - && Objects.equals(this.validatorKey, other.validatorKey) - && Objects.equals(this.owner, other.owner); - } + var other = (ValidatorOwnerCopy) o; + return Objects.equals(this.epochUpdate, other.epochUpdate) + && Objects.equals(this.validatorKey, other.validatorKey) + && Objects.equals(this.owner, other.owner); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorRegisteredCopy.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorRegisteredCopy.java index a8b38ed545..a325a0209c 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorRegisteredCopy.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorRegisteredCopy.java @@ -65,65 +65,58 @@ package com.radixdlt.application.validators.state; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; import java.util.OptionalLong; public final class ValidatorRegisteredCopy implements ValidatorUpdatingData { - private final ECPublicKey validatorKey; - private final boolean isRegistered; - private final OptionalLong epochUpdate; + private final ECPublicKey validatorKey; + private final boolean isRegistered; + private final OptionalLong epochUpdate; - public ValidatorRegisteredCopy( - ECPublicKey validatorKey, - boolean isRegistered - ) { - this.epochUpdate = OptionalLong.empty(); - this.validatorKey = validatorKey; - this.isRegistered = isRegistered; - } + public ValidatorRegisteredCopy(ECPublicKey validatorKey, boolean isRegistered) { + this.epochUpdate = OptionalLong.empty(); + this.validatorKey = validatorKey; + this.isRegistered = isRegistered; + } - public ValidatorRegisteredCopy( - OptionalLong epochUpdate, - ECPublicKey validatorKey, - boolean isRegistered - ) { - this.epochUpdate = epochUpdate; - this.validatorKey = validatorKey; - this.isRegistered = isRegistered; - } + public ValidatorRegisteredCopy( + OptionalLong epochUpdate, ECPublicKey validatorKey, boolean isRegistered) { + this.epochUpdate = epochUpdate; + this.validatorKey = validatorKey; + this.isRegistered = isRegistered; + } - public static ValidatorRegisteredCopy createVirtual(ECPublicKey validatorKey) { - return new ValidatorRegisteredCopy(validatorKey, false); - } + public static ValidatorRegisteredCopy createVirtual(ECPublicKey validatorKey) { + return new ValidatorRegisteredCopy(validatorKey, false); + } - @Override - public OptionalLong getEpochUpdate() { - return epochUpdate; - } + @Override + public OptionalLong getEpochUpdate() { + return epochUpdate; + } - public ECPublicKey getValidatorKey() { - return validatorKey; - } + public ECPublicKey getValidatorKey() { + return validatorKey; + } - public boolean isRegistered() { - return isRegistered; - } + public boolean isRegistered() { + return isRegistered; + } - @Override - public int hashCode() { - return Objects.hash(epochUpdate, validatorKey, isRegistered); - } + @Override + public int hashCode() { + return Objects.hash(epochUpdate, validatorKey, isRegistered); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ValidatorRegisteredCopy)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ValidatorRegisteredCopy)) { + return false; + } - var other = (ValidatorRegisteredCopy) o; - return Objects.equals(this.epochUpdate, other.epochUpdate) - && Objects.equals(this.validatorKey, other.validatorKey) - && this.isRegistered == other.isRegistered; - } + var other = (ValidatorRegisteredCopy) o; + return Objects.equals(this.epochUpdate, other.epochUpdate) + && Objects.equals(this.validatorKey, other.validatorKey) + && this.isRegistered == other.isRegistered; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorSystemMetadata.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorSystemMetadata.java index 89fa1bca3f..f650ab62d5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorSystemMetadata.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorSystemMetadata.java @@ -67,51 +67,50 @@ import com.google.common.hash.HashCode; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.HashUtils; - import java.util.Arrays; import java.util.Objects; public final class ValidatorSystemMetadata implements ValidatorData { - private final ECPublicKey validatorKey; - private final byte[] data; + private final ECPublicKey validatorKey; + private final byte[] data; - public ValidatorSystemMetadata(ECPublicKey validatorKey, byte[] data) { - if (data.length != 32) { - throw new IllegalArgumentException("Invalid number of bytes in data"); - } - this.validatorKey = validatorKey; - this.data = data; - } + public ValidatorSystemMetadata(ECPublicKey validatorKey, byte[] data) { + if (data.length != 32) { + throw new IllegalArgumentException("Invalid number of bytes in data"); + } + this.validatorKey = validatorKey; + this.data = data; + } - public static ValidatorSystemMetadata createVirtual(ECPublicKey validatorKey) { - return new ValidatorSystemMetadata(validatorKey, HashUtils.zero256().asBytes()); - } + public static ValidatorSystemMetadata createVirtual(ECPublicKey validatorKey) { + return new ValidatorSystemMetadata(validatorKey, HashUtils.zero256().asBytes()); + } - public byte[] getData() { - return data; - } + public byte[] getData() { + return data; + } - public HashCode getAsHash() { - return HashCode.fromBytes(data); - } + public HashCode getAsHash() { + return HashCode.fromBytes(data); + } - @Override - public ECPublicKey getValidatorKey() { - return validatorKey; - } + @Override + public ECPublicKey getValidatorKey() { + return validatorKey; + } - @Override - public int hashCode() { - return Objects.hash(validatorKey, Arrays.hashCode(data)); - } + @Override + public int hashCode() { + return Objects.hash(validatorKey, Arrays.hashCode(data)); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ValidatorSystemMetadata)) { - return false; - } - var other = (ValidatorSystemMetadata) o; - return Objects.equals(this.validatorKey, other.validatorKey) - && Arrays.equals(this.data, other.data); - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ValidatorSystemMetadata)) { + return false; + } + var other = (ValidatorSystemMetadata) o; + return Objects.equals(this.validatorKey, other.validatorKey) + && Arrays.equals(this.data, other.data); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorUpdatingData.java b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorUpdatingData.java index 2dd6e1cc2f..11f0916009 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorUpdatingData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/application/validators/state/ValidatorUpdatingData.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,5 +67,5 @@ import java.util.OptionalLong; public interface ValidatorUpdatingData extends ValidatorData { - OptionalLong getEpochUpdate(); + OptionalLong getEpochUpdate(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/ActionConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/ActionConstructor.java index a7d6d7eda2..592b5519a5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/ActionConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/ActionConstructor.java @@ -65,11 +65,10 @@ package com.radixdlt.atom; /** - * Given a TxBuilder builds the high level action as radix engine - * transaction. + * Given a TxBuilder builds the high level action as radix engine transaction. * * @param the action type */ public interface ActionConstructor { - void construct(T action, TxBuilder builder) throws TxBuilderException; + void construct(T action, TxBuilder builder) throws TxBuilderException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/CloseableCursor.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/CloseableCursor.java index 2840fa3bb4..781939c530 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/CloseableCursor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/CloseableCursor.java @@ -65,7 +65,6 @@ package com.radixdlt.atom; import com.google.common.collect.Iterators; - import java.io.Closeable; import java.util.Iterator; import java.util.NoSuchElementException; @@ -74,126 +73,123 @@ import java.util.function.Supplier; /** - * Cursor into a substate store. Will often be a real cursor in a database so - * cursor must be closed after use. + * Cursor into a substate store. Will often be a real cursor in a database so cursor must be closed + * after use. */ public interface CloseableCursor extends Iterator, Closeable { - void close(); - - default CloseableCursor map(Function mapper) { - return new CloseableCursor<>() { - @Override - public void close() { - CloseableCursor.this.close(); - } - - @Override - public boolean hasNext() { - return CloseableCursor.this.hasNext(); - } - - @Override - public U next() { - var next = CloseableCursor.this.next(); - return mapper.apply(next); - } - }; - } - - default CloseableCursor filter(Predicate substatePredicate) { - var iterator = Iterators.filter(this, substatePredicate::test); - return new CloseableCursor<>() { - @Override - public void close() { - CloseableCursor.this.close(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public T next() { - return iterator.next(); - } - }; - } - - default CloseableCursor concat(Supplier> supplier1) { - return new CloseableCursor<>() { - private CloseableCursor cursor1; - - @Override - public void close() { - CloseableCursor.this.close(); - if (cursor1 != null) { - cursor1.close(); - } - } - - @Override - public boolean hasNext() { - if (CloseableCursor.this.hasNext()) { - return true; - } - - if (cursor1 == null) { - cursor1 = supplier1.get(); - } - - return cursor1.hasNext(); - } - - @Override - public T next() { - if (cursor1 != null) { - return cursor1.next(); - } else { - var s = CloseableCursor.this.next(); - if (!CloseableCursor.this.hasNext()) { - cursor1 = supplier1.get(); - } - return s; - } - } - }; - } - - - static CloseableCursor wrapIterator(Iterator i) { - return new CloseableCursor<>() { - @Override - public void close() { - } - - @Override - public boolean hasNext() { - return i.hasNext(); - } - - @Override - public T next() { - return i.next(); - } - }; - } - - static CloseableCursor empty() { - return new CloseableCursor<>() { - @Override - public void close() { - } - - @Override - public boolean hasNext() { - return false; - } - - @Override - public T next() { - throw new NoSuchElementException(); - } - }; - } + void close(); + + default CloseableCursor map(Function mapper) { + return new CloseableCursor<>() { + @Override + public void close() { + CloseableCursor.this.close(); + } + + @Override + public boolean hasNext() { + return CloseableCursor.this.hasNext(); + } + + @Override + public U next() { + var next = CloseableCursor.this.next(); + return mapper.apply(next); + } + }; + } + + default CloseableCursor filter(Predicate substatePredicate) { + var iterator = Iterators.filter(this, substatePredicate::test); + return new CloseableCursor<>() { + @Override + public void close() { + CloseableCursor.this.close(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public T next() { + return iterator.next(); + } + }; + } + + default CloseableCursor concat(Supplier> supplier1) { + return new CloseableCursor<>() { + private CloseableCursor cursor1; + + @Override + public void close() { + CloseableCursor.this.close(); + if (cursor1 != null) { + cursor1.close(); + } + } + + @Override + public boolean hasNext() { + if (CloseableCursor.this.hasNext()) { + return true; + } + + if (cursor1 == null) { + cursor1 = supplier1.get(); + } + + return cursor1.hasNext(); + } + + @Override + public T next() { + if (cursor1 != null) { + return cursor1.next(); + } else { + var s = CloseableCursor.this.next(); + if (!CloseableCursor.this.hasNext()) { + cursor1 = supplier1.get(); + } + return s; + } + } + }; + } + + static CloseableCursor wrapIterator(Iterator i) { + return new CloseableCursor<>() { + @Override + public void close() {} + + @Override + public boolean hasNext() { + return i.hasNext(); + } + + @Override + public T next() { + return i.next(); + } + }; + } + + static CloseableCursor empty() { + return new CloseableCursor<>() { + @Override + public void close() {} + + @Override + public boolean hasNext() { + return false; + } + + @Override + public T next() { + throw new NoSuchElementException(); + } + }; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/FixedTokenDefinition.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/FixedTokenDefinition.java index e9402fd576..a9065ac298 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/FixedTokenDefinition.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/FixedTokenDefinition.java @@ -65,58 +65,54 @@ package com.radixdlt.atom; import com.radixdlt.utils.UInt256; - import java.util.Objects; -/** - * Describes a fixed token definition - */ +/** Describes a fixed token definition */ public final class FixedTokenDefinition { - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenUrl; - private final UInt256 supply; + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenUrl; + private final UInt256 supply; - public FixedTokenDefinition( - String symbol, - String name, - String description, - String iconUrl, - String tokenUrl, - UInt256 supply - ) { - this.symbol = Objects.requireNonNull(symbol); - this.name = Objects.requireNonNull(name); - this.description = Objects.requireNonNull(description); - this.iconUrl = iconUrl; - this.tokenUrl = tokenUrl; - this.supply = Objects.requireNonNull(supply); - } + public FixedTokenDefinition( + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + UInt256 supply) { + this.symbol = Objects.requireNonNull(symbol); + this.name = Objects.requireNonNull(name); + this.description = Objects.requireNonNull(description); + this.iconUrl = iconUrl; + this.tokenUrl = tokenUrl; + this.supply = Objects.requireNonNull(supply); + } - public String getSymbol() { - return symbol; - } + public String getSymbol() { + return symbol; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - public String getIconUrl() { - return iconUrl == null ? "" : iconUrl; - } + public String getIconUrl() { + return iconUrl == null ? "" : iconUrl; + } - public String getTokenUrl() { - return tokenUrl == null ? "" : tokenUrl; - } + public String getTokenUrl() { + return tokenUrl == null ? "" : tokenUrl; + } - public UInt256 getSupply() { - return supply; - } + public UInt256 getSupply() { + return supply; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/LocalSubstate.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/LocalSubstate.java index f4a141ec2e..fd279c78a6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/LocalSubstate.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/LocalSubstate.java @@ -65,47 +65,45 @@ package com.radixdlt.atom; import com.radixdlt.constraintmachine.Particle; - import java.util.Objects; /** - * A particle which only has a local identifier since it's enclosing - * transaction has not yet finished being constructed. + * A particle which only has a local identifier since it's enclosing transaction has not yet + * finished being constructed. */ public final class LocalSubstate { - private final int index; - private final Particle particle; + private final int index; + private final Particle particle; - private LocalSubstate(int index, Particle particle) { - this.index = index; - this.particle = particle; - } + private LocalSubstate(int index, Particle particle) { + this.index = index; + this.particle = particle; + } - public static LocalSubstate create(int index, Particle particle) { - return new LocalSubstate(index, particle); - } + public static LocalSubstate create(int index, Particle particle) { + return new LocalSubstate(index, particle); + } - public Particle getParticle() { - return particle; - } + public Particle getParticle() { + return particle; + } - public int getIndex() { - return index; - } + public int getIndex() { + return index; + } - @Override - public int hashCode() { - return Objects.hash(index, particle); - } + @Override + public int hashCode() { + return Objects.hash(index, particle); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof LocalSubstate)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof LocalSubstate)) { + return false; + } - var other = (LocalSubstate) o; - return this.index == other.index - && Objects.equals(this.particle, other.particle); - } + var other = (LocalSubstate) o; + return this.index == other.index && Objects.equals(this.particle, other.particle); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/MessageTooLongException.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/MessageTooLongException.java index a229bf2685..111c426298 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/MessageTooLongException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/MessageTooLongException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,14 +65,14 @@ package com.radixdlt.atom; public final class MessageTooLongException extends TxBuilderException { - private final int attemptedLength; + private final int attemptedLength; - public MessageTooLongException(int attemptedLength) { - super("Message max lenghth is 255 but message length is " + attemptedLength); - this.attemptedLength = attemptedLength; - } + public MessageTooLongException(int attemptedLength) { + super("Message max lenghth is 255 but message length is " + attemptedLength); + this.attemptedLength = attemptedLength; + } - public int getAttemptedLength() { - return attemptedLength; - } + public int getAttemptedLength() { + return attemptedLength; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/MutableTokenDefinition.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/MutableTokenDefinition.java index 02dc858bc3..e65130eefa 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/MutableTokenDefinition.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/MutableTokenDefinition.java @@ -66,70 +66,66 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; -/** - * Specifies high level parameters to a token definition - */ +/** Specifies high level parameters to a token definition */ public final class MutableTokenDefinition { - private final ECPublicKey key; - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenUrl; + private final ECPublicKey key; + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenUrl; - public MutableTokenDefinition( - ECPublicKey key, - String symbol, - String name, - String description, - String iconUrl, - String tokenUrl - ) { - this.key = key; - this.symbol = symbol.toLowerCase(); - this.name = Objects.requireNonNull(name); - this.description = description; - this.iconUrl = iconUrl; - this.tokenUrl = tokenUrl; - } + public MutableTokenDefinition( + ECPublicKey key, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl) { + this.key = key; + this.symbol = symbol.toLowerCase(); + this.name = Objects.requireNonNull(name); + this.description = description; + this.iconUrl = iconUrl; + this.tokenUrl = tokenUrl; + } - public MutableTokenDefinition(ECPublicKey key, String symbol) { - this.key = key; - this.symbol = symbol.toLowerCase(); - this.name = null; - this.description = null; - this.iconUrl = null; - this.tokenUrl = null; - } + public MutableTokenDefinition(ECPublicKey key, String symbol) { + this.key = key; + this.symbol = symbol.toLowerCase(); + this.name = null; + this.description = null; + this.iconUrl = null; + this.tokenUrl = null; + } - public REAddr getResourceAddress() { - return REAddr.ofHashedKey(key, symbol); - } + public REAddr getResourceAddress() { + return REAddr.ofHashedKey(key, symbol); + } - public ECPublicKey getOwner() { - return key; - } + public ECPublicKey getOwner() { + return key; + } - public String getSymbol() { - return symbol; - } + public String getSymbol() { + return symbol; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getDescription() { - return description == null ? "" : description; - } + public String getDescription() { + return description == null ? "" : description; + } - public String getIconUrl() { - return iconUrl == null ? "" : iconUrl; - } + public String getIconUrl() { + return iconUrl == null ? "" : iconUrl; + } - public String getTokenUrl() { - return tokenUrl == null ? "" : tokenUrl; - } + public String getTokenUrl() { + return tokenUrl == null ? "" : tokenUrl; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/NoSubstateFoundException.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/NoSubstateFoundException.java index ddc56a8020..1a95b83d31 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/NoSubstateFoundException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/NoSubstateFoundException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,7 +65,7 @@ package com.radixdlt.atom; public class NoSubstateFoundException extends TxBuilderException { - public NoSubstateFoundException() { - super("Could not find substate."); - } + public NoSubstateFoundException() { + super("Could not find substate."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/NotEnoughResourcesException.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/NotEnoughResourcesException.java index 894b378904..b1b3c0ef08 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/NotEnoughResourcesException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/NotEnoughResourcesException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -67,22 +68,22 @@ import com.radixdlt.utils.UInt256; public final class NotEnoughResourcesException extends TxBuilderException { - private final UInt256 requested; - private final UInt256 available; - private final Bucket fromBucket; + private final UInt256 requested; + private final UInt256 available; + private final Bucket fromBucket; - public NotEnoughResourcesException(Bucket fromBucket, UInt256 requested, UInt256 available) { - super("Requested " + requested + " but only " + available + " available"); - this.fromBucket = fromBucket; - this.requested = requested; - this.available = available; - } + public NotEnoughResourcesException(Bucket fromBucket, UInt256 requested, UInt256 available) { + super("Requested " + requested + " but only " + available + " available"); + this.fromBucket = fromBucket; + this.requested = requested; + this.available = available; + } - public UInt256 getRequested() { - return requested; - } + public UInt256 getRequested() { + return requested; + } - public UInt256 getAvailable() { - return available; - } + public UInt256 getAvailable() { + return available; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/REConstructor.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/REConstructor.java index 8a887ae38a..1ffcff412a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/REConstructor.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/REConstructor.java @@ -66,57 +66,57 @@ import com.google.common.collect.ImmutableMap; import com.radixdlt.utils.UInt256; - import java.util.Map; import java.util.Optional; -/** - * Set of action to constructors mapper - */ +/** Set of action to constructors mapper */ public final class REConstructor { - private final Map, ActionConstructor> constructors; - private final UInt256 perByteFee; + private final Map, ActionConstructor> constructors; + private final UInt256 perByteFee; - private REConstructor(Map, ActionConstructor> constructors, UInt256 perByteFee) { - this.constructors = constructors; - this.perByteFee = perByteFee; - } + private REConstructor( + Map, ActionConstructor> constructors, UInt256 perByteFee) { + this.constructors = constructors; + this.perByteFee = perByteFee; + } - public static Builder newBuilder() { - return new Builder(); - } + public static Builder newBuilder() { + return new Builder(); + } - public static class Builder { - private ImmutableMap.Builder, ActionConstructor> mapBuilder = ImmutableMap.builder(); - private UInt256 perByteFee; + public static class Builder { + private ImmutableMap.Builder, ActionConstructor> mapBuilder = + ImmutableMap.builder(); + private UInt256 perByteFee; - private Builder() { - } + private Builder() {} - public Builder perByteFee(UInt256 perByteFee) { - this.perByteFee = perByteFee; - return this; - } + public Builder perByteFee(UInt256 perByteFee) { + this.perByteFee = perByteFee; + return this; + } - public Builder put(Class actionClass, ActionConstructor constructor) { - mapBuilder.put(actionClass, constructor); - return this; - } + public Builder put( + Class actionClass, ActionConstructor constructor) { + mapBuilder.put(actionClass, constructor); + return this; + } - public REConstructor build() { - return new REConstructor(mapBuilder.build(), perByteFee); - } - } + public REConstructor build() { + return new REConstructor(mapBuilder.build(), perByteFee); + } + } - public Optional getPerByteFee() { - return Optional.ofNullable(perByteFee); - } + public Optional getPerByteFee() { + return Optional.ofNullable(perByteFee); + } - public void construct(T action, TxBuilder txBuilder) throws TxBuilderException { - var actionConstructor = (ActionConstructor) constructors.get(action.getClass()); - if (actionConstructor == null) { - throw new IllegalArgumentException("Constructor not found for " + action); - } - actionConstructor.construct(action, txBuilder); - } + public void construct(T action, TxBuilder txBuilder) + throws TxBuilderException { + var actionConstructor = (ActionConstructor) constructors.get(action.getClass()); + if (actionConstructor == null) { + throw new IllegalArgumentException("Constructor not found for " + action); + } + actionConstructor.construct(action, txBuilder); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/REFieldSerialization.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/REFieldSerialization.java index d8cbeba149..aeeb525e45 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/REFieldSerialization.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/REFieldSerialization.java @@ -71,7 +71,6 @@ import com.radixdlt.serialization.DeserializeException; import com.radixdlt.utils.RadixConstants; import com.radixdlt.utils.UInt256; - import java.nio.ByteBuffer; import java.util.EnumSet; import java.util.Objects; @@ -80,252 +79,255 @@ import java.util.regex.Pattern; public final class REFieldSerialization { - private static final Pattern OWASP_URL_REGEX = Pattern.compile( - "^((((https?|ftps?|gopher|telnet|nntp)://)|(mailto:|news:))" - + "(%[0-9A-Fa-f]{2}|[-()_.!~*';/?:@&=+$,A-Za-z0-9])+)([).!';/?:,][[:blank:]])?$" - ); - - private REFieldSerialization() { - throw new IllegalStateException("Cannot instantiate."); - } - - public static byte[] serializeSignature(ECDSASignature signature) { - var buf = ByteBuffer.allocate(32 * 2 + 1); - buf.put(signature.getV()); - var rArray = signature.getR().toByteArray(); - var r = rArray.length > 32 ? UInt256.from(rArray, 1) : UInt256.from(rArray); - buf.put(r.toByteArray()); - var sArray = signature.getS().toByteArray(); - var s = sArray.length > 32 ? UInt256.from(sArray, 1) : UInt256.from(sArray); - buf.put(s.toByteArray()); - - return buf.array(); - } - - public static ECDSASignature deserializeSignature(ByteBuffer buf) throws DeserializeException { - var v = buf.get(); - if (v < 0 || v > 3) { - throw new DeserializeException("Invalid V byte " + v); - } - var rArray = new byte[32]; - buf.get(rArray); - var sArray = new byte[32]; - buf.get(sArray); - return ECDSASignature.deserialize(rArray, sArray, v); - } - - public static void serializeBoolean(ByteBuffer buf, boolean bool) { - buf.put((byte) (bool ? 1 : 0)); - } - - public static boolean deserializeBoolean(ByteBuffer buf) throws DeserializeException { - var flag = buf.get(); - if (!(flag == 0 || flag == 1)) { - throw new DeserializeException("Invalid flag"); - } - return flag == 1; - } - - public static void serializeOptionalKey(ByteBuffer buf, Optional addr) { - addr.ifPresentOrElse( - o -> { - buf.put((byte) 0x1); - REFieldSerialization.serializeKey(buf, o); - }, - () -> { - buf.put((byte) 0x0); - buf.put(new byte[ECPublicKey.COMPRESSED_BYTES]); - } - ); - } - - public static Optional deserializeOptionalKey(ByteBuffer buf) throws DeserializeException { - var type = buf.get(); - if (type == 0) { - for (int i = 0; i < ECPublicKey.COMPRESSED_BYTES; i++) { - if (buf.get() != 0) { - throw new DeserializeException("Empty key must have 0 value."); - } - } - return Optional.empty(); - } else if (type == 1) { - return Optional.of(REFieldSerialization.deserializeKey(buf)); - } else { - throw new DeserializeException("Unknown optionalAccountREAddr: " + type); - } - } - - - public static void serializeREAddr(ByteBuffer buf, REAddr rri) { - buf.put(rri.getBytes()); - } - - public static REAddr deserializeREAddr(ByteBuffer buf, EnumSet allowed) throws DeserializeException { - var v = buf.get(); // version - var type = REAddr.REAddrType.parse(v); - if (type.isEmpty()) { - throw new DeserializeException("Unknown address type " + v); - } - if (!allowed.contains(type.get())) { - throw new DeserializeException("Expected address type: " + allowed + " but was: " + type.get()); - } - return type.get().parse(buf); - } - - public static REAddr deserializeResourceAddr(ByteBuffer buf) throws DeserializeException { - return deserializeREAddr(buf, EnumSet.of(REAddr.REAddrType.NATIVE_TOKEN, REAddr.REAddrType.HASHED_KEY)); - } - - public static REAddr deserializeAccountREAddr(ByteBuffer buf) throws DeserializeException { - return deserializeREAddr(buf, EnumSet.of(REAddr.REAddrType.PUB_KEY)); - } - - public static int deserializeInt(ByteBuffer buf) throws DeserializeException { - return buf.getInt(); - } - - public static void deserializeReservedByte(ByteBuffer buf) throws DeserializeException { - var b = buf.get(); - if (b != 0) { - throw new DeserializeException("Reserved byte must be 0"); - } - } - - public static int deserializeUnsignedShort(ByteBuffer buf, int min, int max) throws DeserializeException { - var s = buf.getShort(); - var i = Short.toUnsignedInt(s); - - if (i < min) { - throw new DeserializeException("Min of short value is " + min + " but value is: " + i); - } - - if (i > max) { - throw new DeserializeException("Max of short value is " + max + " but value is: " + i); - } - - return i; - } - - public static void serializeReservedByte(ByteBuffer buf) { - buf.put((byte) 0); - } - - public static void serializeOptionalLong(ByteBuffer buf, OptionalLong optionalLong) { - optionalLong.ifPresentOrElse( - e -> { - buf.put((byte) 0x1); - buf.putLong(e); - }, - () -> { - buf.put((byte) 0x0); - buf.putLong(0); - } - ); - } - - public static OptionalLong deserializeOptionalNonNegativeLong(ByteBuffer buf) throws DeserializeException { - var type = buf.get(); - if (type == 0) { - var value = REFieldSerialization.deserializeNonNegativeLong(buf); - if (value != 0) { - throw new DeserializeException("Empty long must be 0 value."); - } - return OptionalLong.empty(); - } else if (type == 1) { - return OptionalLong.of(REFieldSerialization.deserializeNonNegativeLong(buf)); - } else { - throw new DeserializeException("Unknown optionalLongType: " + type); - } - } - - public static Long deserializeNonNegativeLong(ByteBuffer buf) throws DeserializeException { - var l = buf.getLong(); - if (l < 0) { - throw new DeserializeException("Long must be positive"); - } - return l; - } - - public static UInt256 deserializeUInt256(ByteBuffer buf) { - var amountDest = new byte[UInt256.BYTES]; // amount - buf.get(amountDest); - return UInt256.from(amountDest); - } - - public static void serializeUInt256(ByteBuffer buf, UInt256 u) { - buf.put(u.toByteArray()); - } - - public static UInt256 deserializeNonZeroUInt256(ByteBuffer buf) throws DeserializeException { - var amountDest = new byte[UInt256.BYTES]; // amount - buf.get(amountDest); - var uint256 = UInt256.from(amountDest); - if (uint256.isZero()) { - throw new DeserializeException("Cannot be zero."); - } - return uint256; - } - - public static void serializeKey(ByteBuffer buf, ECPublicKey key) { - buf.put(key.getCompressedBytes()); // address - } - - public static ECPublicKey deserializeKey(ByteBuffer buf) throws DeserializeException { - try { - var keyBytes = new byte[33]; - buf.get(keyBytes); - return ECPublicKey.fromBytes(keyBytes); - } catch (PublicKeyException | IllegalArgumentException e) { - throw new DeserializeException("Could not deserialize key"); - } - } - - public static void serializeFixedLengthBytes(ByteBuffer buf, byte[] bytes) { - buf.put(bytes); - } - - public static byte[] deserializeFixedLengthBytes(ByteBuffer buf, int length) { - final var dest = new byte[length]; - buf.get(dest); - return dest; - } - - public static void serializeString(ByteBuffer buf, String s) { - var sBytes = s.getBytes(RadixConstants.STANDARD_CHARSET); - if (sBytes.length > 255) { - throw new IllegalArgumentException("string cannot be greater than 255 chars"); - } - var len = (short) sBytes.length; - buf.putShort(len); // url length - buf.put(sBytes); // url - } - - public static String deserializeString(ByteBuffer buf) throws DeserializeException { - var len = REFieldSerialization.deserializeUnsignedShort(buf, 0, 255); - var dest = new byte[len]; - buf.get(dest); - return new String(dest, RadixConstants.STANDARD_CHARSET); - } - - public static String deserializeUrl(ByteBuffer buf) throws DeserializeException { - var url = deserializeString(buf); - if (!isUrlValid(url)) { - throw new DeserializeException("URL: not a valid URL: " + url); - } - return url; - } - - public static boolean isUrlValid(String url) { - return url.isEmpty() || OWASP_URL_REGEX.matcher(url).matches(); - } - - public static String requireValidUrl(String url) { - Objects.requireNonNull(url); - - if (isUrlValid(url)) { - return url; - } - - throw new IllegalArgumentException("URL: not a valid URL: " + url); - } + private static final Pattern OWASP_URL_REGEX = + Pattern.compile( + "^((((https?|ftps?|gopher|telnet|nntp)://)|(mailto:|news:))" + + "(%[0-9A-Fa-f]{2}|[-()_.!~*';/?:@&=+$,A-Za-z0-9])+)([).!';/?:,][[:blank:]])?$"); + + private REFieldSerialization() { + throw new IllegalStateException("Cannot instantiate."); + } + + public static byte[] serializeSignature(ECDSASignature signature) { + var buf = ByteBuffer.allocate(32 * 2 + 1); + buf.put(signature.getV()); + var rArray = signature.getR().toByteArray(); + var r = rArray.length > 32 ? UInt256.from(rArray, 1) : UInt256.from(rArray); + buf.put(r.toByteArray()); + var sArray = signature.getS().toByteArray(); + var s = sArray.length > 32 ? UInt256.from(sArray, 1) : UInt256.from(sArray); + buf.put(s.toByteArray()); + + return buf.array(); + } + + public static ECDSASignature deserializeSignature(ByteBuffer buf) throws DeserializeException { + var v = buf.get(); + if (v < 0 || v > 3) { + throw new DeserializeException("Invalid V byte " + v); + } + var rArray = new byte[32]; + buf.get(rArray); + var sArray = new byte[32]; + buf.get(sArray); + return ECDSASignature.deserialize(rArray, sArray, v); + } + + public static void serializeBoolean(ByteBuffer buf, boolean bool) { + buf.put((byte) (bool ? 1 : 0)); + } + + public static boolean deserializeBoolean(ByteBuffer buf) throws DeserializeException { + var flag = buf.get(); + if (!(flag == 0 || flag == 1)) { + throw new DeserializeException("Invalid flag"); + } + return flag == 1; + } + + public static void serializeOptionalKey(ByteBuffer buf, Optional addr) { + addr.ifPresentOrElse( + o -> { + buf.put((byte) 0x1); + REFieldSerialization.serializeKey(buf, o); + }, + () -> { + buf.put((byte) 0x0); + buf.put(new byte[ECPublicKey.COMPRESSED_BYTES]); + }); + } + + public static Optional deserializeOptionalKey(ByteBuffer buf) + throws DeserializeException { + var type = buf.get(); + if (type == 0) { + for (int i = 0; i < ECPublicKey.COMPRESSED_BYTES; i++) { + if (buf.get() != 0) { + throw new DeserializeException("Empty key must have 0 value."); + } + } + return Optional.empty(); + } else if (type == 1) { + return Optional.of(REFieldSerialization.deserializeKey(buf)); + } else { + throw new DeserializeException("Unknown optionalAccountREAddr: " + type); + } + } + + public static void serializeREAddr(ByteBuffer buf, REAddr rri) { + buf.put(rri.getBytes()); + } + + public static REAddr deserializeREAddr(ByteBuffer buf, EnumSet allowed) + throws DeserializeException { + var v = buf.get(); // version + var type = REAddr.REAddrType.parse(v); + if (type.isEmpty()) { + throw new DeserializeException("Unknown address type " + v); + } + if (!allowed.contains(type.get())) { + throw new DeserializeException( + "Expected address type: " + allowed + " but was: " + type.get()); + } + return type.get().parse(buf); + } + + public static REAddr deserializeResourceAddr(ByteBuffer buf) throws DeserializeException { + return deserializeREAddr( + buf, EnumSet.of(REAddr.REAddrType.NATIVE_TOKEN, REAddr.REAddrType.HASHED_KEY)); + } + + public static REAddr deserializeAccountREAddr(ByteBuffer buf) throws DeserializeException { + return deserializeREAddr(buf, EnumSet.of(REAddr.REAddrType.PUB_KEY)); + } + + public static int deserializeInt(ByteBuffer buf) throws DeserializeException { + return buf.getInt(); + } + + public static void deserializeReservedByte(ByteBuffer buf) throws DeserializeException { + var b = buf.get(); + if (b != 0) { + throw new DeserializeException("Reserved byte must be 0"); + } + } + + public static int deserializeUnsignedShort(ByteBuffer buf, int min, int max) + throws DeserializeException { + var s = buf.getShort(); + var i = Short.toUnsignedInt(s); + + if (i < min) { + throw new DeserializeException("Min of short value is " + min + " but value is: " + i); + } + + if (i > max) { + throw new DeserializeException("Max of short value is " + max + " but value is: " + i); + } + + return i; + } + + public static void serializeReservedByte(ByteBuffer buf) { + buf.put((byte) 0); + } + + public static void serializeOptionalLong(ByteBuffer buf, OptionalLong optionalLong) { + optionalLong.ifPresentOrElse( + e -> { + buf.put((byte) 0x1); + buf.putLong(e); + }, + () -> { + buf.put((byte) 0x0); + buf.putLong(0); + }); + } + + public static OptionalLong deserializeOptionalNonNegativeLong(ByteBuffer buf) + throws DeserializeException { + var type = buf.get(); + if (type == 0) { + var value = REFieldSerialization.deserializeNonNegativeLong(buf); + if (value != 0) { + throw new DeserializeException("Empty long must be 0 value."); + } + return OptionalLong.empty(); + } else if (type == 1) { + return OptionalLong.of(REFieldSerialization.deserializeNonNegativeLong(buf)); + } else { + throw new DeserializeException("Unknown optionalLongType: " + type); + } + } + + public static Long deserializeNonNegativeLong(ByteBuffer buf) throws DeserializeException { + var l = buf.getLong(); + if (l < 0) { + throw new DeserializeException("Long must be positive"); + } + return l; + } + + public static UInt256 deserializeUInt256(ByteBuffer buf) { + var amountDest = new byte[UInt256.BYTES]; // amount + buf.get(amountDest); + return UInt256.from(amountDest); + } + + public static void serializeUInt256(ByteBuffer buf, UInt256 u) { + buf.put(u.toByteArray()); + } + + public static UInt256 deserializeNonZeroUInt256(ByteBuffer buf) throws DeserializeException { + var amountDest = new byte[UInt256.BYTES]; // amount + buf.get(amountDest); + var uint256 = UInt256.from(amountDest); + if (uint256.isZero()) { + throw new DeserializeException("Cannot be zero."); + } + return uint256; + } + + public static void serializeKey(ByteBuffer buf, ECPublicKey key) { + buf.put(key.getCompressedBytes()); // address + } + + public static ECPublicKey deserializeKey(ByteBuffer buf) throws DeserializeException { + try { + var keyBytes = new byte[33]; + buf.get(keyBytes); + return ECPublicKey.fromBytes(keyBytes); + } catch (PublicKeyException | IllegalArgumentException e) { + throw new DeserializeException("Could not deserialize key"); + } + } + + public static void serializeFixedLengthBytes(ByteBuffer buf, byte[] bytes) { + buf.put(bytes); + } + + public static byte[] deserializeFixedLengthBytes(ByteBuffer buf, int length) { + final var dest = new byte[length]; + buf.get(dest); + return dest; + } + + public static void serializeString(ByteBuffer buf, String s) { + var sBytes = s.getBytes(RadixConstants.STANDARD_CHARSET); + if (sBytes.length > 255) { + throw new IllegalArgumentException("string cannot be greater than 255 chars"); + } + var len = (short) sBytes.length; + buf.putShort(len); // url length + buf.put(sBytes); // url + } + + public static String deserializeString(ByteBuffer buf) throws DeserializeException { + var len = REFieldSerialization.deserializeUnsignedShort(buf, 0, 255); + var dest = new byte[len]; + buf.get(dest); + return new String(dest, RadixConstants.STANDARD_CHARSET); + } + + public static String deserializeUrl(ByteBuffer buf) throws DeserializeException { + var url = deserializeString(buf); + if (!isUrlValid(url)) { + throw new DeserializeException("URL: not a valid URL: " + url); + } + return url; + } + + public static boolean isUrlValid(String url) { + return url.isEmpty() || OWASP_URL_REGEX.matcher(url).matches(); + } + + public static String requireValidUrl(String url) { + Objects.requireNonNull(url); + + if (isUrlValid(url)) { + return url; + } + + throw new IllegalArgumentException("URL: not a valid URL: " + url); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/Substate.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/Substate.java index 41f775c0f3..a78b2c811c 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/Substate.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/Substate.java @@ -65,54 +65,53 @@ package com.radixdlt.atom; import com.radixdlt.constraintmachine.Particle; - import java.util.Objects; /** - * Application substate which can be booted up or shut down in radix engine - * through transactions. + * Application substate which can be booted up or shut down in radix engine through transactions. */ public final class Substate { - private final Particle particle; - private final SubstateId substateId; + private final Particle particle; + private final SubstateId substateId; - private Substate(Particle particle, SubstateId substateId) { - this.particle = particle; - this.substateId = substateId; - } + private Substate(Particle particle, SubstateId substateId) { + this.particle = particle; + this.substateId = substateId; + } - public static Substate create(Particle particle, SubstateId substateId) { - Objects.requireNonNull(particle); - Objects.requireNonNull(substateId); - return new Substate(particle, substateId); - } + public static Substate create(Particle particle, SubstateId substateId) { + Objects.requireNonNull(particle); + Objects.requireNonNull(substateId); + return new Substate(particle, substateId); + } - public SubstateId getId() { - return substateId; - } + public SubstateId getId() { + return substateId; + } - public Particle getParticle() { - return particle; - } + public Particle getParticle() { + return particle; + } - @Override - public int hashCode() { - return Objects.hash(particle, substateId); - } + @Override + public int hashCode() { + return Objects.hash(particle, substateId); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof Substate)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof Substate)) { + return false; + } - var other = (Substate) o; - return Objects.equals(this.particle, other.particle) - && Objects.equals(this.substateId, other.substateId); - } + var other = (Substate) o; + return Objects.equals(this.particle, other.particle) + && Objects.equals(this.substateId, other.substateId); + } - @Override - public String toString() { - return String.format("%s{raw=%s id=%s}", this.getClass().getSimpleName(), this.particle, this.substateId); - } -} \ No newline at end of file + @Override + public String toString() { + return String.format( + "%s{raw=%s id=%s}", this.getClass().getSimpleName(), this.particle, this.substateId); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateId.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateId.java index 919e506985..a549aee525 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateId.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateId.java @@ -64,109 +64,104 @@ package com.radixdlt.atom; -import org.bouncycastle.util.encoders.Hex; - import com.radixdlt.identifiers.AID; import com.radixdlt.utils.Ints; - import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Objects; import java.util.Optional; +import org.bouncycastle.util.encoders.Hex; - -/** - * The id of a unique substate - */ +/** The id of a unique substate */ public final class SubstateId { - public static final int BYTES = AID.BYTES + Integer.BYTES; - - private final byte[] idBytes; - - private SubstateId(byte[] idBytes) { - this.idBytes = Objects.requireNonNull(idBytes); - } - - public static SubstateId ofSubstate(AID txId, int index) { - byte[] id = new byte[BYTES]; - txId.copyTo(id, 0); - Ints.copyTo(index, id, AID.BYTES); - return new SubstateId(id); - } - - public static SubstateId ofVirtualSubstate(SubstateId substateId, byte[] key) { - if (substateId.isVirtual()) { - throw new IllegalArgumentException(); - } - byte[] id = new byte[BYTES + key.length]; - var buf = ByteBuffer.wrap(id); - buf.put(substateId.asBytes()); - buf.put(key); - return new SubstateId(id); - } - - public static SubstateId fromBytes(byte[] bytes) { - return new SubstateId(bytes); - } - - public static SubstateId fromBuffer(ByteBuffer buf) { - byte[] id = new byte[BYTES]; - buf.get(id); - return fromBytes(id); - } - - public boolean isVirtual() { - return idBytes.length > BYTES; - } - - public byte[] asBytes() { - return idBytes; - } - - public AID getTxnId() { - return AID.from(idBytes); - } - - public Optional getVirtualParent() { - if (idBytes.length <= BYTES) { - return Optional.empty(); - } - var buf = ByteBuffer.wrap(idBytes, 0, BYTES); - return Optional.of(SubstateId.fromBuffer(buf)); - } - - public Optional getVirtualKey() { - if (idBytes.length <= BYTES) { - return Optional.empty(); - } - var buf = ByteBuffer.wrap(idBytes, BYTES, idBytes.length - BYTES); - return Optional.of(buf); - } - - public Optional getIndex() { - return idBytes.length == BYTES - ? Optional.of(Ints.fromByteArray(idBytes, AID.BYTES)) - : Optional.empty(); - } - - @Override - public String toString() { - return Hex.toHexString(idBytes); - } - - @Override - public int hashCode() { - return Arrays.hashCode(idBytes); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof SubstateId)) { - return false; - } - - var other = (SubstateId) o; - - return Arrays.equals(this.idBytes, other.idBytes); - } + public static final int BYTES = AID.BYTES + Integer.BYTES; + + private final byte[] idBytes; + + private SubstateId(byte[] idBytes) { + this.idBytes = Objects.requireNonNull(idBytes); + } + + public static SubstateId ofSubstate(AID txId, int index) { + byte[] id = new byte[BYTES]; + txId.copyTo(id, 0); + Ints.copyTo(index, id, AID.BYTES); + return new SubstateId(id); + } + + public static SubstateId ofVirtualSubstate(SubstateId substateId, byte[] key) { + if (substateId.isVirtual()) { + throw new IllegalArgumentException(); + } + byte[] id = new byte[BYTES + key.length]; + var buf = ByteBuffer.wrap(id); + buf.put(substateId.asBytes()); + buf.put(key); + return new SubstateId(id); + } + + public static SubstateId fromBytes(byte[] bytes) { + return new SubstateId(bytes); + } + + public static SubstateId fromBuffer(ByteBuffer buf) { + byte[] id = new byte[BYTES]; + buf.get(id); + return fromBytes(id); + } + + public boolean isVirtual() { + return idBytes.length > BYTES; + } + + public byte[] asBytes() { + return idBytes; + } + + public AID getTxnId() { + return AID.from(idBytes); + } + + public Optional getVirtualParent() { + if (idBytes.length <= BYTES) { + return Optional.empty(); + } + var buf = ByteBuffer.wrap(idBytes, 0, BYTES); + return Optional.of(SubstateId.fromBuffer(buf)); + } + + public Optional getVirtualKey() { + if (idBytes.length <= BYTES) { + return Optional.empty(); + } + var buf = ByteBuffer.wrap(idBytes, BYTES, idBytes.length - BYTES); + return Optional.of(buf); + } + + public Optional getIndex() { + return idBytes.length == BYTES + ? Optional.of(Ints.fromByteArray(idBytes, AID.BYTES)) + : Optional.empty(); + } + + @Override + public String toString() { + return Hex.toHexString(idBytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(idBytes); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SubstateId)) { + return false; + } + + var other = (SubstateId) o; + + return Arrays.equals(this.idBytes, other.idBytes); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateStore.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateStore.java index 6093e15491..d06ca9afb6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateStore.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateStore.java @@ -67,28 +67,26 @@ import com.radixdlt.constraintmachine.RawSubstateBytes; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SystemMapKey; - import java.util.Optional; -/** - * Store which contains an index into up substates - */ +/** Store which contains an index into up substates */ public interface SubstateStore { - CloseableCursor openIndexedCursor(SubstateIndex index); - Optional get(SystemMapKey key); + CloseableCursor openIndexedCursor(SubstateIndex index); + + Optional get(SystemMapKey key); - static SubstateStore empty() { - return new SubstateStore() { - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return CloseableCursor.empty(); - } + static SubstateStore empty() { + return new SubstateStore() { + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + return CloseableCursor.empty(); + } - @Override - public Optional get(SystemMapKey key) { - return Optional.empty(); - } - }; - } + @Override + public Optional get(SystemMapKey key) { + return Optional.empty(); + } + }; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateTypeId.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateTypeId.java index a4116da03a..90ff30627f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateTypeId.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/SubstateTypeId.java @@ -84,67 +84,68 @@ import com.radixdlt.application.validators.state.ValidatorRegisteredCopy; import com.radixdlt.application.validators.state.ValidatorSystemMetadata; import com.radixdlt.constraintmachine.Particle; - import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; public enum SubstateTypeId { - VIRTUAL_PARENT((byte) 0x0, VirtualParent.class), - UNCLAIMED_READDR((byte) 0x1, UnclaimedREAddr.class), - ROUND_DATA((byte) 0x2, RoundData.class), - EPOCH_DATA((byte) 0x3, EpochData.class), - TOKEN_RESOURCE((byte) 0x4, TokenResource.class), - TOKEN_RESOURCE_METADATA((byte) 0x5, TokenResourceMetadata.class), - TOKENS((byte) 0x6, TokensInAccount.class), - PREPARED_STAKE((byte) 0x7, PreparedStake.class), - STAKE_OWNERSHIP((byte) 0x8, StakeOwnership.class), - PREPARED_UNSTAKE((byte) 0x9, PreparedUnstakeOwnership.class), - EXITING_STAKE((byte) 0xa, ExitingStake.class), - VALIDATOR_META_DATA((byte) 0xb, ValidatorMetaData.class), - VALIDATOR_STAKE_DATA((byte) 0xc, ValidatorStakeData.class), - VALIDATOR_BFT_DATA((byte) 0xd, ValidatorBFTData.class), - VALIDATOR_ALLOW_DELEGATION_FLAG((byte) 0xe, AllowDelegationFlag.class), - VALIDATOR_REGISTERED_FLAG_COPY((byte) 0xf, ValidatorRegisteredCopy.class), - VALIDATOR_RAKE_COPY((byte) 0x10, ValidatorFeeCopy.class), - VALIDATOR_OWNER_COPY((byte) 0x11, ValidatorOwnerCopy.class), - VALIDATOR_SYSTEM_META_DATA((byte) 0x12, ValidatorSystemMetadata.class); + VIRTUAL_PARENT((byte) 0x0, VirtualParent.class), + UNCLAIMED_READDR((byte) 0x1, UnclaimedREAddr.class), + ROUND_DATA((byte) 0x2, RoundData.class), + EPOCH_DATA((byte) 0x3, EpochData.class), + TOKEN_RESOURCE((byte) 0x4, TokenResource.class), + TOKEN_RESOURCE_METADATA((byte) 0x5, TokenResourceMetadata.class), + TOKENS((byte) 0x6, TokensInAccount.class), + PREPARED_STAKE((byte) 0x7, PreparedStake.class), + STAKE_OWNERSHIP((byte) 0x8, StakeOwnership.class), + PREPARED_UNSTAKE((byte) 0x9, PreparedUnstakeOwnership.class), + EXITING_STAKE((byte) 0xa, ExitingStake.class), + VALIDATOR_META_DATA((byte) 0xb, ValidatorMetaData.class), + VALIDATOR_STAKE_DATA((byte) 0xc, ValidatorStakeData.class), + VALIDATOR_BFT_DATA((byte) 0xd, ValidatorBFTData.class), + VALIDATOR_ALLOW_DELEGATION_FLAG((byte) 0xe, AllowDelegationFlag.class), + VALIDATOR_REGISTERED_FLAG_COPY((byte) 0xf, ValidatorRegisteredCopy.class), + VALIDATOR_RAKE_COPY((byte) 0x10, ValidatorFeeCopy.class), + VALIDATOR_OWNER_COPY((byte) 0x11, ValidatorOwnerCopy.class), + VALIDATOR_SYSTEM_META_DATA((byte) 0x12, ValidatorSystemMetadata.class); + + private final byte id; + private final Class substateClass; - private final byte id; - private final Class substateClass; + private static final Map substateTypes; - private static final Map substateTypes; - static { - substateTypes = Arrays.stream(SubstateTypeId.values()).collect(Collectors.toMap(e -> e.id, e -> e)); - } + static { + substateTypes = + Arrays.stream(SubstateTypeId.values()).collect(Collectors.toMap(e -> e.id, e -> e)); + } - SubstateTypeId(byte id, Class substateClass) { - this.id = id; - this.substateClass = substateClass; - } + SubstateTypeId(byte id, Class substateClass) { + this.id = id; + this.substateClass = substateClass; + } - public static SubstateTypeId valueOf(byte typeId) { - var substateType = substateTypes.get(typeId); - if (substateType == null) { - throw new IllegalArgumentException("Unknown typeId " + typeId); - } - return substateType; - } + public static SubstateTypeId valueOf(byte typeId) { + var substateType = substateTypes.get(typeId); + if (substateType == null) { + throw new IllegalArgumentException("Unknown typeId " + typeId); + } + return substateType; + } - public static SubstateTypeId valueOf(Class substateClass) { - for (var substateType : SubstateTypeId.values()) { - if (substateType.substateClass.equals(substateClass)) { - return substateType; - } - } - throw new IllegalArgumentException("Unknown substate class " + substateClass); - } + public static SubstateTypeId valueOf(Class substateClass) { + for (var substateType : SubstateTypeId.values()) { + if (substateType.substateClass.equals(substateClass)) { + return substateType; + } + } + throw new IllegalArgumentException("Unknown substate class " + substateClass); + } - public byte id() { - return id; - } + public byte id() { + return id; + } - public Class getSubstateClass() { - return substateClass; - } + public Class getSubstateClass() { + return substateClass; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxAction.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxAction.java index 3feb35529f..f88f255f9e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxAction.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxAction.java @@ -64,8 +64,5 @@ package com.radixdlt.atom; -/** - * Marker interface for RadixEngine actions. - */ -public interface TxAction { -} +/** Marker interface for RadixEngine actions. */ +public interface TxAction {} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilder.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilder.java index 8af1543fd9..25befa9910 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilder.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilder.java @@ -68,14 +68,14 @@ import com.google.common.hash.HashCode; import com.google.common.primitives.UnsignedBytes; import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.application.system.state.UnclaimedREAddr; import com.radixdlt.application.tokens.ResourceInBucket; import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.tokens.state.TokensInAccount; -import com.radixdlt.application.system.state.UnclaimedREAddr; -import com.radixdlt.constraintmachine.RawSubstateBytes; -import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.Particle; +import com.radixdlt.constraintmachine.RawSubstateBytes; import com.radixdlt.constraintmachine.SubstateDeserialization; +import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SubstateSerialization; import com.radixdlt.constraintmachine.SystemMapKey; import com.radixdlt.crypto.ECDSASignature; @@ -84,9 +84,6 @@ import com.radixdlt.serialization.DeserializeException; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Comparator; @@ -99,495 +96,488 @@ import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Creates a transaction from high level user actions - */ +/** Creates a transaction from high level user actions */ public final class TxBuilder { - private static final Logger logger = LogManager.getLogger(); - private final TxLowLevelBuilder lowLevelBuilder; - private final SubstateStore remoteSubstate; - private final SubstateDeserialization deserialization; - private final SubstateSerialization serialization; - private UInt256 feeReservePut; - private UInt256 feeReserveTake = UInt256.ZERO; - private int numResourcesCreated = 0; - - private TxBuilder( - SubstateStore remoteSubstate, - SubstateDeserialization deserialization, - SubstateSerialization serialization - ) { - this.lowLevelBuilder = TxLowLevelBuilder.newBuilder(serialization); - this.remoteSubstate = remoteSubstate; - this.deserialization = deserialization; - this.serialization = serialization; - } - - public static TxBuilder newBuilder( - SubstateStore remoteSubstate, - SubstateDeserialization deserialization, - SubstateSerialization serialization - ) { - return new TxBuilder(remoteSubstate, deserialization, serialization); - } - - public static TxBuilder newBuilder(SubstateDeserialization deserialization, SubstateSerialization serialization) { - return new TxBuilder(SubstateStore.empty(), deserialization, serialization); - } - - public TxLowLevelBuilder toLowLevelBuilder() { - return lowLevelBuilder; - } - - public void end() { - lowLevelBuilder.end(); - } - - public void up(Particle particle) { - lowLevelBuilder.up(particle); - if (particle instanceof TokenResource) { - numResourcesCreated++; - } - } - - public int getNumResourcesCreated() { - return numResourcesCreated; - } - - public void down(SubstateId substateId) { - lowLevelBuilder.down(substateId); - } - - private void localDown(int index) { - lowLevelBuilder.localDown(index); - } - - private void read(SubstateId substateId) { - lowLevelBuilder.read(substateId); - } - - - private CloseableCursor createRemoteSubstateCursor(SubstateIndex index) { - return remoteSubstate.openIndexedCursor(index) - .filter(s -> !lowLevelBuilder.remoteDownSubstate().contains(SubstateId.fromBytes(s.getId()))); - } - - private CloseableCursor createRemoteSubstateCursor(Class c) { - var b = deserialization.classToByte(c); - return createRemoteSubstateCursor(SubstateIndex.create(new byte[] {b}, c)); - } - - private static Stream iteratorToStream(Iterator iterator) { - return StreamSupport.stream( - Spliterators.spliteratorUnknownSize( - iterator, - Spliterator.ORDERED - ), - false - ); - } - - private Substate deserialize(RawSubstateBytes rawSubstateBytes) { - try { - var raw = deserialization.deserialize(rawSubstateBytes.getData()); - return Substate.create(raw, SubstateId.fromBytes(rawSubstateBytes.getId())); - } catch (DeserializeException e) { - throw new IllegalStateException(e); - } - } - - // For mempool filler - public T downSubstate( - Class particleClass, - Predicate particlePredicate - ) throws TxBuilderException { - var localSubstate = lowLevelBuilder.localUpSubstate().stream() - .filter(s -> particleClass.isInstance(s.getParticle())) - .filter(s -> particlePredicate.test((T) s.getParticle())) - .findFirst(); - - if (localSubstate.isPresent()) { - localDown(localSubstate.get().getIndex()); - return (T) localSubstate.get().getParticle(); - } - - try (var cursor = createRemoteSubstateCursor(particleClass)) { - var substateRead = iteratorToStream(cursor) - .map(this::deserialize) - .filter(s -> particlePredicate.test(particleClass.cast(s.getParticle()))) - .findFirst(); - - if (substateRead.isEmpty()) { - throw new NoSubstateFoundException(); - } - - down(substateRead.get().getId()); - - return (T) substateRead.get().getParticle(); - } - } - - public T findSystem(Class substateClass) { - var typeByte = deserialization.classToByte(substateClass); - var mapKey = SystemMapKey.ofSystem(typeByte); - var localMaybe = lowLevelBuilder.get(mapKey); - if (localMaybe.isPresent()) { - return (T) localMaybe.get().getParticle(); - } - return remoteSubstate.get(mapKey).map(rawSubstate -> { - try { - return (T) deserialization.deserialize(rawSubstate.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - }).orElseThrow(); - } - - - public T find(Class substateClass, Object key) throws TxBuilderException { - var keyBytes = serialization.serializeKey(substateClass, key); - var typeByte = deserialization.classToByte(substateClass); - var mapKey = SystemMapKey.ofSystem(typeByte, keyBytes); - var localMaybe = lowLevelBuilder.get(mapKey); - if (localMaybe.isPresent()) { - return (T) localMaybe.get().getParticle(); - } - var raw = remoteSubstate.get(mapKey); - - if (raw.isPresent()) { - var rawSubstate = raw.get(); - try { - return (T) deserialization.deserialize(rawSubstate.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - } else { - return serialization.mapVirtual(substateClass, key); - } - } - - private void virtualReadDownInternal(byte typeByte, byte[] keyBytes, boolean down) { - var parentMapKey = SystemMapKey.ofSystem(typeByte); - var localParent = lowLevelBuilder.get(parentMapKey); - if (localParent.isPresent()) { - if (down) { - lowLevelBuilder.localVirtualDown(localParent.get().getIndex(), keyBytes); - } else { - lowLevelBuilder.localVirtualRead(localParent.get().getIndex(), keyBytes); - } - } else { - var parent = remoteSubstate.get(parentMapKey).orElseThrow(); - var substateId = SubstateId.fromBytes(parent.getId()); - if (down) { - lowLevelBuilder.virtualDown(substateId, keyBytes); - } else { - lowLevelBuilder.virtualRead(substateId, keyBytes); - } - } - } - - private T readDownInternal(Class substateClass, Object key, boolean down) { - var keyBytes = serialization.serializeKey(substateClass, key); - var typeByte = deserialization.classToByte(substateClass); - var mapKey = SystemMapKey.ofSystem(typeByte, keyBytes); - var localMaybe = lowLevelBuilder.get(mapKey); - if (localMaybe.isPresent()) { - var local = localMaybe.get(); - if (down) { - lowLevelBuilder.localDown(local.getIndex()); - } else { - lowLevelBuilder.localRead(local.getIndex()); - } - return (T) local.getParticle(); - } - - var raw = remoteSubstate.get(mapKey); - - if (raw.isPresent()) { - var rawSubstate = raw.get(); - if (down) { - down(SubstateId.fromBytes(rawSubstate.getId())); - } else { - read(SubstateId.fromBytes(rawSubstate.getId())); - } - try { - return (T) deserialization.deserialize(rawSubstate.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - } else { - this.virtualReadDownInternal(typeByte, keyBytes, down); - return serialization.mapVirtual(substateClass, key); - } - } - - public T down(Class substateClass, Object key) { - return readDownInternal(substateClass, key, true); - } - - public T read(Class substateClass, Object key) { - return readDownInternal(substateClass, key, false); - } - - public T downSystem(Class substateClass) { - var typeByte = deserialization.classToByte(substateClass); - var mapKey = SystemMapKey.ofSystem(typeByte); - var localMaybe = lowLevelBuilder.get(mapKey); - if (localMaybe.isPresent()) { - var local = localMaybe.get(); - lowLevelBuilder.localDown(local.getIndex()); - return (T) local.getParticle(); - } - - var rawSubstate = remoteSubstate.get(mapKey).orElseThrow(); - down(SubstateId.fromBytes(rawSubstate.getId())); - try { - return (T) deserialization.deserialize(rawSubstate.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - } - - public T readSystem(Class substateClass) { - var typeByte = deserialization.classToByte(substateClass); - var mapKey = SystemMapKey.ofSystem(typeByte); - var localMaybe = lowLevelBuilder.get(mapKey); - if (localMaybe.isPresent()) { - var local = localMaybe.get(); - lowLevelBuilder.localRead(local.getIndex()); - return (T) local.getParticle(); - } - - var rawSubstate = remoteSubstate.get(mapKey).orElseThrow(); - read(SubstateId.fromBytes(rawSubstate.getId())); - try { - return (T) deserialization.deserialize(rawSubstate.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - } - - public UnclaimedREAddr downREAddr(REAddr addr) { - var keyBytes = serialization.serializeKey(UnclaimedREAddr.class, addr); - var typeByte = deserialization.classToByte(UnclaimedREAddr.class); - var mapKey = SystemMapKey.ofSystem(typeByte); - var localParent = lowLevelBuilder.get(mapKey); - if (localParent.isPresent()) { - lowLevelBuilder.localVirtualDown(localParent.get().getIndex(), keyBytes); - } else { - var parent = remoteSubstate.get(mapKey).orElseThrow(); - var substateId = SubstateId.fromBytes(parent.getId()); - lowLevelBuilder.virtualDown(substateId, keyBytes); - } - return serialization.mapVirtual(UnclaimedREAddr.class, addr); - } - - public U shutdownAll( - Class particleClass, - Function, U> mapper - ) { - var typeByte = deserialization.classToByte(particleClass); - return shutdownAll(SubstateIndex.create(typeByte, particleClass), mapper); - } - - // FIXME: programmedInTxn is just a hack - public CloseableCursor readIndex(SubstateIndex index, boolean programmedInTxn) { - var comparator = UnsignedBytes.lexicographicalComparator().reversed(); - var cursor = createRemoteSubstateCursor(index); - var localIterator = lowLevelBuilder.localUpSubstate().stream() - .map(LocalSubstate::getParticle) - .filter(index.getSubstateClass()::isInstance) - .map(p -> (T) p) - .map(p -> Pair.of(p, serialization.serialize(p))) - .filter(p -> index.test(p.getSecond())) - .sorted(Comparator.comparing(Pair::getSecond, comparator)) - .iterator(); - - if (programmedInTxn) { - lowLevelBuilder.readIndex(index); - } - - return new CloseableCursor() { - private RawSubstateBytes nextRemote = cursor.hasNext() ? cursor.next() : null; - private Pair nextLocal = localIterator.hasNext() ? localIterator.next() : null; - - private T nextRemote() { - var next = nextRemote; - nextRemote = cursor.hasNext() ? cursor.next() : null; - try { - return (T) deserialization.deserialize(next.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - } - - private T nextLocal() { - var next = nextLocal; - nextLocal = localIterator.hasNext() ? localIterator.next() : null; - return next.getFirst(); - } - - @Override - public void close() { - cursor.close(); - } - - @Override - public boolean hasNext() { - return nextRemote != null || nextLocal != null; - } - - @Override - public T next() { - if (nextRemote != null && nextLocal != null) { - var compare = comparator.compare(nextRemote.getData(), nextLocal.getSecond()); - return compare <= 0 ? nextRemote() : nextLocal(); - } else if (nextRemote != null) { - return nextRemote(); - } else if (nextLocal != null) { - return nextLocal(); - } else { - throw new NoSuchElementException(); - } - } - }; - } - - public U shutdownAll( - SubstateIndex index, - Function, U> mapper - ) { - try (var cursor = createRemoteSubstateCursor(index)) { - var localIterator = lowLevelBuilder.localUpSubstate().stream() - .map(LocalSubstate::getParticle) - .filter(index.getSubstateClass()::isInstance) - .map(p -> (T) p) - .filter(p -> index.test(serialization.serialize(p))) - .iterator(); - var remoteIterator = Iterators.transform(cursor, s -> (T) this.deserialize(s).getParticle()); - var result = mapper.apply(Iterators.concat(localIterator, remoteIterator)); - lowLevelBuilder.downIndex(index); - return result; - } - } - - public UInt256 downFungible( - SubstateIndex index, - Predicate particlePredicate, - UInt256 amount, - Function exceptionSupplier - ) throws X { - var spent = UInt256.ZERO; - for (var l : lowLevelBuilder.localUpSubstate()) { - var p = l.getParticle(); - if (!index.getSubstateClass().isInstance(p) || !particlePredicate.test((T) p)) { - continue; - } - var resource = (T) p; - - spent = spent.add(resource.getAmount()); - localDown(l.getIndex()); - - if (spent.compareTo(amount) >= 0) { - return spent.subtract(amount); - } - } - - try (var cursor = createRemoteSubstateCursor(index)) { - while (cursor.hasNext()) { - var raw = cursor.next(); - try { - var resource = (T) deserialization.deserialize(raw.getData()); - if (!particlePredicate.test(resource)) { - continue; - } - spent = spent.add(resource.getAmount()); - down(SubstateId.fromBytes(raw.getId())); - if (spent.compareTo(amount) >= 0) { - return spent.subtract(amount); - } - - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - } - } - - throw exceptionSupplier.apply(spent); - } - - public UInt256 getFeeReserve() { - return feeReservePut; - } - - public void putFeeReserve( - REAddr feePayer, - UInt256 amount, - Function exceptionSupplier - ) throws X { - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(feePayer.getBytes()); - var index = SubstateIndex.create(buf.array(), TokensInAccount.class); - // Take - var remainder = downFungible( - index, - p -> p.getResourceAddr().isNativeToken() && p.getHoldingAddr().equals(feePayer), - amount, - exceptionSupplier - ); - lowLevelBuilder.syscall(Syscall.FEE_RESERVE_PUT, amount); - if (!remainder.isZero()) { - up(new TokensInAccount(feePayer, REAddr.ofNativeToken(), remainder)); - } - this.feeReservePut = amount; - } - - public void takeFeeReserve( - REAddr addr, - UInt256 amount - ) { - lowLevelBuilder.syscall(Syscall.FEE_RESERVE_TAKE, amount); - if (!amount.isZero()) { - up(new TokensInAccount(addr, REAddr.ofNativeToken(), amount)); - } - this.feeReserveTake = this.feeReserveTake.add(amount); - } - - public TxBuilder mutex(ECPublicKey key, String id) { - final var addr = REAddr.ofHashedKey(key, id); - - lowLevelBuilder.syscall(Syscall.READDR_CLAIM, id.getBytes(StandardCharsets.UTF_8)); - downREAddr(addr); - end(); - - return this; - } - - public TxBuilder message(byte[] message) throws MessageTooLongException { - lowLevelBuilder.message(message); - return this; - } - - public Txn signAndBuild(Function signer) { - var hashToSign = lowLevelBuilder.hashToSign(); - return lowLevelBuilder.sig(signer.apply(hashToSign)).build(); - } - - public Txn buildWithoutSignature() { - return lowLevelBuilder.build(); - } - - public UnsignedTxnData buildForExternalSign() { - var put = Optional.ofNullable(feeReservePut).orElse(UInt256.ZERO); - var take = feeReserveTake; - if (put.compareTo(take) < 0) { - throw new IllegalStateException("Should not get to this state."); - } - var feesPaid = put.subtract(take); - - return new UnsignedTxnData(lowLevelBuilder.blob(), lowLevelBuilder.hashToSign(), feesPaid); - } + private static final Logger logger = LogManager.getLogger(); + private final TxLowLevelBuilder lowLevelBuilder; + private final SubstateStore remoteSubstate; + private final SubstateDeserialization deserialization; + private final SubstateSerialization serialization; + private UInt256 feeReservePut; + private UInt256 feeReserveTake = UInt256.ZERO; + private int numResourcesCreated = 0; + + private TxBuilder( + SubstateStore remoteSubstate, + SubstateDeserialization deserialization, + SubstateSerialization serialization) { + this.lowLevelBuilder = TxLowLevelBuilder.newBuilder(serialization); + this.remoteSubstate = remoteSubstate; + this.deserialization = deserialization; + this.serialization = serialization; + } + + public static TxBuilder newBuilder( + SubstateStore remoteSubstate, + SubstateDeserialization deserialization, + SubstateSerialization serialization) { + return new TxBuilder(remoteSubstate, deserialization, serialization); + } + + public static TxBuilder newBuilder( + SubstateDeserialization deserialization, SubstateSerialization serialization) { + return new TxBuilder(SubstateStore.empty(), deserialization, serialization); + } + + public TxLowLevelBuilder toLowLevelBuilder() { + return lowLevelBuilder; + } + + public void end() { + lowLevelBuilder.end(); + } + + public void up(Particle particle) { + lowLevelBuilder.up(particle); + if (particle instanceof TokenResource) { + numResourcesCreated++; + } + } + + public int getNumResourcesCreated() { + return numResourcesCreated; + } + + public void down(SubstateId substateId) { + lowLevelBuilder.down(substateId); + } + + private void localDown(int index) { + lowLevelBuilder.localDown(index); + } + + private void read(SubstateId substateId) { + lowLevelBuilder.read(substateId); + } + + private CloseableCursor createRemoteSubstateCursor(SubstateIndex index) { + return remoteSubstate + .openIndexedCursor(index) + .filter( + s -> !lowLevelBuilder.remoteDownSubstate().contains(SubstateId.fromBytes(s.getId()))); + } + + private CloseableCursor createRemoteSubstateCursor( + Class c) { + var b = deserialization.classToByte(c); + return createRemoteSubstateCursor(SubstateIndex.create(new byte[] {b}, c)); + } + + private static Stream iteratorToStream(Iterator iterator) { + return StreamSupport.stream( + Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false); + } + + private Substate deserialize(RawSubstateBytes rawSubstateBytes) { + try { + var raw = deserialization.deserialize(rawSubstateBytes.getData()); + return Substate.create(raw, SubstateId.fromBytes(rawSubstateBytes.getId())); + } catch (DeserializeException e) { + throw new IllegalStateException(e); + } + } + + // For mempool filler + public T downSubstate(Class particleClass, Predicate particlePredicate) + throws TxBuilderException { + var localSubstate = + lowLevelBuilder.localUpSubstate().stream() + .filter(s -> particleClass.isInstance(s.getParticle())) + .filter(s -> particlePredicate.test((T) s.getParticle())) + .findFirst(); + + if (localSubstate.isPresent()) { + localDown(localSubstate.get().getIndex()); + return (T) localSubstate.get().getParticle(); + } + + try (var cursor = createRemoteSubstateCursor(particleClass)) { + var substateRead = + iteratorToStream(cursor) + .map(this::deserialize) + .filter(s -> particlePredicate.test(particleClass.cast(s.getParticle()))) + .findFirst(); + + if (substateRead.isEmpty()) { + throw new NoSubstateFoundException(); + } + + down(substateRead.get().getId()); + + return (T) substateRead.get().getParticle(); + } + } + + public T findSystem(Class substateClass) { + var typeByte = deserialization.classToByte(substateClass); + var mapKey = SystemMapKey.ofSystem(typeByte); + var localMaybe = lowLevelBuilder.get(mapKey); + if (localMaybe.isPresent()) { + return (T) localMaybe.get().getParticle(); + } + return remoteSubstate + .get(mapKey) + .map( + rawSubstate -> { + try { + return (T) deserialization.deserialize(rawSubstate.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + }) + .orElseThrow(); + } + + public T find(Class substateClass, Object key) throws TxBuilderException { + var keyBytes = serialization.serializeKey(substateClass, key); + var typeByte = deserialization.classToByte(substateClass); + var mapKey = SystemMapKey.ofSystem(typeByte, keyBytes); + var localMaybe = lowLevelBuilder.get(mapKey); + if (localMaybe.isPresent()) { + return (T) localMaybe.get().getParticle(); + } + var raw = remoteSubstate.get(mapKey); + + if (raw.isPresent()) { + var rawSubstate = raw.get(); + try { + return (T) deserialization.deserialize(rawSubstate.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + } else { + return serialization.mapVirtual(substateClass, key); + } + } + + private void virtualReadDownInternal(byte typeByte, byte[] keyBytes, boolean down) { + var parentMapKey = SystemMapKey.ofSystem(typeByte); + var localParent = lowLevelBuilder.get(parentMapKey); + if (localParent.isPresent()) { + if (down) { + lowLevelBuilder.localVirtualDown(localParent.get().getIndex(), keyBytes); + } else { + lowLevelBuilder.localVirtualRead(localParent.get().getIndex(), keyBytes); + } + } else { + var parent = remoteSubstate.get(parentMapKey).orElseThrow(); + var substateId = SubstateId.fromBytes(parent.getId()); + if (down) { + lowLevelBuilder.virtualDown(substateId, keyBytes); + } else { + lowLevelBuilder.virtualRead(substateId, keyBytes); + } + } + } + + private T readDownInternal( + Class substateClass, Object key, boolean down) { + var keyBytes = serialization.serializeKey(substateClass, key); + var typeByte = deserialization.classToByte(substateClass); + var mapKey = SystemMapKey.ofSystem(typeByte, keyBytes); + var localMaybe = lowLevelBuilder.get(mapKey); + if (localMaybe.isPresent()) { + var local = localMaybe.get(); + if (down) { + lowLevelBuilder.localDown(local.getIndex()); + } else { + lowLevelBuilder.localRead(local.getIndex()); + } + return (T) local.getParticle(); + } + + var raw = remoteSubstate.get(mapKey); + + if (raw.isPresent()) { + var rawSubstate = raw.get(); + if (down) { + down(SubstateId.fromBytes(rawSubstate.getId())); + } else { + read(SubstateId.fromBytes(rawSubstate.getId())); + } + try { + return (T) deserialization.deserialize(rawSubstate.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + } else { + this.virtualReadDownInternal(typeByte, keyBytes, down); + return serialization.mapVirtual(substateClass, key); + } + } + + public T down(Class substateClass, Object key) { + return readDownInternal(substateClass, key, true); + } + + public T read(Class substateClass, Object key) { + return readDownInternal(substateClass, key, false); + } + + public T downSystem(Class substateClass) { + var typeByte = deserialization.classToByte(substateClass); + var mapKey = SystemMapKey.ofSystem(typeByte); + var localMaybe = lowLevelBuilder.get(mapKey); + if (localMaybe.isPresent()) { + var local = localMaybe.get(); + lowLevelBuilder.localDown(local.getIndex()); + return (T) local.getParticle(); + } + + var rawSubstate = remoteSubstate.get(mapKey).orElseThrow(); + down(SubstateId.fromBytes(rawSubstate.getId())); + try { + return (T) deserialization.deserialize(rawSubstate.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + } + + public T readSystem(Class substateClass) { + var typeByte = deserialization.classToByte(substateClass); + var mapKey = SystemMapKey.ofSystem(typeByte); + var localMaybe = lowLevelBuilder.get(mapKey); + if (localMaybe.isPresent()) { + var local = localMaybe.get(); + lowLevelBuilder.localRead(local.getIndex()); + return (T) local.getParticle(); + } + + var rawSubstate = remoteSubstate.get(mapKey).orElseThrow(); + read(SubstateId.fromBytes(rawSubstate.getId())); + try { + return (T) deserialization.deserialize(rawSubstate.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + } + + public UnclaimedREAddr downREAddr(REAddr addr) { + var keyBytes = serialization.serializeKey(UnclaimedREAddr.class, addr); + var typeByte = deserialization.classToByte(UnclaimedREAddr.class); + var mapKey = SystemMapKey.ofSystem(typeByte); + var localParent = lowLevelBuilder.get(mapKey); + if (localParent.isPresent()) { + lowLevelBuilder.localVirtualDown(localParent.get().getIndex(), keyBytes); + } else { + var parent = remoteSubstate.get(mapKey).orElseThrow(); + var substateId = SubstateId.fromBytes(parent.getId()); + lowLevelBuilder.virtualDown(substateId, keyBytes); + } + return serialization.mapVirtual(UnclaimedREAddr.class, addr); + } + + public U shutdownAll( + Class particleClass, Function, U> mapper) { + var typeByte = deserialization.classToByte(particleClass); + return shutdownAll(SubstateIndex.create(typeByte, particleClass), mapper); + } + + // FIXME: programmedInTxn is just a hack + public CloseableCursor readIndex( + SubstateIndex index, boolean programmedInTxn) { + var comparator = UnsignedBytes.lexicographicalComparator().reversed(); + var cursor = createRemoteSubstateCursor(index); + var localIterator = + lowLevelBuilder.localUpSubstate().stream() + .map(LocalSubstate::getParticle) + .filter(index.getSubstateClass()::isInstance) + .map(p -> (T) p) + .map(p -> Pair.of(p, serialization.serialize(p))) + .filter(p -> index.test(p.getSecond())) + .sorted(Comparator.comparing(Pair::getSecond, comparator)) + .iterator(); + + if (programmedInTxn) { + lowLevelBuilder.readIndex(index); + } + + return new CloseableCursor() { + private RawSubstateBytes nextRemote = cursor.hasNext() ? cursor.next() : null; + private Pair nextLocal = localIterator.hasNext() ? localIterator.next() : null; + + private T nextRemote() { + var next = nextRemote; + nextRemote = cursor.hasNext() ? cursor.next() : null; + try { + return (T) deserialization.deserialize(next.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + } + + private T nextLocal() { + var next = nextLocal; + nextLocal = localIterator.hasNext() ? localIterator.next() : null; + return next.getFirst(); + } + + @Override + public void close() { + cursor.close(); + } + + @Override + public boolean hasNext() { + return nextRemote != null || nextLocal != null; + } + + @Override + public T next() { + if (nextRemote != null && nextLocal != null) { + var compare = comparator.compare(nextRemote.getData(), nextLocal.getSecond()); + return compare <= 0 ? nextRemote() : nextLocal(); + } else if (nextRemote != null) { + return nextRemote(); + } else if (nextLocal != null) { + return nextLocal(); + } else { + throw new NoSuchElementException(); + } + } + }; + } + + public U shutdownAll( + SubstateIndex index, Function, U> mapper) { + try (var cursor = createRemoteSubstateCursor(index)) { + var localIterator = + lowLevelBuilder.localUpSubstate().stream() + .map(LocalSubstate::getParticle) + .filter(index.getSubstateClass()::isInstance) + .map(p -> (T) p) + .filter(p -> index.test(serialization.serialize(p))) + .iterator(); + var remoteIterator = Iterators.transform(cursor, s -> (T) this.deserialize(s).getParticle()); + var result = mapper.apply(Iterators.concat(localIterator, remoteIterator)); + lowLevelBuilder.downIndex(index); + return result; + } + } + + public UInt256 downFungible( + SubstateIndex index, + Predicate particlePredicate, + UInt256 amount, + Function exceptionSupplier) + throws X { + var spent = UInt256.ZERO; + for (var l : lowLevelBuilder.localUpSubstate()) { + var p = l.getParticle(); + if (!index.getSubstateClass().isInstance(p) || !particlePredicate.test((T) p)) { + continue; + } + var resource = (T) p; + + spent = spent.add(resource.getAmount()); + localDown(l.getIndex()); + + if (spent.compareTo(amount) >= 0) { + return spent.subtract(amount); + } + } + + try (var cursor = createRemoteSubstateCursor(index)) { + while (cursor.hasNext()) { + var raw = cursor.next(); + try { + var resource = (T) deserialization.deserialize(raw.getData()); + if (!particlePredicate.test(resource)) { + continue; + } + spent = spent.add(resource.getAmount()); + down(SubstateId.fromBytes(raw.getId())); + if (spent.compareTo(amount) >= 0) { + return spent.subtract(amount); + } + + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + } + } + + throw exceptionSupplier.apply(spent); + } + + public UInt256 getFeeReserve() { + return feeReservePut; + } + + public void putFeeReserve( + REAddr feePayer, UInt256 amount, Function exceptionSupplier) throws X { + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(feePayer.getBytes()); + var index = SubstateIndex.create(buf.array(), TokensInAccount.class); + // Take + var remainder = + downFungible( + index, + p -> p.getResourceAddr().isNativeToken() && p.getHoldingAddr().equals(feePayer), + amount, + exceptionSupplier); + lowLevelBuilder.syscall(Syscall.FEE_RESERVE_PUT, amount); + if (!remainder.isZero()) { + up(new TokensInAccount(feePayer, REAddr.ofNativeToken(), remainder)); + } + this.feeReservePut = amount; + } + + public void takeFeeReserve(REAddr addr, UInt256 amount) { + lowLevelBuilder.syscall(Syscall.FEE_RESERVE_TAKE, amount); + if (!amount.isZero()) { + up(new TokensInAccount(addr, REAddr.ofNativeToken(), amount)); + } + this.feeReserveTake = this.feeReserveTake.add(amount); + } + + public TxBuilder mutex(ECPublicKey key, String id) { + final var addr = REAddr.ofHashedKey(key, id); + + lowLevelBuilder.syscall(Syscall.READDR_CLAIM, id.getBytes(StandardCharsets.UTF_8)); + downREAddr(addr); + end(); + + return this; + } + + public TxBuilder message(byte[] message) throws MessageTooLongException { + lowLevelBuilder.message(message); + return this; + } + + public Txn signAndBuild(Function signer) { + var hashToSign = lowLevelBuilder.hashToSign(); + return lowLevelBuilder.sig(signer.apply(hashToSign)).build(); + } + + public Txn buildWithoutSignature() { + return lowLevelBuilder.build(); + } + + public UnsignedTxnData buildForExternalSign() { + var put = Optional.ofNullable(feeReservePut).orElse(UInt256.ZERO); + var take = feeReserveTake; + if (put.compareTo(take) < 0) { + throw new IllegalStateException("Should not get to this state."); + } + var feesPaid = put.subtract(take); + + return new UnsignedTxnData(lowLevelBuilder.blob(), lowLevelBuilder.hashToSign(), feesPaid); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilderException.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilderException.java index 6721226d8c..b7e205d278 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilderException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxBuilderException.java @@ -64,11 +64,9 @@ package com.radixdlt.atom; -/** - * Exception which occurs when trying to build a transaction - */ +/** Exception which occurs when trying to build a transaction */ public abstract class TxBuilderException extends Exception { - protected TxBuilderException(String message) { - super(message); - } + protected TxBuilderException(String message) { + super(message); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxLowLevelBuilder.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxLowLevelBuilder.java index 50a0c89bf7..81fed3f67f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxLowLevelBuilder.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxLowLevelBuilder.java @@ -69,8 +69,8 @@ import com.radixdlt.application.system.state.SystemData; import com.radixdlt.application.system.state.VirtualParent; import com.radixdlt.application.validators.state.ValidatorData; -import com.radixdlt.constraintmachine.REInstruction; import com.radixdlt.constraintmachine.Particle; +import com.radixdlt.constraintmachine.REInstruction; import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SubstateSerialization; import com.radixdlt.constraintmachine.SystemMapKey; @@ -78,7 +78,6 @@ import com.radixdlt.crypto.HashUtils; import com.radixdlt.utils.Shorts; import com.radixdlt.utils.UInt256; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -92,269 +91,268 @@ import java.util.Optional; import java.util.Set; -/** - * Low level builder class for transactions - */ +/** Low level builder class for transactions */ public final class TxLowLevelBuilder { - private final ByteArrayOutputStream blobStream; - private final Map localMapValues = new HashMap<>(); - private final Map localUpParticles = new HashMap<>(); - private final Set remoteDownSubstate = new HashSet<>(); - private final SubstateSerialization serialization; - private int upParticleCount = 0; - - TxLowLevelBuilder(SubstateSerialization serialization, ByteArrayOutputStream blobStream) { - this.serialization = serialization; - this.blobStream = blobStream; - } - - public static TxLowLevelBuilder newBuilder(byte[] blob) { - var blobStream = new ByteArrayOutputStream(); - try { - blobStream.write(blob); - } catch (IOException e) { - throw new IllegalStateException("Unable to write data.", e); - } - // TODO: Cleanup null serialization, but works for now as only used for client side signing - return new TxLowLevelBuilder(null, blobStream); - } - - public static TxLowLevelBuilder newBuilder(SubstateSerialization serialization) { - return new TxLowLevelBuilder(serialization, new ByteArrayOutputStream()); - } - - public Set remoteDownSubstate() { - return remoteDownSubstate; - } - - public Optional get(SystemMapKey mapKey) { - return Optional.ofNullable(localMapValues.get(mapKey)); - } - - public List localUpSubstate() { - return new ArrayList<>(localUpParticles.values()); - } - - // TODO: Remove array copies - private byte[] varLengthData(byte[] bytes) { - if (bytes.length > 255) { - throw new IllegalArgumentException("Data length is " + bytes.length + " but must be <= " + 255); - } - var data = new byte[Short.BYTES + bytes.length]; - data[0] = 0; - data[1] = (byte) bytes.length; - System.arraycopy(bytes, 0, data, 2, bytes.length); - return data; - } - - private void instruction(REInstruction.REMicroOp op, ByteBuffer buffer) { - blobStream.write(op.opCode()); - blobStream.write(buffer.array(), buffer.position(), buffer.remaining()); - } - - public void instruction(REInstruction.REMicroOp op, byte[] data) { - blobStream.write(op.opCode()); - try { - blobStream.write(data); - } catch (IOException e) { - throw new IllegalStateException("Unable to write data.", e); - } - } - - public TxLowLevelBuilder message(byte[] bytes) throws MessageTooLongException { - if (bytes.length > 255) { - throw new MessageTooLongException(bytes.length); - } - - instruction(REInstruction.REMicroOp.MSG, varLengthData(bytes)); - return this; - } - - public TxLowLevelBuilder message(String message) throws MessageTooLongException { - var bytes = message.getBytes(StandardCharsets.UTF_8); - return message(bytes); - } - - public TxLowLevelBuilder up(Particle particle) { - Objects.requireNonNull(particle, "particle is required"); - - var localSubstate = LocalSubstate.create(upParticleCount, particle); - - if (particle instanceof ValidatorData) { - var p = (ValidatorData) particle; - var b = serialization.classToByte(p.getClass()); - var k = SystemMapKey.ofSystem(b, p.getValidatorKey().getCompressedBytes()); - this.localMapValues.put(k, localSubstate); - } else if (particle instanceof SystemData) { - var b = serialization.classToByte(particle.getClass()); - var k = SystemMapKey.ofSystem(b); - this.localMapValues.put(k, localSubstate); - } else if (particle instanceof VirtualParent) { - var p = (VirtualParent) particle; - var typeByte = p.getData()[0]; - var k = SystemMapKey.ofSystem(typeByte); - this.localMapValues.put(k, localSubstate); - } - - this.localUpParticles.put(upParticleCount, localSubstate); - - var buf = ByteBuffer.allocate(1024); - buf.putShort((short) 0); - serialization.serialize(particle, buf); - var limit = buf.position(); - buf.putShort(0, (short) (limit - 2)); - buf.position(0); - buf.limit(limit); - instruction(REInstruction.REMicroOp.UP, buf); - upParticleCount++; - return this; - } - - public TxLowLevelBuilder localVirtualDown(int index, byte[] virtualKey) { - if (virtualKey.length > 128) { - throw new IllegalStateException(); - } - var buf = ByteBuffer.allocate(Short.BYTES + Short.BYTES + virtualKey.length); - buf.putShort((short) (virtualKey.length + Short.BYTES)); - buf.putShort((short) index); - buf.put(virtualKey); - instruction(REInstruction.REMicroOp.LVDOWN, buf.array()); - return this; - } - - public TxLowLevelBuilder localVirtualRead(int index, byte[] virtualKey) { - if (virtualKey.length > 128) { - throw new IllegalStateException(); - } - var buf = ByteBuffer.allocate(Short.BYTES + Short.BYTES + virtualKey.length); - buf.putShort((short) (virtualKey.length + Short.BYTES)); - buf.putShort((short) index); - buf.put(virtualKey); - instruction(REInstruction.REMicroOp.LVREAD, buf.array()); - return this; - } - - public TxLowLevelBuilder virtualDown(SubstateId parent, byte[] virtualKey) { - if (virtualKey.length > 128) { - throw new IllegalStateException(); - } - var id = SubstateId.ofVirtualSubstate(parent, virtualKey); - var buf = ByteBuffer.allocate(Short.BYTES + id.asBytes().length); - buf.putShort((short) id.asBytes().length); - buf.put(id.asBytes()); - instruction(REInstruction.REMicroOp.VDOWN, buf.array()); - return this; - } - - public TxLowLevelBuilder virtualRead(SubstateId parent, byte[] virtualKey) { - if (virtualKey.length > 128) { - throw new IllegalStateException(); - } - var id = SubstateId.ofVirtualSubstate(parent, virtualKey); - var buf = ByteBuffer.allocate(Short.BYTES + id.asBytes().length); - buf.putShort((short) id.asBytes().length); - buf.put(id.asBytes()); - instruction(REInstruction.REMicroOp.VREAD, buf.array()); - return this; - } - - public TxLowLevelBuilder localRead(int index) { - var particle = localUpParticles.get(index); - if (particle == null) { - throw new IllegalStateException("Local particle does not exist: " + index); - } - instruction(REInstruction.REMicroOp.LREAD, Shorts.toByteArray((short) index)); - return this; - } - - public TxLowLevelBuilder read(SubstateId substateId) { - instruction(REInstruction.REMicroOp.READ, substateId.asBytes()); - return this; - } - - public TxLowLevelBuilder localDown(int index) { - var particle = localUpParticles.remove(index); - if (particle == null) { - throw new IllegalStateException("Local particle does not exist: " + index); - } - instruction(REInstruction.REMicroOp.LDOWN, Shorts.toByteArray((short) index)); - return this; - } - - public TxLowLevelBuilder down(SubstateId substateId) { - instruction(REInstruction.REMicroOp.DOWN, substateId.asBytes()); - this.remoteDownSubstate.add(substateId); - return this; - } - - public TxLowLevelBuilder readIndex(SubstateIndex index) { - var buf = ByteBuffer.allocate(Short.BYTES + index.getPrefix().length); - buf.putShort((short) index.getPrefix().length); - buf.put(index.getPrefix()); - instruction(REInstruction.REMicroOp.READINDEX, buf.array()); - return this; - } - - public TxLowLevelBuilder downIndex(SubstateIndex index) { - var buf = ByteBuffer.allocate(Short.BYTES + index.getPrefix().length); - buf.putShort((short) index.getPrefix().length); - buf.put(index.getPrefix()); - instruction(REInstruction.REMicroOp.DOWNINDEX, buf.array()); - return this; - } - - public TxLowLevelBuilder end() { - instruction(REInstruction.REMicroOp.END, new byte[0]); - return this; - } - - public TxLowLevelBuilder syscall(Syscall syscall, byte[] bytes) { - if (bytes.length < 1 || bytes.length > 32) { - throw new IllegalStateException("Length must be >= 1 and <= 32 but was " + bytes.length); - } - var data = new byte[Short.BYTES + 1 + bytes.length]; - data[0] = 0; - data[1] = (byte) (bytes.length + 1); - data[2] = syscall.id(); - System.arraycopy(bytes, 0, data, 3, bytes.length); - instruction(REInstruction.REMicroOp.SYSCALL, data); - return this; - } - - public TxLowLevelBuilder syscall(Syscall syscall, UInt256 amount) { - var data = new byte[Short.BYTES + 1 + UInt256.BYTES]; - data[0] = 0; - data[1] = UInt256.BYTES + 1; - data[2] = syscall.id(); - amount.toByteArray(data, 3); - instruction(REInstruction.REMicroOp.SYSCALL, data); - return this; - } - - public TxLowLevelBuilder disableResourceAllocAndDestroy() { - var data = new byte[] {0, 1}; - instruction(REInstruction.REMicroOp.HEADER, data); - return this; - } - - public int size() { - return blobStream.size(); - } - - public byte[] blob() { - return blobStream.toByteArray(); - } - - public HashCode hashToSign() { - return HashUtils.sha256(blob()); // this is a double hash - } - - public TxLowLevelBuilder sig(ECDSASignature signature) { - instruction(REInstruction.REMicroOp.SIG, REFieldSerialization.serializeSignature(signature)); - return this; - } - - public Txn build() { - return Txn.create(blobStream.toByteArray()); - } -} \ No newline at end of file + private final ByteArrayOutputStream blobStream; + private final Map localMapValues = new HashMap<>(); + private final Map localUpParticles = new HashMap<>(); + private final Set remoteDownSubstate = new HashSet<>(); + private final SubstateSerialization serialization; + private int upParticleCount = 0; + + TxLowLevelBuilder(SubstateSerialization serialization, ByteArrayOutputStream blobStream) { + this.serialization = serialization; + this.blobStream = blobStream; + } + + public static TxLowLevelBuilder newBuilder(byte[] blob) { + var blobStream = new ByteArrayOutputStream(); + try { + blobStream.write(blob); + } catch (IOException e) { + throw new IllegalStateException("Unable to write data.", e); + } + // TODO: Cleanup null serialization, but works for now as only used for client side signing + return new TxLowLevelBuilder(null, blobStream); + } + + public static TxLowLevelBuilder newBuilder(SubstateSerialization serialization) { + return new TxLowLevelBuilder(serialization, new ByteArrayOutputStream()); + } + + public Set remoteDownSubstate() { + return remoteDownSubstate; + } + + public Optional get(SystemMapKey mapKey) { + return Optional.ofNullable(localMapValues.get(mapKey)); + } + + public List localUpSubstate() { + return new ArrayList<>(localUpParticles.values()); + } + + // TODO: Remove array copies + private byte[] varLengthData(byte[] bytes) { + if (bytes.length > 255) { + throw new IllegalArgumentException( + "Data length is " + bytes.length + " but must be <= " + 255); + } + var data = new byte[Short.BYTES + bytes.length]; + data[0] = 0; + data[1] = (byte) bytes.length; + System.arraycopy(bytes, 0, data, 2, bytes.length); + return data; + } + + private void instruction(REInstruction.REMicroOp op, ByteBuffer buffer) { + blobStream.write(op.opCode()); + blobStream.write(buffer.array(), buffer.position(), buffer.remaining()); + } + + public void instruction(REInstruction.REMicroOp op, byte[] data) { + blobStream.write(op.opCode()); + try { + blobStream.write(data); + } catch (IOException e) { + throw new IllegalStateException("Unable to write data.", e); + } + } + + public TxLowLevelBuilder message(byte[] bytes) throws MessageTooLongException { + if (bytes.length > 255) { + throw new MessageTooLongException(bytes.length); + } + + instruction(REInstruction.REMicroOp.MSG, varLengthData(bytes)); + return this; + } + + public TxLowLevelBuilder message(String message) throws MessageTooLongException { + var bytes = message.getBytes(StandardCharsets.UTF_8); + return message(bytes); + } + + public TxLowLevelBuilder up(Particle particle) { + Objects.requireNonNull(particle, "particle is required"); + + var localSubstate = LocalSubstate.create(upParticleCount, particle); + + if (particle instanceof ValidatorData) { + var p = (ValidatorData) particle; + var b = serialization.classToByte(p.getClass()); + var k = SystemMapKey.ofSystem(b, p.getValidatorKey().getCompressedBytes()); + this.localMapValues.put(k, localSubstate); + } else if (particle instanceof SystemData) { + var b = serialization.classToByte(particle.getClass()); + var k = SystemMapKey.ofSystem(b); + this.localMapValues.put(k, localSubstate); + } else if (particle instanceof VirtualParent) { + var p = (VirtualParent) particle; + var typeByte = p.getData()[0]; + var k = SystemMapKey.ofSystem(typeByte); + this.localMapValues.put(k, localSubstate); + } + + this.localUpParticles.put(upParticleCount, localSubstate); + + var buf = ByteBuffer.allocate(1024); + buf.putShort((short) 0); + serialization.serialize(particle, buf); + var limit = buf.position(); + buf.putShort(0, (short) (limit - 2)); + buf.position(0); + buf.limit(limit); + instruction(REInstruction.REMicroOp.UP, buf); + upParticleCount++; + return this; + } + + public TxLowLevelBuilder localVirtualDown(int index, byte[] virtualKey) { + if (virtualKey.length > 128) { + throw new IllegalStateException(); + } + var buf = ByteBuffer.allocate(Short.BYTES + Short.BYTES + virtualKey.length); + buf.putShort((short) (virtualKey.length + Short.BYTES)); + buf.putShort((short) index); + buf.put(virtualKey); + instruction(REInstruction.REMicroOp.LVDOWN, buf.array()); + return this; + } + + public TxLowLevelBuilder localVirtualRead(int index, byte[] virtualKey) { + if (virtualKey.length > 128) { + throw new IllegalStateException(); + } + var buf = ByteBuffer.allocate(Short.BYTES + Short.BYTES + virtualKey.length); + buf.putShort((short) (virtualKey.length + Short.BYTES)); + buf.putShort((short) index); + buf.put(virtualKey); + instruction(REInstruction.REMicroOp.LVREAD, buf.array()); + return this; + } + + public TxLowLevelBuilder virtualDown(SubstateId parent, byte[] virtualKey) { + if (virtualKey.length > 128) { + throw new IllegalStateException(); + } + var id = SubstateId.ofVirtualSubstate(parent, virtualKey); + var buf = ByteBuffer.allocate(Short.BYTES + id.asBytes().length); + buf.putShort((short) id.asBytes().length); + buf.put(id.asBytes()); + instruction(REInstruction.REMicroOp.VDOWN, buf.array()); + return this; + } + + public TxLowLevelBuilder virtualRead(SubstateId parent, byte[] virtualKey) { + if (virtualKey.length > 128) { + throw new IllegalStateException(); + } + var id = SubstateId.ofVirtualSubstate(parent, virtualKey); + var buf = ByteBuffer.allocate(Short.BYTES + id.asBytes().length); + buf.putShort((short) id.asBytes().length); + buf.put(id.asBytes()); + instruction(REInstruction.REMicroOp.VREAD, buf.array()); + return this; + } + + public TxLowLevelBuilder localRead(int index) { + var particle = localUpParticles.get(index); + if (particle == null) { + throw new IllegalStateException("Local particle does not exist: " + index); + } + instruction(REInstruction.REMicroOp.LREAD, Shorts.toByteArray((short) index)); + return this; + } + + public TxLowLevelBuilder read(SubstateId substateId) { + instruction(REInstruction.REMicroOp.READ, substateId.asBytes()); + return this; + } + + public TxLowLevelBuilder localDown(int index) { + var particle = localUpParticles.remove(index); + if (particle == null) { + throw new IllegalStateException("Local particle does not exist: " + index); + } + instruction(REInstruction.REMicroOp.LDOWN, Shorts.toByteArray((short) index)); + return this; + } + + public TxLowLevelBuilder down(SubstateId substateId) { + instruction(REInstruction.REMicroOp.DOWN, substateId.asBytes()); + this.remoteDownSubstate.add(substateId); + return this; + } + + public TxLowLevelBuilder readIndex(SubstateIndex index) { + var buf = ByteBuffer.allocate(Short.BYTES + index.getPrefix().length); + buf.putShort((short) index.getPrefix().length); + buf.put(index.getPrefix()); + instruction(REInstruction.REMicroOp.READINDEX, buf.array()); + return this; + } + + public TxLowLevelBuilder downIndex(SubstateIndex index) { + var buf = ByteBuffer.allocate(Short.BYTES + index.getPrefix().length); + buf.putShort((short) index.getPrefix().length); + buf.put(index.getPrefix()); + instruction(REInstruction.REMicroOp.DOWNINDEX, buf.array()); + return this; + } + + public TxLowLevelBuilder end() { + instruction(REInstruction.REMicroOp.END, new byte[0]); + return this; + } + + public TxLowLevelBuilder syscall(Syscall syscall, byte[] bytes) { + if (bytes.length < 1 || bytes.length > 32) { + throw new IllegalStateException("Length must be >= 1 and <= 32 but was " + bytes.length); + } + var data = new byte[Short.BYTES + 1 + bytes.length]; + data[0] = 0; + data[1] = (byte) (bytes.length + 1); + data[2] = syscall.id(); + System.arraycopy(bytes, 0, data, 3, bytes.length); + instruction(REInstruction.REMicroOp.SYSCALL, data); + return this; + } + + public TxLowLevelBuilder syscall(Syscall syscall, UInt256 amount) { + var data = new byte[Short.BYTES + 1 + UInt256.BYTES]; + data[0] = 0; + data[1] = UInt256.BYTES + 1; + data[2] = syscall.id(); + amount.toByteArray(data, 3); + instruction(REInstruction.REMicroOp.SYSCALL, data); + return this; + } + + public TxLowLevelBuilder disableResourceAllocAndDestroy() { + var data = new byte[] {0, 1}; + instruction(REInstruction.REMicroOp.HEADER, data); + return this; + } + + public int size() { + return blobStream.size(); + } + + public byte[] blob() { + return blobStream.toByteArray(); + } + + public HashCode hashToSign() { + return HashUtils.sha256(blob()); // this is a double hash + } + + public TxLowLevelBuilder sig(ECDSASignature signature) { + instruction(REInstruction.REMicroOp.SIG, REFieldSerialization.serializeSignature(signature)); + return this; + } + + public Txn build() { + return Txn.create(blobStream.toByteArray()); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxValidatorAction.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxValidatorAction.java index 0a51d9dfa4..1623b997a1 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxValidatorAction.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxValidatorAction.java @@ -66,9 +66,7 @@ import com.radixdlt.crypto.ECPublicKey; -/** - * Marker interface for actions containing validator key. - */ +/** Marker interface for actions containing validator key. */ public interface TxValidatorAction extends TxAction { - ECPublicKey validatorKey(); + ECPublicKey validatorKey(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/Txn.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/Txn.java index 9bdf9430cc..31a94ad235 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/Txn.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/Txn.java @@ -68,49 +68,48 @@ import com.fasterxml.jackson.annotation.JsonValue; import com.radixdlt.crypto.HashUtils; import com.radixdlt.identifiers.AID; - import java.util.Objects; public final class Txn { - private final byte[] payload; - private final AID id; + private final byte[] payload; + private final AID id; - private Txn(byte[] payload) { - this.payload = Objects.requireNonNull(payload); - this.id = AID.from(HashUtils.transactionIdHash(payload).asBytes()); - } + private Txn(byte[] payload) { + this.payload = Objects.requireNonNull(payload); + this.id = AID.from(HashUtils.transactionIdHash(payload).asBytes()); + } - @JsonCreator - public static Txn create(byte[] payload) { - return new Txn(payload); - } + @JsonCreator + public static Txn create(byte[] payload) { + return new Txn(payload); + } - public AID getId() { - return id; - } + public AID getId() { + return id; + } - @JsonValue - public byte[] getPayload() { - return payload; - } + @JsonValue + public byte[] getPayload() { + return payload; + } - @Override - public int hashCode() { - return Objects.hash(id); - } + @Override + public int hashCode() { + return Objects.hash(id); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof Txn)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof Txn)) { + return false; + } - Txn other = (Txn) o; - return Objects.equals(this.id, other.id); - } + Txn other = (Txn) o; + return Objects.equals(this.id, other.id); + } - @Override - public String toString() { - return String.format("%s{id=%s}", this.getClass().getSimpleName(), this.id); - } + @Override + public String toString() { + return String.format("%s{id=%s}", this.getClass().getSimpleName(), this.id); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxnConstructionRequest.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxnConstructionRequest.java index 4d40a3cbc7..64e3987d92 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxnConstructionRequest.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxnConstructionRequest.java @@ -66,8 +66,8 @@ import com.radixdlt.atom.actions.BurnToken; import com.radixdlt.atom.actions.CreateMutableToken; -import com.radixdlt.atom.actions.MintToken; import com.radixdlt.atom.actions.FeeReservePut; +import com.radixdlt.atom.actions.MintToken; import com.radixdlt.atom.actions.RegisterValidator; import com.radixdlt.atom.actions.SplitToken; import com.radixdlt.atom.actions.TransferToken; @@ -75,125 +75,123 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; public class TxnConstructionRequest { - private boolean disableResourceAllocAndDestroy = false; - private final List actions = new ArrayList<>(); - private byte[] msg = null; - private Set toAvoid; - private REAddr feePayer; - - private TxnConstructionRequest() { - } - - public static TxnConstructionRequest create() { - return new TxnConstructionRequest(); - } - - public TxnConstructionRequest feePayer(REAddr feePayer) { - this.feePayer = feePayer; - return this; - } - - public Optional getFeePayer() { - return Optional.ofNullable(feePayer); - } - - public Optional getMsg() { - return Optional.ofNullable(msg); - } - - public TxnConstructionRequest msg(byte[] msg) { - this.msg = msg; - return this; - } - - public boolean isDisableResourceAllocAndDestroy() { - return disableResourceAllocAndDestroy; - } - - public TxnConstructionRequest disableResourceAllocAndDestroy() { - this.disableResourceAllocAndDestroy = true; - return this; - } - - public TxnConstructionRequest action(TxAction txAction) { - actions.add(txAction); - return this; - } - - public TxnConstructionRequest actions(List actions) { - this.actions.addAll(actions); - return this; - } - - public TxnConstructionRequest createMutableToken(MutableTokenDefinition def) { - actions.add(new CreateMutableToken(def)); - return this; - } - - public TxnConstructionRequest registerAsValidator(ECPublicKey validatorKey) { - var action = new RegisterValidator(validatorKey); - actions.add(action); - return this; - } - - public TxnConstructionRequest unregisterAsValidator(ECPublicKey validatorKey) { - var action = new UnregisterValidator(validatorKey); - actions.add(action); - return this; - } - - public TxnConstructionRequest splitNative(REAddr rri, REAddr userAcct, UInt256 minSize) { - var action = new SplitToken(rri, userAcct, minSize); - actions.add(action); - return this; - } - - public TxnConstructionRequest transfer(REAddr rri, REAddr from, REAddr to, UInt256 amount) { - var action = new TransferToken(rri, from, to, amount); - actions.add(action); - return this; - } - - public TxnConstructionRequest mint(REAddr rri, REAddr to, UInt256 amount) { - var action = new MintToken(rri, to, amount); - actions.add(action); - return this; - } - - public TxnConstructionRequest payFee(REAddr from, UInt256 amount) { - var action = new FeeReservePut(from, amount); - actions.add(action); - return this; - } - - public TxnConstructionRequest burn(REAddr rri, REAddr from, UInt256 amount) { - var action = new BurnToken(rri, from, amount); - actions.add(action); - return this; - } - - public TxnConstructionRequest avoidSubstates(Set toAvoid) { - this.toAvoid = toAvoid; - return this; - } - - public Set getSubstatesToAvoid() { - return this.toAvoid == null ? Set.of() : this.toAvoid; - } - - public List getActions() { - return actions; - } - - @Override - public String toString() { - return String.format("%s{actions=%s}", this.getClass().getSimpleName(), actions); - } -} \ No newline at end of file + private boolean disableResourceAllocAndDestroy = false; + private final List actions = new ArrayList<>(); + private byte[] msg = null; + private Set toAvoid; + private REAddr feePayer; + + private TxnConstructionRequest() {} + + public static TxnConstructionRequest create() { + return new TxnConstructionRequest(); + } + + public TxnConstructionRequest feePayer(REAddr feePayer) { + this.feePayer = feePayer; + return this; + } + + public Optional getFeePayer() { + return Optional.ofNullable(feePayer); + } + + public Optional getMsg() { + return Optional.ofNullable(msg); + } + + public TxnConstructionRequest msg(byte[] msg) { + this.msg = msg; + return this; + } + + public boolean isDisableResourceAllocAndDestroy() { + return disableResourceAllocAndDestroy; + } + + public TxnConstructionRequest disableResourceAllocAndDestroy() { + this.disableResourceAllocAndDestroy = true; + return this; + } + + public TxnConstructionRequest action(TxAction txAction) { + actions.add(txAction); + return this; + } + + public TxnConstructionRequest actions(List actions) { + this.actions.addAll(actions); + return this; + } + + public TxnConstructionRequest createMutableToken(MutableTokenDefinition def) { + actions.add(new CreateMutableToken(def)); + return this; + } + + public TxnConstructionRequest registerAsValidator(ECPublicKey validatorKey) { + var action = new RegisterValidator(validatorKey); + actions.add(action); + return this; + } + + public TxnConstructionRequest unregisterAsValidator(ECPublicKey validatorKey) { + var action = new UnregisterValidator(validatorKey); + actions.add(action); + return this; + } + + public TxnConstructionRequest splitNative(REAddr rri, REAddr userAcct, UInt256 minSize) { + var action = new SplitToken(rri, userAcct, minSize); + actions.add(action); + return this; + } + + public TxnConstructionRequest transfer(REAddr rri, REAddr from, REAddr to, UInt256 amount) { + var action = new TransferToken(rri, from, to, amount); + actions.add(action); + return this; + } + + public TxnConstructionRequest mint(REAddr rri, REAddr to, UInt256 amount) { + var action = new MintToken(rri, to, amount); + actions.add(action); + return this; + } + + public TxnConstructionRequest payFee(REAddr from, UInt256 amount) { + var action = new FeeReservePut(from, amount); + actions.add(action); + return this; + } + + public TxnConstructionRequest burn(REAddr rri, REAddr from, UInt256 amount) { + var action = new BurnToken(rri, from, amount); + actions.add(action); + return this; + } + + public TxnConstructionRequest avoidSubstates(Set toAvoid) { + this.toAvoid = toAvoid; + return this; + } + + public Set getSubstatesToAvoid() { + return this.toAvoid == null ? Set.of() : this.toAvoid; + } + + public List getActions() { + return actions; + } + + @Override + public String toString() { + return String.format("%s{actions=%s}", this.getClass().getSimpleName(), actions); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/UnsignedTxnData.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/UnsignedTxnData.java index 3088cba586..8891d87f38 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/UnsignedTxnData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/UnsignedTxnData.java @@ -68,25 +68,25 @@ import com.radixdlt.utils.UInt256; public final class UnsignedTxnData { - private final byte[] blob; - private final HashCode hashToSign; - private final UInt256 feesPaid; + private final byte[] blob; + private final HashCode hashToSign; + private final UInt256 feesPaid; - public UnsignedTxnData(byte[] blob, HashCode hashToSign, UInt256 feesPaid) { - this.blob = blob; - this.hashToSign = hashToSign; - this.feesPaid = feesPaid; - } + public UnsignedTxnData(byte[] blob, HashCode hashToSign, UInt256 feesPaid) { + this.blob = blob; + this.hashToSign = hashToSign; + this.feesPaid = feesPaid; + } - public byte[] blob() { - return blob; - } + public byte[] blob() { + return blob; + } - public HashCode hashToSign() { - return hashToSign; - } + public HashCode hashToSign() { + return hashToSign; + } - public UInt256 feesPaid() { - return feesPaid; - } + public UInt256 feesPaid() { + return feesPaid; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/ActionErrors.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/ActionErrors.java index b607fb4c70..fe258b17e9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/ActionErrors.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/ActionErrors.java @@ -67,47 +67,47 @@ import com.radixdlt.utils.functional.Failure; public enum ActionErrors implements Failure { - UNABLE_TO_SUBMIT_TX(1500, "Transaction submission failed: {0}"), + UNABLE_TO_SUBMIT_TX(1500, "Transaction submission failed: {0}"), - MALFORMED_TRANSACTION(1300, "Transaction request is malformed"), + MALFORMED_TRANSACTION(1300, "Transaction request is malformed"), - INSUFFICIENT_FUNDS(1301, "Insufficient balance"), - NOT_PERMITTED(1302, "Not permitted"), - ADDRESS_IS_MISSING(1303, "Address is missing"), - NOT_A_SYSTEM(1304, "Not a system"), - RRI_NOT_AVAILABLE(1305, "RRI is not available"), - ALREADY_A_VALIDATOR(1306, "Already a validator"), - INVALID_STATE(1307, "Invalid state"), - NO_SYSTEM_PARTICLE(1308, "No system particle"), - NOT_ENOUGH_STAKED(1309, "Not enough stacked"), - NEXT_VIEW_LE_CURRENT(1310, "Next view is less or equal to current"), - ALREADY_UNREGISTERED(1311, "Already unregistered"), - INSUFFICIENT_FUNDS_FOR_FEE(1312, "Insufficient funds for fee"), - DIFFERENT_SOURCE_ADDRESSES(1313, "Source addresses for actions must be identical"), - INVALID_ACTION(1314, "Invalid action"), - INVALID_ACTION_TYPE(1315, "Invalid action type"), - INVALID_RRI(1316, "Invalid RRI"), - INVALID_ADDRESS(1317, "Invalid address"), - INVALID_VALIDATOR_ADDRESS(1318, "Invalid validator address"), - INVALID_AMOUNT(1319, "Invalid amount"), - TRANSACTION_ADDRESS_DOES_NOT_MATCH(1320, "Provided txID does not match provided transaction"), - EMPTY_TRANSACTIONS_NOT_SUPPORTED(1321, "Empty transactions are not supported"); + INSUFFICIENT_FUNDS(1301, "Insufficient balance"), + NOT_PERMITTED(1302, "Not permitted"), + ADDRESS_IS_MISSING(1303, "Address is missing"), + NOT_A_SYSTEM(1304, "Not a system"), + RRI_NOT_AVAILABLE(1305, "RRI is not available"), + ALREADY_A_VALIDATOR(1306, "Already a validator"), + INVALID_STATE(1307, "Invalid state"), + NO_SYSTEM_PARTICLE(1308, "No system particle"), + NOT_ENOUGH_STAKED(1309, "Not enough stacked"), + NEXT_VIEW_LE_CURRENT(1310, "Next view is less or equal to current"), + ALREADY_UNREGISTERED(1311, "Already unregistered"), + INSUFFICIENT_FUNDS_FOR_FEE(1312, "Insufficient funds for fee"), + DIFFERENT_SOURCE_ADDRESSES(1313, "Source addresses for actions must be identical"), + INVALID_ACTION(1314, "Invalid action"), + INVALID_ACTION_TYPE(1315, "Invalid action type"), + INVALID_RRI(1316, "Invalid RRI"), + INVALID_ADDRESS(1317, "Invalid address"), + INVALID_VALIDATOR_ADDRESS(1318, "Invalid validator address"), + INVALID_AMOUNT(1319, "Invalid amount"), + TRANSACTION_ADDRESS_DOES_NOT_MATCH(1320, "Provided txID does not match provided transaction"), + EMPTY_TRANSACTIONS_NOT_SUPPORTED(1321, "Empty transactions are not supported"); - private final int code; - private final String message; + private final int code; + private final String message; - ActionErrors(int code, String message) { - this.code = code; - this.message = message; - } + ActionErrors(int code, String message) { + this.code = code; + this.message = message; + } - @Override - public String message() { - return message; - } + @Override + public String message() { + return message; + } - @Override - public int code() { - return code; - } + @Override + public int code() { + return code; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/BurnToken.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/BurnToken.java index 5d929063dc..68a90cf460 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/BurnToken.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/BurnToken.java @@ -69,31 +69,29 @@ import com.radixdlt.utils.UInt256; public final class BurnToken implements TxAction { - private final REAddr resourceAddr; - private final REAddr accountAddr; - private final UInt256 amount; + private final REAddr resourceAddr; + private final REAddr accountAddr; + private final UInt256 amount; - public BurnToken(REAddr resourceAddr, REAddr accountAddr, UInt256 amount) { - if (amount.isZero()) { - throw new IllegalArgumentException("Amount must be > 0."); - } + public BurnToken(REAddr resourceAddr, REAddr accountAddr, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Amount must be > 0."); + } - this.resourceAddr = resourceAddr; - this.accountAddr = accountAddr; - this.amount = amount; - } + this.resourceAddr = resourceAddr; + this.accountAddr = accountAddr; + this.amount = amount; + } - public REAddr from() { - return accountAddr; - } + public REAddr from() { + return accountAddr; + } - public REAddr resourceAddr() { - return resourceAddr; - } - - public UInt256 amount() { - return amount; - } + public REAddr resourceAddr() { + return resourceAddr; + } + public UInt256 amount() { + return amount; + } } - diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateFixedToken.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateFixedToken.java index 43239ec117..5f1f662112 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateFixedToken.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateFixedToken.java @@ -67,74 +67,72 @@ import com.radixdlt.atom.TxAction; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.Objects; public class CreateFixedToken implements TxAction { - private final REAddr resourceAddr; - private final REAddr accountAddr; - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenUrl; - private final UInt256 supply; + private final REAddr resourceAddr; + private final REAddr accountAddr; + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenUrl; + private final UInt256 supply; - public CreateFixedToken( - REAddr resourceAddr, - REAddr accountAddr, - String symbol, - String name, - String description, - String iconUrl, - String tokenUrl, - UInt256 supply - ) { - if (resourceAddr.getType() != REAddr.REAddrType.HASHED_KEY) { - throw new IllegalArgumentException("Invalid resource address."); - } - this.resourceAddr = resourceAddr; - this.accountAddr = Objects.requireNonNull(accountAddr); - this.symbol = Objects.requireNonNull(symbol); - this.name = Objects.requireNonNull(name); - this.description = Objects.requireNonNull(description); - this.iconUrl = Objects.requireNonNull(iconUrl); - this.tokenUrl = Objects.requireNonNull(tokenUrl); - if (supply.isZero()) { - throw new IllegalArgumentException("Supply must be > 0."); - } - this.supply = supply; - } + public CreateFixedToken( + REAddr resourceAddr, + REAddr accountAddr, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + UInt256 supply) { + if (resourceAddr.getType() != REAddr.REAddrType.HASHED_KEY) { + throw new IllegalArgumentException("Invalid resource address."); + } + this.resourceAddr = resourceAddr; + this.accountAddr = Objects.requireNonNull(accountAddr); + this.symbol = Objects.requireNonNull(symbol); + this.name = Objects.requireNonNull(name); + this.description = Objects.requireNonNull(description); + this.iconUrl = Objects.requireNonNull(iconUrl); + this.tokenUrl = Objects.requireNonNull(tokenUrl); + if (supply.isZero()) { + throw new IllegalArgumentException("Supply must be > 0."); + } + this.supply = supply; + } - public REAddr getResourceAddr() { - return resourceAddr; - } + public REAddr getResourceAddr() { + return resourceAddr; + } - public REAddr getAccountAddr() { - return accountAddr; - } + public REAddr getAccountAddr() { + return accountAddr; + } - public UInt256 getSupply() { - return supply; - } + public UInt256 getSupply() { + return supply; + } - public String getSymbol() { - return symbol; - } + public String getSymbol() { + return symbol; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - public String getIconUrl() { - return iconUrl; - } + public String getIconUrl() { + return iconUrl; + } - public String getTokenUrl() { - return tokenUrl; - } + public String getTokenUrl() { + return tokenUrl; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateMutableToken.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateMutableToken.java index c2eeb0e0f7..d53f50073b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateMutableToken.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateMutableToken.java @@ -70,69 +70,67 @@ import com.radixdlt.identifiers.REAddr; public final class CreateMutableToken implements TxAction { - private final REAddr resourceAddress; - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenUrl; - private final ECPublicKey owner; + private final REAddr resourceAddress; + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenUrl; + private final ECPublicKey owner; - public CreateMutableToken(MutableTokenDefinition def) { - this( - def.getResourceAddress(), - def.getSymbol(), - def.getName(), - def.getDescription(), - def.getIconUrl(), - def.getTokenUrl(), - def.getOwner() - ); - } + public CreateMutableToken(MutableTokenDefinition def) { + this( + def.getResourceAddress(), + def.getSymbol(), + def.getName(), + def.getDescription(), + def.getIconUrl(), + def.getTokenUrl(), + def.getOwner()); + } - public CreateMutableToken( - REAddr resourceAddress, - String symbol, - String name, - String description, - String iconUrl, - String tokenUrl, - ECPublicKey owner - ) { - this.resourceAddress = resourceAddress; - this.symbol = symbol.toLowerCase(); - this.name = name; - this.description = description; - this.iconUrl = iconUrl; - this.tokenUrl = tokenUrl; - this.owner = owner; - } + public CreateMutableToken( + REAddr resourceAddress, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + ECPublicKey owner) { + this.resourceAddress = resourceAddress; + this.symbol = symbol.toLowerCase(); + this.name = name; + this.description = description; + this.iconUrl = iconUrl; + this.tokenUrl = tokenUrl; + this.owner = owner; + } - public ECPublicKey getOwner() { - return owner; - } + public ECPublicKey getOwner() { + return owner; + } - public REAddr getResourceAddress() { - return resourceAddress; - } + public REAddr getResourceAddress() { + return resourceAddress; + } - public String getSymbol() { - return symbol; - } + public String getSymbol() { + return symbol; + } - public String getName() { - return name == null ? "" : name; - } + public String getName() { + return name == null ? "" : name; + } - public String getDescription() { - return description == null ? "" : description; - } + public String getDescription() { + return description == null ? "" : description; + } - public String getIconUrl() { - return iconUrl == null ? "" : iconUrl; - } + public String getIconUrl() { + return iconUrl == null ? "" : iconUrl; + } - public String getTokenUrl() { - return tokenUrl == null ? "" : tokenUrl; - } + public String getTokenUrl() { + return tokenUrl == null ? "" : tokenUrl; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateSystem.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateSystem.java index 9736594ffe..aa17740979 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateSystem.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/CreateSystem.java @@ -67,12 +67,13 @@ import com.radixdlt.atom.TxAction; public final class CreateSystem implements TxAction { - private final long timestamp; - public CreateSystem(long timestamp) { - this.timestamp = timestamp; - } + private final long timestamp; - public long getTimestamp() { - return timestamp; - } + public CreateSystem(long timestamp) { + this.timestamp = timestamp; + } + + public long getTimestamp() { + return timestamp; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveComplete.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveComplete.java index 7023a31e67..b5dd9a499d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveComplete.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveComplete.java @@ -68,16 +68,16 @@ import com.radixdlt.identifiers.REAddr; public class FeeReserveComplete implements TxAction { - private final REAddr to; + private final REAddr to; - public FeeReserveComplete(REAddr to) { - if (!to.isAccount()) { - throw new IllegalArgumentException("Address must be an account"); - } - this.to = to; - } + public FeeReserveComplete(REAddr to) { + if (!to.isAccount()) { + throw new IllegalArgumentException("Address must be an account"); + } + this.to = to; + } - public REAddr to() { - return to; - } + public REAddr to() { + return to; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReservePut.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReservePut.java index a36a799e00..b39ad1bdb5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReservePut.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReservePut.java @@ -69,19 +69,19 @@ import com.radixdlt.utils.UInt256; public class FeeReservePut implements TxAction { - private final REAddr accountAddr; - private final UInt256 amount; + private final REAddr accountAddr; + private final UInt256 amount; - public FeeReservePut(REAddr accountAddr, UInt256 amount) { - this.accountAddr = accountAddr; - this.amount = amount; - } + public FeeReservePut(REAddr accountAddr, UInt256 amount) { + this.accountAddr = accountAddr; + this.amount = amount; + } - public REAddr from() { - return accountAddr; - } + public REAddr from() { + return accountAddr; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveTake.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveTake.java index 2f09aed100..2240a0ba28 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveTake.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/FeeReserveTake.java @@ -69,19 +69,19 @@ import com.radixdlt.utils.UInt256; public class FeeReserveTake implements TxAction { - private final REAddr accountAddr; - private final UInt256 amount; + private final REAddr accountAddr; + private final UInt256 amount; - public FeeReserveTake(REAddr accountAddr, UInt256 amount) { - this.accountAddr = accountAddr; - this.amount = amount; - } + public FeeReserveTake(REAddr accountAddr, UInt256 amount) { + this.accountAddr = accountAddr; + this.amount = amount; + } - public REAddr to() { - return accountAddr; - } + public REAddr to() { + return accountAddr; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MintToken.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MintToken.java index 97c8db3cf4..1fd5ba255e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MintToken.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MintToken.java @@ -69,28 +69,28 @@ import com.radixdlt.utils.UInt256; public final class MintToken implements TxAction { - private final REAddr resourceAddr; - private final REAddr to; - private final UInt256 amount; + private final REAddr resourceAddr; + private final REAddr to; + private final UInt256 amount; - public MintToken(REAddr resourceAddr, REAddr to, UInt256 amount) { - if (amount.isZero()) { - throw new IllegalArgumentException("Amount must be > 0."); - } - this.resourceAddr = resourceAddr; - this.to = to; - this.amount = amount; - } + public MintToken(REAddr resourceAddr, REAddr to, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Amount must be > 0."); + } + this.resourceAddr = resourceAddr; + this.to = to; + this.amount = amount; + } - public REAddr resourceAddr() { - return resourceAddr; - } + public REAddr resourceAddr() { + return resourceAddr; + } - public REAddr to() { - return to; - } + public REAddr to() { + return to; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextEpoch.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextEpoch.java index 3e48e38489..3e28c56703 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextEpoch.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextEpoch.java @@ -67,13 +67,13 @@ import com.radixdlt.atom.TxAction; public final class NextEpoch implements TxAction { - private final long timestamp; + private final long timestamp; - public NextEpoch(long timestamp) { - this.timestamp = timestamp; - } + public NextEpoch(long timestamp) { + this.timestamp = timestamp; + } - public long timestamp() { - return timestamp; - } + public long timestamp() { + return timestamp; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextRound.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextRound.java index 40d20ee75e..69af152cfc 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextRound.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/NextRound.java @@ -66,40 +66,40 @@ import com.radixdlt.atom.TxAction; import com.radixdlt.crypto.ECPublicKey; - import java.util.function.LongFunction; public final class NextRound implements TxAction { - private final long view; - private final boolean isTimeout; - private final long timestamp; - private final LongFunction leaderMapping; + private final long view; + private final boolean isTimeout; + private final long timestamp; + private final LongFunction leaderMapping; - public NextRound(long view, boolean isTimeout, long timestamp, LongFunction leaderMapping) { - this.view = view; - this.isTimeout = isTimeout; - this.timestamp = timestamp; - this.leaderMapping = leaderMapping; - } + public NextRound( + long view, boolean isTimeout, long timestamp, LongFunction leaderMapping) { + this.view = view; + this.isTimeout = isTimeout; + this.timestamp = timestamp; + this.leaderMapping = leaderMapping; + } - public boolean isTimeout() { - return isTimeout; - } + public boolean isTimeout() { + return isTimeout; + } - public long view() { - return view; - } + public long view() { + return view; + } - public long timestamp() { - return timestamp; - } + public long timestamp() { + return timestamp; + } - public LongFunction leaderMapping() { - return leaderMapping; - } + public LongFunction leaderMapping() { + return leaderMapping; + } - @Override - public String toString() { - return String.format("%s{view=%s}", this.getClass().getSimpleName(), view); - } + @Override + public String toString() { + return String.format("%s{view=%s}", this.getClass().getSimpleName(), view); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/RegisterValidator.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/RegisterValidator.java index 7deba46812..5c31cf7003 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/RegisterValidator.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/RegisterValidator.java @@ -68,19 +68,19 @@ import com.radixdlt.crypto.ECPublicKey; public final class RegisterValidator implements TxValidatorAction { - private final ECPublicKey validatorKey; + private final ECPublicKey validatorKey; - public RegisterValidator(ECPublicKey validatorKey) { - this.validatorKey = validatorKey; - } + public RegisterValidator(ECPublicKey validatorKey) { + this.validatorKey = validatorKey; + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } - @Override - public String toString() { - return String.format("%s{key=%s}", this.getClass().getSimpleName(), validatorKey.toHex()); - } + @Override + public String toString() { + return String.format("%s{key=%s}", this.getClass().getSimpleName(), validatorKey.toHex()); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/SplitToken.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/SplitToken.java index 751ab72bc8..fcbfea2145 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/SplitToken.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/SplitToken.java @@ -69,25 +69,25 @@ import com.radixdlt.utils.UInt256; public final class SplitToken implements TxAction { - private final REAddr userAcct; - private final REAddr rri; - private final UInt256 minSize; + private final REAddr userAcct; + private final REAddr rri; + private final UInt256 minSize; - public SplitToken(REAddr rri, REAddr userAcct, UInt256 minSize) { - this.rri = rri; - this.userAcct = userAcct; - this.minSize = minSize; - } + public SplitToken(REAddr rri, REAddr userAcct, UInt256 minSize) { + this.rri = rri; + this.userAcct = userAcct; + this.minSize = minSize; + } - public REAddr userAcct() { - return userAcct; - } + public REAddr userAcct() { + return userAcct; + } - public REAddr rri() { - return rri; - } + public REAddr rri() { + return rri; + } - public UInt256 minSize() { - return minSize; - } + public UInt256 minSize() { + return minSize; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/StakeTokens.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/StakeTokens.java index b3895646fe..2a40ededd0 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/StakeTokens.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/StakeTokens.java @@ -70,33 +70,33 @@ import com.radixdlt.utils.UInt256; public final class StakeTokens implements TxAction { - private final REAddr fromAcct; - private final ECPublicKey delegateKey; - private final UInt256 amount; + private final REAddr fromAcct; + private final ECPublicKey delegateKey; + private final UInt256 amount; - public StakeTokens(REAddr fromAcct, ECPublicKey delegateKey, UInt256 amount) { - if (amount.isZero()) { - throw new IllegalArgumentException("Amount must be > 0."); - } - this.fromAcct = fromAcct; - this.delegateKey = delegateKey; - this.amount = amount; - } + public StakeTokens(REAddr fromAcct, ECPublicKey delegateKey, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Amount must be > 0."); + } + this.fromAcct = fromAcct; + this.delegateKey = delegateKey; + this.amount = amount; + } - public REAddr from() { - return fromAcct; - } + public REAddr from() { + return fromAcct; + } - public ECPublicKey to() { - return delegateKey; - } + public ECPublicKey to() { + return delegateKey; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } - @Override - public String toString() { - return String.format("%s{amt=%s}", this.getClass().getSimpleName(), amount); - } + @Override + public String toString() { + return String.format("%s{amt=%s}", this.getClass().getSimpleName(), amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/TransferToken.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/TransferToken.java index 39732ff032..94ee55ac55 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/TransferToken.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/TransferToken.java @@ -69,41 +69,42 @@ import com.radixdlt.utils.UInt256; public final class TransferToken implements TxAction { - private final REAddr from; - private final REAddr resourceAddr; - private final REAddr to; - private final UInt256 amount; + private final REAddr from; + private final REAddr resourceAddr; + private final REAddr to; + private final UInt256 amount; - public TransferToken(REAddr resourceAddr, REAddr from, REAddr to, UInt256 amount) { - if (amount.isZero()) { - throw new IllegalArgumentException("Must transfer > 0."); - } + public TransferToken(REAddr resourceAddr, REAddr from, REAddr to, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Must transfer > 0."); + } - this.resourceAddr = resourceAddr; - this.from = from; - this.to = to; - this.amount = amount; - } + this.resourceAddr = resourceAddr; + this.from = from; + this.to = to; + this.amount = amount; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } - public REAddr resourceAddr() { - return resourceAddr; - } + public REAddr resourceAddr() { + return resourceAddr; + } - public REAddr from() { - return from; - } + public REAddr from() { + return from; + } - public REAddr to() { - return to; - } + public REAddr to() { + return to; + } - @Override - public String toString() { - return String.format("%s{resource=%s from=%s to=%s amount=%s}", this.getClass().getSimpleName(), - resourceAddr, from, to, amount); - } + @Override + public String toString() { + return String.format( + "%s{resource=%s from=%s to=%s amount=%s}", + this.getClass().getSimpleName(), resourceAddr, from, to, amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/Unknown.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/Unknown.java index a44d1a7100..df49e879c5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/Unknown.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/Unknown.java @@ -68,10 +68,9 @@ // TODO: Remove once all actions accounted for public final class Unknown implements TxAction { - private Unknown() { - } + private Unknown() {} - public static Unknown create() { - return new Unknown(); - } + public static Unknown create() { + return new Unknown(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnregisterValidator.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnregisterValidator.java index 635817501a..8ca05ad0c9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnregisterValidator.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnregisterValidator.java @@ -66,18 +66,17 @@ import com.radixdlt.atom.TxValidatorAction; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; public final class UnregisterValidator implements TxValidatorAction { - private final ECPublicKey validatorKey; + private final ECPublicKey validatorKey; - public UnregisterValidator(ECPublicKey validatorKey) { - this.validatorKey = Objects.requireNonNull(validatorKey); - } + public UnregisterValidator(ECPublicKey validatorKey) { + this.validatorKey = Objects.requireNonNull(validatorKey); + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeOwnership.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeOwnership.java index 2c3961016e..aac18057e4 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeOwnership.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeOwnership.java @@ -70,34 +70,34 @@ import com.radixdlt.utils.UInt256; public class UnstakeOwnership implements TxAction { - private final REAddr accountAddr; - private final ECPublicKey delegateAddress; - private final UInt256 amount; + private final REAddr accountAddr; + private final ECPublicKey delegateAddress; + private final UInt256 amount; - public UnstakeOwnership(REAddr accountAddr, ECPublicKey delegateAddress, UInt256 amount) { - if (amount.isZero()) { - throw new IllegalArgumentException("Amount must be > 0."); - } + public UnstakeOwnership(REAddr accountAddr, ECPublicKey delegateAddress, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Amount must be > 0."); + } - this.accountAddr = accountAddr; - this.delegateAddress = delegateAddress; - this.amount = amount; - } + this.accountAddr = accountAddr; + this.delegateAddress = delegateAddress; + this.amount = amount; + } - public REAddr accountAddr() { - return accountAddr; - } + public REAddr accountAddr() { + return accountAddr; + } - public ECPublicKey from() { - return delegateAddress; - } + public ECPublicKey from() { + return delegateAddress; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } - @Override - public String toString() { - return String.format("%s{amt=%s}", this.getClass().getSimpleName(), amount); - } + @Override + public String toString() { + return String.format("%s{amt=%s}", this.getClass().getSimpleName(), amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeTokens.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeTokens.java index 6f68ef656a..67658d5f5b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeTokens.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UnstakeTokens.java @@ -70,33 +70,33 @@ import com.radixdlt.utils.UInt256; public class UnstakeTokens implements TxAction { - private final REAddr accountAddr; - private final ECPublicKey validatorKey; - private final UInt256 amount; + private final REAddr accountAddr; + private final ECPublicKey validatorKey; + private final UInt256 amount; - public UnstakeTokens(ECPublicKey validatorKey, REAddr accountAddr, UInt256 amount) { - if (amount.isZero()) { - throw new IllegalArgumentException("Amount must be > 0."); - } - this.accountAddr = accountAddr; - this.validatorKey = validatorKey; - this.amount = amount; - } + public UnstakeTokens(ECPublicKey validatorKey, REAddr accountAddr, UInt256 amount) { + if (amount.isZero()) { + throw new IllegalArgumentException("Amount must be > 0."); + } + this.accountAddr = accountAddr; + this.validatorKey = validatorKey; + this.amount = amount; + } - public REAddr accountAddr() { - return accountAddr; - } + public REAddr accountAddr() { + return accountAddr; + } - public ECPublicKey from() { - return validatorKey; - } + public ECPublicKey from() { + return validatorKey; + } - public UInt256 amount() { - return amount; - } + public UInt256 amount() { + return amount; + } - @Override - public String toString() { - return String.format("%s{amt=%s}", this.getClass().getSimpleName(), amount); - } + @Override + public String toString() { + return String.format("%s{amt=%s}", this.getClass().getSimpleName(), amount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateAllowDelegationFlag.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateAllowDelegationFlag.java index fe7e65d718..eb5919479d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateAllowDelegationFlag.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateAllowDelegationFlag.java @@ -68,20 +68,20 @@ import com.radixdlt.crypto.ECPublicKey; public final class UpdateAllowDelegationFlag implements TxValidatorAction { - private final ECPublicKey validatorKey; - private final boolean allowDelegation; + private final ECPublicKey validatorKey; + private final boolean allowDelegation; - public UpdateAllowDelegationFlag(ECPublicKey validatorKey, boolean allowDelegation) { - this.validatorKey = validatorKey; - this.allowDelegation = allowDelegation; - } + public UpdateAllowDelegationFlag(ECPublicKey validatorKey, boolean allowDelegation) { + this.validatorKey = validatorKey; + this.allowDelegation = allowDelegation; + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } - public boolean allowDelegation() { - return allowDelegation; - } + public boolean allowDelegation() { + return allowDelegation; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorFee.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorFee.java index 7a5808c32d..3acb6b80f0 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorFee.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorFee.java @@ -71,20 +71,20 @@ * @see com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt for details */ public class UpdateValidatorFee implements TxValidatorAction { - private final ECPublicKey validatorKey; - private final int feePercentage; + private final ECPublicKey validatorKey; + private final int feePercentage; - public UpdateValidatorFee(ECPublicKey validatorKey, int feePercentage) { - this.validatorKey = validatorKey; - this.feePercentage = feePercentage; - } + public UpdateValidatorFee(ECPublicKey validatorKey, int feePercentage) { + this.validatorKey = validatorKey; + this.feePercentage = feePercentage; + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } - public int getFeePercentage() { - return feePercentage; - } + public int getFeePercentage() { + return feePercentage; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorMetadata.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorMetadata.java index c7ff3bc74f..12f0717df3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorMetadata.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorMetadata.java @@ -66,34 +66,29 @@ import com.radixdlt.atom.TxValidatorAction; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; public class UpdateValidatorMetadata implements TxValidatorAction { - private final ECPublicKey validatorKey; - private final String name; - private final String url; + private final ECPublicKey validatorKey; + private final String name; + private final String url; - public UpdateValidatorMetadata( - ECPublicKey validatorKey, - String name, - String url - ) { - this.validatorKey = Objects.requireNonNull(validatorKey); - this.name = name; - this.url = url; - } + public UpdateValidatorMetadata(ECPublicKey validatorKey, String name, String url) { + this.validatorKey = Objects.requireNonNull(validatorKey); + this.name = name; + this.url = url; + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } - public String name() { - return name; - } + public String name() { + return name; + } - public String url() { - return url; - } + public String url() { + return url; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorOwner.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorOwner.java index 5f79b9495d..defb28601d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorOwner.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorOwner.java @@ -69,20 +69,20 @@ import com.radixdlt.identifiers.REAddr; public final class UpdateValidatorOwner implements TxValidatorAction { - private final ECPublicKey validatorKey; - private final REAddr ownerAddress; + private final ECPublicKey validatorKey; + private final REAddr ownerAddress; - public UpdateValidatorOwner(ECPublicKey validatorKey, REAddr ownerAddress) { - this.validatorKey = validatorKey; - this.ownerAddress = ownerAddress; - } + public UpdateValidatorOwner(ECPublicKey validatorKey, REAddr ownerAddress) { + this.validatorKey = validatorKey; + this.ownerAddress = ownerAddress; + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } - public REAddr getOwnerAddress() { - return ownerAddress; - } + public REAddr getOwnerAddress() { + return ownerAddress; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorSystemMetadata.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorSystemMetadata.java index 344b5a378a..94ec483978 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorSystemMetadata.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidatorSystemMetadata.java @@ -66,27 +66,23 @@ import com.radixdlt.atom.TxValidatorAction; import com.radixdlt.crypto.ECPublicKey; - import java.util.Objects; public class UpdateValidatorSystemMetadata implements TxValidatorAction { - private final ECPublicKey validatorKey; - private final byte[] bytes; + private final ECPublicKey validatorKey; + private final byte[] bytes; - public UpdateValidatorSystemMetadata( - ECPublicKey validatorKey, - byte[] bytes - ) { - this.validatorKey = Objects.requireNonNull(validatorKey); - this.bytes = bytes; - } + public UpdateValidatorSystemMetadata(ECPublicKey validatorKey, byte[] bytes) { + this.validatorKey = Objects.requireNonNull(validatorKey); + this.bytes = bytes; + } - @Override - public ECPublicKey validatorKey() { - return validatorKey; - } + @Override + public ECPublicKey validatorKey() { + return validatorKey; + } - public byte[] bytes() { - return bytes; - } + public byte[] bytes() { + return bytes; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atomos/CMAtomOS.java b/radixdlt-engine/src/main/java/com/radixdlt/atomos/CMAtomOS.java index 3542f18fe2..96a795b9f2 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atomos/CMAtomOS.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atomos/CMAtomOS.java @@ -70,37 +70,36 @@ import com.radixdlt.constraintmachine.SubstateDeserialization; import com.radixdlt.constraintmachine.SubstateSerialization; import com.radixdlt.constraintmachine.VirtualSubstateDeserialization; - import java.util.HashMap; import java.util.Map; public final class CMAtomOS { - private final Map, SubstateDefinition> substateDefinitions = new HashMap<>(); - private Procedures procedures = Procedures.empty(); + private final Map, SubstateDefinition> + substateDefinitions = new HashMap<>(); + private Procedures procedures = Procedures.empty(); - public CMAtomOS() { - } + public CMAtomOS() {} - public void load(ConstraintScrypt constraintScrypt) { - var constraintScryptEnv = new ConstraintScryptEnv(ImmutableMap.copyOf(substateDefinitions)); - constraintScrypt.main(constraintScryptEnv); - substateDefinitions.putAll(constraintScryptEnv.getScryptParticleDefinitions()); - procedures = procedures.combine(constraintScryptEnv.getProcedures()); - } + public void load(ConstraintScrypt constraintScrypt) { + var constraintScryptEnv = new ConstraintScryptEnv(ImmutableMap.copyOf(substateDefinitions)); + constraintScrypt.main(constraintScryptEnv); + substateDefinitions.putAll(constraintScryptEnv.getScryptParticleDefinitions()); + procedures = procedures.combine(constraintScryptEnv.getProcedures()); + } - public Procedures getProcedures() { - return procedures; - } + public Procedures getProcedures() { + return procedures; + } - public SubstateDeserialization buildSubstateDeserialization() { - return new SubstateDeserialization(substateDefinitions.values()); - } + public SubstateDeserialization buildSubstateDeserialization() { + return new SubstateDeserialization(substateDefinitions.values()); + } - public SubstateSerialization buildSubstateSerialization() { - return new SubstateSerialization(substateDefinitions.values()); - } + public SubstateSerialization buildSubstateSerialization() { + return new SubstateSerialization(substateDefinitions.values()); + } - public VirtualSubstateDeserialization buildVirtualSubstateDeserialization() { - return new VirtualSubstateDeserialization(substateDefinitions.values()); - } + public VirtualSubstateDeserialization buildVirtualSubstateDeserialization() { + return new VirtualSubstateDeserialization(substateDefinitions.values()); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScrypt.java index 375a898e3f..7fbdca10e6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScrypt.java @@ -64,17 +64,14 @@ package com.radixdlt.atomos; -/** - * Radix-style smart-contract-like entrypoint for application constraints. - */ +/** Radix-style smart-contract-like entrypoint for application constraints. */ public interface ConstraintScrypt { - /** - * Entrypoint to the constraint scrypt's logic. With the given - * AtomOS interface the smart constraint adds custom - * application constraints on top of the ledger - * - * @param os interface to the system calls to configure constraints on the ledger - */ - void main(Loader os); + /** + * Entrypoint to the constraint scrypt's logic. With the given AtomOS interface the smart + * constraint adds custom application constraints on top of the ledger + * + * @param os interface to the system calls to configure constraints on the ledger + */ + void main(Loader os); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScryptEnv.java b/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScryptEnv.java index 6c79eebfbc..9ca777fe65 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScryptEnv.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atomos/ConstraintScryptEnv.java @@ -69,57 +69,58 @@ import com.radixdlt.constraintmachine.Procedure; import com.radixdlt.constraintmachine.ProcedureKey; import com.radixdlt.constraintmachine.Procedures; - import java.util.HashMap; import java.util.Map; -/** - * SysCall environment for CMAtomOS Constraint Scrypts. - */ +/** SysCall environment for CMAtomOS Constraint Scrypts. */ // FIXME: unchecked, rawtypes @SuppressWarnings({"unchecked", "rawtypes"}) public final class ConstraintScryptEnv implements Loader { - private final ImmutableMap, SubstateDefinition> particleDefinitions; + private final ImmutableMap, SubstateDefinition> + particleDefinitions; - private final Map, SubstateDefinition> scryptParticleDefinitions; - private final Map procedures; + private final Map, SubstateDefinition> + scryptParticleDefinitions; + private final Map procedures; - ConstraintScryptEnv( - ImmutableMap, SubstateDefinition> particleDefinitions - ) { - this.particleDefinitions = particleDefinitions; - this.scryptParticleDefinitions = new HashMap<>(); - this.procedures = new HashMap<>(); - } + ConstraintScryptEnv( + ImmutableMap, SubstateDefinition> + particleDefinitions) { + this.particleDefinitions = particleDefinitions; + this.scryptParticleDefinitions = new HashMap<>(); + this.procedures = new HashMap<>(); + } - public Map, SubstateDefinition> getScryptParticleDefinitions() { - return scryptParticleDefinitions; - } + public Map, SubstateDefinition> + getScryptParticleDefinitions() { + return scryptParticleDefinitions; + } - public Procedures getProcedures() { - return new Procedures(procedures); - } + public Procedures getProcedures() { + return new Procedures(procedures); + } - private boolean particleDefinitionExists(Class particleClass) { - return particleDefinitions.containsKey(particleClass) || scryptParticleDefinitions.containsKey(particleClass); - } + private boolean particleDefinitionExists(Class particleClass) { + return particleDefinitions.containsKey(particleClass) + || scryptParticleDefinitions.containsKey(particleClass); + } - @Override - public void substate(SubstateDefinition substateDefinition) { - var substateClass = substateDefinition.getSubstateClass(); - if (particleDefinitionExists(substateClass)) { - throw new IllegalStateException("Substate " + substateClass + " is already registered"); - } + @Override + public void substate(SubstateDefinition substateDefinition) { + var substateClass = substateDefinition.getSubstateClass(); + if (particleDefinitionExists(substateClass)) { + throw new IllegalStateException("Substate " + substateClass + " is already registered"); + } - scryptParticleDefinitions.put(substateClass, substateDefinition); - } + scryptParticleDefinitions.put(substateClass, substateDefinition); + } - @Override - public void procedure(Procedure procedure) { - var key = procedure.key(); - if (procedures.containsKey(key)) { - throw new IllegalStateException(key + " already created"); - } - procedures.put(key, procedure); - } + @Override + public void procedure(Procedure procedure) { + var key = procedure.key(); + if (procedures.containsKey(key)) { + throw new IllegalStateException(key + " already created"); + } + procedures.put(key, procedure); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atomos/Loader.java b/radixdlt-engine/src/main/java/com/radixdlt/atomos/Loader.java index 56a61ea5bd..67ecbbfd90 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atomos/Loader.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atomos/Loader.java @@ -67,16 +67,15 @@ import com.radixdlt.constraintmachine.Particle; import com.radixdlt.constraintmachine.Procedure; -/** - * The interface in which a constraint scrypt can be programmed against. - */ +/** The interface in which a constraint scrypt can be programmed against. */ public interface Loader { - /** - * Registers a Particle. - * This is required for all other system calls using the particle. - * @param substateDefinition The particle definition - * @param The type of the particle - */ - void substate(SubstateDefinition substateDefinition); - void procedure(Procedure procedure); + /** + * Registers a Particle. This is required for all other system calls using the particle. + * + * @param substateDefinition The particle definition + * @param The type of the particle + */ + void substate(SubstateDefinition substateDefinition); + + void procedure(Procedure procedure); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atomos/SubstateDefinition.java b/radixdlt-engine/src/main/java/com/radixdlt/atomos/SubstateDefinition.java index e3ff6dc622..e126157a6b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atomos/SubstateDefinition.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atomos/SubstateDefinition.java @@ -74,106 +74,109 @@ /** * Defines how to retrieve important properties from a given particle type. + * * @param the particle class */ // FIXME: unchecked, rawtypes @SuppressWarnings({"unchecked", "rawtypes"}) public final class SubstateDefinition { - private final Class substateClass; - private final byte typeByte; - private final SubstateDeserializer deserializer; - private final SubstateSerializer serializer; - - private final KeyDeserializer keyDeserializer; - private final KeySerializer keySerializer; - private final VirtualMapper virtualSerializer; - - public SubstateDefinition( - Class substateClass, - byte typeByte, - SubstateDeserializer deserializer, - SubstateSerializer serializer - ) { - this.substateClass = substateClass; - this.typeByte = typeByte; - this.deserializer = deserializer; - this.serializer = serializer; - this.keyDeserializer = buf -> { - throw new DeserializeException("Virtual substate not supported"); - }; - this.keySerializer = (k, buf) -> { - throw new IllegalStateException("Cannot create key"); - }; - this.virtualSerializer = o -> { - throw new IllegalStateException("Cannot virtualize"); - }; - } - - public SubstateDefinition( - Class substateClass, - byte typeByte, - SubstateDeserializer deserializer, - SubstateSerializer serializer, - KeySerializer keySerializer - ) { - this.substateClass = substateClass; - this.typeByte = typeByte; - this.deserializer = deserializer; - this.serializer = serializer; - - this.keySerializer = keySerializer; - this.keyDeserializer = buf -> { - throw new DeserializeException("Virtual substate not supported"); - }; - this.virtualSerializer = o -> { - throw new IllegalStateException("Cannot virtualize"); - }; - } - - public SubstateDefinition( - Class substateClass, - byte typeByte, - SubstateDeserializer deserializer, - SubstateSerializer serializer, - KeyDeserializer keyDeserializer, - KeySerializer keySerializer, - VirtualMapper virtualMapper - ) { - this.substateClass = substateClass; - this.typeByte = typeByte; - this.deserializer = deserializer; - this.serializer = serializer; - - this.keyDeserializer = keyDeserializer; - this.keySerializer = keySerializer; - this.virtualSerializer = virtualMapper; - } - - public byte getTypeByte() { - return typeByte; - } - - public Class getSubstateClass() { - return substateClass; - } - - public SubstateSerializer getSerializer() { - return serializer; - } - - public SubstateDeserializer getDeserializer() { - return deserializer; - } - - public KeySerializer getKeySerializer() { - return keySerializer; - } - - public KeyDeserializer getKeyDeserializer() { - return keyDeserializer; - } - - public VirtualMapper getVirtualMapper() { - return virtualSerializer; - } + private final Class substateClass; + private final byte typeByte; + private final SubstateDeserializer deserializer; + private final SubstateSerializer serializer; + + private final KeyDeserializer keyDeserializer; + private final KeySerializer keySerializer; + private final VirtualMapper virtualSerializer; + + public SubstateDefinition( + Class substateClass, + byte typeByte, + SubstateDeserializer deserializer, + SubstateSerializer serializer) { + this.substateClass = substateClass; + this.typeByte = typeByte; + this.deserializer = deserializer; + this.serializer = serializer; + this.keyDeserializer = + buf -> { + throw new DeserializeException("Virtual substate not supported"); + }; + this.keySerializer = + (k, buf) -> { + throw new IllegalStateException("Cannot create key"); + }; + this.virtualSerializer = + o -> { + throw new IllegalStateException("Cannot virtualize"); + }; + } + + public SubstateDefinition( + Class substateClass, + byte typeByte, + SubstateDeserializer deserializer, + SubstateSerializer serializer, + KeySerializer keySerializer) { + this.substateClass = substateClass; + this.typeByte = typeByte; + this.deserializer = deserializer; + this.serializer = serializer; + + this.keySerializer = keySerializer; + this.keyDeserializer = + buf -> { + throw new DeserializeException("Virtual substate not supported"); + }; + this.virtualSerializer = + o -> { + throw new IllegalStateException("Cannot virtualize"); + }; + } + + public SubstateDefinition( + Class substateClass, + byte typeByte, + SubstateDeserializer deserializer, + SubstateSerializer serializer, + KeyDeserializer keyDeserializer, + KeySerializer keySerializer, + VirtualMapper virtualMapper) { + this.substateClass = substateClass; + this.typeByte = typeByte; + this.deserializer = deserializer; + this.serializer = serializer; + + this.keyDeserializer = keyDeserializer; + this.keySerializer = keySerializer; + this.virtualSerializer = virtualMapper; + } + + public byte getTypeByte() { + return typeByte; + } + + public Class getSubstateClass() { + return substateClass; + } + + public SubstateSerializer getSerializer() { + return serializer; + } + + public SubstateDeserializer getDeserializer() { + return deserializer; + } + + public KeySerializer getKeySerializer() { + return keySerializer; + } + + public KeyDeserializer getKeyDeserializer() { + return keyDeserializer; + } + + public VirtualMapper getVirtualMapper() { + return virtualSerializer; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Authorization.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Authorization.java index a5d5d9ecfb..cf44c3d75f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Authorization.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Authorization.java @@ -64,26 +64,25 @@ package com.radixdlt.constraintmachine; -/** - * Validates whether a specific transition procedure is permissible - */ +/** Validates whether a specific transition procedure is permissible */ public final class Authorization { - public interface Authorizer { - void verify(Resources immutableAddrs, ExecutionContext context) throws Exception; - } - private final PermissionLevel permissionLevel; - private final Authorizer authorizer; + public interface Authorizer { + void verify(Resources immutableAddrs, ExecutionContext context) throws Exception; + } + + private final PermissionLevel permissionLevel; + private final Authorizer authorizer; - public Authorization(PermissionLevel permissionLevel, Authorizer authorizer) { - this.permissionLevel = permissionLevel; - this.authorizer = authorizer; - } + public Authorization(PermissionLevel permissionLevel, Authorizer authorizer) { + this.permissionLevel = permissionLevel; + this.authorizer = authorizer; + } - public PermissionLevel permissionLevel() { - return permissionLevel; - } + public PermissionLevel permissionLevel() { + return permissionLevel; + } - public Authorizer authorizer() { - return authorizer; - } + public Authorizer authorizer() { + return authorizer; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/CallData.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/CallData.java index 2e1659646e..0772af5cc6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/CallData.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/CallData.java @@ -68,44 +68,44 @@ import com.radixdlt.engine.parser.exceptions.TrailingBytesException; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.UInt256; - import java.util.Arrays; import java.util.Objects; public final class CallData { - private final byte[] data; - public CallData(byte[] data) { - this.data = Objects.requireNonNull(data); - } + private final byte[] data; + + public CallData(byte[] data) { + this.data = Objects.requireNonNull(data); + } - public byte get(int offset) throws CallDataAccessException { - if (offset < 0 || (offset + Byte.BYTES) > data.length) { - throw new CallDataAccessException(data.length, offset, Byte.BYTES); - } - return data[offset]; - } + public byte get(int offset) throws CallDataAccessException { + if (offset < 0 || (offset + Byte.BYTES) > data.length) { + throw new CallDataAccessException(data.length, offset, Byte.BYTES); + } + return data[offset]; + } - public UInt256 getUInt256(int offset) throws CallDataAccessException, TrailingBytesException { - if (offset < 0 || (offset + UInt256.BYTES) > data.length) { - throw new CallDataAccessException(data.length, offset, UInt256.BYTES); - } + public UInt256 getUInt256(int offset) throws CallDataAccessException, TrailingBytesException { + if (offset < 0 || (offset + UInt256.BYTES) > data.length) { + throw new CallDataAccessException(data.length, offset, UInt256.BYTES); + } - if (data.length > offset + UInt256.BYTES) { - throw new TrailingBytesException("Call data has " + data.length + " bytes."); - } + if (data.length > offset + UInt256.BYTES) { + throw new TrailingBytesException("Call data has " + data.length + " bytes."); + } - return UInt256.from(data, offset); - } + return UInt256.from(data, offset); + } - public byte[] getRemainingBytes(int offset) throws CallDataAccessException { - if (offset < 0 || offset >= data.length) { - throw new CallDataAccessException(data.length, offset, 0); - } - return Arrays.copyOfRange(data, offset, data.length); - } + public byte[] getRemainingBytes(int offset) throws CallDataAccessException { + if (offset < 0 || offset >= data.length) { + throw new CallDataAccessException(data.length, offset, 0); + } + return Arrays.copyOfRange(data, offset, data.length); + } - @Override - public String toString() { - return String.format("%s{data=%s}", this.getClass().getSimpleName(), Bytes.toHexString(data)); - } + @Override + public String toString() { + return String.format("%s{data=%s}", this.getClass().getSimpleName(), Bytes.toHexString(data)); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachine.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachine.java index e809e55e8c..4fab418e6f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachine.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachine.java @@ -65,10 +65,10 @@ package com.radixdlt.constraintmachine; import com.radixdlt.application.system.state.VirtualParent; -import com.radixdlt.atom.Substate; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.atom.CloseableCursor; +import com.radixdlt.atom.Substate; import com.radixdlt.atom.SubstateId; -import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.exceptions.ConstraintMachineException; import com.radixdlt.constraintmachine.exceptions.InvalidPermissionException; @@ -81,14 +81,13 @@ import com.radixdlt.constraintmachine.exceptions.SubstateNotFoundException; import com.radixdlt.constraintmachine.exceptions.VirtualParentStateDoesNotExist; import com.radixdlt.constraintmachine.exceptions.VirtualSubstateAlreadyDownException; +import com.radixdlt.constraintmachine.meter.Meter; import com.radixdlt.engine.parser.exceptions.TrailingBytesException; import com.radixdlt.engine.parser.exceptions.TxnParseException; -import com.radixdlt.constraintmachine.meter.Meter; import com.radixdlt.identifiers.REAddr; import com.radixdlt.serialization.DeserializeException; import com.radixdlt.store.CMStore; import com.radixdlt.utils.Pair; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; @@ -101,434 +100,455 @@ import java.util.Set; import java.util.function.Supplier; -/** - * An implementation of a UTXO based constraint machine which uses Radix's atom structure. - */ +/** An implementation of a UTXO based constraint machine which uses Radix's atom structure. */ // FIXME: unchecked, rawtypes @SuppressWarnings({"unchecked", "rawtypes"}) public final class ConstraintMachine { - private final Procedures procedures; - private final VirtualSubstateDeserialization virtualSubstateDeserialization; - private final SubstateDeserialization deserialization; - private final Meter meter; - - public ConstraintMachine( - Procedures procedures, - SubstateDeserialization deserialization, - VirtualSubstateDeserialization virtualSubstateDeserialization - ) { - this(procedures, deserialization, virtualSubstateDeserialization, Meter.EMPTY); - } - - public ConstraintMachine( - Procedures procedures, - SubstateDeserialization deserialization, - VirtualSubstateDeserialization virtualSubstateDeserialization, - Meter meter - ) { - this.procedures = Objects.requireNonNull(procedures); - this.deserialization = deserialization; - this.virtualSubstateDeserialization = virtualSubstateDeserialization; - this.meter = Objects.requireNonNull(meter); - } - - public SubstateDeserialization getDeserialization() { - return deserialization; - } - - public VirtualSubstateDeserialization getVirtualDeserialization() { - return virtualSubstateDeserialization; - } - - private static final class CMValidationState { - private final Map localResources = new HashMap<>(); - private final Map>> localUpParticles = new HashMap<>(); - private final Set remoteDownParticles = new HashSet<>(); - private final CMStore store; - private final SubstateDeserialization deserialization; - private final VirtualSubstateDeserialization virtualSubstateDeserialization; - private int bootupCount = 0; - - CMValidationState( - VirtualSubstateDeserialization virtualSubstateDeserialization, - SubstateDeserialization deserialization, - CMStore store - ) { - this.deserialization = deserialization; - this.virtualSubstateDeserialization = virtualSubstateDeserialization; - this.store = store; - } - - public Resources resources() { - return addr -> { - var local = localResources.get(addr); - if (local != null) { - return local; - } - - var p = store.loadResource(addr).map(b -> { - try { - return deserialization.deserialize(b); - } catch (DeserializeException e) { - throw new IllegalStateException(e); - } - }); - if (p.isEmpty()) { - throw new NotAResourceException(addr); - } - var substate = p.get(); - if (!(substate instanceof TokenResource)) { - throw new NotAResourceException(addr); - } - return (TokenResource) substate; - }; - } - - public Optional loadUpParticle(SubstateId substateId) { - if (remoteDownParticles.contains(substateId)) { - return Optional.empty(); - } - - var raw = store.loadSubstate(substateId); - return raw.map(b -> { - try { - return deserialization.deserialize(b); - } catch (DeserializeException e) { - throw new IllegalStateException(e); - } - }); - } - - public void bootUp(Substate substate, Supplier buffer) { - localUpParticles.put(bootupCount, Pair.of(substate, buffer)); - if (substate.getParticle() instanceof TokenResource) { - var resource = (TokenResource) substate.getParticle(); - localResources.put(resource.getAddr(), resource); - } - bootupCount++; - } - - public Particle virtualRead(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, DeserializeException { - if (remoteDownParticles.contains(substateId)) { - throw new VirtualSubstateAlreadyDownException(substateId); - } - - var parentBuf = store.verifyVirtualSubstate(substateId); - var parent = (VirtualParent) deserialization.deserialize(parentBuf); - var typeByte = parent.getData()[0]; - var keyBuf = substateId.getVirtualKey().orElseThrow(); - return virtualSubstateDeserialization.keyToSubstate(typeByte, keyBuf); - } - - public Particle virtualShutdown(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, DeserializeException { - var p = virtualRead(substateId); - remoteDownParticles.add(substateId); - return p; - } - - - public Particle localVirtualRead(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, DeserializeException { - if (remoteDownParticles.contains(substateId)) { - throw new VirtualSubstateAlreadyDownException(substateId); - } - - var parentId = substateId.getVirtualParent().orElseThrow(); - var substate = localUpParticles.get(parentId.getIndex().orElseThrow()); - if (substate == null || !(substate.getFirst().getParticle() instanceof VirtualParent)) { - throw new VirtualParentStateDoesNotExist(parentId); - } - var parent = (VirtualParent) substate.getFirst().getParticle(); - var typeByte = parent.getData()[0]; - var keyBuf = substateId.getVirtualKey().orElseThrow(); - return virtualSubstateDeserialization.keyToSubstate(typeByte, keyBuf); - } - - public Particle localVirtualShutdown(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, DeserializeException { - var p = localVirtualRead(substateId); - remoteDownParticles.add(substateId); - return p; - } - - public Particle localShutdown(int index) throws LocalSubstateNotFoundException { - var substate = localUpParticles.remove(index); - if (substate == null) { - throw new LocalSubstateNotFoundException(index); - } - - return substate.getFirst().getParticle(); - } - - public Particle localRead(int index) throws LocalSubstateNotFoundException { - var substate = localUpParticles.get(index); - if (substate == null) { - throw new LocalSubstateNotFoundException(index); - } - - return substate.getFirst().getParticle(); - } - - public Particle read(SubstateId substateId) throws SubstateNotFoundException { - var read = loadUpParticle(substateId); - if (read.isEmpty()) { - throw new SubstateNotFoundException(substateId); - } - return read.get(); - } - - public Particle shutdown(SubstateId substateId) throws SubstateNotFoundException { - var substate = read(substateId); - remoteDownParticles.add(substateId); - return substate; - } - - public CloseableCursor getIndexedCursor(SubstateIndex index) { - return CloseableCursor.wrapIterator(localUpParticles.values().stream() - .filter(s -> index.test(s.getSecond().get())).map(Pair::getFirst).iterator() - ).concat(() -> store.openIndexedCursor(index) - .map(r -> { - try { - var substate = deserialization.deserialize(r.getData()); - return Substate.create(substate, SubstateId.fromBytes(r.getId())); - } catch (DeserializeException e) { - throw new IllegalStateException(); - } - }) - .filter(s -> !remoteDownParticles.contains(s.getId())) - ); - } - } - - private Procedure loadProcedure( - ReducerState reducerState, - OpSignature opSignature - ) throws MissingProcedureException { - var reducerStateClass = reducerState != null - ? reducerState.getClass() - : VoidReducerState.class; - var key = ProcedureKey.of(reducerStateClass, opSignature); - return this.procedures.getProcedure(key); - } - - /** - * Executes a transition procedure given the next spun particle and a current validation state. - */ - private ReducerState callProcedure( - Procedure procedure, - Object procedureParam, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws SignedSystemException, InvalidPermissionException, AuthorizationException, MeterException, ProcedureException { - // System permissions don't require additional authorization - var authorization = procedure.authorization(procedureParam); - var requiredLevel = authorization.permissionLevel(); - context.verifyPermissionLevel(requiredLevel); - if (context.permissionLevel() != PermissionLevel.SYSTEM) { - try { - if (requiredLevel == PermissionLevel.USER) { - this.meter.onUserProcedure(procedure.key(), procedureParam, context); - } else if (requiredLevel == PermissionLevel.SUPER_USER) { - this.meter.onSuperUserProcedure(procedure.key(), procedureParam, context); - } - } catch (Exception e) { - throw new MeterException(e); - } - - if (!context.skipAuthorization()) { - try { - authorization.authorizer().verify(immutableAddrs, context); - } catch (Exception e) { - throw new AuthorizationException(e); - } - } - } - - return procedure.call(procedureParam, reducerState, immutableAddrs, context).state(); - } - - private static class MissingExpectedEndException extends Exception { - } - - /** - * Executes transition procedures and witness validators in a particle group and validates - * that the particle group is well formed. - */ - List> statefulVerify( - ExecutionContext context, - CMValidationState validationState, - List instructions - ) throws ConstraintMachineException { - int instIndex = 0; - var expectEnd = false; - ReducerState reducerState = null; - var readableAddrs = validationState.resources(); - var groupedStateUpdates = new ArrayList>(); - var stateUpdates = new ArrayList(); - - meter.onStart(context); - - for (REInstruction inst : instructions) { - try { - if (expectEnd && inst.getMicroOp() != REInstruction.REMicroOp.END) { - throw new MissingExpectedEndException(); - } - - if (inst.getMicroOp() == REInstruction.REMicroOp.SYSCALL) { - CallData callData = inst.getData(); - var opSignature = OpSignature.ofMethod(inst.getMicroOp().getOp(), REAddr.ofSystem()); - var methodProcedure = loadProcedure(reducerState, opSignature); - reducerState = callProcedure(methodProcedure, callData, reducerState, readableAddrs, context); - } else if (inst.getMicroOp().getOp() == REOp.READ) { - final Particle nextParticle; - if (inst.getMicroOp() == REInstruction.REMicroOp.VREAD) { - SubstateId substateId = inst.getData(); - nextParticle = validationState.virtualRead(substateId); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.READ) { - SubstateId substateId = inst.getData(); - nextParticle = validationState.read(substateId); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.LREAD) { - SubstateId substateId = inst.getData(); - nextParticle = validationState.localRead(substateId.getIndex().orElseThrow()); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.LVREAD) { - SubstateId substateId = inst.getData(); - nextParticle = validationState.localVirtualRead(substateId); - } else { - throw new IllegalStateException("Unknown read op " + inst.getMicroOp()); - } - var eventId = OpSignature.ofSubstateUpdate(inst.getMicroOp().getOp(), nextParticle.getClass()); - var methodProcedure = loadProcedure(reducerState, eventId); - reducerState = callProcedure(methodProcedure, nextParticle, reducerState, readableAddrs, context); - expectEnd = reducerState == null; - } else if (inst.getMicroOp().getOp() == REOp.DOWNINDEX || inst.getMicroOp().getOp() == REOp.READINDEX) { - byte[] raw = inst.getData(); - var index = SubstateIndex.create(raw, validationState.deserialization.byteToClass(raw[0])); - var substateCursor = validationState.getIndexedCursor(index); - var tmp = stateUpdates; - final int tmpInstIndex = instIndex; - var iterator = new Iterator() { - @Override - public boolean hasNext() { - return substateCursor.hasNext(); - } - - @Override - public Particle next() { - // FIXME: this is a hack - // FIXME: do this via shutdownAll state update rather than individually - var substate = substateCursor.next(); - if (inst.getMicroOp().getOp() == REOp.DOWNINDEX) { - var typeByte = deserialization.classToByte(substate.getParticle().getClass()); - tmp.add(REStateUpdate.of(REOp.DOWN, tmpInstIndex, substate.getId(), typeByte, substate.getParticle(), null)); - } - return substate.getParticle(); - } - }; - var substateIterator = new IndexedSubstateIterator<>(index, iterator); - try { - var eventId = OpSignature.ofSubstateUpdate( - inst.getMicroOp().getOp(), index.getSubstateClass() - ); - var methodProcedure = loadProcedure(reducerState, eventId); - reducerState = callProcedure(methodProcedure, substateIterator, reducerState, readableAddrs, context); - } finally { - substateCursor.close(); - } - } else if (inst.isStateUpdate()) { - final SubstateId substateId; - final Particle nextParticle; - final Supplier substateBuffer; - if (inst.getMicroOp() == REInstruction.REMicroOp.UP) { - // TODO: Cleanup indexing of substate class - UpSubstate upSubstate = inst.getData(); - var buf = upSubstate.getSubstateBuffer(); - nextParticle = validationState.deserialization.deserialize(buf); - if (buf.hasRemaining()) { - throw new TrailingBytesException("Substate has trailing bytes."); - } - substateId = upSubstate.getSubstateId(); - substateBuffer = upSubstate::getSubstateBuffer; - validationState.bootUp(Substate.create(nextParticle, substateId), upSubstate::getSubstateBuffer); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.VDOWN) { - substateId = inst.getData(); - substateBuffer = null; - nextParticle = validationState.virtualShutdown(substateId); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.DOWN) { - substateId = inst.getData(); - substateBuffer = null; - nextParticle = validationState.shutdown(substateId); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.LDOWN) { - substateId = inst.getData(); - substateBuffer = null; - nextParticle = validationState.localShutdown(substateId.getIndex().orElseThrow()); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.LVDOWN) { - substateId = inst.getData(); - substateBuffer = null; - nextParticle = validationState.localVirtualShutdown(substateId); - } else { - throw new IllegalStateException("Unhandled op: " + inst.getMicroOp()); - } - - var op = inst.getMicroOp().getOp(); - var typeByte = deserialization.classToByte(nextParticle.getClass()); - stateUpdates.add(REStateUpdate.of(op, instIndex, substateId, typeByte, nextParticle, substateBuffer)); - var eventId = OpSignature.ofSubstateUpdate(op, nextParticle.getClass()); - var methodProcedure = loadProcedure(reducerState, eventId); - reducerState = callProcedure(methodProcedure, nextParticle, reducerState, readableAddrs, context); - expectEnd = reducerState == null; - } else if (inst.getMicroOp() == REInstruction.REMicroOp.END) { - groupedStateUpdates.add(stateUpdates); - stateUpdates = new ArrayList<>(); - - if (reducerState != null) { - var eventId = OpSignature.ofSubstateUpdate(inst.getMicroOp().getOp(), null); - var methodProcedure = loadProcedure(reducerState, eventId); - reducerState = callProcedure(methodProcedure, reducerState, reducerState, readableAddrs, context); - } - - expectEnd = false; - } else if (inst.getMicroOp() == REInstruction.REMicroOp.SIG) { - if (context.permissionLevel() != PermissionLevel.SYSTEM) { - meter.onSigInstruction(context); - } - } else { - // Collect no-ops here - if (inst.getMicroOp() != REInstruction.REMicroOp.MSG - && inst.getMicroOp() != REInstruction.REMicroOp.HEADER) { - throw new ProcedureException("Unknown op " + inst.getMicroOp()); - } - } - } catch (Exception e) { - throw new ConstraintMachineException(instIndex, instructions, reducerState, e); - } - - instIndex++; - } - - try { - context.destroy(); - } catch (Exception e) { - throw new ConstraintMachineException(instIndex, instructions, reducerState, e); - } - - return groupedStateUpdates; - } - - /** - * Validates a CM instruction and calculates the necessary state checks and post-validation - * write logic. - * - * @return the first error found, otherwise an empty optional - */ - public List> verify( - CMStore cmStore, - ExecutionContext context, - List instructions - ) throws TxnParseException, ConstraintMachineException { - var validationState = new CMValidationState(virtualSubstateDeserialization, deserialization, cmStore); - return this.statefulVerify(context, validationState, instructions); - } + private final Procedures procedures; + private final VirtualSubstateDeserialization virtualSubstateDeserialization; + private final SubstateDeserialization deserialization; + private final Meter meter; + + public ConstraintMachine( + Procedures procedures, + SubstateDeserialization deserialization, + VirtualSubstateDeserialization virtualSubstateDeserialization) { + this(procedures, deserialization, virtualSubstateDeserialization, Meter.EMPTY); + } + + public ConstraintMachine( + Procedures procedures, + SubstateDeserialization deserialization, + VirtualSubstateDeserialization virtualSubstateDeserialization, + Meter meter) { + this.procedures = Objects.requireNonNull(procedures); + this.deserialization = deserialization; + this.virtualSubstateDeserialization = virtualSubstateDeserialization; + this.meter = Objects.requireNonNull(meter); + } + + public SubstateDeserialization getDeserialization() { + return deserialization; + } + + public VirtualSubstateDeserialization getVirtualDeserialization() { + return virtualSubstateDeserialization; + } + + private static final class CMValidationState { + private final Map localResources = new HashMap<>(); + private final Map>> localUpParticles = + new HashMap<>(); + private final Set remoteDownParticles = new HashSet<>(); + private final CMStore store; + private final SubstateDeserialization deserialization; + private final VirtualSubstateDeserialization virtualSubstateDeserialization; + private int bootupCount = 0; + + CMValidationState( + VirtualSubstateDeserialization virtualSubstateDeserialization, + SubstateDeserialization deserialization, + CMStore store) { + this.deserialization = deserialization; + this.virtualSubstateDeserialization = virtualSubstateDeserialization; + this.store = store; + } + + public Resources resources() { + return addr -> { + var local = localResources.get(addr); + if (local != null) { + return local; + } + + var p = + store + .loadResource(addr) + .map( + b -> { + try { + return deserialization.deserialize(b); + } catch (DeserializeException e) { + throw new IllegalStateException(e); + } + }); + if (p.isEmpty()) { + throw new NotAResourceException(addr); + } + var substate = p.get(); + if (!(substate instanceof TokenResource)) { + throw new NotAResourceException(addr); + } + return (TokenResource) substate; + }; + } + + public Optional loadUpParticle(SubstateId substateId) { + if (remoteDownParticles.contains(substateId)) { + return Optional.empty(); + } + + var raw = store.loadSubstate(substateId); + return raw.map( + b -> { + try { + return deserialization.deserialize(b); + } catch (DeserializeException e) { + throw new IllegalStateException(e); + } + }); + } + + public void bootUp(Substate substate, Supplier buffer) { + localUpParticles.put(bootupCount, Pair.of(substate, buffer)); + if (substate.getParticle() instanceof TokenResource) { + var resource = (TokenResource) substate.getParticle(); + localResources.put(resource.getAddr(), resource); + } + bootupCount++; + } + + public Particle virtualRead(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, + DeserializeException { + if (remoteDownParticles.contains(substateId)) { + throw new VirtualSubstateAlreadyDownException(substateId); + } + + var parentBuf = store.verifyVirtualSubstate(substateId); + var parent = (VirtualParent) deserialization.deserialize(parentBuf); + var typeByte = parent.getData()[0]; + var keyBuf = substateId.getVirtualKey().orElseThrow(); + return virtualSubstateDeserialization.keyToSubstate(typeByte, keyBuf); + } + + public Particle virtualShutdown(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, + DeserializeException { + var p = virtualRead(substateId); + remoteDownParticles.add(substateId); + return p; + } + + public Particle localVirtualRead(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, + DeserializeException { + if (remoteDownParticles.contains(substateId)) { + throw new VirtualSubstateAlreadyDownException(substateId); + } + + var parentId = substateId.getVirtualParent().orElseThrow(); + var substate = localUpParticles.get(parentId.getIndex().orElseThrow()); + if (substate == null || !(substate.getFirst().getParticle() instanceof VirtualParent)) { + throw new VirtualParentStateDoesNotExist(parentId); + } + var parent = (VirtualParent) substate.getFirst().getParticle(); + var typeByte = parent.getData()[0]; + var keyBuf = substateId.getVirtualKey().orElseThrow(); + return virtualSubstateDeserialization.keyToSubstate(typeByte, keyBuf); + } + + public Particle localVirtualShutdown(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist, + DeserializeException { + var p = localVirtualRead(substateId); + remoteDownParticles.add(substateId); + return p; + } + + public Particle localShutdown(int index) throws LocalSubstateNotFoundException { + var substate = localUpParticles.remove(index); + if (substate == null) { + throw new LocalSubstateNotFoundException(index); + } + + return substate.getFirst().getParticle(); + } + + public Particle localRead(int index) throws LocalSubstateNotFoundException { + var substate = localUpParticles.get(index); + if (substate == null) { + throw new LocalSubstateNotFoundException(index); + } + + return substate.getFirst().getParticle(); + } + + public Particle read(SubstateId substateId) throws SubstateNotFoundException { + var read = loadUpParticle(substateId); + if (read.isEmpty()) { + throw new SubstateNotFoundException(substateId); + } + return read.get(); + } + + public Particle shutdown(SubstateId substateId) throws SubstateNotFoundException { + var substate = read(substateId); + remoteDownParticles.add(substateId); + return substate; + } + + public CloseableCursor getIndexedCursor(SubstateIndex index) { + return CloseableCursor.wrapIterator( + localUpParticles.values().stream() + .filter(s -> index.test(s.getSecond().get())) + .map(Pair::getFirst) + .iterator()) + .concat( + () -> + store + .openIndexedCursor(index) + .map( + r -> { + try { + var substate = deserialization.deserialize(r.getData()); + return Substate.create(substate, SubstateId.fromBytes(r.getId())); + } catch (DeserializeException e) { + throw new IllegalStateException(); + } + }) + .filter(s -> !remoteDownParticles.contains(s.getId()))); + } + } + + private Procedure loadProcedure(ReducerState reducerState, OpSignature opSignature) + throws MissingProcedureException { + var reducerStateClass = reducerState != null ? reducerState.getClass() : VoidReducerState.class; + var key = ProcedureKey.of(reducerStateClass, opSignature); + return this.procedures.getProcedure(key); + } + + /** + * Executes a transition procedure given the next spun particle and a current validation state. + */ + private ReducerState callProcedure( + Procedure procedure, + Object procedureParam, + ReducerState reducerState, + Resources immutableAddrs, + ExecutionContext context) + throws SignedSystemException, InvalidPermissionException, AuthorizationException, + MeterException, ProcedureException { + // System permissions don't require additional authorization + var authorization = procedure.authorization(procedureParam); + var requiredLevel = authorization.permissionLevel(); + context.verifyPermissionLevel(requiredLevel); + if (context.permissionLevel() != PermissionLevel.SYSTEM) { + try { + if (requiredLevel == PermissionLevel.USER) { + this.meter.onUserProcedure(procedure.key(), procedureParam, context); + } else if (requiredLevel == PermissionLevel.SUPER_USER) { + this.meter.onSuperUserProcedure(procedure.key(), procedureParam, context); + } + } catch (Exception e) { + throw new MeterException(e); + } + + if (!context.skipAuthorization()) { + try { + authorization.authorizer().verify(immutableAddrs, context); + } catch (Exception e) { + throw new AuthorizationException(e); + } + } + } + + return procedure.call(procedureParam, reducerState, immutableAddrs, context).state(); + } + + private static class MissingExpectedEndException extends Exception {} + + /** + * Executes transition procedures and witness validators in a particle group and validates that + * the particle group is well formed. + */ + List> statefulVerify( + ExecutionContext context, CMValidationState validationState, List instructions) + throws ConstraintMachineException { + int instIndex = 0; + var expectEnd = false; + ReducerState reducerState = null; + var readableAddrs = validationState.resources(); + var groupedStateUpdates = new ArrayList>(); + var stateUpdates = new ArrayList(); + + meter.onStart(context); + + for (REInstruction inst : instructions) { + try { + if (expectEnd && inst.getMicroOp() != REInstruction.REMicroOp.END) { + throw new MissingExpectedEndException(); + } + + if (inst.getMicroOp() == REInstruction.REMicroOp.SYSCALL) { + CallData callData = inst.getData(); + var opSignature = OpSignature.ofMethod(inst.getMicroOp().getOp(), REAddr.ofSystem()); + var methodProcedure = loadProcedure(reducerState, opSignature); + reducerState = + callProcedure(methodProcedure, callData, reducerState, readableAddrs, context); + } else if (inst.getMicroOp().getOp() == REOp.READ) { + final Particle nextParticle; + if (inst.getMicroOp() == REInstruction.REMicroOp.VREAD) { + SubstateId substateId = inst.getData(); + nextParticle = validationState.virtualRead(substateId); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.READ) { + SubstateId substateId = inst.getData(); + nextParticle = validationState.read(substateId); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.LREAD) { + SubstateId substateId = inst.getData(); + nextParticle = validationState.localRead(substateId.getIndex().orElseThrow()); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.LVREAD) { + SubstateId substateId = inst.getData(); + nextParticle = validationState.localVirtualRead(substateId); + } else { + throw new IllegalStateException("Unknown read op " + inst.getMicroOp()); + } + var eventId = + OpSignature.ofSubstateUpdate(inst.getMicroOp().getOp(), nextParticle.getClass()); + var methodProcedure = loadProcedure(reducerState, eventId); + reducerState = + callProcedure(methodProcedure, nextParticle, reducerState, readableAddrs, context); + expectEnd = reducerState == null; + } else if (inst.getMicroOp().getOp() == REOp.DOWNINDEX + || inst.getMicroOp().getOp() == REOp.READINDEX) { + byte[] raw = inst.getData(); + var index = + SubstateIndex.create(raw, validationState.deserialization.byteToClass(raw[0])); + var substateCursor = validationState.getIndexedCursor(index); + var tmp = stateUpdates; + final int tmpInstIndex = instIndex; + var iterator = + new Iterator() { + @Override + public boolean hasNext() { + return substateCursor.hasNext(); + } + + @Override + public Particle next() { + // FIXME: this is a hack + // FIXME: do this via shutdownAll state update rather than individually + var substate = substateCursor.next(); + if (inst.getMicroOp().getOp() == REOp.DOWNINDEX) { + var typeByte = deserialization.classToByte(substate.getParticle().getClass()); + tmp.add( + REStateUpdate.of( + REOp.DOWN, + tmpInstIndex, + substate.getId(), + typeByte, + substate.getParticle(), + null)); + } + return substate.getParticle(); + } + }; + var substateIterator = new IndexedSubstateIterator<>(index, iterator); + try { + var eventId = + OpSignature.ofSubstateUpdate(inst.getMicroOp().getOp(), index.getSubstateClass()); + var methodProcedure = loadProcedure(reducerState, eventId); + reducerState = + callProcedure( + methodProcedure, substateIterator, reducerState, readableAddrs, context); + } finally { + substateCursor.close(); + } + } else if (inst.isStateUpdate()) { + final SubstateId substateId; + final Particle nextParticle; + final Supplier substateBuffer; + if (inst.getMicroOp() == REInstruction.REMicroOp.UP) { + // TODO: Cleanup indexing of substate class + UpSubstate upSubstate = inst.getData(); + var buf = upSubstate.getSubstateBuffer(); + nextParticle = validationState.deserialization.deserialize(buf); + if (buf.hasRemaining()) { + throw new TrailingBytesException("Substate has trailing bytes."); + } + substateId = upSubstate.getSubstateId(); + substateBuffer = upSubstate::getSubstateBuffer; + validationState.bootUp( + Substate.create(nextParticle, substateId), upSubstate::getSubstateBuffer); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.VDOWN) { + substateId = inst.getData(); + substateBuffer = null; + nextParticle = validationState.virtualShutdown(substateId); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.DOWN) { + substateId = inst.getData(); + substateBuffer = null; + nextParticle = validationState.shutdown(substateId); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.LDOWN) { + substateId = inst.getData(); + substateBuffer = null; + nextParticle = validationState.localShutdown(substateId.getIndex().orElseThrow()); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.LVDOWN) { + substateId = inst.getData(); + substateBuffer = null; + nextParticle = validationState.localVirtualShutdown(substateId); + } else { + throw new IllegalStateException("Unhandled op: " + inst.getMicroOp()); + } + + var op = inst.getMicroOp().getOp(); + var typeByte = deserialization.classToByte(nextParticle.getClass()); + stateUpdates.add( + REStateUpdate.of(op, instIndex, substateId, typeByte, nextParticle, substateBuffer)); + var eventId = OpSignature.ofSubstateUpdate(op, nextParticle.getClass()); + var methodProcedure = loadProcedure(reducerState, eventId); + reducerState = + callProcedure(methodProcedure, nextParticle, reducerState, readableAddrs, context); + expectEnd = reducerState == null; + } else if (inst.getMicroOp() == REInstruction.REMicroOp.END) { + groupedStateUpdates.add(stateUpdates); + stateUpdates = new ArrayList<>(); + + if (reducerState != null) { + var eventId = OpSignature.ofSubstateUpdate(inst.getMicroOp().getOp(), null); + var methodProcedure = loadProcedure(reducerState, eventId); + reducerState = + callProcedure(methodProcedure, reducerState, reducerState, readableAddrs, context); + } + + expectEnd = false; + } else if (inst.getMicroOp() == REInstruction.REMicroOp.SIG) { + if (context.permissionLevel() != PermissionLevel.SYSTEM) { + meter.onSigInstruction(context); + } + } else { + // Collect no-ops here + if (inst.getMicroOp() != REInstruction.REMicroOp.MSG + && inst.getMicroOp() != REInstruction.REMicroOp.HEADER) { + throw new ProcedureException("Unknown op " + inst.getMicroOp()); + } + } + } catch (Exception e) { + throw new ConstraintMachineException(instIndex, instructions, reducerState, e); + } + + instIndex++; + } + + try { + context.destroy(); + } catch (Exception e) { + throw new ConstraintMachineException(instIndex, instructions, reducerState, e); + } + + return groupedStateUpdates; + } + + /** + * Validates a CM instruction and calculates the necessary state checks and post-validation write + * logic. + * + * @return the first error found, otherwise an empty optional + */ + public List> verify( + CMStore cmStore, ExecutionContext context, List instructions) + throws TxnParseException, ConstraintMachineException { + var validationState = + new CMValidationState(virtualSubstateDeserialization, deserialization, cmStore); + return this.statefulVerify(context, validationState, instructions); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachineConfig.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachineConfig.java index 63b24eb1fb..ed76b9653f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachineConfig.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ConstraintMachineConfig.java @@ -67,37 +67,35 @@ import com.radixdlt.constraintmachine.meter.Meter; public final class ConstraintMachineConfig { - private final Procedures procedures; - private final SubstateDeserialization deserialization; - private final VirtualSubstateDeserialization virtualSubstateDeserialization; - private final Meter metering; + private final Procedures procedures; + private final SubstateDeserialization deserialization; + private final VirtualSubstateDeserialization virtualSubstateDeserialization; + private final Meter metering; - public ConstraintMachineConfig( - Procedures procedures, - SubstateDeserialization deserialization, - VirtualSubstateDeserialization virtualSubstateDeserialization, - Meter metering - ) { - this.procedures = procedures; - this.deserialization = deserialization; - this.virtualSubstateDeserialization = virtualSubstateDeserialization; - this.metering = metering; - } + public ConstraintMachineConfig( + Procedures procedures, + SubstateDeserialization deserialization, + VirtualSubstateDeserialization virtualSubstateDeserialization, + Meter metering) { + this.procedures = procedures; + this.deserialization = deserialization; + this.virtualSubstateDeserialization = virtualSubstateDeserialization; + this.metering = metering; + } - public SubstateDeserialization getDeserialization() { - return deserialization; - } + public SubstateDeserialization getDeserialization() { + return deserialization; + } - public VirtualSubstateDeserialization getVirtualSubstateDeserialization() { - return virtualSubstateDeserialization; - } + public VirtualSubstateDeserialization getVirtualSubstateDeserialization() { + return virtualSubstateDeserialization; + } - public Procedures getProcedures() { - return procedures; - } - - public Meter getMeter() { - return metering; - } + public Procedures getProcedures() { + return procedures; + } + public Meter getMeter() { + return metering; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownProcedure.java index 933a09f38a..ad6f9df87c 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownProcedure.java @@ -65,47 +65,43 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.ProcedureException; - import java.util.function.Function; public class DownProcedure implements Procedure { - private final Class downClass; - private final Class reducerStateClass; - private final DownReducer downReducer; - private final Function authorization; + private final Class downClass; + private final Class reducerStateClass; + private final DownReducer downReducer; + private final Function authorization; - public DownProcedure( - Class reducerStateClass, Class downClass, - Function authorization, - DownReducer downReducer - ) { - this.downClass = downClass; - this.reducerStateClass = reducerStateClass; - this.downReducer = downReducer; - this.authorization = authorization; - } + public DownProcedure( + Class reducerStateClass, + Class downClass, + Function authorization, + DownReducer downReducer) { + this.downClass = downClass; + this.reducerStateClass = reducerStateClass; + this.downReducer = downReducer; + this.authorization = authorization; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.DOWN, downClass)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.DOWN, downClass)); + } - @Override - public Authorization authorization(Object o) { - return authorization.apply((D) o); - } + @Override + public Authorization authorization(Object o) { + return authorization.apply((D) o); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException { - try { - return downReducer.reduce((D) o, (S) reducerState, immutableAddrs, context); - } catch (Exception e) { - throw new ProcedureException(e); - } - } + @Override + public ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException { + try { + return downReducer.reduce((D) o, (S) reducerState, immutableAddrs, context); + } catch (Exception e) { + throw new ProcedureException(e); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownReducer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownReducer.java index 24ee699165..85107f7f1b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownReducer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/DownReducer.java @@ -65,5 +65,7 @@ package com.radixdlt.constraintmachine; public interface DownReducer { - ReducerResult reduce(I inputParticle, S reducerState, Resources immutableAddrs, ExecutionContext context) throws Exception; + ReducerResult reduce( + I inputParticle, S reducerState, Resources immutableAddrs, ExecutionContext context) + throws Exception; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndProcedure.java index 108dbafed1..4cbf54e695 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndProcedure.java @@ -65,46 +65,44 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.ProcedureException; - import java.util.function.Function; public class EndProcedure implements Procedure { - private final Class reducerStateClass; - private final Function authorization; - private final EndReducer endReducer; + private final Class reducerStateClass; + private final Function authorization; + private final EndReducer endReducer; - public EndProcedure( - Class reducerStateClass, - Function authorization, - EndReducer endReducer - ) { - this.reducerStateClass = reducerStateClass; - this.authorization = authorization; - this.endReducer = endReducer; - } + public EndProcedure( + Class reducerStateClass, + Function authorization, + EndReducer endReducer) { + this.reducerStateClass = reducerStateClass; + this.authorization = authorization; + this.endReducer = endReducer; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.END, null)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.END, null)); + } - @Override - public Authorization authorization(Object o) { - return authorization.apply((S) o); - } + @Override + public Authorization authorization(Object o) { + return authorization.apply((S) o); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext executionContext - ) throws ProcedureException { - try { - endReducer.reduce((S) reducerState, executionContext, immutableAddrs); - return ReducerResult.complete(); - } catch (Exception e) { - throw new ProcedureException(e); - } - } + @Override + public ReducerResult call( + Object o, + ReducerState reducerState, + Resources immutableAddrs, + ExecutionContext executionContext) + throws ProcedureException { + try { + endReducer.reduce((S) reducerState, executionContext, immutableAddrs); + return ReducerResult.complete(); + } catch (Exception e) { + throw new ProcedureException(e); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndReducer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndReducer.java index 350cd73339..a4d56794f8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndReducer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/EndReducer.java @@ -65,5 +65,5 @@ package com.radixdlt.constraintmachine; public interface EndReducer { - void reduce(S reducerState, ExecutionContext context, Resources immutableAddrs) throws Exception; + void reduce(S reducerState, ExecutionContext context, Resources immutableAddrs) throws Exception; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ExecutionContext.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ExecutionContext.java index 77dcc76284..c0064ce0c1 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ExecutionContext.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ExecutionContext.java @@ -64,15 +64,15 @@ package com.radixdlt.constraintmachine; -import com.radixdlt.atom.Txn; -import com.radixdlt.application.tokens.scrypt.Tokens; import com.radixdlt.application.tokens.scrypt.TokenHoldingBucket; +import com.radixdlt.application.tokens.scrypt.Tokens; +import com.radixdlt.atom.Txn; import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.exceptions.DefaultedSystemLoanException; +import com.radixdlt.constraintmachine.exceptions.DepletedFeeReserveException; import com.radixdlt.constraintmachine.exceptions.ExecutionContextDestroyException; import com.radixdlt.constraintmachine.exceptions.InvalidPermissionException; import com.radixdlt.constraintmachine.exceptions.InvalidResourceException; -import com.radixdlt.constraintmachine.exceptions.DepletedFeeReserveException; import com.radixdlt.constraintmachine.exceptions.MultipleFeeReserveDepositException; import com.radixdlt.constraintmachine.exceptions.NotEnoughResourcesException; import com.radixdlt.constraintmachine.exceptions.ResourceAllocationAndDestructionException; @@ -80,7 +80,6 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; - import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -88,149 +87,148 @@ // TODO: Cleanup permissions to access to these methods public final class ExecutionContext { - private final Txn txn; - private final PermissionLevel level; - private final boolean skipAuthorization; - private final TokenHoldingBucket reserve; - private ECPublicKey key; - private boolean disableResourceAllocAndDestroy; - private UInt256 feeDeposit; - private UInt256 systemLoan = UInt256.ZERO; - private int sigsLeft; - private boolean chargedOneTimeFee = false; - private List events = new ArrayList<>(); - - public ExecutionContext( - Txn txn, - PermissionLevel level, - boolean skipAuthorization, - int sigsLeft - ) { - this.txn = txn; - this.level = level; - this.skipAuthorization = skipAuthorization; - this.sigsLeft = sigsLeft; - this.reserve = new TokenHoldingBucket(Tokens.create(REAddr.ofNativeToken(), UInt256.ZERO)); - } - - public boolean skipAuthorization() { - return skipAuthorization; - } - - public void addSystemLoan(UInt256 loan) { - this.systemLoan = this.systemLoan.add(loan); - try { - this.reserve.deposit(Tokens.create(REAddr.ofNativeToken(), loan)); - } catch (InvalidResourceException e) { - throw new IllegalStateException(e); - } - } - - public List getEvents() { - return events; - } - - public void emitEvent(REEvent event) { - this.events.add(event); - } - - public void resetSigs(int sigs) { - this.sigsLeft = sigs; - } - - public void sig() throws AuthorizationException { - if (this.sigsLeft == 0) { - throw new AuthorizationException("Used up all signatures allowed"); - } - this.sigsLeft--; - } - - public int sigsLeft() { - return sigsLeft; - } - - public Tokens withdrawFeeReserve(UInt256 amount) throws InvalidResourceException, NotEnoughResourcesException { - return reserve.withdraw(REAddr.ofNativeToken(), amount); - } - - public void depositFeeReserve(Tokens tokens) throws InvalidResourceException, MultipleFeeReserveDepositException { - if (feeDeposit != null) { - throw new MultipleFeeReserveDepositException(); - } - reserve.deposit(tokens); - feeDeposit = tokens.getAmount().getLow(); - } - - public void chargeOneTimeTransactionFee(Function feeComputer) throws DepletedFeeReserveException { - if (chargedOneTimeFee) { - return; - } - - var fee = feeComputer.apply(txn); - charge(fee); - chargedOneTimeFee = true; - } - - public void charge(UInt256 amount) throws DepletedFeeReserveException { - try { - reserve.withdraw(REAddr.ofNativeToken(), amount); - } catch (InvalidResourceException e) { - throw new IllegalStateException("Should not get here", e); - } catch (NotEnoughResourcesException e) { - throw new DepletedFeeReserveException(e); - } - } - - public void payOffLoan() throws DefaultedSystemLoanException { - if (systemLoan.isZero()) { - return; - } - - try { - charge(systemLoan); - } catch (DepletedFeeReserveException e) { - throw new DefaultedSystemLoanException(e, feeDeposit); - } - systemLoan = UInt256.ZERO; - } - - public void verifyCanAllocAndDestroyResources() throws ResourceAllocationAndDestructionException { - if (disableResourceAllocAndDestroy) { - throw new ResourceAllocationAndDestructionException(); - } - } - - public void setDisableResourceAllocAndDestroy(boolean disableResourceAllocAndDestroy) { - this.disableResourceAllocAndDestroy = disableResourceAllocAndDestroy; - } - - public void setKey(ECPublicKey key) { - this.key = key; - } - - public Optional key() { - return Optional.ofNullable(key); - } - - public PermissionLevel permissionLevel() { - return level; - } - - public void verifyPermissionLevel(PermissionLevel requiredLevel) throws SignedSystemException, InvalidPermissionException { - if (this.level.compareTo(requiredLevel) < 0) { - throw new InvalidPermissionException(requiredLevel, level); - } - - if (requiredLevel.compareTo(PermissionLevel.SUPER_USER) >= 0 && key != null) { - throw new SignedSystemException(); - } - } - - public void destroy() throws DefaultedSystemLoanException, ExecutionContextDestroyException { - payOffLoan(); - - if (!reserve.isEmpty()) { - throw new ExecutionContextDestroyException(reserve); - } - } + private final Txn txn; + private final PermissionLevel level; + private final boolean skipAuthorization; + private final TokenHoldingBucket reserve; + private ECPublicKey key; + private boolean disableResourceAllocAndDestroy; + private UInt256 feeDeposit; + private UInt256 systemLoan = UInt256.ZERO; + private int sigsLeft; + private boolean chargedOneTimeFee = false; + private List events = new ArrayList<>(); + + public ExecutionContext(Txn txn, PermissionLevel level, boolean skipAuthorization, int sigsLeft) { + this.txn = txn; + this.level = level; + this.skipAuthorization = skipAuthorization; + this.sigsLeft = sigsLeft; + this.reserve = new TokenHoldingBucket(Tokens.create(REAddr.ofNativeToken(), UInt256.ZERO)); + } + + public boolean skipAuthorization() { + return skipAuthorization; + } + + public void addSystemLoan(UInt256 loan) { + this.systemLoan = this.systemLoan.add(loan); + try { + this.reserve.deposit(Tokens.create(REAddr.ofNativeToken(), loan)); + } catch (InvalidResourceException e) { + throw new IllegalStateException(e); + } + } + + public List getEvents() { + return events; + } + + public void emitEvent(REEvent event) { + this.events.add(event); + } + + public void resetSigs(int sigs) { + this.sigsLeft = sigs; + } + + public void sig() throws AuthorizationException { + if (this.sigsLeft == 0) { + throw new AuthorizationException("Used up all signatures allowed"); + } + this.sigsLeft--; + } + + public int sigsLeft() { + return sigsLeft; + } + + public Tokens withdrawFeeReserve(UInt256 amount) + throws InvalidResourceException, NotEnoughResourcesException { + return reserve.withdraw(REAddr.ofNativeToken(), amount); + } + + public void depositFeeReserve(Tokens tokens) + throws InvalidResourceException, MultipleFeeReserveDepositException { + if (feeDeposit != null) { + throw new MultipleFeeReserveDepositException(); + } + reserve.deposit(tokens); + feeDeposit = tokens.getAmount().getLow(); + } + + public void chargeOneTimeTransactionFee(Function feeComputer) + throws DepletedFeeReserveException { + if (chargedOneTimeFee) { + return; + } + + var fee = feeComputer.apply(txn); + charge(fee); + chargedOneTimeFee = true; + } + + public void charge(UInt256 amount) throws DepletedFeeReserveException { + try { + reserve.withdraw(REAddr.ofNativeToken(), amount); + } catch (InvalidResourceException e) { + throw new IllegalStateException("Should not get here", e); + } catch (NotEnoughResourcesException e) { + throw new DepletedFeeReserveException(e); + } + } + + public void payOffLoan() throws DefaultedSystemLoanException { + if (systemLoan.isZero()) { + return; + } + + try { + charge(systemLoan); + } catch (DepletedFeeReserveException e) { + throw new DefaultedSystemLoanException(e, feeDeposit); + } + systemLoan = UInt256.ZERO; + } + + public void verifyCanAllocAndDestroyResources() throws ResourceAllocationAndDestructionException { + if (disableResourceAllocAndDestroy) { + throw new ResourceAllocationAndDestructionException(); + } + } + + public void setDisableResourceAllocAndDestroy(boolean disableResourceAllocAndDestroy) { + this.disableResourceAllocAndDestroy = disableResourceAllocAndDestroy; + } + + public void setKey(ECPublicKey key) { + this.key = key; + } + + public Optional key() { + return Optional.ofNullable(key); + } + + public PermissionLevel permissionLevel() { + return level; + } + + public void verifyPermissionLevel(PermissionLevel requiredLevel) + throws SignedSystemException, InvalidPermissionException { + if (this.level.compareTo(requiredLevel) < 0) { + throw new InvalidPermissionException(requiredLevel, level); + } + + if (requiredLevel.compareTo(PermissionLevel.SUPER_USER) >= 0 && key != null) { + throw new SignedSystemException(); + } + } + + public void destroy() throws DefaultedSystemLoanException, ExecutionContextDestroyException { + payOffLoan(); + + if (!reserve.isEmpty()) { + throw new ExecutionContextDestroyException(reserve); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedReducer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedReducer.java index c81dc2acb5..4df0317fe5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedReducer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedReducer.java @@ -67,10 +67,10 @@ import com.radixdlt.constraintmachine.exceptions.ProcedureException; public interface IndexedReducer { - ReducerResult reduce( - S reducerState, - IndexedSubstateIterator inputState, - ExecutionContext context, - Resources immutableAddrs - ) throws ProcedureException; + ReducerResult reduce( + S reducerState, + IndexedSubstateIterator inputState, + ExecutionContext context, + Resources immutableAddrs) + throws ProcedureException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedSubstateIterator.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedSubstateIterator.java index b1ef0f2fd5..9675e434e4 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedSubstateIterator.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/IndexedSubstateIterator.java @@ -66,39 +66,39 @@ import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.utils.Bytes; - import java.util.Iterator; public final class IndexedSubstateIterator { - private SubstateIndex index; - private final Iterator iterator; + private SubstateIndex index; + private final Iterator iterator; - public IndexedSubstateIterator(SubstateIndex index, Iterator iterator) { - this.index = index; - this.iterator = iterator; - } + public IndexedSubstateIterator(SubstateIndex index, Iterator iterator) { + this.index = index; + this.iterator = iterator; + } - public void verifyPostTypePrefixEquals(byte[] prefix) throws ProcedureException { - if (index.getPrefix().length != 1 + prefix.length) { - throw new ProcedureException("Invalid shutdownAll prefix"); - } - for (int i = 0; i < prefix.length; i++) { - if (index.getPrefix()[i + 1] != prefix[i]) { - throw new ProcedureException( - "Invalid shutdownAll prefix, expected " + Bytes.toHexString(prefix) - + " but was " + Bytes.toHexString(index.getPrefix()) - ); - } - } - } + public void verifyPostTypePrefixEquals(byte[] prefix) throws ProcedureException { + if (index.getPrefix().length != 1 + prefix.length) { + throw new ProcedureException("Invalid shutdownAll prefix"); + } + for (int i = 0; i < prefix.length; i++) { + if (index.getPrefix()[i + 1] != prefix[i]) { + throw new ProcedureException( + "Invalid shutdownAll prefix, expected " + + Bytes.toHexString(prefix) + + " but was " + + Bytes.toHexString(index.getPrefix())); + } + } + } - public void verifyPostTypePrefixIsEmpty() throws ProcedureException { - if (index.getPrefix().length != 1) { - throw new ProcedureException("Invalid shutdownAll prefix"); - } - } + public void verifyPostTypePrefixIsEmpty() throws ProcedureException { + if (index.getPrefix().length != 1) { + throw new ProcedureException("Invalid shutdownAll prefix"); + } + } - public Iterator iterator() { - return iterator; - } + public Iterator iterator() { + return iterator; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeyDeserializer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeyDeserializer.java index 9c41341974..6234285cf8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeyDeserializer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeyDeserializer.java @@ -65,9 +65,8 @@ package com.radixdlt.constraintmachine; import com.radixdlt.serialization.DeserializeException; - import java.nio.ByteBuffer; public interface KeyDeserializer { - Object deserialize(ByteBuffer buf) throws DeserializeException; + Object deserialize(ByteBuffer buf) throws DeserializeException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeySerializer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeySerializer.java index 779bcfa19e..7c8163f772 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeySerializer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/KeySerializer.java @@ -67,5 +67,5 @@ import java.nio.ByteBuffer; public interface KeySerializer { - void serialize(Object key, ByteBuffer buf); + void serialize(Object key, ByteBuffer buf); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/OpSignature.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/OpSignature.java index bf398b616b..76fcf706f9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/OpSignature.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/OpSignature.java @@ -65,48 +65,46 @@ package com.radixdlt.constraintmachine; import com.radixdlt.identifiers.REAddr; - import java.util.Objects; public final class OpSignature { - private final REOp op; - private final Object type; + private final REOp op; + private final Object type; - private OpSignature(REOp op, Object type) { - this.op = op; - this.type = type; - } + private OpSignature(REOp op, Object type) { + this.op = op; + this.type = type; + } - public REOp op() { - return op; - } + public REOp op() { + return op; + } - public static OpSignature ofSubstateUpdate(REOp op, Class particleClass) { - return new OpSignature(op, particleClass); - } + public static OpSignature ofSubstateUpdate(REOp op, Class particleClass) { + return new OpSignature(op, particleClass); + } - public static OpSignature ofMethod(REOp op, REAddr addr) { - return new OpSignature(op, addr); - } + public static OpSignature ofMethod(REOp op, REAddr addr) { + return new OpSignature(op, addr); + } - @Override - public int hashCode() { - return Objects.hash(op, type); - } + @Override + public int hashCode() { + return Objects.hash(op, type); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof OpSignature)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof OpSignature)) { + return false; + } - var other = (OpSignature) o; - return Objects.equals(this.op, other.op) - && Objects.equals(this.type, other.type); - } + var other = (OpSignature) o; + return Objects.equals(this.op, other.op) && Objects.equals(this.type, other.type); + } - @Override - public String toString() { - return String.format("%s{op=%s type=%s}", this.getClass().getSimpleName(), this.op, this.type); - } + @Override + public String toString() { + return String.format("%s{op=%s type=%s}", this.getClass().getSimpleName(), this.op, this.type); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Particle.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Particle.java index a2b306d2ec..dd0d7ea1a6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Particle.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Particle.java @@ -64,8 +64,5 @@ package com.radixdlt.constraintmachine; -/** - * Marker interface for raw substates - */ -public interface Particle { -} +/** Marker interface for raw substates */ +public interface Particle {} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/PermissionLevel.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/PermissionLevel.java index b9096c3a6a..2d4337ff27 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/PermissionLevel.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/PermissionLevel.java @@ -65,12 +65,11 @@ package com.radixdlt.constraintmachine; /** - * Ordered Execution level which specifies what level of permission the - * constraint machine is running on. Depending on this level, some particle - * transitions may be allowed or not. + * Ordered Execution level which specifies what level of permission the constraint machine is + * running on. Depending on this level, some particle transitions may be allowed or not. */ public enum PermissionLevel { - USER, - SUPER_USER, - SYSTEM // ordering matters + USER, + SUPER_USER, + SYSTEM // ordering matters } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedure.java index c024dc0b34..1b1cd8b651 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedure.java @@ -67,12 +67,11 @@ import com.radixdlt.constraintmachine.exceptions.ProcedureException; public interface Procedure { - ProcedureKey key(); - Authorization authorization(Object o); - ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException; + ProcedureKey key(); + + Authorization authorization(Object o); + + ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ProcedureKey.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ProcedureKey.java index f15dbe1286..beb1a987b8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ProcedureKey.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ProcedureKey.java @@ -67,40 +67,43 @@ import java.util.Objects; public final class ProcedureKey { - private final Class currentState; - private final OpSignature opSignature; + private final Class currentState; + private final OpSignature opSignature; - private ProcedureKey(Class currentState, OpSignature opSignature) { - this.currentState = currentState; - this.opSignature = opSignature; - } + private ProcedureKey(Class currentState, OpSignature opSignature) { + this.currentState = currentState; + this.opSignature = opSignature; + } - public static ProcedureKey of(Class currentState, OpSignature opSignature) { - return new ProcedureKey(currentState, opSignature); - } + public static ProcedureKey of( + Class currentState, OpSignature opSignature) { + return new ProcedureKey(currentState, opSignature); + } - public OpSignature opSignature() { - return opSignature; - } + public OpSignature opSignature() { + return opSignature; + } - @Override - public int hashCode() { - return Objects.hash(opSignature, currentState); - } + @Override + public int hashCode() { + return Objects.hash(opSignature, currentState); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof ProcedureKey)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof ProcedureKey)) { + return false; + } - var other = (ProcedureKey) o; - return Objects.equals(this.opSignature, other.opSignature) - && Objects.equals(this.currentState, other.currentState); - } + var other = (ProcedureKey) o; + return Objects.equals(this.opSignature, other.opSignature) + && Objects.equals(this.currentState, other.currentState); + } - @Override - public String toString() { - return String.format("%s{current=%s opSignature=%s}", this.getClass().getSimpleName(), currentState, opSignature); - } + @Override + public String toString() { + return String.format( + "%s{current=%s opSignature=%s}", + this.getClass().getSimpleName(), currentState, opSignature); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedures.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedures.java index 5d6525dba7..a710e6d7ea 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedures.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Procedures.java @@ -65,37 +65,34 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.MissingProcedureException; - import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; public final class Procedures { - private final Map procedures; + private final Map procedures; - public Procedures(Map procedures) { - this.procedures = procedures; - } + public Procedures(Map procedures) { + this.procedures = procedures; + } - public static Procedures empty() { - return new Procedures(Map.of()); - } + public static Procedures empty() { + return new Procedures(Map.of()); + } - public Procedures combine(Procedures other) { - var combinedProcedures = - Stream.concat( - this.procedures.entrySet().stream(), - other.procedures.entrySet().stream() - ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + public Procedures combine(Procedures other) { + var combinedProcedures = + Stream.concat(this.procedures.entrySet().stream(), other.procedures.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - return new Procedures(combinedProcedures); - } + return new Procedures(combinedProcedures); + } - public Procedure getProcedure(ProcedureKey key) throws MissingProcedureException { - var procedure = procedures.get(key); - if (procedure == null) { - throw new MissingProcedureException(key); - } - return procedure; - } + public Procedure getProcedure(ProcedureKey key) throws MissingProcedureException { + var procedure = procedures.get(key); + if (procedure == null) { + throw new MissingProcedureException(key); + } + return procedure; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REEvent.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REEvent.java index a81dd29118..bd895ca7c5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REEvent.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REEvent.java @@ -64,5 +64,4 @@ package com.radixdlt.constraintmachine; -public interface REEvent { -} +public interface REEvent {} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REInstruction.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REInstruction.java index 47f575989a..1ec2e80b8b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REInstruction.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REInstruction.java @@ -64,292 +64,308 @@ package com.radixdlt.constraintmachine; -import com.radixdlt.atom.SubstateId; import com.radixdlt.atom.REFieldSerialization; +import com.radixdlt.atom.SubstateId; import com.radixdlt.engine.parser.REParser; import com.radixdlt.engine.parser.exceptions.REInstructionDataDeserializeException; import com.radixdlt.serialization.DeserializeException; import com.radixdlt.utils.Bytes; - import java.nio.ByteBuffer; -/** - * Unparsed Low level instruction into Radix Engine - */ +/** Unparsed Low level instruction into Radix Engine */ public final class REInstruction { - private enum LengthType { - FIXED { - @Override - void verifyLimits(int min, int max) { - if (min != max) { - throw new IllegalStateException(); - } - } + private enum LengthType { + FIXED { + @Override + void verifyLimits(int min, int max) { + if (min != max) { + throw new IllegalStateException(); + } + } - @Override - ByteBuffer setNextLimit(ByteBuffer buf, int min, int max) { - return buf.limit(buf.position() + max); - } - }, - VARIABLE { - @Override - void verifyLimits(int min, int max) { - if (min >= max) { - throw new IllegalStateException(); - } - } + @Override + ByteBuffer setNextLimit(ByteBuffer buf, int min, int max) { + return buf.limit(buf.position() + max); + } + }, + VARIABLE { + @Override + void verifyLimits(int min, int max) { + if (min >= max) { + throw new IllegalStateException(); + } + } - @Override - ByteBuffer setNextLimit(ByteBuffer buf, int min, int max) throws DeserializeException { - int size = REFieldSerialization.deserializeUnsignedShort(buf, min, max); - return buf.limit(buf.position() + size); - } - }; + @Override + ByteBuffer setNextLimit(ByteBuffer buf, int min, int max) throws DeserializeException { + int size = REFieldSerialization.deserializeUnsignedShort(buf, min, max); + return buf.limit(buf.position() + size); + } + }; - abstract void verifyLimits(int min, int max); + abstract void verifyLimits(int min, int max); - abstract ByteBuffer setNextLimit(ByteBuffer buf, int min, int max) throws DeserializeException; - } + abstract ByteBuffer setNextLimit(ByteBuffer buf, int min, int max) throws DeserializeException; + } - public enum REMicroOp { - END((byte) 0x0, REOp.END, LengthType.FIXED, 0, 0) { - @Override - Object read(REParser.ParserState parserState, ByteBuffer buf) { - return null; - } - }, - SYSCALL((byte) 0x1, REOp.SYSCALL, LengthType.VARIABLE, 0, 512) { - @Override - Object read(REParser.ParserState parserState, ByteBuffer buf) { - // TODO: Remove buffer copy - var callData = new byte[buf.remaining()]; - buf.get(callData, 0, buf.remaining()); - return new CallData(callData); - } - }, - UP((byte) 0x2, REOp.UP, LengthType.VARIABLE, 2, 512) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var substateId = SubstateId.ofSubstate(parserState.txnId(), parserState.upSubstateCount()); - var start = buf.position(); - buf.position(start + buf.remaining()); - return new UpSubstate(substateId, buf.array(), start, buf.limit() - start); - } - }, - READ((byte) 0x3, REOp.READ, LengthType.FIXED, SubstateId.BYTES, SubstateId.BYTES) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - return SubstateId.fromBuffer(buf); - } - }, - LREAD((byte) 0x4, REOp.READ, LengthType.FIXED, Short.BYTES, Short.BYTES) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - int index = REFieldSerialization.deserializeUnsignedShort(buf, 0, parserState.upSubstateCount() - 1); - return SubstateId.ofSubstate(parserState.txnId(), index); - } - }, - VREAD((byte) 0x5, REOp.READ, LengthType.VARIABLE, SubstateId.BYTES + 1, 512) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var bytes = new byte[buf.remaining()]; - buf.get(bytes, 0, buf.remaining()); - return SubstateId.fromBytes(bytes); - } - }, - LVREAD((byte) 0x6, REOp.READ, LengthType.VARIABLE, Short.BYTES + 1, 512) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var index = REFieldSerialization.deserializeUnsignedShort(buf, 0, parserState.upSubstateCount() - 1); - var parent = SubstateId.ofSubstate(parserState.txnId(), index); - var bytes = new byte[buf.remaining()]; - buf.get(bytes, 0, buf.remaining()); - return SubstateId.ofVirtualSubstate(parent, bytes); - } - }, - DOWN((byte) 0x7, REOp.DOWN, LengthType.FIXED, SubstateId.BYTES, SubstateId.BYTES) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - return SubstateId.fromBuffer(buf); - } - }, - LDOWN((byte) 0x8, REOp.DOWN, LengthType.FIXED, Short.BYTES, Short.BYTES) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var index = REFieldSerialization.deserializeUnsignedShort(buf, 0, parserState.upSubstateCount() - 1); - return SubstateId.ofSubstate(parserState.txnId(), index); - } - }, - VDOWN((byte) 0x9, REOp.DOWN, LengthType.VARIABLE, SubstateId.BYTES + 1, 512) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var bytes = new byte[buf.remaining()]; - buf.get(bytes, 0, buf.remaining()); - return SubstateId.fromBytes(bytes); - } - }, - LVDOWN((byte) 0xa, REOp.DOWN, LengthType.VARIABLE, Short.BYTES + 1, 512) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var index = REFieldSerialization.deserializeUnsignedShort(buf, 0, parserState.upSubstateCount() - 1); - var parent = SubstateId.ofSubstate(parserState.txnId(), index); - var bytes = new byte[buf.remaining()]; - buf.get(bytes); - return SubstateId.ofVirtualSubstate(parent, bytes); - } - }, - SIG((byte) 0xb, REOp.SIG, LengthType.FIXED, 1 + 32 + 32, 1 + 32 + 32) { - @Override - Object read(REParser.ParserState parserState, ByteBuffer b) throws DeserializeException { - return REFieldSerialization.deserializeSignature(b); - } - }, - MSG((byte) 0xc, REOp.MSG, LengthType.VARIABLE, 1, 255) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException { - var bytes = new byte[buf.remaining()]; - buf.get(bytes); - return bytes; - } - }, - HEADER((byte) 0xd, REOp.HEADER, LengthType.FIXED, 2, 2) { - @Override - Object read(REParser.ParserState parserState, ByteBuffer b) throws DeserializeException { - int version = b.get(); - if (version != 0) { - throw new DeserializeException("Version must be 0"); - } - var flags = b.get(); - if ((flags & 0xe) != 0) { - throw new DeserializeException("Invalid flags"); - } - return (flags & 0x1) == 1; - } - }, - READINDEX((byte) 0xe, REOp.READINDEX, LengthType.VARIABLE, 1, 64) { - @Override - Object read(REParser.ParserState parserState, ByteBuffer buf) { - var bytes = new byte[buf.remaining()]; - buf.get(bytes); - return bytes; - } - }, - DOWNINDEX((byte) 0xf, REOp.DOWNINDEX, LengthType.VARIABLE, 1, 64) { - @Override - public Object read(REParser.ParserState parserState, ByteBuffer buf) { - var bytes = new byte[buf.remaining()]; - buf.get(bytes); - return bytes; - } - }; + public enum REMicroOp { + END((byte) 0x0, REOp.END, LengthType.FIXED, 0, 0) { + @Override + Object read(REParser.ParserState parserState, ByteBuffer buf) { + return null; + } + }, + SYSCALL((byte) 0x1, REOp.SYSCALL, LengthType.VARIABLE, 0, 512) { + @Override + Object read(REParser.ParserState parserState, ByteBuffer buf) { + // TODO: Remove buffer copy + var callData = new byte[buf.remaining()]; + buf.get(callData, 0, buf.remaining()); + return new CallData(callData); + } + }, + UP((byte) 0x2, REOp.UP, LengthType.VARIABLE, 2, 512) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var substateId = SubstateId.ofSubstate(parserState.txnId(), parserState.upSubstateCount()); + var start = buf.position(); + buf.position(start + buf.remaining()); + return new UpSubstate(substateId, buf.array(), start, buf.limit() - start); + } + }, + READ((byte) 0x3, REOp.READ, LengthType.FIXED, SubstateId.BYTES, SubstateId.BYTES) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + return SubstateId.fromBuffer(buf); + } + }, + LREAD((byte) 0x4, REOp.READ, LengthType.FIXED, Short.BYTES, Short.BYTES) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + int index = + REFieldSerialization.deserializeUnsignedShort( + buf, 0, parserState.upSubstateCount() - 1); + return SubstateId.ofSubstate(parserState.txnId(), index); + } + }, + VREAD((byte) 0x5, REOp.READ, LengthType.VARIABLE, SubstateId.BYTES + 1, 512) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var bytes = new byte[buf.remaining()]; + buf.get(bytes, 0, buf.remaining()); + return SubstateId.fromBytes(bytes); + } + }, + LVREAD((byte) 0x6, REOp.READ, LengthType.VARIABLE, Short.BYTES + 1, 512) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var index = + REFieldSerialization.deserializeUnsignedShort( + buf, 0, parserState.upSubstateCount() - 1); + var parent = SubstateId.ofSubstate(parserState.txnId(), index); + var bytes = new byte[buf.remaining()]; + buf.get(bytes, 0, buf.remaining()); + return SubstateId.ofVirtualSubstate(parent, bytes); + } + }, + DOWN((byte) 0x7, REOp.DOWN, LengthType.FIXED, SubstateId.BYTES, SubstateId.BYTES) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + return SubstateId.fromBuffer(buf); + } + }, + LDOWN((byte) 0x8, REOp.DOWN, LengthType.FIXED, Short.BYTES, Short.BYTES) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var index = + REFieldSerialization.deserializeUnsignedShort( + buf, 0, parserState.upSubstateCount() - 1); + return SubstateId.ofSubstate(parserState.txnId(), index); + } + }, + VDOWN((byte) 0x9, REOp.DOWN, LengthType.VARIABLE, SubstateId.BYTES + 1, 512) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var bytes = new byte[buf.remaining()]; + buf.get(bytes, 0, buf.remaining()); + return SubstateId.fromBytes(bytes); + } + }, + LVDOWN((byte) 0xa, REOp.DOWN, LengthType.VARIABLE, Short.BYTES + 1, 512) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var index = + REFieldSerialization.deserializeUnsignedShort( + buf, 0, parserState.upSubstateCount() - 1); + var parent = SubstateId.ofSubstate(parserState.txnId(), index); + var bytes = new byte[buf.remaining()]; + buf.get(bytes); + return SubstateId.ofVirtualSubstate(parent, bytes); + } + }, + SIG((byte) 0xb, REOp.SIG, LengthType.FIXED, 1 + 32 + 32, 1 + 32 + 32) { + @Override + Object read(REParser.ParserState parserState, ByteBuffer b) throws DeserializeException { + return REFieldSerialization.deserializeSignature(b); + } + }, + MSG((byte) 0xc, REOp.MSG, LengthType.VARIABLE, 1, 255) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException { + var bytes = new byte[buf.remaining()]; + buf.get(bytes); + return bytes; + } + }, + HEADER((byte) 0xd, REOp.HEADER, LengthType.FIXED, 2, 2) { + @Override + Object read(REParser.ParserState parserState, ByteBuffer b) throws DeserializeException { + int version = b.get(); + if (version != 0) { + throw new DeserializeException("Version must be 0"); + } + var flags = b.get(); + if ((flags & 0xe) != 0) { + throw new DeserializeException("Invalid flags"); + } + return (flags & 0x1) == 1; + } + }, + READINDEX((byte) 0xe, REOp.READINDEX, LengthType.VARIABLE, 1, 64) { + @Override + Object read(REParser.ParserState parserState, ByteBuffer buf) { + var bytes = new byte[buf.remaining()]; + buf.get(bytes); + return bytes; + } + }, + DOWNINDEX((byte) 0xf, REOp.DOWNINDEX, LengthType.VARIABLE, 1, 64) { + @Override + public Object read(REParser.ParserState parserState, ByteBuffer buf) { + var bytes = new byte[buf.remaining()]; + buf.get(bytes); + return bytes; + } + }; - private final REOp op; - private final byte opCode; - private final LengthType lengthType; - private final int minLength; - private final int maxLength; + private final REOp op; + private final byte opCode; + private final LengthType lengthType; + private final int minLength; + private final int maxLength; - REMicroOp(byte opCode, REOp op, LengthType lengthType, int minLength, int maxLength) { - // Sanity checks - lengthType.verifyLimits(minLength, maxLength); + REMicroOp(byte opCode, REOp op, LengthType lengthType, int minLength, int maxLength) { + // Sanity checks + lengthType.verifyLimits(minLength, maxLength); - this.opCode = opCode; - this.op = op; - this.lengthType = lengthType; - this.minLength = minLength; - this.maxLength = maxLength; - } + this.opCode = opCode; + this.op = op; + this.lengthType = lengthType; + this.minLength = minLength; + this.maxLength = maxLength; + } - public REOp getOp() { - return op; - } + public REOp getOp() { + return op; + } - abstract Object read(REParser.ParserState parserState, ByteBuffer buf) throws DeserializeException; + abstract Object read(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException; - public byte opCode() { - return opCode; - } + public byte opCode() { + return opCode; + } - static REMicroOp fromByte(byte op) throws DeserializeException { - for (var microOp : REMicroOp.values()) { - if (microOp.opCode == op) { - return microOp; - } - } + static REMicroOp fromByte(byte op) throws DeserializeException { + for (var microOp : REMicroOp.values()) { + if (microOp.opCode == op) { + return microOp; + } + } - throw new DeserializeException("Unknown opcode: " + op); - } - } + throw new DeserializeException("Unknown opcode: " + op); + } + } - private final REMicroOp microOp; - private final Object data; - private final byte[] array; - private final int offset; - private final int length; + private final REMicroOp microOp; + private final Object data; + private final byte[] array; + private final int offset; + private final int length; - private REInstruction(REMicroOp microOp, Object data, byte[] array, int offset, int length) { - this.microOp = microOp; - this.data = data; - this.array = array; - this.offset = offset; - this.length = length; - } + private REInstruction(REMicroOp microOp, Object data, byte[] array, int offset, int length) { + this.microOp = microOp; + this.data = data; + this.array = array; + this.offset = offset; + this.length = length; + } - public int getDataLength() { - return length; - } + public int getDataLength() { + return length; + } - public REMicroOp getMicroOp() { - return microOp; - } + public REMicroOp getMicroOp() { + return microOp; + } - public ByteBuffer getDataByteBuffer() { - return ByteBuffer.wrap(array, offset, length); - } + public ByteBuffer getDataByteBuffer() { + return ByteBuffer.wrap(array, offset, length); + } - public T getData() { - return (T) data; - } + public T getData() { + return (T) data; + } - public boolean isStateUpdate() { - return microOp.op.isSubstateUpdate(); - } + public boolean isStateUpdate() { + return microOp.op.isSubstateUpdate(); + } - public static REInstruction readFrom(REParser.ParserState parserState, ByteBuffer buf) - throws DeserializeException, REInstructionDataDeserializeException { + public static REInstruction readFrom(REParser.ParserState parserState, ByteBuffer buf) + throws DeserializeException, REInstructionDataDeserializeException { - var microOp = REMicroOp.fromByte(buf.get()); - var savedLimit = buf.limit(); - var pos = buf.position(); - try { - // Set limit - buf = microOp.lengthType.setNextLimit(buf, microOp.minLength, microOp.maxLength); + var microOp = REMicroOp.fromByte(buf.get()); + var savedLimit = buf.limit(); + var pos = buf.position(); + try { + // Set limit + buf = microOp.lengthType.setNextLimit(buf, microOp.minLength, microOp.maxLength); - var data = microOp.read(parserState, buf); + var data = microOp.read(parserState, buf); - // Sanity check - if (buf.hasRemaining()) { - throw new IllegalStateException(); - } + // Sanity check + if (buf.hasRemaining()) { + throw new IllegalStateException(); + } - var length = buf.position() - pos; - buf.limit(savedLimit); - return new REInstruction(microOp, data, buf.array(), pos, length); - } catch (Exception e) { - throw new REInstructionDataDeserializeException(microOp, e); - } - } + var length = buf.position() - pos; + buf.limit(savedLimit); + return new REInstruction(microOp, data, buf.array(), pos, length); + } catch (Exception e) { + throw new REInstructionDataDeserializeException(microOp, e); + } + } - private static Object dataString(Object data) { - if (data instanceof byte[]) { - return Bytes.toHexString((byte[]) data); - } else { - return data; - } - } + private static Object dataString(Object data) { + if (data instanceof byte[]) { + return Bytes.toHexString((byte[]) data); + } else { + return data; + } + } - @Override - public String toString() { - return String.format("%s %s", microOp, dataString(data)); - } + @Override + public String toString() { + return String.format("%s %s", microOp, dataString(data)); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REOp.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REOp.java index 6774bd0121..a36a9bed6b 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REOp.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REOp.java @@ -65,29 +65,29 @@ package com.radixdlt.constraintmachine; public enum REOp { - END(false), - SYSCALL(false), - UP(true), - READ(false), - DOWN(true), - DOWNINDEX(true), - READINDEX(false), - SIG(false), - MSG(false), - HEADER(false); + END(false), + SYSCALL(false), + UP(true), + READ(false), + DOWN(true), + DOWNINDEX(true), + READINDEX(false), + SIG(false), + MSG(false), + HEADER(false); - private final boolean isSubstateUpdate; + private final boolean isSubstateUpdate; - REOp(boolean isSubstateUpdate) { - this.isSubstateUpdate = isSubstateUpdate; - } + REOp(boolean isSubstateUpdate) { + this.isSubstateUpdate = isSubstateUpdate; + } - public boolean isSubstateUpdate() { - return isSubstateUpdate; - } + public boolean isSubstateUpdate() { + return isSubstateUpdate; + } - @Override - public String toString() { - return this.name(); - } -} \ No newline at end of file + @Override + public String toString() { + return this.name(); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REProcessedTxn.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REProcessedTxn.java index c9b129a619..ebe73fdba3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REProcessedTxn.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REProcessedTxn.java @@ -64,115 +64,114 @@ package com.radixdlt.constraintmachine; -import com.radixdlt.atom.SubstateId; -import com.radixdlt.atom.Txn; import com.radixdlt.application.system.state.EpochData; import com.radixdlt.application.system.state.RoundData; -import com.radixdlt.engine.parser.ParsedTxn; +import com.radixdlt.atom.SubstateId; +import com.radixdlt.atom.Txn; import com.radixdlt.crypto.ECPublicKey; +import com.radixdlt.engine.parser.ParsedTxn; import com.radixdlt.identifiers.AID; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -/** - * Transaction which has been successfully parsed and state checked by radix engine - */ +/** Transaction which has been successfully parsed and state checked by radix engine */ public final class REProcessedTxn { - private final List> stateUpdates; - private final ParsedTxn parsedTxn; - private final ECPublicKey signedByKey; - private final List events; - - public REProcessedTxn( - ParsedTxn parsedTxn, - ECPublicKey signedByKey, - List> stateUpdates, - List events - ) { - this.parsedTxn = parsedTxn; - this.signedByKey = signedByKey; - this.stateUpdates = stateUpdates; - this.events = events; - } - - public List getEvents() { - return events; - } - - public ParsedTxn getParsedTxn() { - return parsedTxn; - } - - public UInt256 getFeePaid() { - return parsedTxn.getFeePaid(); - } - - public Optional getMsg() { - return parsedTxn.getMsg(); - } - - public Optional getSignedBy() { - return Optional.ofNullable(signedByKey); - } - - public AID getTxnId() { - return parsedTxn.txn().getId(); - } - - public Txn getTxn() { - return parsedTxn.txn(); - } - - // FIXME: Currently a hack, better would be to put this at transaction layer for fees - public boolean isSystemOnly() { - return stateUpdates().anyMatch(i -> i.getParsed() instanceof RoundData) - || stateUpdates().anyMatch(i -> i.getParsed() instanceof EpochData); - } - - public List> getGroupedStateUpdates() { - return stateUpdates; - } - - public Stream stateUpdates() { - return stateUpdates.stream().flatMap(List::stream); - } - - public Stream substateDependencies() { - return parsedTxn.instructions().stream() - .flatMap(i -> { - if (i.getMicroOp() == REInstruction.REMicroOp.DOWN || i.getMicroOp() == REInstruction.REMicroOp.READ) { - SubstateId substateId = i.getData(); - return Stream.of(substateId); - } else if (i.getMicroOp() == REInstruction.REMicroOp.VDOWN || i.getMicroOp() == REInstruction.REMicroOp.VREAD) { - SubstateId substateId = i.getData(); - return Stream.of(substateId); - } - return Stream.empty(); - }); - } - - @Override - public int hashCode() { - return Objects.hash(stateUpdates, parsedTxn); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof REProcessedTxn)) { - return false; - } - - var other = (REProcessedTxn) o; - return Objects.equals(this.stateUpdates, other.stateUpdates) - && Objects.equals(this.parsedTxn, other.parsedTxn); - } - - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), parsedTxn); - } + private final List> stateUpdates; + private final ParsedTxn parsedTxn; + private final ECPublicKey signedByKey; + private final List events; + + public REProcessedTxn( + ParsedTxn parsedTxn, + ECPublicKey signedByKey, + List> stateUpdates, + List events) { + this.parsedTxn = parsedTxn; + this.signedByKey = signedByKey; + this.stateUpdates = stateUpdates; + this.events = events; + } + + public List getEvents() { + return events; + } + + public ParsedTxn getParsedTxn() { + return parsedTxn; + } + + public UInt256 getFeePaid() { + return parsedTxn.getFeePaid(); + } + + public Optional getMsg() { + return parsedTxn.getMsg(); + } + + public Optional getSignedBy() { + return Optional.ofNullable(signedByKey); + } + + public AID getTxnId() { + return parsedTxn.txn().getId(); + } + + public Txn getTxn() { + return parsedTxn.txn(); + } + + // FIXME: Currently a hack, better would be to put this at transaction layer for fees + public boolean isSystemOnly() { + return stateUpdates().anyMatch(i -> i.getParsed() instanceof RoundData) + || stateUpdates().anyMatch(i -> i.getParsed() instanceof EpochData); + } + + public List> getGroupedStateUpdates() { + return stateUpdates; + } + + public Stream stateUpdates() { + return stateUpdates.stream().flatMap(List::stream); + } + + public Stream substateDependencies() { + return parsedTxn.instructions().stream() + .flatMap( + i -> { + if (i.getMicroOp() == REInstruction.REMicroOp.DOWN + || i.getMicroOp() == REInstruction.REMicroOp.READ) { + SubstateId substateId = i.getData(); + return Stream.of(substateId); + } else if (i.getMicroOp() == REInstruction.REMicroOp.VDOWN + || i.getMicroOp() == REInstruction.REMicroOp.VREAD) { + SubstateId substateId = i.getData(); + return Stream.of(substateId); + } + return Stream.empty(); + }); + } + + @Override + public int hashCode() { + return Objects.hash(stateUpdates, parsedTxn); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof REProcessedTxn)) { + return false; + } + + var other = (REProcessedTxn) o; + return Objects.equals(this.stateUpdates, other.stateUpdates) + && Objects.equals(this.parsedTxn, other.parsedTxn); + } + + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), parsedTxn); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REStateUpdate.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REStateUpdate.java index 43e10e8078..b1919c7bba 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REStateUpdate.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/REStateUpdate.java @@ -65,85 +65,87 @@ package com.radixdlt.constraintmachine; import com.radixdlt.atom.SubstateId; - import java.nio.ByteBuffer; import java.util.Objects; import java.util.function.Supplier; -/** - * Instruction which has been parsed and state checked by Radix Engine - */ +/** Instruction which has been parsed and state checked by Radix Engine */ public final class REStateUpdate { - private final REOp op; - private final SubstateId id; - private final byte typeByte; - private final Object parsed; - private final Supplier stateBuf; - private final int instructionIndex; - - private REStateUpdate(REOp op, int instructionIndex, SubstateId id, byte typeByte, Object parsed, Supplier stateBuf) { - Objects.requireNonNull(op); - - this.op = op; - this.instructionIndex = instructionIndex; - this.id = id; - this.typeByte = typeByte; - this.parsed = parsed; - this.stateBuf = stateBuf; - } - - public static REStateUpdate of( - REOp op, - int instructionIndex, - SubstateId substateId, - byte typeByte, - Object parsed, - Supplier stateBuf - ) { - if (op != REOp.DOWN && op != REOp.UP) { - throw new IllegalArgumentException(); - } - return new REStateUpdate(op, instructionIndex, substateId, typeByte, parsed, stateBuf); - } - - public byte typeByte() { - return typeByte; - } - - public int getInstructionIndex() { - return instructionIndex; - } - - public SubstateId getId() { - return id; - } - - public ByteBuffer getStateBuf() { - return stateBuf.get(); - } - - public boolean isBootUp() { - return this.op == REOp.UP; - } - - public boolean isShutDown() { - return this.op == REOp.DOWN; - } - - public Object getParsed() { - return parsed; - } - - public RawSubstateBytes getRawSubstateBytes() { - var buffer = stateBuf.get(); - int remaining = buffer.remaining(); - var buf = new byte[remaining]; - buffer.get(buf); - return new RawSubstateBytes(id.asBytes(), buf); - } - - @Override - public String toString() { - return String.format("%s{op=%s state=%s}", getClass().getSimpleName(), op, parsed); - } + private final REOp op; + private final SubstateId id; + private final byte typeByte; + private final Object parsed; + private final Supplier stateBuf; + private final int instructionIndex; + + private REStateUpdate( + REOp op, + int instructionIndex, + SubstateId id, + byte typeByte, + Object parsed, + Supplier stateBuf) { + Objects.requireNonNull(op); + + this.op = op; + this.instructionIndex = instructionIndex; + this.id = id; + this.typeByte = typeByte; + this.parsed = parsed; + this.stateBuf = stateBuf; + } + + public static REStateUpdate of( + REOp op, + int instructionIndex, + SubstateId substateId, + byte typeByte, + Object parsed, + Supplier stateBuf) { + if (op != REOp.DOWN && op != REOp.UP) { + throw new IllegalArgumentException(); + } + return new REStateUpdate(op, instructionIndex, substateId, typeByte, parsed, stateBuf); + } + + public byte typeByte() { + return typeByte; + } + + public int getInstructionIndex() { + return instructionIndex; + } + + public SubstateId getId() { + return id; + } + + public ByteBuffer getStateBuf() { + return stateBuf.get(); + } + + public boolean isBootUp() { + return this.op == REOp.UP; + } + + public boolean isShutDown() { + return this.op == REOp.DOWN; + } + + public Object getParsed() { + return parsed; + } + + public RawSubstateBytes getRawSubstateBytes() { + var buffer = stateBuf.get(); + int remaining = buffer.remaining(); + var buf = new byte[remaining]; + buffer.get(buf); + return new RawSubstateBytes(id.asBytes(), buf); + } + + @Override + public String toString() { + return String.format("%s{op=%s state=%s}", getClass().getSimpleName(), op, parsed); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/RawSubstateBytes.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/RawSubstateBytes.java index 92289f6ef8..8b568eea7d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/RawSubstateBytes.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/RawSubstateBytes.java @@ -65,19 +65,19 @@ package com.radixdlt.constraintmachine; public final class RawSubstateBytes { - private final byte[] id; - private final byte[] data; + private final byte[] id; + private final byte[] data; - public RawSubstateBytes(byte[] id, byte[] data) { - this.id = id; - this.data = data; - } + public RawSubstateBytes(byte[] id, byte[] data) { + this.id = id; + this.data = data; + } - public byte[] getId() { - return id; - } + public byte[] getId() { + return id; + } - public byte[] getData() { - return data; - } + public byte[] getData() { + return data; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadIndexProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadIndexProcedure.java index a6bec7b0b5..0fbeb0e5e9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadIndexProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadIndexProcedure.java @@ -65,44 +65,41 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.ProcedureException; - import java.util.function.Supplier; public class ReadIndexProcedure implements Procedure { - private final Class readClass; - private final Class reducerStateClass; - private final IndexedReducer readReducer; - private final Supplier authorization; + private final Class readClass; + private final Class reducerStateClass; + private final IndexedReducer readReducer; + private final Supplier authorization; - public ReadIndexProcedure( - Class reducerStateClass, - Class readClass, - Supplier authorization, - IndexedReducer readReducer - ) { - this.readClass = readClass; - this.reducerStateClass = reducerStateClass; - this.readReducer = readReducer; - this.authorization = authorization; - } + public ReadIndexProcedure( + Class reducerStateClass, + Class readClass, + Supplier authorization, + IndexedReducer readReducer) { + this.readClass = readClass; + this.reducerStateClass = reducerStateClass; + this.readReducer = readReducer; + this.authorization = authorization; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.READINDEX, readClass)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of( + reducerStateClass, OpSignature.ofSubstateUpdate(REOp.READINDEX, readClass)); + } - @Override - public Authorization authorization(Object o) { - return authorization.get(); - } + @Override + public Authorization authorization(Object o) { + return authorization.get(); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException { - return readReducer.reduce((S) reducerState, (IndexedSubstateIterator) o, context, immutableAddrs); - } + @Override + public ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException { + return readReducer.reduce( + (S) reducerState, (IndexedSubstateIterator) o, context, immutableAddrs); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadProcedure.java index 28850035c6..5e10afd9f1 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadProcedure.java @@ -65,43 +65,39 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.ProcedureException; - import java.util.function.Function; -public final class ReadProcedure implements Procedure { - private final Class readClass; - private final Class reducerStateClass; - private final ReadReducer readReducer; - private final Function authorization; +public final class ReadProcedure implements Procedure { + private final Class readClass; + private final Class reducerStateClass; + private final ReadReducer readReducer; + private final Function authorization; - public ReadProcedure( - Class reducerStateClass, Class readClass, - Function authorization, - ReadReducer readReducer - ) { - this.readClass = readClass; - this.reducerStateClass = reducerStateClass; - this.readReducer = readReducer; - this.authorization = authorization; - } + public ReadProcedure( + Class reducerStateClass, + Class readClass, + Function authorization, + ReadReducer readReducer) { + this.readClass = readClass; + this.reducerStateClass = reducerStateClass; + this.readReducer = readReducer; + this.authorization = authorization; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.READ, readClass)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.READ, readClass)); + } - @Override - public Authorization authorization(Object o) { - return authorization.apply((D) o); - } + @Override + public Authorization authorization(Object o) { + return authorization.apply((D) o); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException { - return readReducer.reduce((S) reducerState, (D) o, immutableAddrs); - } + @Override + public ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException { + return readReducer.reduce((S) reducerState, (D) o, immutableAddrs); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadReducer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadReducer.java index 3c082ade96..c48943740e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadReducer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReadReducer.java @@ -67,5 +67,6 @@ import com.radixdlt.constraintmachine.exceptions.ProcedureException; public interface ReadReducer { - ReducerResult reduce(S reducerState, I readSubstate, Resources immutableAddrs) throws ProcedureException; + ReducerResult reduce(S reducerState, I readSubstate, Resources immutableAddrs) + throws ProcedureException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerResult.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerResult.java index 87261a0492..5ee96ec8bb 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerResult.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerResult.java @@ -65,21 +65,21 @@ package com.radixdlt.constraintmachine; public final class ReducerResult { - private final ReducerState reducerState; + private final ReducerState reducerState; - private ReducerResult(ReducerState reducerState) { - this.reducerState = reducerState; - } + private ReducerResult(ReducerState reducerState) { + this.reducerState = reducerState; + } - public static ReducerResult incomplete(ReducerState reducerState) { - return new ReducerResult(reducerState); - } + public static ReducerResult incomplete(ReducerState reducerState) { + return new ReducerResult(reducerState); + } - public static ReducerResult complete() { - return new ReducerResult(null); - } + public static ReducerResult complete() { + return new ReducerResult(null); + } - public ReducerState state() { - return reducerState; - } -} \ No newline at end of file + public ReducerState state() { + return reducerState; + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerState.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerState.java index 2ca9f24ff5..9af0c12913 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerState.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ReducerState.java @@ -64,5 +64,4 @@ package com.radixdlt.constraintmachine; -public interface ReducerState { -} +public interface ReducerState {} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Resources.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Resources.java index 5b7e5d407b..ee144d9963 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Resources.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/Resources.java @@ -69,5 +69,5 @@ import com.radixdlt.identifiers.REAddr; public interface Resources { - TokenResource loadResource(REAddr addr) throws NotAResourceException; + TokenResource loadResource(REAddr addr) throws NotAResourceException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ShutdownAllProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ShutdownAllProcedure.java index 8b9c662a2f..660c0f7302 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ShutdownAllProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/ShutdownAllProcedure.java @@ -65,44 +65,41 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.ProcedureException; - import java.util.function.Supplier; public class ShutdownAllProcedure implements Procedure { - private final Class downClass; - private final Class reducerStateClass; - private final IndexedReducer downReducer; - private final Supplier authorization; + private final Class downClass; + private final Class reducerStateClass; + private final IndexedReducer downReducer; + private final Supplier authorization; - public ShutdownAllProcedure( - Class downClass, - Class reducerStateClass, - Supplier authorization, - IndexedReducer downReducer - ) { - this.downClass = downClass; - this.reducerStateClass = reducerStateClass; - this.downReducer = downReducer; - this.authorization = authorization; - } + public ShutdownAllProcedure( + Class downClass, + Class reducerStateClass, + Supplier authorization, + IndexedReducer downReducer) { + this.downClass = downClass; + this.reducerStateClass = reducerStateClass; + this.downReducer = downReducer; + this.authorization = authorization; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.DOWNINDEX, downClass)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of( + reducerStateClass, OpSignature.ofSubstateUpdate(REOp.DOWNINDEX, downClass)); + } - @Override - public Authorization authorization(Object o) { - return authorization.get(); - } + @Override + public Authorization authorization(Object o) { + return authorization.get(); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException { - return downReducer.reduce((S) reducerState, (IndexedSubstateIterator) o, context, immutableAddrs); - } + @Override + public ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException { + return downReducer.reduce( + (S) reducerState, (IndexedSubstateIterator) o, context, immutableAddrs); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserialization.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserialization.java index a029f61490..05fe6ed275 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserialization.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserialization.java @@ -67,60 +67,60 @@ import com.radixdlt.atomos.SubstateDefinition; import com.radixdlt.engine.parser.exceptions.SubstateDeserializationException; import com.radixdlt.serialization.DeserializeException; - import java.nio.ByteBuffer; import java.util.Collection; import java.util.Map; import java.util.stream.Collectors; public final class SubstateDeserialization { - private final Map> byteToDeserializer; - private final Map, Byte> classToTypeByte; + private final Map> byteToDeserializer; + private final Map, Byte> classToTypeByte; - public SubstateDeserialization( - Collection> definitions - ) { - this.byteToDeserializer = definitions.stream() - .collect(Collectors.toMap(SubstateDefinition::getTypeByte, d -> d)); - this.classToTypeByte = definitions.stream() - .collect(Collectors.toMap(SubstateDefinition::getSubstateClass, SubstateDefinition::getTypeByte)); - } + public SubstateDeserialization(Collection> definitions) { + this.byteToDeserializer = + definitions.stream().collect(Collectors.toMap(SubstateDefinition::getTypeByte, d -> d)); + this.classToTypeByte = + definitions.stream() + .collect( + Collectors.toMap( + SubstateDefinition::getSubstateClass, SubstateDefinition::getTypeByte)); + } - public Class byteToClass(Byte typeByte) throws DeserializeException { - var definition = byteToDeserializer.get(typeByte); - if (definition == null) { - throw new DeserializeException("Unknown substate byte type: " + typeByte); - } - return definition.getSubstateClass(); - } + public Class byteToClass(Byte typeByte) throws DeserializeException { + var definition = byteToDeserializer.get(typeByte); + if (definition == null) { + throw new DeserializeException("Unknown substate byte type: " + typeByte); + } + return definition.getSubstateClass(); + } - public byte classToByte(Class substateClass) { - var b = classToTypeByte.get(substateClass); - if (b == null) { - throw new IllegalStateException("Unknown substateClass: " + substateClass); - } - return b; - } + public byte classToByte(Class substateClass) { + var b = classToTypeByte.get(substateClass); + if (b == null) { + throw new IllegalStateException("Unknown substateClass: " + substateClass); + } + return b; + } - public SubstateIndex index(Class substateClass) { - return SubstateIndex.create(classToByte(substateClass), substateClass); - } + public SubstateIndex index(Class substateClass) { + return SubstateIndex.create(classToByte(substateClass), substateClass); + } - public Particle deserialize(byte[] b) throws DeserializeException { - return deserialize(ByteBuffer.wrap(b)); - } + public Particle deserialize(byte[] b) throws DeserializeException { + return deserialize(ByteBuffer.wrap(b)); + } - public Particle deserialize(ByteBuffer buf) throws DeserializeException { - var typeByte = buf.get(); - var deserializer = byteToDeserializer.get(typeByte); - if (deserializer == null) { - throw new DeserializeException("Unknown byte type: " + typeByte); - } + public Particle deserialize(ByteBuffer buf) throws DeserializeException { + var typeByte = buf.get(); + var deserializer = byteToDeserializer.get(typeByte); + if (deserializer == null) { + throw new DeserializeException("Unknown byte type: " + typeByte); + } - try { - return deserializer.getDeserializer().deserialize(buf); - } catch (Exception e) { - throw new SubstateDeserializationException(deserializer.getSubstateClass(), e); - } - } + try { + return deserializer.getDeserializer().deserialize(buf); + } catch (Exception e) { + throw new SubstateDeserializationException(deserializer.getSubstateClass(), e); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserializer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserializer.java index 5cb8456459..45c0224cc3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserializer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateDeserializer.java @@ -65,9 +65,8 @@ package com.radixdlt.constraintmachine; import com.radixdlt.serialization.DeserializeException; - import java.nio.ByteBuffer; public interface SubstateDeserializer { - T deserialize(ByteBuffer buf) throws DeserializeException; + T deserialize(ByteBuffer buf) throws DeserializeException; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateIndex.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateIndex.java index 37b4eccb84..95a5709eea 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateIndex.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateIndex.java @@ -66,94 +66,96 @@ import com.radixdlt.atom.SubstateTypeId; import com.radixdlt.utils.Bytes; - import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Objects; import java.util.Optional; public final class SubstateIndex { - private final byte[] index; - private final Class substateClass; - - private SubstateIndex(byte[] index, Class substateClass) { - this.index = index; - this.substateClass = substateClass; - } - - public static SubstateIndex create(byte[] prefix) { - return new SubstateIndex<>(prefix, SubstateTypeId.valueOf(prefix[0]).getSubstateClass()); - } - - public static SubstateIndex create(byte[] prefix, Class substateClass) { - return new SubstateIndex<>(prefix, substateClass); - } - - public static SubstateIndex create(byte typeByte, Class substateClass) { - return new SubstateIndex<>(new byte[] {typeByte}, substateClass); - } - - public boolean test(RawSubstateBytes bytes) { - return test(bytes.getData()); - } - - public boolean test(byte[] dataBytes) { - if (dataBytes.length < index.length) { - return false; - } - - return Arrays.equals(dataBytes, 0, index.length, index, 0, index.length); - } - - public boolean test(ByteBuffer buffer) { - buffer.mark(); - if (buffer.remaining() < index.length) { - return false; - } - - for (byte b : index) { - if (buffer.get() != b) { - return false; - } - } - buffer.reset(); - - return true; - } - - public byte[] getPrefix() { - return index; - } - - public Class getSubstateClass() { - return substateClass; - } - - public Optional> toSubstateIndex(Class substateClass) { - if (this.substateClass.equals(substateClass)) { - return Optional.of(new SubstateIndex<>(this.index, substateClass)); - } - return Optional.empty(); - } - - @Override - public int hashCode() { - return Objects.hash(Arrays.hashCode(index), substateClass); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof SubstateIndex)) { - return false; - } - - var other = (SubstateIndex) o; - return Arrays.equals(this.index, other.index) - && Objects.equals(this.substateClass, other.substateClass); - } - - @Override - public String toString() { - return String.format("%s{index=%s}", this.getClass().getSimpleName(), Bytes.toHexString(this.index)); - } + private final byte[] index; + private final Class substateClass; + + private SubstateIndex(byte[] index, Class substateClass) { + this.index = index; + this.substateClass = substateClass; + } + + public static SubstateIndex create(byte[] prefix) { + return new SubstateIndex<>(prefix, SubstateTypeId.valueOf(prefix[0]).getSubstateClass()); + } + + public static SubstateIndex create( + byte[] prefix, Class substateClass) { + return new SubstateIndex<>(prefix, substateClass); + } + + public static SubstateIndex create( + byte typeByte, Class substateClass) { + return new SubstateIndex<>(new byte[] {typeByte}, substateClass); + } + + public boolean test(RawSubstateBytes bytes) { + return test(bytes.getData()); + } + + public boolean test(byte[] dataBytes) { + if (dataBytes.length < index.length) { + return false; + } + + return Arrays.equals(dataBytes, 0, index.length, index, 0, index.length); + } + + public boolean test(ByteBuffer buffer) { + buffer.mark(); + if (buffer.remaining() < index.length) { + return false; + } + + for (byte b : index) { + if (buffer.get() != b) { + return false; + } + } + buffer.reset(); + + return true; + } + + public byte[] getPrefix() { + return index; + } + + public Class getSubstateClass() { + return substateClass; + } + + public Optional> toSubstateIndex(Class substateClass) { + if (this.substateClass.equals(substateClass)) { + return Optional.of(new SubstateIndex<>(this.index, substateClass)); + } + return Optional.empty(); + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(index), substateClass); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SubstateIndex)) { + return false; + } + + var other = (SubstateIndex) o; + return Arrays.equals(this.index, other.index) + && Objects.equals(this.substateClass, other.substateClass); + } + + @Override + public String toString() { + return String.format( + "%s{index=%s}", this.getClass().getSimpleName(), Bytes.toHexString(this.index)); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerialization.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerialization.java index 0a8dab3343..4d7765b6df 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerialization.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerialization.java @@ -65,85 +65,88 @@ package com.radixdlt.constraintmachine; import com.radixdlt.atomos.SubstateDefinition; - import java.nio.ByteBuffer; import java.util.Collection; import java.util.Map; import java.util.stream.Collectors; public final class SubstateSerialization { - private final Map, SubstateSerializer> classToSerializer; - private final Map, VirtualMapper> classToVirtualSerializer; - private final Map, KeySerializer> classToKeySerializer; - private final Map, Byte> classToTypeByte; + private final Map, SubstateSerializer> classToSerializer; + private final Map, VirtualMapper> classToVirtualSerializer; + private final Map, KeySerializer> classToKeySerializer; + private final Map, Byte> classToTypeByte; - public SubstateSerialization( - Collection> definitions - ) { - this.classToTypeByte = definitions.stream() - .collect(Collectors.toMap(SubstateDefinition::getSubstateClass, SubstateDefinition::getTypeByte)); - this.classToSerializer = definitions.stream() - .collect(Collectors.toMap( - SubstateDefinition::getSubstateClass, - d -> (s, buf) -> ((SubstateSerializer) d.getSerializer()).serialize(s, buf) - )); - this.classToKeySerializer = definitions.stream() - .collect(Collectors.toMap( - SubstateDefinition::getSubstateClass, - SubstateDefinition::getKeySerializer - )); - this.classToVirtualSerializer = definitions.stream() - .collect(Collectors.toMap( - SubstateDefinition::getSubstateClass, - SubstateDefinition::getVirtualMapper - )); - } + public SubstateSerialization(Collection> definitions) { + this.classToTypeByte = + definitions.stream() + .collect( + Collectors.toMap( + SubstateDefinition::getSubstateClass, SubstateDefinition::getTypeByte)); + this.classToSerializer = + definitions.stream() + .collect( + Collectors.toMap( + SubstateDefinition::getSubstateClass, + d -> + (s, buf) -> + ((SubstateSerializer) d.getSerializer()).serialize(s, buf))); + this.classToKeySerializer = + definitions.stream() + .collect( + Collectors.toMap( + SubstateDefinition::getSubstateClass, SubstateDefinition::getKeySerializer)); + this.classToVirtualSerializer = + definitions.stream() + .collect( + Collectors.toMap( + SubstateDefinition::getSubstateClass, SubstateDefinition::getVirtualMapper)); + } - public byte classToByte(Class substateClass) { - var b = this.classToTypeByte.get(substateClass); - if (b == null) { - throw new IllegalStateException("No serializer for substate: " + substateClass); - } - return b; - } + public byte classToByte(Class substateClass) { + var b = this.classToTypeByte.get(substateClass); + if (b == null) { + throw new IllegalStateException("No serializer for substate: " + substateClass); + } + return b; + } - public byte[] serialize(Particle p) { - var serializer = classToSerializer.get(p.getClass()); - if (serializer == null) { - throw new IllegalStateException("No serializer for particle: " + p); - } + public byte[] serialize(Particle p) { + var serializer = classToSerializer.get(p.getClass()); + if (serializer == null) { + throw new IllegalStateException("No serializer for particle: " + p); + } - // TODO: Remove buf allocation - var buf = ByteBuffer.allocate(1024); - buf.put(classToTypeByte.get(p.getClass())); - serializer.serialize(p, buf); - var position = buf.position(); - buf.rewind(); - var bytes = new byte[position]; - buf.get(bytes); - return bytes; - } + // TODO: Remove buf allocation + var buf = ByteBuffer.allocate(1024); + buf.put(classToTypeByte.get(p.getClass())); + serializer.serialize(p, buf); + var position = buf.position(); + buf.rewind(); + var bytes = new byte[position]; + buf.get(bytes); + return bytes; + } - public void serialize(Particle p, ByteBuffer buffer) { - buffer.put(classToTypeByte.get(p.getClass())); - var serializer = classToSerializer.get(p.getClass()); - serializer.serialize(p, buffer); - } + public void serialize(Particle p, ByteBuffer buffer) { + buffer.put(classToTypeByte.get(p.getClass())); + var serializer = classToSerializer.get(p.getClass()); + serializer.serialize(p, buffer); + } - public byte[] serializeKey(Class substateClass, Object key) { - var serializer = classToKeySerializer.get(substateClass); - // TODO: Remove buf allocation - var buf = ByteBuffer.allocate(1024); - serializer.serialize(key, buf); - var position = buf.position(); - buf.rewind(); - var bytes = new byte[position]; - buf.get(bytes); - return bytes; - } + public byte[] serializeKey(Class substateClass, Object key) { + var serializer = classToKeySerializer.get(substateClass); + // TODO: Remove buf allocation + var buf = ByteBuffer.allocate(1024); + serializer.serialize(key, buf); + var position = buf.position(); + buf.rewind(); + var bytes = new byte[position]; + buf.get(bytes); + return bytes; + } - public T mapVirtual(Class substateClass, Object key) { - var serializer = classToVirtualSerializer.get(substateClass); - return (T) serializer.map(key); - } + public T mapVirtual(Class substateClass, Object key) { + var serializer = classToVirtualSerializer.get(substateClass); + return (T) serializer.map(key); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerializer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerializer.java index b38345c991..31208c9a5d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerializer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SubstateSerializer.java @@ -67,5 +67,5 @@ import java.nio.ByteBuffer; public interface SubstateSerializer { - void serialize(T substate, ByteBuffer buf); + void serialize(T substate, ByteBuffer buf); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallProcedure.java index 120f366d8e..24c33b11c3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallProcedure.java @@ -66,48 +66,43 @@ import com.radixdlt.constraintmachine.exceptions.ProcedureException; import com.radixdlt.identifiers.REAddr; - import java.util.function.Supplier; public class SystemCallProcedure implements Procedure { - private final Class reducerStateClass; - private final REAddr addr; - private final SystemCallReducer reducer; - private final Supplier authorization; + private final Class reducerStateClass; + private final REAddr addr; + private final SystemCallReducer reducer; + private final Supplier authorization; - public SystemCallProcedure( - Class reducerStateClass, - REAddr addr, - Supplier authorization, - SystemCallReducer reducer - ) { - this.reducerStateClass = reducerStateClass; - this.addr = addr; - this.reducer = reducer; - this.authorization = authorization; - } + public SystemCallProcedure( + Class reducerStateClass, + REAddr addr, + Supplier authorization, + SystemCallReducer reducer) { + this.reducerStateClass = reducerStateClass; + this.addr = addr; + this.reducer = reducer; + this.authorization = authorization; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofMethod(REOp.SYSCALL, addr)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of(reducerStateClass, OpSignature.ofMethod(REOp.SYSCALL, addr)); + } - @Override - public Authorization authorization(Object o) { - return authorization.get(); - } + @Override + public Authorization authorization(Object o) { + return authorization.get(); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException { - try { - return reducer.reduce((S) reducerState, (CallData) o, context); - } catch (Exception e) { - throw new ProcedureException(e); - } - } + @Override + public ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException { + try { + return reducer.reduce((S) reducerState, (CallData) o, context); + } catch (Exception e) { + throw new ProcedureException(e); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallReducer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallReducer.java index 3707d805e6..de29bcdbcd 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallReducer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemCallReducer.java @@ -65,5 +65,5 @@ package com.radixdlt.constraintmachine; public interface SystemCallReducer { - ReducerResult reduce(S reducerState, CallData c, ExecutionContext context) throws Exception; + ReducerResult reduce(S reducerState, CallData c, ExecutionContext context) throws Exception; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemMapKey.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemMapKey.java index e4b813c2aa..a350fe1b3d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemMapKey.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/SystemMapKey.java @@ -65,64 +65,63 @@ package com.radixdlt.constraintmachine; import com.radixdlt.identifiers.REAddr; - import java.nio.ByteBuffer; import java.util.Arrays; public final class SystemMapKey { - private final byte[] key; + private final byte[] key; - private SystemMapKey(byte[] key) { - this.key = key; - } + private SystemMapKey(byte[] key) { + this.key = key; + } - private static SystemMapKey of(REAddr addr, byte key) { - var buf = ByteBuffer.allocate(addr.getBytes().length + 1); - buf.put(addr.getBytes()); - buf.put(key); - return new SystemMapKey(buf.array()); - } + private static SystemMapKey of(REAddr addr, byte key) { + var buf = ByteBuffer.allocate(addr.getBytes().length + 1); + buf.put(addr.getBytes()); + buf.put(key); + return new SystemMapKey(buf.array()); + } - private static SystemMapKey of(REAddr addr, byte key0, byte[] key1) { - var buf = ByteBuffer.allocate(addr.getBytes().length + 1 + key1.length); - buf.put(addr.getBytes()); - buf.put(key0); - buf.put(key1); - return new SystemMapKey(buf.array()); - } + private static SystemMapKey of(REAddr addr, byte key0, byte[] key1) { + var buf = ByteBuffer.allocate(addr.getBytes().length + 1 + key1.length); + buf.put(addr.getBytes()); + buf.put(key0); + buf.put(key1); + return new SystemMapKey(buf.array()); + } - public static SystemMapKey create(byte[] key) { - return new SystemMapKey(key); - } + public static SystemMapKey create(byte[] key) { + return new SystemMapKey(key); + } - public static SystemMapKey ofSystem(byte key) { - return of(REAddr.ofSystem(), key); - } + public static SystemMapKey ofSystem(byte key) { + return of(REAddr.ofSystem(), key); + } - public static SystemMapKey ofSystem(byte typeId, byte[] mapKey) { - return of(REAddr.ofSystem(), typeId, mapKey); - } + public static SystemMapKey ofSystem(byte typeId, byte[] mapKey) { + return of(REAddr.ofSystem(), typeId, mapKey); + } - public static SystemMapKey ofResourceData(REAddr addr, byte typeId) { - return of(addr, typeId); - } + public static SystemMapKey ofResourceData(REAddr addr, byte typeId) { + return of(addr, typeId); + } - public byte[] array() { - return key; - } + public byte[] array() { + return key; + } - @Override - public int hashCode() { - return Arrays.hashCode(key); - } + @Override + public int hashCode() { + return Arrays.hashCode(key); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof SystemMapKey)) { - return false; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof SystemMapKey)) { + return false; + } - var other = (SystemMapKey) o; - return Arrays.equals(this.key, other.key); - } + var other = (SystemMapKey) o; + return Arrays.equals(this.key, other.key); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpProcedure.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpProcedure.java index c8730166e4..a3e1183399 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpProcedure.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpProcedure.java @@ -65,48 +65,43 @@ package com.radixdlt.constraintmachine; import com.radixdlt.constraintmachine.exceptions.ProcedureException; - import java.util.function.Function; public final class UpProcedure implements Procedure { - private final Class reducerStateClass; - private final Class upClass; - private final UpReducer upReducer; - private final Function authorization; + private final Class reducerStateClass; + private final Class upClass; + private final UpReducer upReducer; + private final Function authorization; - public UpProcedure( - Class reducerStateClass, - Class upClass, - Function authorization, - UpReducer upReducer - ) { - this.reducerStateClass = reducerStateClass; - this.upClass = upClass; - this.upReducer = upReducer; - this.authorization = authorization; - } + public UpProcedure( + Class reducerStateClass, + Class upClass, + Function authorization, + UpReducer upReducer) { + this.reducerStateClass = reducerStateClass; + this.upClass = upClass; + this.upReducer = upReducer; + this.authorization = authorization; + } - @Override - public ProcedureKey key() { - return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.UP, upClass)); - } + @Override + public ProcedureKey key() { + return ProcedureKey.of(reducerStateClass, OpSignature.ofSubstateUpdate(REOp.UP, upClass)); + } - @Override - public Authorization authorization(Object o) { - return authorization.apply((U) o); - } + @Override + public Authorization authorization(Object o) { + return authorization.apply((U) o); + } - @Override - public ReducerResult call( - Object o, - ReducerState reducerState, - Resources immutableAddrs, - ExecutionContext context - ) throws ProcedureException { - try { - return upReducer.reduce((S) reducerState, (U) o, context, immutableAddrs); - } catch (Exception e) { - throw new ProcedureException(e); - } - } + @Override + public ReducerResult call( + Object o, ReducerState reducerState, Resources immutableAddrs, ExecutionContext context) + throws ProcedureException { + try { + return upReducer.reduce((S) reducerState, (U) o, context, immutableAddrs); + } catch (Exception e) { + throw new ProcedureException(e); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpReducer.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpReducer.java index bda4b96f65..afece08f9e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpReducer.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpReducer.java @@ -65,5 +65,6 @@ package com.radixdlt.constraintmachine; public interface UpReducer { - ReducerResult reduce(S reducerState, O up, ExecutionContext context, Resources immutableAddrs) throws Exception; + ReducerResult reduce(S reducerState, O up, ExecutionContext context, Resources immutableAddrs) + throws Exception; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpSubstate.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpSubstate.java index 7a54a79101..678d770d8d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpSubstate.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/UpSubstate.java @@ -66,32 +66,32 @@ import com.radixdlt.atom.SubstateId; import com.radixdlt.utils.Bytes; - import java.nio.ByteBuffer; public class UpSubstate { - private final SubstateId substateId; - private final byte[] array; - private final int offset; - private final int length; + private final SubstateId substateId; + private final byte[] array; + private final int offset; + private final int length; - public UpSubstate(SubstateId substateId, byte[] array, int offset, int length) { - this.substateId = substateId; - this.array = array; - this.offset = offset; - this.length = length; - } + public UpSubstate(SubstateId substateId, byte[] array, int offset, int length) { + this.substateId = substateId; + this.array = array; + this.offset = offset; + this.length = length; + } - public ByteBuffer getSubstateBuffer() { - return ByteBuffer.wrap(array, offset, length); - } + public ByteBuffer getSubstateBuffer() { + return ByteBuffer.wrap(array, offset, length); + } - public SubstateId getSubstateId() { - return substateId; - } + public SubstateId getSubstateId() { + return substateId; + } - @Override - public String toString() { - return String.format("%s{data=%s}", this.getClass().getSimpleName(), Bytes.toHexString(array, offset, length)); - } + @Override + public String toString() { + return String.format( + "%s{data=%s}", this.getClass().getSimpleName(), Bytes.toHexString(array, offset, length)); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualMapper.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualMapper.java index 109c32caf5..89232193bf 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualMapper.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualMapper.java @@ -65,5 +65,5 @@ package com.radixdlt.constraintmachine; public interface VirtualMapper { - T map(Object key); + T map(Object key); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualSubstateDeserialization.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualSubstateDeserialization.java index ef1b1451a4..0296c7b3c7 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualSubstateDeserialization.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VirtualSubstateDeserialization.java @@ -68,37 +68,37 @@ import com.radixdlt.engine.parser.exceptions.SubstateDeserializationException; import com.radixdlt.engine.parser.exceptions.TrailingBytesException; import com.radixdlt.serialization.DeserializeException; - import java.nio.ByteBuffer; import java.util.Collection; import java.util.Map; import java.util.stream.Collectors; public class VirtualSubstateDeserialization { - private final Map> byteToDeserializer; + private final Map> byteToDeserializer; - public VirtualSubstateDeserialization(Collection> definitions) { - this.byteToDeserializer = definitions.stream() - .collect(Collectors.toMap(SubstateDefinition::getTypeByte, d -> d)); - } + public VirtualSubstateDeserialization( + Collection> definitions) { + this.byteToDeserializer = + definitions.stream().collect(Collectors.toMap(SubstateDefinition::getTypeByte, d -> d)); + } - public Particle keyToSubstate(byte typeByte, ByteBuffer buf) throws DeserializeException { - var deserializer = byteToDeserializer.get(typeByte); - if (deserializer == null) { - throw new DeserializeException("Unknown byte type: " + typeByte); - } - Particle rawSubstate; - try { - var key = deserializer.getKeyDeserializer().deserialize(buf); - rawSubstate = deserializer.getVirtualMapper().map(key); - } catch (Exception e) { - throw new SubstateDeserializationException(deserializer.getSubstateClass(), e); - } + public Particle keyToSubstate(byte typeByte, ByteBuffer buf) throws DeserializeException { + var deserializer = byteToDeserializer.get(typeByte); + if (deserializer == null) { + throw new DeserializeException("Unknown byte type: " + typeByte); + } + Particle rawSubstate; + try { + var key = deserializer.getKeyDeserializer().deserialize(buf); + rawSubstate = deserializer.getVirtualMapper().map(key); + } catch (Exception e) { + throw new SubstateDeserializationException(deserializer.getSubstateClass(), e); + } - if (buf.hasRemaining()) { - throw new TrailingBytesException("Buffer has " + buf.remaining() + " bytes remaining."); - } + if (buf.hasRemaining()) { + throw new TrailingBytesException("Buffer has " + buf.remaining() + " bytes remaining."); + } - return rawSubstate; - } + return rawSubstate; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VoidReducerState.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VoidReducerState.java index 8ba044e4f1..990be6279d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VoidReducerState.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/VoidReducerState.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine; public final class VoidReducerState implements ReducerState { - private VoidReducerState() { - throw new IllegalStateException("Cannot instantiate."); - } + private VoidReducerState() { + throw new IllegalStateException("Cannot instantiate."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/AuthorizationException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/AuthorizationException.java index b67df1e077..f1132b8718 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/AuthorizationException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/AuthorizationException.java @@ -66,15 +66,15 @@ public class AuthorizationException extends Exception { - public AuthorizationException(Exception cause) { - super(cause); - } + public AuthorizationException(Exception cause) { + super(cause); + } - public AuthorizationException(String msg) { - super(msg); - } + public AuthorizationException(String msg) { + super(msg); + } - public AuthorizationException(String msg, Exception cause) { - super(msg, cause); - } + public AuthorizationException(String msg, Exception cause) { + super(msg, cause); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/CallDataAccessException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/CallDataAccessException.java index de12e0fdd6..f1c312f1c4 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/CallDataAccessException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/CallDataAccessException.java @@ -65,7 +65,13 @@ package com.radixdlt.constraintmachine.exceptions; public class CallDataAccessException extends Exception { - public CallDataAccessException(int callDataSize, int index, int accessSize) { - super("CallData invalid access (index: " + index + " accessSize: " + accessSize + ") on size " + callDataSize); - } + public CallDataAccessException(int callDataSize, int index, int accessSize) { + super( + "CallData invalid access (index: " + + index + + " accessSize: " + + accessSize + + ") on size " + + callDataSize); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ConstraintMachineException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ConstraintMachineException.java index 95682e0be3..d2eb0681c3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ConstraintMachineException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ConstraintMachineException.java @@ -66,30 +66,37 @@ import com.radixdlt.constraintmachine.REInstruction; import com.radixdlt.constraintmachine.ReducerState; - import java.util.List; public final class ConstraintMachineException extends Exception { - public ConstraintMachineException(int instIndex, List instructions, ReducerState reducerState, Throwable cause) { - super("index=" + instIndex + " reducerState=" + reducerState + "\n" + toMessage(instIndex, instructions), cause); - } + public ConstraintMachineException( + int instIndex, List instructions, ReducerState reducerState, Throwable cause) { + super( + "index=" + + instIndex + + " reducerState=" + + reducerState + + "\n" + + toMessage(instIndex, instructions), + cause); + } - private static String toMessage(int instIndex, List instructions) { - var builder = new StringBuilder(); + private static String toMessage(int instIndex, List instructions) { + var builder = new StringBuilder(); - var start = Math.max(0, instIndex - 32); - var end = Math.min(instructions.size(), instIndex + 32); + var start = Math.max(0, instIndex - 32); + var end = Math.min(instructions.size(), instIndex + 32); - for (int i = start; i < end; i++) { - if (i == instIndex) { - builder.append("<<<>>> "); - } - builder.append(i); - builder.append(": "); - builder.append(instructions.get(i)); - builder.append("\n"); - } + for (int i = start; i < end; i++) { + if (i == instIndex) { + builder.append("<<<>>> "); + } + builder.append(i); + builder.append(": "); + builder.append(instructions.get(i)); + builder.append("\n"); + } - return builder.toString(); - } + return builder.toString(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DefaultedSystemLoanException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DefaultedSystemLoanException.java index f7780a20c0..4c015dcc39 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DefaultedSystemLoanException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DefaultedSystemLoanException.java @@ -65,12 +65,14 @@ package com.radixdlt.constraintmachine.exceptions; import com.radixdlt.utils.UInt256; - import java.util.Optional; public class DefaultedSystemLoanException extends Exception { - public DefaultedSystemLoanException(DepletedFeeReserveException cause, UInt256 feeDeposited) { - super("Reserve fee deposit " + feeDeposited + " not enough to cover basic txn fee of " - + Optional.ofNullable(feeDeposited).orElse(UInt256.ZERO).add(cause.getMissingAmount())); - } + public DefaultedSystemLoanException(DepletedFeeReserveException cause, UInt256 feeDeposited) { + super( + "Reserve fee deposit " + + feeDeposited + + " not enough to cover basic txn fee of " + + Optional.ofNullable(feeDeposited).orElse(UInt256.ZERO).add(cause.getMissingAmount())); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DepletedFeeReserveException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DepletedFeeReserveException.java index e1cc5146b4..b1e1df3e0e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DepletedFeeReserveException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/DepletedFeeReserveException.java @@ -67,24 +67,24 @@ import com.radixdlt.utils.UInt256; public class DepletedFeeReserveException extends AuthorizationException { - private final UInt256 charge; - private final UInt256 reserveAmount; + private final UInt256 charge; + private final UInt256 reserveAmount; - public DepletedFeeReserveException(NotEnoughResourcesException cause) { - super("Charging " + cause.getRequest() + " but fee reserve only contains " + cause.getAmount()); - this.charge = cause.getRequest(); - this.reserveAmount = cause.getAmount(); - } + public DepletedFeeReserveException(NotEnoughResourcesException cause) { + super("Charging " + cause.getRequest() + " but fee reserve only contains " + cause.getAmount()); + this.charge = cause.getRequest(); + this.reserveAmount = cause.getAmount(); + } - public UInt256 getCharge() { - return charge; - } + public UInt256 getCharge() { + return charge; + } - public UInt256 getReserveAmount() { - return reserveAmount; - } + public UInt256 getReserveAmount() { + return reserveAmount; + } - public UInt256 getMissingAmount() { - return charge.subtract(reserveAmount); - } + public UInt256 getMissingAmount() { + return charge.subtract(reserveAmount); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ExecutionContextDestroyException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ExecutionContextDestroyException.java index 543e6e494d..47e8a3b6c9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ExecutionContextDestroyException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ExecutionContextDestroyException.java @@ -67,7 +67,7 @@ import com.radixdlt.application.tokens.scrypt.TokenHoldingBucket; public class ExecutionContextDestroyException extends Exception { - public ExecutionContextDestroyException(TokenHoldingBucket reserve) { - super("Reserve not empty: " + reserve); - } + public ExecutionContextDestroyException(TokenHoldingBucket reserve) { + super("Reserve not empty: " + reserve); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidDelegationException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidDelegationException.java index 4c62c2ae33..24d537ad8f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidDelegationException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidDelegationException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public class InvalidDelegationException extends Exception { - public InvalidDelegationException() { - super("Delegation not allowed by owner."); - } + public InvalidDelegationException() { + super("Delegation not allowed by owner."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidHashedKeyException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidHashedKeyException.java index c73c5938cf..5a22433bf8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidHashedKeyException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidHashedKeyException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public class InvalidHashedKeyException extends Exception { - public InvalidHashedKeyException(String message) { - super(message); - } + public InvalidHashedKeyException(String message) { + super(message); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidPermissionException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidPermissionException.java index c00e57e73d..45127eb794 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidPermissionException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidPermissionException.java @@ -67,7 +67,7 @@ import com.radixdlt.constraintmachine.PermissionLevel; public class InvalidPermissionException extends Exception { - public InvalidPermissionException(PermissionLevel required, PermissionLevel actual) { - super("Required: " + required + " Actual: " + actual); - } + public InvalidPermissionException(PermissionLevel required, PermissionLevel actual) { + super("Required: " + required + " Actual: " + actual); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidResourceException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidResourceException.java index 9fa564b479..3125be800a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidResourceException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidResourceException.java @@ -67,12 +67,12 @@ import com.radixdlt.identifiers.REAddr; public final class InvalidResourceException extends Exception { - private final REAddr expected; - private final REAddr actual; + private final REAddr expected; + private final REAddr actual; - public InvalidResourceException(REAddr expected, REAddr actual) { - super("Expected resource " + expected + " but was " + actual); - this.expected = expected; - this.actual = actual; - } + public InvalidResourceException(REAddr expected, REAddr actual) { + super("Expected resource " + expected + " but was " + actual); + this.expected = expected; + this.actual = actual; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidVirtualSubstateException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidVirtualSubstateException.java index 9bf5b71184..cef59abc59 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidVirtualSubstateException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/InvalidVirtualSubstateException.java @@ -67,7 +67,7 @@ import com.radixdlt.constraintmachine.Particle; public class InvalidVirtualSubstateException extends Exception { - public InvalidVirtualSubstateException(Particle particle) { - super(particle + " is not a valid virtual substate."); - } + public InvalidVirtualSubstateException(Particle particle) { + super(particle + " is not a valid virtual substate."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/LocalSubstateNotFoundException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/LocalSubstateNotFoundException.java index 964adcd158..915800db32 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/LocalSubstateNotFoundException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/LocalSubstateNotFoundException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public class LocalSubstateNotFoundException extends Exception { - public LocalSubstateNotFoundException(int index) { - super("Local substate with index " + index + " not found"); - } + public LocalSubstateNotFoundException(int index) { + super("Local substate with index " + index + " not found"); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MeterException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MeterException.java index 6fcfcf41ac..b99751fb26 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MeterException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MeterException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public class MeterException extends Exception { - public MeterException(Exception cause) { - super(cause); - } + public MeterException(Exception cause) { + super(cause); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MinimumStakeException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MinimumStakeException.java index 6128bf8fc5..148f2856b7 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MinimumStakeException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MinimumStakeException.java @@ -67,7 +67,7 @@ import com.radixdlt.utils.UInt256; public class MinimumStakeException extends Exception { - public MinimumStakeException(UInt256 minimum, UInt256 actual) { - super("Minimum amount to stake must be >= " + minimum + " but trying to stake " + actual); - } + public MinimumStakeException(UInt256 minimum, UInt256 actual) { + super("Minimum amount to stake must be >= " + minimum + " but trying to stake " + actual); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MismatchException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MismatchException.java index be44a9c0b5..a5a6e295bc 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MismatchException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MismatchException.java @@ -68,11 +68,11 @@ public class MismatchException extends Exception { - public MismatchException(Particle expected, Particle actual) { - super("Expected substate " + expected + " but was " + actual); - } + public MismatchException(Particle expected, Particle actual) { + super("Expected substate " + expected + " but was " + actual); + } - public MismatchException(String msg) { - super(msg); - } + public MismatchException(String msg) { + super(msg); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MissingProcedureException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MissingProcedureException.java index 42feae5f61..da71f88bee 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MissingProcedureException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MissingProcedureException.java @@ -67,9 +67,7 @@ import com.radixdlt.constraintmachine.ProcedureKey; public class MissingProcedureException extends Exception { - public MissingProcedureException( - ProcedureKey key - ) { - super("Missing: " + key); - } + public MissingProcedureException(ProcedureKey key) { + super("Missing: " + key); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MultipleFeeReserveDepositException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MultipleFeeReserveDepositException.java index df5c2c3684..12d3da6957 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MultipleFeeReserveDepositException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/MultipleFeeReserveDepositException.java @@ -64,5 +64,4 @@ package com.radixdlt.constraintmachine.exceptions; -public class MultipleFeeReserveDepositException extends Exception { -} +public class MultipleFeeReserveDepositException extends Exception {} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotAResourceException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotAResourceException.java index 7316624f34..bc3afba240 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotAResourceException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotAResourceException.java @@ -67,7 +67,7 @@ import com.radixdlt.identifiers.REAddr; public class NotAResourceException extends Exception { - public NotAResourceException(REAddr addr) { - super(addr + " is not a resource"); - } + public NotAResourceException(REAddr addr) { + super(addr + " is not a resource"); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotEnoughResourcesException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotEnoughResourcesException.java index fd5c53a987..d81cb3e6ca 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotEnoughResourcesException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/NotEnoughResourcesException.java @@ -67,20 +67,20 @@ import com.radixdlt.utils.UInt256; public final class NotEnoughResourcesException extends Exception { - private final UInt256 request; - private final UInt256 amount; + private final UInt256 request; + private final UInt256 amount; - public NotEnoughResourcesException(UInt256 request, UInt256 amount) { - super("request: " + request + " amount: " + amount); - this.request = request; - this.amount = amount; - } + public NotEnoughResourcesException(UInt256 request, UInt256 amount) { + super("request: " + request + " amount: " + amount); + this.request = request; + this.amount = amount; + } - public UInt256 getRequest() { - return request; - } + public UInt256 getRequest() { + return request; + } - public UInt256 getAmount() { - return amount; - } + public UInt256 getAmount() { + return amount; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ProcedureException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ProcedureException.java index 5b765026d0..24a895f438 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ProcedureException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ProcedureException.java @@ -65,11 +65,11 @@ package com.radixdlt.constraintmachine.exceptions; public class ProcedureException extends Exception { - public ProcedureException(Throwable cause) { - super(cause); - } + public ProcedureException(Throwable cause) { + super(cause); + } - public ProcedureException(String msg) { - super(msg); - } + public ProcedureException(String msg) { + super(msg); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ReservedSymbolException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ReservedSymbolException.java index 30aab09dd6..4af2be86ae 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ReservedSymbolException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ReservedSymbolException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public class ReservedSymbolException extends Exception { - public ReservedSymbolException(String symbol) { - super("Trying to use reserved symbol " + symbol); - } + public ReservedSymbolException(String symbol) { + super("Trying to use reserved symbol " + symbol); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ResourceAllocationAndDestructionException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ResourceAllocationAndDestructionException.java index d217c1e9d0..1d61026bbc 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ResourceAllocationAndDestructionException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/ResourceAllocationAndDestructionException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public class ResourceAllocationAndDestructionException extends Exception { - public ResourceAllocationAndDestructionException() { - super("Allocation and destruction of resources not enabled."); - } + public ResourceAllocationAndDestructionException() { + super("Allocation and destruction of resources not enabled."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SignedSystemException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SignedSystemException.java index 8a279f2a9f..3f768a31ab 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SignedSystemException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SignedSystemException.java @@ -65,7 +65,7 @@ package com.radixdlt.constraintmachine.exceptions; public final class SignedSystemException extends Exception { - public SignedSystemException() { - super("System updates should not be signed."); - } + public SignedSystemException() { + super("System updates should not be signed."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SubstateNotFoundException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SubstateNotFoundException.java index c827a3bf59..a35b8df395 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SubstateNotFoundException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/SubstateNotFoundException.java @@ -67,15 +67,15 @@ import com.radixdlt.atom.SubstateId; public class SubstateNotFoundException extends Exception { - private final SubstateId substateId; + private final SubstateId substateId; - public SubstateNotFoundException(SubstateId substateId) { - super("Substate " + substateId + " not found."); + public SubstateNotFoundException(SubstateId substateId) { + super("Substate " + substateId + " not found."); - this.substateId = substateId; - } + this.substateId = substateId; + } - public SubstateId getSubstateId() { - return substateId; - } -} \ No newline at end of file + public SubstateId getSubstateId() { + return substateId; + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualParentStateDoesNotExist.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualParentStateDoesNotExist.java index 212d74b84d..dec537052d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualParentStateDoesNotExist.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualParentStateDoesNotExist.java @@ -67,7 +67,7 @@ import com.radixdlt.atom.SubstateId; public class VirtualParentStateDoesNotExist extends Exception { - public VirtualParentStateDoesNotExist(SubstateId substateId) { - super("Virtual parent " + substateId + " does not exist."); - } + public VirtualParentStateDoesNotExist(SubstateId substateId) { + super("Virtual parent " + substateId + " does not exist."); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualSubstateAlreadyDownException.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualSubstateAlreadyDownException.java index 00ccc77d70..58b2cacacd 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualSubstateAlreadyDownException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/exceptions/VirtualSubstateAlreadyDownException.java @@ -67,7 +67,7 @@ import com.radixdlt.atom.SubstateId; public class VirtualSubstateAlreadyDownException extends SubstateNotFoundException { - public VirtualSubstateAlreadyDownException(SubstateId substateId) { - super(substateId); - } + public VirtualSubstateAlreadyDownException(SubstateId substateId) { + super(substateId); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/FixedFeeMeter.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/FixedFeeMeter.java index 136368ff01..72b6f99af5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/FixedFeeMeter.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/FixedFeeMeter.java @@ -71,49 +71,51 @@ import com.radixdlt.utils.UInt256; public final class FixedFeeMeter implements Meter { - private final UInt256 fixedFee; + private final UInt256 fixedFee; - private FixedFeeMeter(UInt256 fixedFee) { - this.fixedFee = fixedFee; - } + private FixedFeeMeter(UInt256 fixedFee) { + this.fixedFee = fixedFee; + } - public static FixedFeeMeter create(UInt256 fixedFee) { - return new FixedFeeMeter(fixedFee); - } + public static FixedFeeMeter create(UInt256 fixedFee) { + return new FixedFeeMeter(fixedFee); + } - @Override - public void onStart(ExecutionContext context) { - context.addSystemLoan(fixedFee); - } + @Override + public void onStart(ExecutionContext context) { + context.addSystemLoan(fixedFee); + } - @Override - public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - context.chargeOneTimeTransactionFee(txn -> fixedFee); + @Override + public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) + throws Exception { + context.chargeOneTimeTransactionFee(txn -> fixedFee); - if (procedureKey.opSignature().op() == REOp.SYSCALL) { - return; - } + if (procedureKey.opSignature().op() == REOp.SYSCALL) { + return; + } - // TODO: Clean this up - if (procedureKey.opSignature().op() == REOp.DOWN) { - if (param instanceof TokensInAccount) { - var tokensInAccount = (TokensInAccount) param; - if (tokensInAccount.getResourceAddr().isNativeToken()) { - return; - } - } - } + // TODO: Clean this up + if (procedureKey.opSignature().op() == REOp.DOWN) { + if (param instanceof TokensInAccount) { + var tokensInAccount = (TokensInAccount) param; + if (tokensInAccount.getResourceAddr().isNativeToken()) { + return; + } + } + } - context.payOffLoan(); - } + context.payOffLoan(); + } - @Override - public void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - context.payOffLoan(); - } + @Override + public void onSuperUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { + context.payOffLoan(); + } - @Override - public void onSigInstruction(ExecutionContext context) { - // No-op - } + @Override + public void onSigInstruction(ExecutionContext context) { + // No-op + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meter.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meter.java index 541b2f6337..37e79bb740 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meter.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meter.java @@ -69,41 +69,38 @@ import com.radixdlt.constraintmachine.exceptions.AuthorizationException; public interface Meter { - void onStart(ExecutionContext context); + void onStart(ExecutionContext context); - void onUserProcedure( - ProcedureKey procedureKey, - Object param, - ExecutionContext context - ) throws Exception; + void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) + throws Exception; - void onSuperUserProcedure( - ProcedureKey procedureKey, - Object param, - ExecutionContext context - ) throws Exception; + void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) + throws Exception; - void onSigInstruction(ExecutionContext context) throws AuthorizationException; + void onSigInstruction(ExecutionContext context) throws AuthorizationException; - Meter EMPTY = new Meter() { - @Override - public void onStart(ExecutionContext context) { - // no-op - } + Meter EMPTY = + new Meter() { + @Override + public void onStart(ExecutionContext context) { + // no-op + } - @Override - public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) { - // no-op - } + @Override + public void onUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) { + // no-op + } - @Override - public void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) { - // no-op - } + @Override + public void onSuperUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) { + // no-op + } - @Override - public void onSigInstruction(ExecutionContext context) { - // no-op - } - }; + @Override + public void onSigInstruction(ExecutionContext context) { + // no-op + } + }; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meters.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meters.java index e17cb5a29a..07b5e721bf 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meters.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/Meters.java @@ -69,35 +69,37 @@ import com.radixdlt.constraintmachine.exceptions.AuthorizationException; public final class Meters { - private Meters() { - throw new IllegalStateException("Cannot instantiate."); - } + private Meters() { + throw new IllegalStateException("Cannot instantiate."); + } - public static Meter combine(Meter m0, Meter m1) { - return new Meter() { - @Override - public void onStart(ExecutionContext context) { - m0.onStart(context); - m1.onStart(context); - } + public static Meter combine(Meter m0, Meter m1) { + return new Meter() { + @Override + public void onStart(ExecutionContext context) { + m0.onStart(context); + m1.onStart(context); + } - @Override - public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - m0.onUserProcedure(procedureKey, param, context); - m1.onUserProcedure(procedureKey, param, context); - } + @Override + public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) + throws Exception { + m0.onUserProcedure(procedureKey, param, context); + m1.onUserProcedure(procedureKey, param, context); + } - @Override - public void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - m0.onSuperUserProcedure(procedureKey, param, context); - m1.onSuperUserProcedure(procedureKey, param, context); - } + @Override + public void onSuperUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { + m0.onSuperUserProcedure(procedureKey, param, context); + m1.onSuperUserProcedure(procedureKey, param, context); + } - @Override - public void onSigInstruction(ExecutionContext context) throws AuthorizationException { - m0.onSigInstruction(context); - m1.onSigInstruction(context); - } - }; - } + @Override + public void onSigInstruction(ExecutionContext context) throws AuthorizationException { + m0.onSigInstruction(context); + m1.onSigInstruction(context); + } + }; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/SigsPerRoundMeter.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/SigsPerRoundMeter.java index 781b0b8d78..4ecf3331d3 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/SigsPerRoundMeter.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/SigsPerRoundMeter.java @@ -70,32 +70,32 @@ public final class SigsPerRoundMeter implements Meter { - private final int sigsPerRound; + private final int sigsPerRound; - private SigsPerRoundMeter(int sigsPerRound) { - this.sigsPerRound = sigsPerRound; - } + private SigsPerRoundMeter(int sigsPerRound) { + this.sigsPerRound = sigsPerRound; + } - public static SigsPerRoundMeter create(int sigsPerRound) { - return new SigsPerRoundMeter(sigsPerRound); - } + public static SigsPerRoundMeter create(int sigsPerRound) { + return new SigsPerRoundMeter(sigsPerRound); + } - @Override - public void onStart(ExecutionContext context) { - // No-op - } + @Override + public void onStart(ExecutionContext context) { + // No-op + } - @Override - public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) { - } + @Override + public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) {} - @Override - public void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) { - context.resetSigs(sigsPerRound); - } + @Override + public void onSuperUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) { + context.resetSigs(sigsPerRound); + } - @Override - public void onSigInstruction(ExecutionContext context) throws AuthorizationException { - context.sig(); - } + @Override + public void onSigInstruction(ExecutionContext context) throws AuthorizationException { + context.sig(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/TxnSizeFeeMeter.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/TxnSizeFeeMeter.java index 26b8e9cf34..2308380c89 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/TxnSizeFeeMeter.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/TxnSizeFeeMeter.java @@ -71,52 +71,55 @@ import com.radixdlt.utils.UInt256; public class TxnSizeFeeMeter implements Meter { - private final UInt256 feePerByte; - private final UInt256 systemLoan; + private final UInt256 feePerByte; + private final UInt256 systemLoan; - private TxnSizeFeeMeter(UInt256 feePerByte, UInt256 systemLoan) { - this.feePerByte = feePerByte; - this.systemLoan = systemLoan; - } + private TxnSizeFeeMeter(UInt256 feePerByte, UInt256 systemLoan) { + this.feePerByte = feePerByte; + this.systemLoan = systemLoan; + } - public static TxnSizeFeeMeter create(UInt256 feePerByte, long maxSize) { - var systemLoan = feePerByte.multiply(UInt256.from(maxSize)); - return new TxnSizeFeeMeter(feePerByte, systemLoan); - } + public static TxnSizeFeeMeter create(UInt256 feePerByte, long maxSize) { + var systemLoan = feePerByte.multiply(UInt256.from(maxSize)); + return new TxnSizeFeeMeter(feePerByte, systemLoan); + } - @Override - public void onStart(ExecutionContext context) { - context.addSystemLoan(systemLoan); - } + @Override + public void onStart(ExecutionContext context) { + context.addSystemLoan(systemLoan); + } - @Override - public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - context.chargeOneTimeTransactionFee(txn -> UInt256.from(txn.getPayload().length).multiply(feePerByte)); + @Override + public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) + throws Exception { + context.chargeOneTimeTransactionFee( + txn -> UInt256.from(txn.getPayload().length).multiply(feePerByte)); - if (procedureKey.opSignature().op() == REOp.SYSCALL) { - return; - } + if (procedureKey.opSignature().op() == REOp.SYSCALL) { + return; + } - // TODO: Clean this up - if (procedureKey.opSignature().op() == REOp.DOWN) { - if (param instanceof TokensInAccount) { - var tokensInAccount = (TokensInAccount) param; - if (tokensInAccount.getResourceAddr().isNativeToken()) { - return; - } - } - } + // TODO: Clean this up + if (procedureKey.opSignature().op() == REOp.DOWN) { + if (param instanceof TokensInAccount) { + var tokensInAccount = (TokensInAccount) param; + if (tokensInAccount.getResourceAddr().isNativeToken()) { + return; + } + } + } - context.payOffLoan(); - } + context.payOffLoan(); + } - @Override - public void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - context.payOffLoan(); - } + @Override + public void onSuperUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { + context.payOffLoan(); + } - @Override - public void onSigInstruction(ExecutionContext context) { - // No-op - } + @Override + public void onSigInstruction(ExecutionContext context) { + // No-op + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/UpSubstateFeeMeter.java b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/UpSubstateFeeMeter.java index 4caca44d2d..5b48446ae6 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/UpSubstateFeeMeter.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/constraintmachine/meter/UpSubstateFeeMeter.java @@ -70,45 +70,47 @@ import com.radixdlt.constraintmachine.REOp; import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.utils.UInt256; - import java.util.Map; public final class UpSubstateFeeMeter implements Meter { - private final Map, UInt256> perUpSubstateFee; + private final Map, UInt256> perUpSubstateFee; - private UpSubstateFeeMeter(Map, UInt256> perUpSubstateFee) { - this.perUpSubstateFee = perUpSubstateFee; - } + private UpSubstateFeeMeter(Map, UInt256> perUpSubstateFee) { + this.perUpSubstateFee = perUpSubstateFee; + } - public static UpSubstateFeeMeter create(Map, UInt256> perUpSubstateFee) { - return new UpSubstateFeeMeter(perUpSubstateFee); - } + public static UpSubstateFeeMeter create( + Map, UInt256> perUpSubstateFee) { + return new UpSubstateFeeMeter(perUpSubstateFee); + } - @Override - public void onStart(ExecutionContext context) { - // No-op - } + @Override + public void onStart(ExecutionContext context) { + // No-op + } - @Override - public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - // TODO: Clean this up - if (procedureKey.opSignature().op() == REOp.UP && param instanceof Particle) { - var substate = (Particle) param; - var c = substate.getClass(); - var fee = perUpSubstateFee.get(c); - if (fee != null) { - context.charge(fee); - } - } - } + @Override + public void onUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) + throws Exception { + // TODO: Clean this up + if (procedureKey.opSignature().op() == REOp.UP && param instanceof Particle) { + var substate = (Particle) param; + var c = substate.getClass(); + var fee = perUpSubstateFee.get(c); + if (fee != null) { + context.charge(fee); + } + } + } - @Override - public void onSuperUserProcedure(ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { - // No-op - } + @Override + public void onSuperUserProcedure( + ProcedureKey procedureKey, Object param, ExecutionContext context) throws Exception { + // No-op + } - @Override - public void onSigInstruction(ExecutionContext context) throws AuthorizationException { - // No-op - } + @Override + public void onSigInstruction(ExecutionContext context) throws AuthorizationException { + // No-op + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/crypto/ECKeyOps.java b/radixdlt-engine/src/main/java/com/radixdlt/crypto/ECKeyOps.java index 87f21e91fe..32a5722806 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/crypto/ECKeyOps.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/crypto/ECKeyOps.java @@ -64,50 +64,44 @@ package com.radixdlt.crypto; +import java.math.BigInteger; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import java.math.BigInteger; - -/** - * An interface used for actions that require access to node's private key - */ +/** An interface used for actions that require access to node's private key */ public interface ECKeyOps { - /** - * Calculates an ECDH agreement using node's private key - */ - BigInteger ecdhAgreement(ECPublicKey publicKey); + /** Calculates an ECDH agreement using node's private key */ + BigInteger ecdhAgreement(ECPublicKey publicKey); - /** - * Decrypts a message using node's private key - */ - byte[] eciesDecrypt(byte[] cipher, byte[] macData) throws InvalidCipherTextException; + /** Decrypts a message using node's private key */ + byte[] eciesDecrypt(byte[] cipher, byte[] macData) throws InvalidCipherTextException; - /** - * Returns this node's public key. - */ - ECPublicKey nodePubKey(); + /** Returns this node's public key. */ + ECPublicKey nodePubKey(); - static ECKeyOps fromKeyPair(ECKeyPair keyPair) { - return new ECKeyOps() { - @Override - public BigInteger ecdhAgreement(ECPublicKey publicKey) { - final var agreement = new ECDHBasicAgreement(); - agreement.init(new ECPrivateKeyParameters(new BigInteger(1, keyPair.getPrivateKey()), ECKeyUtils.domain())); - return agreement.calculateAgreement(new ECPublicKeyParameters(publicKey.getEcPoint(), ECKeyUtils.domain())); - } + static ECKeyOps fromKeyPair(ECKeyPair keyPair) { + return new ECKeyOps() { + @Override + public BigInteger ecdhAgreement(ECPublicKey publicKey) { + final var agreement = new ECDHBasicAgreement(); + agreement.init( + new ECPrivateKeyParameters( + new BigInteger(1, keyPair.getPrivateKey()), ECKeyUtils.domain())); + return agreement.calculateAgreement( + new ECPublicKeyParameters(publicKey.getEcPoint(), ECKeyUtils.domain())); + } - @Override - public byte[] eciesDecrypt(byte[] cipher, byte[] macData) throws InvalidCipherTextException { - return ECIESCoder.decrypt(new BigInteger(1, keyPair.getPrivateKey()), cipher, macData); - } + @Override + public byte[] eciesDecrypt(byte[] cipher, byte[] macData) throws InvalidCipherTextException { + return ECIESCoder.decrypt(new BigInteger(1, keyPair.getPrivateKey()), cipher, macData); + } - @Override - public ECPublicKey nodePubKey() { - return keyPair.getPublicKey(); - } - }; - } + @Override + public ECPublicKey nodePubKey() { + return keyPair.getPublicKey(); + } + }; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/crypto/Hasher.java b/radixdlt-engine/src/main/java/com/radixdlt/crypto/Hasher.java index 309bfa78a4..34768fbccd 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/crypto/Hasher.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/crypto/Hasher.java @@ -66,22 +66,21 @@ import com.google.common.hash.HashCode; -/** - * An object capable of hashing an object - */ +/** An object capable of hashing an object */ public interface Hasher { - int bytes(); + int bytes(); - /** - * Serializes and hashes an arbitrary object into a byte array. - * - * @param o object to hash - */ - HashCode hash(Object o); + /** + * Serializes and hashes an arbitrary object into a byte array. + * + * @param o object to hash + */ + HashCode hash(Object o); - /** - * Hahes a raw byte array. - * @param bytes byte array to hash - */ - HashCode hashBytes(byte[] bytes); + /** + * Hahes a raw byte array. + * + * @param bytes byte array to hash + */ + HashCode hashBytes(byte[] bytes); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/BatchVerifier.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/BatchVerifier.java index e9cbed98ac..0d06e58c61 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/BatchVerifier.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/BatchVerifier.java @@ -65,20 +65,17 @@ package com.radixdlt.engine; import com.radixdlt.constraintmachine.REProcessedTxn; - import java.util.List; /** - * Verifies that batched atoms executed on Radix Engine follow some - * specified rules. + * Verifies that batched atoms executed on Radix Engine follow some specified rules. * * @param class of metadata */ public interface BatchVerifier { - default void testMetadata(M metadata, List txns) throws MetadataException { - } + default void testMetadata(M metadata, List txns) throws MetadataException {} - static BatchVerifier empty() { - return new BatchVerifier<>() {}; - } + static BatchVerifier empty() { + return new BatchVerifier<>() {}; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/FeeConstructionException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/FeeConstructionException.java index 5e03ec1798..95f9cc5879 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/FeeConstructionException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/FeeConstructionException.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -66,14 +67,14 @@ import com.radixdlt.atom.TxBuilderException; public final class FeeConstructionException extends TxBuilderException { - private final int attempts; + private final int attempts; - public FeeConstructionException(int attempts) { - super("Unable to construct transaction with fees after " + attempts + " attempts."); - this.attempts = attempts; - } + public FeeConstructionException(int attempts) { + super("Unable to construct transaction with fees after " + attempts + " attempts."); + this.attempts = attempts; + } - public int getAttempts() { - return attempts; - } + public int getAttempts() { + return attempts; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/MetadataException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/MetadataException.java index 3e2694d6cb..bacc3ebad1 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/MetadataException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/MetadataException.java @@ -66,7 +66,7 @@ // TODO: Change to Exception public class MetadataException extends RuntimeException { - public MetadataException(String msg) { - super(msg); - } + public MetadataException(String msg) { + super(msg); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngine.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngine.java index 249db4e864..4c782e8300 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngine.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngine.java @@ -102,9 +102,6 @@ import com.radixdlt.store.TransientEngineStore; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt384; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -117,506 +114,522 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Top Level Class for the Radix Engine, a real-time, shardable, distributed state machine. - */ +/** Top Level Class for the Radix Engine, a real-time, shardable, distributed state machine. */ public final class RadixEngine { - private static final Logger logger = LogManager.getLogger(); - private final EngineStore engineStore; - private final Object stateUpdateEngineLock = new Object(); - private final List> branches = new ArrayList<>(); - - private REParser parser; - private SubstateSerialization serialization; - private BatchVerifier batchVerifier; - private REConstructor actionConstructors; - private ConstraintMachine constraintMachine; - - public RadixEngine( - REParser parser, - SubstateSerialization serialization, - REConstructor actionConstructors, - ConstraintMachine constraintMachine, - EngineStore engineStore - ) { - this( - parser, - serialization, - actionConstructors, - constraintMachine, - engineStore, - BatchVerifier.empty() - ); - } - - public RadixEngine( - REParser parser, - SubstateSerialization serialization, - REConstructor actionConstructors, - ConstraintMachine constraintMachine, - EngineStore engineStore, - BatchVerifier batchVerifier - ) { - this.parser = Objects.requireNonNull(parser); - this.serialization = Objects.requireNonNull(serialization); - this.actionConstructors = Objects.requireNonNull(actionConstructors); - this.constraintMachine = Objects.requireNonNull(constraintMachine); - this.engineStore = Objects.requireNonNull(engineStore); - this.batchVerifier = batchVerifier; - } - - public void replaceConstraintMachine( - ConstraintMachineConfig constraintMachineConfig, - SubstateSerialization serialization, - REConstructor actionToConstructorMap, - BatchVerifier batchVerifier, - REParser parser - ) { - synchronized (stateUpdateEngineLock) { - this.constraintMachine = new ConstraintMachine( - constraintMachineConfig.getProcedures(), - constraintMachineConfig.getDeserialization(), - constraintMachineConfig.getVirtualSubstateDeserialization(), - constraintMachineConfig.getMeter() - ); - this.actionConstructors = actionToConstructorMap; - this.batchVerifier = batchVerifier; - this.parser = parser; - this.serialization = serialization; - } - } - - /** - * A cheap radix engine branch which is purely transient - */ - public static class RadixEngineBranch { - private final RadixEngine engine; - private boolean deleted = false; - - private RadixEngineBranch( - REParser parser, - SubstateSerialization serialization, - REConstructor actionToConstructorMap, - ConstraintMachine constraintMachine, - EngineStore parentStore - ) { - var transientEngineStore = new TransientEngineStore<>(parentStore); - - this.engine = new RadixEngine<>( - parser, - serialization, - actionToConstructorMap, - constraintMachine, - transientEngineStore, - BatchVerifier.empty() - ); - } - - private void delete() { - deleted = true; - } - - private void assertNotDeleted() { - if (deleted) { - throw new IllegalStateException("Radix engine branch is deleted"); - } - } - - public RadixEngineResult execute(List txns) throws RadixEngineException { - assertNotDeleted(); - return engine.execute(txns); - } - - public RadixEngineResult execute(List txns, boolean skipAuthorization) throws RadixEngineException { - assertNotDeleted(); - return engine.execute(txns, null, PermissionLevel.USER, skipAuthorization); - } - - public RadixEngineResult execute(List txns, PermissionLevel permissionLevel) throws RadixEngineException { - assertNotDeleted(); - return engine.execute(txns, null, permissionLevel); - } - - public TxBuilder construct(TxAction action) throws TxBuilderException { - assertNotDeleted(); - return engine.construct(action); - } - - public TxBuilder construct(TxnConstructionRequest request) throws TxBuilderException { - assertNotDeleted(); - return engine.construct(request); - } - } - - public void deleteBranches() { - synchronized (stateUpdateEngineLock) { - branches.forEach(RadixEngineBranch::delete); - branches.clear(); - } - } - - public RadixEngineBranch transientBranch() { - synchronized (stateUpdateEngineLock) { - RadixEngineBranch branch = new RadixEngineBranch<>( - this.parser, - this.serialization, - this.actionConstructors, - this.constraintMachine, - this.engineStore - ); - - branches.add(branch); - - return branch; - } - } - - private Optional getSignedByKey(ParsedTxn parsedTxn, ExecutionContext context) throws AuthorizationException { - if (!context.skipAuthorization() && context.permissionLevel() != PermissionLevel.SYSTEM) { - var payloadHashAndSigMaybe = parsedTxn.getPayloadHashAndSig(); - if (payloadHashAndSigMaybe.isPresent()) { - var payloadHashAndSig = payloadHashAndSigMaybe.get(); - var hash = payloadHashAndSig.getFirst(); - var sig = payloadHashAndSig.getSecond(); - var pubKey = ECPublicKey.recoverFrom(hash, sig).orElseThrow(() -> new AuthorizationException("Invalid signature")); - // TODO: do we still need this verify? - if (!pubKey.verify(hash, sig)) { - throw new AuthorizationException("Invalid signature"); - } - - return Optional.of(pubKey); - } - } - - return Optional.empty(); - } - - private REProcessedTxn verify(EngineStore.EngineStoreInTransaction engineStoreInTransaction, Txn txn, ExecutionContext context) - throws AuthorizationException, TxnParseException, ConstraintMachineException { - - var parsedTxn = parser.parse(txn); - var signedByKey = getSignedByKey(parsedTxn, context); - signedByKey.ifPresent(context::setKey); - - context.setDisableResourceAllocAndDestroy(parsedTxn.disableResourceAllocAndDestroy()); - - var stateUpdates = constraintMachine.verify( - engineStoreInTransaction, - context, - parsedTxn.instructions() - ); - - return new REProcessedTxn(parsedTxn, signedByKey.orElse(null), stateUpdates, context.getEvents()); - } - - public RadixEngineResult execute(List txns) throws RadixEngineException { - return execute(txns, null, PermissionLevel.USER); - } - - public RadixEngineResult execute(List txns, M meta, PermissionLevel permissionLevel) throws RadixEngineException { - return execute(txns, meta, permissionLevel, false); - } - - /** - * Atomically stores the given atom into the store. If the atom - * has any conflicts or dependency issues the atom will not be stored. - * - * @param txns transactions to execute - * @param permissionLevel permission level to execute on - * @throws RadixEngineException on state conflict or dependency issues - */ - public RadixEngineResult execute(List txns, M meta, PermissionLevel permissionLevel, boolean skipAuthorization) throws RadixEngineException { - synchronized (stateUpdateEngineLock) { - if (!branches.isEmpty()) { - throw new IllegalStateException( - String.format( - "%s transient branches still exist. Must delete branches before storing additional atoms.", - branches.size() - ) - ); - } - return engineStore.transaction(store -> executeInternal(store, txns, meta, permissionLevel, skipAuthorization)); - } - } - - private RadixEngineResult executeInternal( - EngineStore.EngineStoreInTransaction engineStoreInTransaction, - List txns, - M meta, - PermissionLevel permissionLevel, - boolean skipAuthorization - ) throws RadixEngineException { - var processedTxns = new ArrayList(); - - // FIXME: This is quite the hack to increase sigsLeft for execution on noncommits (e.g. mempool) - // FIXME: Should probably just change metering - var sigsLeft = meta != null ? 0 : 1000; // Start with 0 - var storageStopwatch = Stopwatch.createUnstarted(); - var verificationStopwatch = Stopwatch.createUnstarted(); - - for (int i = 0; i < txns.size(); i++) { - var txn = txns.get(i); - - verificationStopwatch.start(); - var context = new ExecutionContext(txn, permissionLevel, skipAuthorization, sigsLeft); - final REProcessedTxn processedTxn; - try { - processedTxn = this.verify(engineStoreInTransaction, txn, context); - } catch (TxnParseException | AuthorizationException | ConstraintMachineException e) { - throw new RadixEngineException(i, txns.size(), txn, e); - } - verificationStopwatch.stop(); - - // Carry sigs left to the next transaction - sigsLeft = context.sigsLeft(); - - storageStopwatch.start(); - try { - engineStoreInTransaction.storeTxn(processedTxn); - } catch (Exception e) { - logger.error("Store of atom failed: " + processedTxn, e); - throw e; - } - storageStopwatch.stop(); - - processedTxns.add(processedTxn); - } - - try { - batchVerifier.testMetadata(meta, processedTxns); - } catch (MetadataException e) { - logger.error("Invalid metadata: " + processedTxns); - throw e; - } - - if (meta != null) { - engineStoreInTransaction.storeMetadata(meta); - } - - return RadixEngineResult.create( - processedTxns, - verificationStopwatch.elapsed(TimeUnit.MILLISECONDS), - storageStopwatch.elapsed(TimeUnit.MILLISECONDS) - ); - } - - public interface TxBuilderExecutable { - void execute(TxBuilder txBuilder) throws TxBuilderException; - } - - public TxBuilder construct(TxBuilderExecutable executable) throws TxBuilderException { - return construct(executable, Set.of()); - } - - private TxBuilder construct(TxBuilderExecutable executable, Set avoid) throws TxBuilderException { - synchronized (stateUpdateEngineLock) { - SubstateStore filteredStore = new SubstateStore() { - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return engineStore.openIndexedCursor(index) - .filter(i -> !avoid.contains(SubstateId.fromBytes(i.getId()))); - } - - @Override - public Optional get(SystemMapKey key) { - return engineStore.get(key); - } - }; - - var txBuilder = TxBuilder.newBuilder( - filteredStore, - constraintMachine.getDeserialization(), - serialization - ); - - executable.execute(txBuilder); - - return txBuilder; - } - } - - public TxBuilder construct(TxAction action) throws TxBuilderException { - return construct(TxnConstructionRequest.create().action(action)); - } - - public TxBuilder construct(TxnConstructionRequest request) throws TxBuilderException { - var feePayer = request.getFeePayer(); - if (feePayer.isPresent()) { - return constructWithFees(request, feePayer.get()); - } else { - return construct( - txBuilder -> { - if (request.isDisableResourceAllocAndDestroy()) { - txBuilder.toLowLevelBuilder().disableResourceAllocAndDestroy(); - } - for (var action : request.getActions()) { - this.actionConstructors.construct(action, txBuilder); - } - var msg = request.getMsg(); - if (msg.isPresent()) { - txBuilder.message(msg.get()); - } - }, - request.getSubstatesToAvoid() - ); - } - } - - public TxBuilder constructWithFees( - TxBuilderExecutable executable, - boolean disableResourceAllocAndDestroy, - REAddr feePayer, - BiFunction notEnoughFeesExceptionSupplier - ) throws TxBuilderException { - int maxTries = 5; - var perByteFee = this.actionConstructors.getPerByteFee().orElse(UInt256.ZERO); - var feeGuess = new AtomicReference<>(perByteFee.multiply(UInt256.from(100))); // Close to minimum size - for (int i = 0; i < maxTries; i++) { - try { - return construct(txBuilder -> { - if (disableResourceAllocAndDestroy) { - txBuilder.toLowLevelBuilder().disableResourceAllocAndDestroy(); - } - - txBuilder.putFeeReserve(feePayer, feeGuess.get(), available -> notEnoughFeesExceptionSupplier.apply(feeGuess.get(), available)); - txBuilder.end(); - - executable.execute(txBuilder); - this.actionConstructors.construct(new FeeReserveComplete(feePayer), txBuilder); - }); - } catch (FeeReserveCompleteException e) { - feeGuess.set(e.getExpectedFee()); - } - } - - throw new FeeConstructionException(maxTries); - } - - private TxBuilder constructWithFees(TxnConstructionRequest request, REAddr feePayer) throws TxBuilderException { - int maxTries = 5; - var perByteFee = this.actionConstructors.getPerByteFee().orElse(UInt256.ZERO); - var feeGuess = new AtomicReference<>(perByteFee.multiply(UInt256.from(100))); // Close to minimum size - for (int i = 0; i < maxTries; i++) { - try { - return construct( - txBuilder -> { - if (request.isDisableResourceAllocAndDestroy()) { - txBuilder.toLowLevelBuilder().disableResourceAllocAndDestroy(); - } - - this.actionConstructors.construct(new FeeReservePut(feePayer, feeGuess.get()), txBuilder); - for (var action : request.getActions()) { - this.actionConstructors.construct(action, txBuilder); - } - var msg = request.getMsg(); - if (msg.isPresent()) { - txBuilder.message(msg.get()); - } - this.actionConstructors.construct(new FeeReserveComplete(feePayer), txBuilder); - }, - request.getSubstatesToAvoid() - ); - } catch (FeeReserveCompleteException e) { - feeGuess.set(e.getExpectedFee()); - } - } - - throw new FeeConstructionException(maxTries); - } - - public REParser getParser() { - synchronized (stateUpdateEngineLock) { - return parser; - } - } - - public SubstateSerialization getSubstateSerialization() { - synchronized (stateUpdateEngineLock) { - return serialization; - } - } - - public SubstateDeserialization getSubstateDeserialization() { - synchronized (stateUpdateEngineLock) { - return constraintMachine.getDeserialization(); - } - } - - public VirtualSubstateDeserialization getVirtualSubstateDeserialization() { - synchronized (stateUpdateEngineLock) { - return constraintMachine.getVirtualDeserialization(); - } - } - - public V read(Function, V> readEngineStore) { - synchronized (stateUpdateEngineLock) { - return readEngineStore.apply(new RadixEngineReader() { - @Override - public M getMetadata() { - return engineStore.getMetadata(); - } - - @Override - public Optional get(SystemMapKey mapKey) { - var deserialization = constraintMachine.getDeserialization(); - return engineStore.get(mapKey).map(raw -> { - try { - return deserialization.deserialize(raw.getData()); - } catch (DeserializeException e) { - throw new IllegalStateException(e); - } - }); - } - - @Override - public Map reduceResources(Class c, Function keyMapper) { - var deserialization = constraintMachine.getDeserialization(); - return reduce(deserialization.index(c), new HashMap<>(), - (m, t) -> { - m.merge(keyMapper.apply(t), UInt384.from(t.getAmount()), UInt384::add); - return m; - } - ); - } - - @Override - public Map reduceResources( - SubstateIndex index, Function keyMapper, Predicate predicate - ) { - return reduce(index, new HashMap<>(), - (m, t) -> { - if (predicate.test(t)) { - m.merge(keyMapper.apply(t), UInt384.from(t.getAmount()), UInt384::add); - } - return m; - } - ); - } - - private U reduce(SubstateIndex i, U identity, BiFunction accumulator) { - var deserialization = constraintMachine.getDeserialization(); - var u = identity; - try (var cursor = engineStore.openIndexedCursor(i)) { - while (cursor.hasNext()) { - try { - var t = (T) deserialization.deserialize(cursor.next().getData()); - u = accumulator.apply(u, t); - } catch (DeserializeException e) { - throw new IllegalStateException(e); - } - } - } - return u; - } - - @Override - public U reduce(Class c, U identity, BiFunction accumulator) { - var deserialization = constraintMachine.getDeserialization(); - var index = deserialization.index(c); - return reduce(index, identity, accumulator); - } - }); - } - } + private static final Logger logger = LogManager.getLogger(); + private final EngineStore engineStore; + private final Object stateUpdateEngineLock = new Object(); + private final List> branches = new ArrayList<>(); + + private REParser parser; + private SubstateSerialization serialization; + private BatchVerifier batchVerifier; + private REConstructor actionConstructors; + private ConstraintMachine constraintMachine; + + public RadixEngine( + REParser parser, + SubstateSerialization serialization, + REConstructor actionConstructors, + ConstraintMachine constraintMachine, + EngineStore engineStore) { + this( + parser, + serialization, + actionConstructors, + constraintMachine, + engineStore, + BatchVerifier.empty()); + } + + public RadixEngine( + REParser parser, + SubstateSerialization serialization, + REConstructor actionConstructors, + ConstraintMachine constraintMachine, + EngineStore engineStore, + BatchVerifier batchVerifier) { + this.parser = Objects.requireNonNull(parser); + this.serialization = Objects.requireNonNull(serialization); + this.actionConstructors = Objects.requireNonNull(actionConstructors); + this.constraintMachine = Objects.requireNonNull(constraintMachine); + this.engineStore = Objects.requireNonNull(engineStore); + this.batchVerifier = batchVerifier; + } + + public void replaceConstraintMachine( + ConstraintMachineConfig constraintMachineConfig, + SubstateSerialization serialization, + REConstructor actionToConstructorMap, + BatchVerifier batchVerifier, + REParser parser) { + synchronized (stateUpdateEngineLock) { + this.constraintMachine = + new ConstraintMachine( + constraintMachineConfig.getProcedures(), + constraintMachineConfig.getDeserialization(), + constraintMachineConfig.getVirtualSubstateDeserialization(), + constraintMachineConfig.getMeter()); + this.actionConstructors = actionToConstructorMap; + this.batchVerifier = batchVerifier; + this.parser = parser; + this.serialization = serialization; + } + } + + /** A cheap radix engine branch which is purely transient */ + public static class RadixEngineBranch { + private final RadixEngine engine; + private boolean deleted = false; + + private RadixEngineBranch( + REParser parser, + SubstateSerialization serialization, + REConstructor actionToConstructorMap, + ConstraintMachine constraintMachine, + EngineStore parentStore) { + var transientEngineStore = new TransientEngineStore<>(parentStore); + + this.engine = + new RadixEngine<>( + parser, + serialization, + actionToConstructorMap, + constraintMachine, + transientEngineStore, + BatchVerifier.empty()); + } + + private void delete() { + deleted = true; + } + + private void assertNotDeleted() { + if (deleted) { + throw new IllegalStateException("Radix engine branch is deleted"); + } + } + + public RadixEngineResult execute(List txns) throws RadixEngineException { + assertNotDeleted(); + return engine.execute(txns); + } + + public RadixEngineResult execute(List txns, boolean skipAuthorization) + throws RadixEngineException { + assertNotDeleted(); + return engine.execute(txns, null, PermissionLevel.USER, skipAuthorization); + } + + public RadixEngineResult execute(List txns, PermissionLevel permissionLevel) + throws RadixEngineException { + assertNotDeleted(); + return engine.execute(txns, null, permissionLevel); + } + + public TxBuilder construct(TxAction action) throws TxBuilderException { + assertNotDeleted(); + return engine.construct(action); + } + + public TxBuilder construct(TxnConstructionRequest request) throws TxBuilderException { + assertNotDeleted(); + return engine.construct(request); + } + } + + public void deleteBranches() { + synchronized (stateUpdateEngineLock) { + branches.forEach(RadixEngineBranch::delete); + branches.clear(); + } + } + + public RadixEngineBranch transientBranch() { + synchronized (stateUpdateEngineLock) { + RadixEngineBranch branch = + new RadixEngineBranch<>( + this.parser, + this.serialization, + this.actionConstructors, + this.constraintMachine, + this.engineStore); + + branches.add(branch); + + return branch; + } + } + + private Optional getSignedByKey(ParsedTxn parsedTxn, ExecutionContext context) + throws AuthorizationException { + if (!context.skipAuthorization() && context.permissionLevel() != PermissionLevel.SYSTEM) { + var payloadHashAndSigMaybe = parsedTxn.getPayloadHashAndSig(); + if (payloadHashAndSigMaybe.isPresent()) { + var payloadHashAndSig = payloadHashAndSigMaybe.get(); + var hash = payloadHashAndSig.getFirst(); + var sig = payloadHashAndSig.getSecond(); + var pubKey = + ECPublicKey.recoverFrom(hash, sig) + .orElseThrow(() -> new AuthorizationException("Invalid signature")); + // TODO: do we still need this verify? + if (!pubKey.verify(hash, sig)) { + throw new AuthorizationException("Invalid signature"); + } + + return Optional.of(pubKey); + } + } + + return Optional.empty(); + } + + private REProcessedTxn verify( + EngineStore.EngineStoreInTransaction engineStoreInTransaction, + Txn txn, + ExecutionContext context) + throws AuthorizationException, TxnParseException, ConstraintMachineException { + + var parsedTxn = parser.parse(txn); + var signedByKey = getSignedByKey(parsedTxn, context); + signedByKey.ifPresent(context::setKey); + + context.setDisableResourceAllocAndDestroy(parsedTxn.disableResourceAllocAndDestroy()); + + var stateUpdates = + constraintMachine.verify(engineStoreInTransaction, context, parsedTxn.instructions()); + + return new REProcessedTxn( + parsedTxn, signedByKey.orElse(null), stateUpdates, context.getEvents()); + } + + public RadixEngineResult execute(List txns) throws RadixEngineException { + return execute(txns, null, PermissionLevel.USER); + } + + public RadixEngineResult execute(List txns, M meta, PermissionLevel permissionLevel) + throws RadixEngineException { + return execute(txns, meta, permissionLevel, false); + } + + /** + * Atomically stores the given atom into the store. If the atom has any conflicts or dependency + * issues the atom will not be stored. + * + * @param txns transactions to execute + * @param permissionLevel permission level to execute on + * @throws RadixEngineException on state conflict or dependency issues + */ + public RadixEngineResult execute( + List txns, M meta, PermissionLevel permissionLevel, boolean skipAuthorization) + throws RadixEngineException { + synchronized (stateUpdateEngineLock) { + if (!branches.isEmpty()) { + throw new IllegalStateException( + String.format( + "%s transient branches still exist. Must delete branches before storing additional" + + " atoms.", + branches.size())); + } + return engineStore.transaction( + store -> executeInternal(store, txns, meta, permissionLevel, skipAuthorization)); + } + } + + private RadixEngineResult executeInternal( + EngineStore.EngineStoreInTransaction engineStoreInTransaction, + List txns, + M meta, + PermissionLevel permissionLevel, + boolean skipAuthorization) + throws RadixEngineException { + var processedTxns = new ArrayList(); + + // FIXME: This is quite the hack to increase sigsLeft for execution on noncommits (e.g. mempool) + // FIXME: Should probably just change metering + var sigsLeft = meta != null ? 0 : 1000; // Start with 0 + var storageStopwatch = Stopwatch.createUnstarted(); + var verificationStopwatch = Stopwatch.createUnstarted(); + + for (int i = 0; i < txns.size(); i++) { + var txn = txns.get(i); + + verificationStopwatch.start(); + var context = new ExecutionContext(txn, permissionLevel, skipAuthorization, sigsLeft); + final REProcessedTxn processedTxn; + try { + processedTxn = this.verify(engineStoreInTransaction, txn, context); + } catch (TxnParseException | AuthorizationException | ConstraintMachineException e) { + throw new RadixEngineException(i, txns.size(), txn, e); + } + verificationStopwatch.stop(); + + // Carry sigs left to the next transaction + sigsLeft = context.sigsLeft(); + + storageStopwatch.start(); + try { + engineStoreInTransaction.storeTxn(processedTxn); + } catch (Exception e) { + logger.error("Store of atom failed: " + processedTxn, e); + throw e; + } + storageStopwatch.stop(); + + processedTxns.add(processedTxn); + } + + try { + batchVerifier.testMetadata(meta, processedTxns); + } catch (MetadataException e) { + logger.error("Invalid metadata: " + processedTxns); + throw e; + } + + if (meta != null) { + engineStoreInTransaction.storeMetadata(meta); + } + + return RadixEngineResult.create( + processedTxns, + verificationStopwatch.elapsed(TimeUnit.MILLISECONDS), + storageStopwatch.elapsed(TimeUnit.MILLISECONDS)); + } + + public interface TxBuilderExecutable { + void execute(TxBuilder txBuilder) throws TxBuilderException; + } + + public TxBuilder construct(TxBuilderExecutable executable) throws TxBuilderException { + return construct(executable, Set.of()); + } + + private TxBuilder construct(TxBuilderExecutable executable, Set avoid) + throws TxBuilderException { + synchronized (stateUpdateEngineLock) { + SubstateStore filteredStore = + new SubstateStore() { + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + return engineStore + .openIndexedCursor(index) + .filter(i -> !avoid.contains(SubstateId.fromBytes(i.getId()))); + } + + @Override + public Optional get(SystemMapKey key) { + return engineStore.get(key); + } + }; + + var txBuilder = + TxBuilder.newBuilder( + filteredStore, constraintMachine.getDeserialization(), serialization); + + executable.execute(txBuilder); + + return txBuilder; + } + } + + public TxBuilder construct(TxAction action) throws TxBuilderException { + return construct(TxnConstructionRequest.create().action(action)); + } + + public TxBuilder construct(TxnConstructionRequest request) throws TxBuilderException { + var feePayer = request.getFeePayer(); + if (feePayer.isPresent()) { + return constructWithFees(request, feePayer.get()); + } else { + return construct( + txBuilder -> { + if (request.isDisableResourceAllocAndDestroy()) { + txBuilder.toLowLevelBuilder().disableResourceAllocAndDestroy(); + } + for (var action : request.getActions()) { + this.actionConstructors.construct(action, txBuilder); + } + var msg = request.getMsg(); + if (msg.isPresent()) { + txBuilder.message(msg.get()); + } + }, + request.getSubstatesToAvoid()); + } + } + + public TxBuilder constructWithFees( + TxBuilderExecutable executable, + boolean disableResourceAllocAndDestroy, + REAddr feePayer, + BiFunction notEnoughFeesExceptionSupplier) + throws TxBuilderException { + int maxTries = 5; + var perByteFee = this.actionConstructors.getPerByteFee().orElse(UInt256.ZERO); + var feeGuess = + new AtomicReference<>(perByteFee.multiply(UInt256.from(100))); // Close to minimum size + for (int i = 0; i < maxTries; i++) { + try { + return construct( + txBuilder -> { + if (disableResourceAllocAndDestroy) { + txBuilder.toLowLevelBuilder().disableResourceAllocAndDestroy(); + } + + txBuilder.putFeeReserve( + feePayer, + feeGuess.get(), + available -> notEnoughFeesExceptionSupplier.apply(feeGuess.get(), available)); + txBuilder.end(); + + executable.execute(txBuilder); + this.actionConstructors.construct(new FeeReserveComplete(feePayer), txBuilder); + }); + } catch (FeeReserveCompleteException e) { + feeGuess.set(e.getExpectedFee()); + } + } + + throw new FeeConstructionException(maxTries); + } + + private TxBuilder constructWithFees(TxnConstructionRequest request, REAddr feePayer) + throws TxBuilderException { + int maxTries = 5; + var perByteFee = this.actionConstructors.getPerByteFee().orElse(UInt256.ZERO); + var feeGuess = + new AtomicReference<>(perByteFee.multiply(UInt256.from(100))); // Close to minimum size + for (int i = 0; i < maxTries; i++) { + try { + return construct( + txBuilder -> { + if (request.isDisableResourceAllocAndDestroy()) { + txBuilder.toLowLevelBuilder().disableResourceAllocAndDestroy(); + } + + this.actionConstructors.construct( + new FeeReservePut(feePayer, feeGuess.get()), txBuilder); + for (var action : request.getActions()) { + this.actionConstructors.construct(action, txBuilder); + } + var msg = request.getMsg(); + if (msg.isPresent()) { + txBuilder.message(msg.get()); + } + this.actionConstructors.construct(new FeeReserveComplete(feePayer), txBuilder); + }, + request.getSubstatesToAvoid()); + } catch (FeeReserveCompleteException e) { + feeGuess.set(e.getExpectedFee()); + } + } + + throw new FeeConstructionException(maxTries); + } + + public REParser getParser() { + synchronized (stateUpdateEngineLock) { + return parser; + } + } + + public SubstateSerialization getSubstateSerialization() { + synchronized (stateUpdateEngineLock) { + return serialization; + } + } + + public SubstateDeserialization getSubstateDeserialization() { + synchronized (stateUpdateEngineLock) { + return constraintMachine.getDeserialization(); + } + } + + public VirtualSubstateDeserialization getVirtualSubstateDeserialization() { + synchronized (stateUpdateEngineLock) { + return constraintMachine.getVirtualDeserialization(); + } + } + + public V read(Function, V> readEngineStore) { + synchronized (stateUpdateEngineLock) { + return readEngineStore.apply( + new RadixEngineReader() { + @Override + public M getMetadata() { + return engineStore.getMetadata(); + } + + @Override + public Optional get(SystemMapKey mapKey) { + var deserialization = constraintMachine.getDeserialization(); + return engineStore + .get(mapKey) + .map( + raw -> { + try { + return deserialization.deserialize(raw.getData()); + } catch (DeserializeException e) { + throw new IllegalStateException(e); + } + }); + } + + @Override + public Map reduceResources( + Class c, Function keyMapper) { + var deserialization = constraintMachine.getDeserialization(); + return reduce( + deserialization.index(c), + new HashMap<>(), + (m, t) -> { + m.merge(keyMapper.apply(t), UInt384.from(t.getAmount()), UInt384::add); + return m; + }); + } + + @Override + public Map reduceResources( + SubstateIndex index, Function keyMapper, Predicate predicate) { + return reduce( + index, + new HashMap<>(), + (m, t) -> { + if (predicate.test(t)) { + m.merge(keyMapper.apply(t), UInt384.from(t.getAmount()), UInt384::add); + } + return m; + }); + } + + private U reduce( + SubstateIndex i, U identity, BiFunction accumulator) { + var deserialization = constraintMachine.getDeserialization(); + var u = identity; + try (var cursor = engineStore.openIndexedCursor(i)) { + while (cursor.hasNext()) { + try { + var t = (T) deserialization.deserialize(cursor.next().getData()); + u = accumulator.apply(u, t); + } catch (DeserializeException e) { + throw new IllegalStateException(e); + } + } + } + return u; + } + + @Override + public U reduce( + Class c, U identity, BiFunction accumulator) { + var deserialization = constraintMachine.getDeserialization(); + var index = deserialization.index(c); + return reduce(index, identity, accumulator); + } + }); + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineErrorCode.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineErrorCode.java index a8bbb95943..e616235b9c 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineErrorCode.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineErrorCode.java @@ -64,11 +64,9 @@ package com.radixdlt.engine; -/** - * Error codes that describe exceptions thrown by Radix Engine - */ +/** Error codes that describe exceptions thrown by Radix Engine */ public enum RadixEngineErrorCode { - TXN_ERROR, - CM_ERROR, - HOOK_ERROR + TXN_ERROR, + CM_ERROR, + HOOK_ERROR } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineException.java index e8bc5038fe..d884e671a8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineException.java @@ -67,40 +67,48 @@ import com.radixdlt.atom.Txn; import com.radixdlt.utils.Bytes; -/** - * Exception thrown by Radix Engine - */ +/** Exception thrown by Radix Engine */ @SuppressWarnings("serial") public final class RadixEngineException extends Exception { - private final Txn txn; - private final int txnIndex; - private final int batchSize; + private final Txn txn; + private final int txnIndex; + private final int batchSize; - public RadixEngineException(int txnIndex, int batchSize, Txn txn, Exception cause) { - super("index=" + txnIndex + " batchSize=" + batchSize + " txnId=" + txn.getId() - + " txn_size=" + txn.getPayload().length + " txn=" + txnToString(txn), cause); - this.txn = txn; - this.txnIndex = txnIndex; - this.batchSize = batchSize; - } + public RadixEngineException(int txnIndex, int batchSize, Txn txn, Exception cause) { + super( + "index=" + + txnIndex + + " batchSize=" + + batchSize + + " txnId=" + + txn.getId() + + " txn_size=" + + txn.getPayload().length + + " txn=" + + txnToString(txn), + cause); + this.txn = txn; + this.txnIndex = txnIndex; + this.batchSize = batchSize; + } - private static String txnToString(Txn txn) { - if (txn.getPayload().length > 2048) { - return Bytes.toHexString(txn.getPayload()).substring(0, 2048) + "..."; - } else { - return Bytes.toHexString(txn.getPayload()); - } - } + private static String txnToString(Txn txn) { + if (txn.getPayload().length > 2048) { + return Bytes.toHexString(txn.getPayload()).substring(0, 2048) + "..."; + } else { + return Bytes.toHexString(txn.getPayload()); + } + } - public int getBatchSize() { - return batchSize; - } + public int getBatchSize() { + return batchSize; + } - public Txn getTxn() { - return txn; - } + public Txn getTxn() { + return txn; + } - public int getTxnIndex() { - return txnIndex; - } + public int getTxnIndex() { + return txnIndex; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineReader.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineReader.java index e68d080234..ec0f12ad9d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineReader.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineReader.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -68,7 +69,6 @@ import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SystemMapKey; import com.radixdlt.utils.UInt384; - import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; @@ -76,13 +76,15 @@ import java.util.function.Predicate; public interface RadixEngineReader { - M getMetadata(); - Optional get(SystemMapKey mapKey); - Map reduceResources(Class c, Function keyMapper); - Map reduceResources( - SubstateIndex index, - Function keyMapper, - Predicate predicate - ); - U reduce(Class c, U identity, BiFunction accumulator); + M getMetadata(); + + Optional get(SystemMapKey mapKey); + + Map reduceResources( + Class c, Function keyMapper); + + Map reduceResources( + SubstateIndex index, Function keyMapper, Predicate predicate); + + U reduce(Class c, U identity, BiFunction accumulator); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineResult.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineResult.java index 25bbcd3e30..4ac8af6992 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineResult.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngineResult.java @@ -65,45 +65,42 @@ package com.radixdlt.engine; import com.radixdlt.constraintmachine.REProcessedTxn; - import java.util.List; public final class RadixEngineResult { - private final List processedTxns; - private final long verificationTime; - private final long storeTime; + private final List processedTxns; + private final long verificationTime; + private final long storeTime; - private RadixEngineResult(List processedTxns, long verificationTime, long storeTime) { - this.processedTxns = processedTxns; - this.verificationTime = verificationTime; - this.storeTime = storeTime; - } + private RadixEngineResult( + List processedTxns, long verificationTime, long storeTime) { + this.processedTxns = processedTxns; + this.verificationTime = verificationTime; + this.storeTime = storeTime; + } - public static RadixEngineResult create( - List processedTxns, - long verificationTime, - long storeTime - ) { - return new RadixEngineResult(processedTxns, verificationTime, storeTime); - } + public static RadixEngineResult create( + List processedTxns, long verificationTime, long storeTime) { + return new RadixEngineResult(processedTxns, verificationTime, storeTime); + } - public long getVerificationTime() { - return verificationTime; - } + public long getVerificationTime() { + return verificationTime; + } - public long getStoreTime() { - return storeTime; - } + public long getStoreTime() { + return storeTime; + } - // TODO: Create separate class for single transaction results - public REProcessedTxn getProcessedTxn() { - if (processedTxns.size() > 1) { - throw new IllegalStateException("Multiple processed transactions."); - } - return processedTxns.get(0); - } + // TODO: Create separate class for single transaction results + public REProcessedTxn getProcessedTxn() { + if (processedTxns.size() > 1) { + throw new IllegalStateException("Multiple processed transactions."); + } + return processedTxns.get(0); + } - public List getProcessedTxns() { - return processedTxns; - } + public List getProcessedTxns() { + return processedTxns; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/ParsedTxn.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/ParsedTxn.java index dc0f951d69..5d78d4c92e 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/ParsedTxn.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/ParsedTxn.java @@ -70,55 +70,53 @@ import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Optional; public final class ParsedTxn { - private final List instructions; - private final Txn txn; - private final byte[] msg; - private final UInt256 feePaid; - private final boolean disableResourceAllocAndDestroy; - private final Pair payloadHashAndSig; + private final List instructions; + private final Txn txn; + private final byte[] msg; + private final UInt256 feePaid; + private final boolean disableResourceAllocAndDestroy; + private final Pair payloadHashAndSig; - public ParsedTxn( - Txn txn, - UInt256 feePaid, - List instructions, - byte[] msg, - Pair payloadHashAndSig, - boolean disableResourceAllocAndDestroy - ) { - this.txn = txn; - this.feePaid = feePaid; - this.instructions = instructions; - this.msg = msg; - this.payloadHashAndSig = payloadHashAndSig; - this.disableResourceAllocAndDestroy = disableResourceAllocAndDestroy; - } + public ParsedTxn( + Txn txn, + UInt256 feePaid, + List instructions, + byte[] msg, + Pair payloadHashAndSig, + boolean disableResourceAllocAndDestroy) { + this.txn = txn; + this.feePaid = feePaid; + this.instructions = instructions; + this.msg = msg; + this.payloadHashAndSig = payloadHashAndSig; + this.disableResourceAllocAndDestroy = disableResourceAllocAndDestroy; + } - public Txn txn() { - return txn; - } + public Txn txn() { + return txn; + } - public UInt256 getFeePaid() { - return feePaid == null ? UInt256.ZERO : feePaid; - } + public UInt256 getFeePaid() { + return feePaid == null ? UInt256.ZERO : feePaid; + } - public List instructions() { - return instructions; - } + public List instructions() { + return instructions; + } - public Optional getMsg() { - return Optional.ofNullable(msg); - } + public Optional getMsg() { + return Optional.ofNullable(msg); + } - public Optional> getPayloadHashAndSig() { - return Optional.ofNullable(payloadHashAndSig); - } + public Optional> getPayloadHashAndSig() { + return Optional.ofNullable(payloadHashAndSig); + } - public boolean disableResourceAllocAndDestroy() { - return disableResourceAllocAndDestroy; - } + public boolean disableResourceAllocAndDestroy() { + return disableResourceAllocAndDestroy; + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/REParser.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/REParser.java index cdc45ae5f8..817db1163d 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/REParser.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/REParser.java @@ -65,213 +65,214 @@ package com.radixdlt.engine.parser; import com.google.common.hash.HashCode; -import com.radixdlt.atom.Txn; import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.atom.Txn; import com.radixdlt.constraintmachine.CallData; -import com.radixdlt.constraintmachine.exceptions.CallDataAccessException; import com.radixdlt.constraintmachine.REInstruction; import com.radixdlt.constraintmachine.REOp; import com.radixdlt.constraintmachine.SubstateDeserialization; -import com.radixdlt.engine.parser.exceptions.TrailingBytesException; -import com.radixdlt.engine.parser.exceptions.TxnParseException; +import com.radixdlt.constraintmachine.exceptions.CallDataAccessException; import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.HashUtils; +import com.radixdlt.engine.parser.exceptions.TrailingBytesException; +import com.radixdlt.engine.parser.exceptions.TxnParseException; import com.radixdlt.identifiers.AID; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; public final class REParser { - private final SubstateDeserialization substateDeserialization; + private final SubstateDeserialization substateDeserialization; - public REParser(SubstateDeserialization substateDeserialization) { - this.substateDeserialization = substateDeserialization; - } + public REParser(SubstateDeserialization substateDeserialization) { + this.substateDeserialization = substateDeserialization; + } - public SubstateDeserialization getSubstateDeserialization() { - return substateDeserialization; - } + public SubstateDeserialization getSubstateDeserialization() { + return substateDeserialization; + } - public static class ParserState { - private final Txn txn; - private final List instructions = new ArrayList<>(); - private byte[] msg = null; - private int upSubstateCount = 0; - private int substateUpdateCount = 0; - private int endCount = 0; - private int position = 0; - private boolean disableResourceAllocAndDestroy = false; + public static class ParserState { + private final Txn txn; + private final List instructions = new ArrayList<>(); + private byte[] msg = null; + private int upSubstateCount = 0; + private int substateUpdateCount = 0; + private int endCount = 0; + private int position = 0; + private boolean disableResourceAllocAndDestroy = false; - ParserState(Txn txn) { - this.txn = txn; - } + ParserState(Txn txn) { + this.txn = txn; + } - public List instructions() { - return instructions; - } + public List instructions() { + return instructions; + } - void header(boolean disableResourceAllocAndDestroy) throws TxnParseException { - if (instructions.size() != 1) { - throw new TxnParseException(this, "Header must be first"); - } - this.disableResourceAllocAndDestroy = disableResourceAllocAndDestroy; - } + void header(boolean disableResourceAllocAndDestroy) throws TxnParseException { + if (instructions.size() != 1) { + throw new TxnParseException(this, "Header must be first"); + } + this.disableResourceAllocAndDestroy = disableResourceAllocAndDestroy; + } - void read() { - } + void read() {} - void pos(int curPos) { - this.position = curPos; - } + void pos(int curPos) { + this.position = curPos; + } - public int curPosition() { - return this.position; - } + public int curPosition() { + return this.position; + } - void nextInstruction(REInstruction inst) { - instructions.add(inst); - } + void nextInstruction(REInstruction inst) { + instructions.add(inst); + } - public int curIndex() { - return instructions.size(); - } + public int curIndex() { + return instructions.size(); + } - public AID txnId() { - return txn.getId(); - } + public AID txnId() { + return txn.getId(); + } - public int upSubstateCount() { - return upSubstateCount; - } + public int upSubstateCount() { + return upSubstateCount; + } - void msg(byte[] msg) throws TxnParseException { - if (this.msg != null) { - throw new TxnParseException(this, "Too many messages"); - } - this.msg = msg; - } + void msg(byte[] msg) throws TxnParseException { + if (this.msg != null) { + throw new TxnParseException(this, "Too many messages"); + } + this.msg = msg; + } - void substateUpdate(REOp op) { - substateUpdateCount++; - if (op == REOp.UP) { - upSubstateCount++; - } - } + void substateUpdate(REOp op) { + substateUpdateCount++; + if (op == REOp.UP) { + upSubstateCount++; + } + } - void end() throws TxnParseException { - if (substateUpdateCount == 0) { - throw new TxnParseException(this, "Empty group"); - } - endCount++; - substateUpdateCount = 0; - } + void end() throws TxnParseException { + if (substateUpdateCount == 0) { + throw new TxnParseException(this, "Empty group"); + } + endCount++; + substateUpdateCount = 0; + } - void finish() throws TxnParseException { - if (substateUpdateCount != 0) { - throw new TxnParseException(this, "Missing end"); - } + void finish() throws TxnParseException { + if (substateUpdateCount != 0) { + throw new TxnParseException(this, "Missing end"); + } - if (endCount == 0) { - throw new TxnParseException(this, "No state updates"); - } - } - } + if (endCount == 0) { + throw new TxnParseException(this, "No state updates"); + } + } + } - @SuppressWarnings("rawtypes") - public ParsedTxn parse(Txn txn) throws TxnParseException { - UInt256 feePaid = null; - ECDSASignature sig = null; - int sigPosition = 0; - var parserState = new ParserState(txn); + @SuppressWarnings("rawtypes") + public ParsedTxn parse(Txn txn) throws TxnParseException { + UInt256 feePaid = null; + ECDSASignature sig = null; + int sigPosition = 0; + var parserState = new ParserState(txn); - var buf = ByteBuffer.wrap(txn.getPayload()); - while (buf.hasRemaining()) { - if (sig != null) { - throw new TxnParseException(parserState, "Signature must be last"); - } + var buf = ByteBuffer.wrap(txn.getPayload()); + while (buf.hasRemaining()) { + if (sig != null) { + throw new TxnParseException(parserState, "Signature must be last"); + } - int curPos = buf.position(); - parserState.pos(curPos); - final REInstruction inst; - try { - inst = REInstruction.readFrom(parserState, buf); - } catch (Exception e) { - throw new TxnParseException(parserState, "Could not read instruction", e); - } - parserState.nextInstruction(inst); + int curPos = buf.position(); + parserState.pos(curPos); + final REInstruction inst; + try { + inst = REInstruction.readFrom(parserState, buf); + } catch (Exception e) { + throw new TxnParseException(parserState, "Could not read instruction", e); + } + parserState.nextInstruction(inst); - if (inst.isStateUpdate()) { - parserState.substateUpdate(inst.getMicroOp().getOp()); - } else if (inst.getMicroOp().getOp() == REOp.READ || inst.getMicroOp().getOp() == REOp.READINDEX) { - parserState.read(); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.HEADER) { - parserState.header(inst.getData()); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.SYSCALL) { - try { - CallData callData = inst.getData(); - byte id = callData.get(0); - var syscall = Syscall.of(id).orElseThrow(() -> new TxnParseException(parserState, "Invalid call data type: " + id)); + if (inst.isStateUpdate()) { + parserState.substateUpdate(inst.getMicroOp().getOp()); + } else if (inst.getMicroOp().getOp() == REOp.READ + || inst.getMicroOp().getOp() == REOp.READINDEX) { + parserState.read(); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.HEADER) { + parserState.header(inst.getData()); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.SYSCALL) { + try { + CallData callData = inst.getData(); + byte id = callData.get(0); + var syscall = + Syscall.of(id) + .orElseThrow( + () -> new TxnParseException(parserState, "Invalid call data type: " + id)); - switch (syscall) { - case FEE_RESERVE_PUT: - if (feePaid != null) { - throw new TxnParseException(parserState, "Should only pay fees once."); - } - feePaid = callData.getUInt256(1); - break; - case FEE_RESERVE_TAKE: - if (feePaid == null) { - throw new TxnParseException(parserState, "No fees paid"); - } - var takeAmount = callData.getUInt256(1); - if (takeAmount.compareTo(feePaid) > 0) { - throw new TxnParseException(parserState, "Trying to take more fees than paid"); - } - feePaid = feePaid.subtract(takeAmount); - break; - case READDR_CLAIM: - break; - // TODO: Need to rethink how stateless verification occurs here - // TODO: Along with FeeConstraintScrypt.java - default: - throw new TxnParseException(parserState, "Invalid call data type: " + id); - } - } catch (CallDataAccessException | TrailingBytesException e) { - throw new TxnParseException(parserState, e); - } - } else if (inst.getMicroOp() == REInstruction.REMicroOp.MSG) { - parserState.msg(inst.getData()); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.END) { - parserState.end(); - } else if (inst.getMicroOp() == REInstruction.REMicroOp.SIG) { - sigPosition = curPos; - sig = inst.getData(); - } else { - throw new TxnParseException(parserState, "Unknown CM Op " + inst.getMicroOp()); - } - } + switch (syscall) { + case FEE_RESERVE_PUT: + if (feePaid != null) { + throw new TxnParseException(parserState, "Should only pay fees once."); + } + feePaid = callData.getUInt256(1); + break; + case FEE_RESERVE_TAKE: + if (feePaid == null) { + throw new TxnParseException(parserState, "No fees paid"); + } + var takeAmount = callData.getUInt256(1); + if (takeAmount.compareTo(feePaid) > 0) { + throw new TxnParseException(parserState, "Trying to take more fees than paid"); + } + feePaid = feePaid.subtract(takeAmount); + break; + case READDR_CLAIM: + break; + // TODO: Need to rethink how stateless verification occurs here + // TODO: Along with FeeConstraintScrypt.java + default: + throw new TxnParseException(parserState, "Invalid call data type: " + id); + } + } catch (CallDataAccessException | TrailingBytesException e) { + throw new TxnParseException(parserState, e); + } + } else if (inst.getMicroOp() == REInstruction.REMicroOp.MSG) { + parserState.msg(inst.getData()); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.END) { + parserState.end(); + } else if (inst.getMicroOp() == REInstruction.REMicroOp.SIG) { + sigPosition = curPos; + sig = inst.getData(); + } else { + throw new TxnParseException(parserState, "Unknown CM Op " + inst.getMicroOp()); + } + } - parserState.finish(); + parserState.finish(); - final Pair payloadHashAndSig; - if (sig != null) { - var payloadHash = HashUtils.sha256(txn.getPayload(), 0, sigPosition); // This is a double hash - payloadHashAndSig = Pair.of(payloadHash, sig); + final Pair payloadHashAndSig; + if (sig != null) { + var payloadHash = HashUtils.sha256(txn.getPayload(), 0, sigPosition); // This is a double hash + payloadHashAndSig = Pair.of(payloadHash, sig); - } else { - payloadHashAndSig = null; - } + } else { + payloadHashAndSig = null; + } - return new ParsedTxn( - txn, - feePaid, - parserState.instructions, - parserState.msg, - payloadHashAndSig, - parserState.disableResourceAllocAndDestroy - ); - } + return new ParsedTxn( + txn, + feePaid, + parserState.instructions, + parserState.msg, + payloadHashAndSig, + parserState.disableResourceAllocAndDestroy); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/REInstructionDataDeserializeException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/REInstructionDataDeserializeException.java index 5f3ba98606..726d81427f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/REInstructionDataDeserializeException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/REInstructionDataDeserializeException.java @@ -67,7 +67,7 @@ import com.radixdlt.constraintmachine.REInstruction; public class REInstructionDataDeserializeException extends Exception { - public REInstructionDataDeserializeException(REInstruction.REMicroOp microOp, Throwable cause) { - super("op=" + microOp, cause); - } + public REInstructionDataDeserializeException(REInstruction.REMicroOp microOp, Throwable cause) { + super("op=" + microOp, cause); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/SubstateDeserializationException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/SubstateDeserializationException.java index f6ea384286..d6f9cc07e8 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/SubstateDeserializationException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/SubstateDeserializationException.java @@ -68,7 +68,8 @@ import com.radixdlt.serialization.DeserializeException; public class SubstateDeserializationException extends DeserializeException { - public SubstateDeserializationException(Class substateClass, Throwable cause) { - super("Failed to deserialize " + substateClass.getSimpleName(), cause); - } + public SubstateDeserializationException( + Class substateClass, Throwable cause) { + super("Failed to deserialize " + substateClass.getSimpleName(), cause); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TrailingBytesException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TrailingBytesException.java index 685af4f00c..00887ab28a 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TrailingBytesException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TrailingBytesException.java @@ -67,7 +67,7 @@ import com.radixdlt.serialization.DeserializeException; public class TrailingBytesException extends DeserializeException { - public TrailingBytesException(String message) { - super(message); - } + public TrailingBytesException(String message) { + super(message); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TxnParseException.java b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TxnParseException.java index ebe121fdf9..38d36084af 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TxnParseException.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/parser/exceptions/TxnParseException.java @@ -67,33 +67,33 @@ import com.radixdlt.engine.parser.REParser; public class TxnParseException extends Exception { - private final REParser.ParserState parserState; + private final REParser.ParserState parserState; - public TxnParseException(REParser.ParserState parserState, String message, Throwable cause) { - super(toCurrent(parserState) + ": " + message + "\n" + toParsed(parserState), cause); - this.parserState = parserState; - } + public TxnParseException(REParser.ParserState parserState, String message, Throwable cause) { + super(toCurrent(parserState) + ": " + message + "\n" + toParsed(parserState), cause); + this.parserState = parserState; + } - public TxnParseException(REParser.ParserState parserState, Throwable cause) { - this(parserState, "", cause); - } + public TxnParseException(REParser.ParserState parserState, Throwable cause) { + this(parserState, "", cause); + } - public TxnParseException(REParser.ParserState parserState, String message) { - this(parserState, message, null); - } + public TxnParseException(REParser.ParserState parserState, String message) { + this(parserState, message, null); + } - private static String toCurrent(REParser.ParserState parserState) { - return String.format("pos=%s inst_index=%s", parserState.curPosition(), parserState.curIndex()); - } + private static String toCurrent(REParser.ParserState parserState) { + return String.format("pos=%s inst_index=%s", parserState.curPosition(), parserState.curIndex()); + } - private static String toParsed(REParser.ParserState parserState) { - var builder = new StringBuilder(); - for (int i = 0; i < parserState.instructions().size(); i++) { - builder.append(i); - builder.append(": "); - builder.append(parserState.instructions().get(i)); - builder.append("\n"); - } - return builder.toString(); - } + private static String toParsed(REParser.ParserState parserState) { + var builder = new StringBuilder(); + for (int i = 0; i < parserState.instructions().size(); i++) { + builder.append(i); + builder.append(": "); + builder.append(parserState.instructions().get(i)); + builder.append("\n"); + } + return builder.toString(); + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/store/CMStore.java b/radixdlt-engine/src/main/java/com/radixdlt/store/CMStore.java index cab14d130e..cbd81014f9 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/store/CMStore.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/store/CMStore.java @@ -66,19 +66,19 @@ import com.radixdlt.atom.CloseableCursor; import com.radixdlt.atom.SubstateId; -import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.RawSubstateBytes; +import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.exceptions.VirtualParentStateDoesNotExist; import com.radixdlt.constraintmachine.exceptions.VirtualSubstateAlreadyDownException; - import java.nio.ByteBuffer; import java.util.Optional; -/** - * Read only store interface for Constraint Machine validation - */ +/** Read only store interface for Constraint Machine validation */ public interface CMStore extends ResourceStore { - ByteBuffer verifyVirtualSubstate(SubstateId substateId) throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist; - Optional loadSubstate(SubstateId substateId); - CloseableCursor openIndexedCursor(SubstateIndex index); + ByteBuffer verifyVirtualSubstate(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist; + + Optional loadSubstate(SubstateId substateId); + + CloseableCursor openIndexedCursor(SubstateIndex index); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/store/EngineStore.java b/radixdlt-engine/src/main/java/com/radixdlt/store/EngineStore.java index 806dac3f10..5256fd34d4 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/store/EngineStore.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/store/EngineStore.java @@ -69,17 +69,18 @@ import com.radixdlt.engine.RadixEngineException; public interface EngineStore extends SubstateStore { - /** - * For verification - */ - interface EngineStoreInTransaction extends CMStore { - void storeTxn(REProcessedTxn txn); - void storeMetadata(M metadata); - } - interface TransactionEngineStoreConsumer { - R start(EngineStoreInTransaction store) throws RadixEngineException; - } - R transaction(TransactionEngineStoreConsumer consumer) throws RadixEngineException; + /** For verification */ + interface EngineStoreInTransaction extends CMStore { + void storeTxn(REProcessedTxn txn); - M getMetadata(); + void storeMetadata(M metadata); + } + + interface TransactionEngineStoreConsumer { + R start(EngineStoreInTransaction store) throws RadixEngineException; + } + + R transaction(TransactionEngineStoreConsumer consumer) throws RadixEngineException; + + M getMetadata(); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/store/InMemoryEngineStore.java b/radixdlt-engine/src/main/java/com/radixdlt/store/InMemoryEngineStore.java index 0a83a83333..e2b47d048f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/store/InMemoryEngineStore.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/store/InMemoryEngineStore.java @@ -67,20 +67,19 @@ import com.google.common.primitives.UnsignedBytes; import com.radixdlt.application.system.state.SystemData; import com.radixdlt.application.system.state.VirtualParent; +import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.validators.state.ValidatorData; import com.radixdlt.atom.CloseableCursor; import com.radixdlt.atom.SubstateId; -import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.constraintmachine.REProcessedTxn; -import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.REStateUpdate; import com.radixdlt.constraintmachine.RawSubstateBytes; +import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SystemMapKey; import com.radixdlt.constraintmachine.exceptions.VirtualParentStateDoesNotExist; import com.radixdlt.constraintmachine.exceptions.VirtualSubstateAlreadyDownException; import com.radixdlt.engine.RadixEngineException; import com.radixdlt.identifiers.REAddr; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Comparator; @@ -91,143 +90,147 @@ import java.util.function.Supplier; public final class InMemoryEngineStore implements EngineStore { - private final Object lock = new Object(); - private final Map storedState = new HashMap<>(); - private final Map> resources = new HashMap<>(); - private final Map maps = new HashMap<>(); - private M metadata; - - @Override - public R transaction(TransactionEngineStoreConsumer consumer) throws RadixEngineException { - return consumer.start(new EngineStoreInTransaction() { - @Override - public void storeTxn(REProcessedTxn txn) { - synchronized (lock) { - txn.stateUpdates().forEach(update -> { - storedState.put(update.getId(), update); - - // FIXME: Superhack - if (update.isBootUp()) { - if (update.getParsed() instanceof TokenResource) { - var tokenDef = (TokenResource) update.getParsed(); - resources.put(tokenDef.getAddr(), update::getStateBuf); - } else if (update.getParsed() instanceof VirtualParent) { - var p = (VirtualParent) update.getParsed(); - var typeByte = p.getData()[0]; - var mapKey = SystemMapKey.ofSystem(typeByte); - maps.put(mapKey, update.getRawSubstateBytes()); - } else if (update.getParsed() instanceof ValidatorData) { - var data = (ValidatorData) update.getParsed(); - var mapKey = SystemMapKey.ofSystem( - update.typeByte(), - data.getValidatorKey().getCompressedBytes() - ); - maps.put(mapKey, update.getRawSubstateBytes()); - } else if (update.getParsed() instanceof SystemData) { - var mapKey = SystemMapKey.ofSystem(update.typeByte()); - maps.put(mapKey, update.getRawSubstateBytes()); - } - } else if (update.isShutDown()) { - if (update.getParsed() instanceof ValidatorData) { - var data = (ValidatorData) update.getParsed(); - var mapKey = SystemMapKey.ofSystem( - update.typeByte(), - data.getValidatorKey().getCompressedBytes() - ); - maps.remove(mapKey); - } else if (update.getParsed() instanceof SystemData) { - var mapKey = SystemMapKey.ofSystem(update.typeByte()); - maps.remove(mapKey); - } - } - }); - } - } - - @Override - public void storeMetadata(M metadata) { - InMemoryEngineStore.this.metadata = metadata; - } - - @Override - public ByteBuffer verifyVirtualSubstate(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist { - synchronized (lock) { - var parent = substateId.getVirtualParent().orElseThrow(); - var update = storedState.get(parent); - if (update == null || !(update.getParsed() instanceof VirtualParent)) { - throw new VirtualParentStateDoesNotExist(parent); - } - - var inst = storedState.get(substateId); - if (inst != null && inst.isShutDown()) { - throw new VirtualSubstateAlreadyDownException(substateId); - } - - return update.getStateBuf(); - } - } - - @Override - public Optional loadSubstate(SubstateId substateId) { - synchronized (lock) { - var inst = storedState.get(substateId); - if (inst == null || !inst.isBootUp()) { - return Optional.empty(); - } - - return Optional.of(inst.getStateBuf()); - } - } - - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return InMemoryEngineStore.this.openIndexedCursor(index); - } - - @Override - public Optional loadResource(REAddr addr) { - synchronized (lock) { - var supplier = resources.get(addr); - return supplier == null ? Optional.empty() : Optional.of(supplier.get()); - } - } - }); - } - - @Override - public M getMetadata() { - return metadata; - } - - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - final List substates = new ArrayList<>(); - synchronized (lock) { - for (var i : storedState.values()) { - if (!i.isBootUp()) { - continue; - } - if (!index.test(i.getRawSubstateBytes())) { - continue; - } - substates.add(i.getRawSubstateBytes()); - } - } - substates.sort(Comparator.comparing(RawSubstateBytes::getData, UnsignedBytes.lexicographicalComparator().reversed())); - - return CloseableCursor.wrapIterator(substates.iterator()); - } - - @Override - public Optional get(SystemMapKey key) { - return Optional.ofNullable(maps.get(key)); - } - - public boolean contains(SubstateId substateId) { - synchronized (lock) { - var inst = storedState.get(substateId); - return inst != null; - } - } + private final Object lock = new Object(); + private final Map storedState = new HashMap<>(); + private final Map> resources = new HashMap<>(); + private final Map maps = new HashMap<>(); + private M metadata; + + @Override + public R transaction(TransactionEngineStoreConsumer consumer) + throws RadixEngineException { + return consumer.start( + new EngineStoreInTransaction() { + @Override + public void storeTxn(REProcessedTxn txn) { + synchronized (lock) { + txn.stateUpdates() + .forEach( + update -> { + storedState.put(update.getId(), update); + + // FIXME: Superhack + if (update.isBootUp()) { + if (update.getParsed() instanceof TokenResource) { + var tokenDef = (TokenResource) update.getParsed(); + resources.put(tokenDef.getAddr(), update::getStateBuf); + } else if (update.getParsed() instanceof VirtualParent) { + var p = (VirtualParent) update.getParsed(); + var typeByte = p.getData()[0]; + var mapKey = SystemMapKey.ofSystem(typeByte); + maps.put(mapKey, update.getRawSubstateBytes()); + } else if (update.getParsed() instanceof ValidatorData) { + var data = (ValidatorData) update.getParsed(); + var mapKey = + SystemMapKey.ofSystem( + update.typeByte(), data.getValidatorKey().getCompressedBytes()); + maps.put(mapKey, update.getRawSubstateBytes()); + } else if (update.getParsed() instanceof SystemData) { + var mapKey = SystemMapKey.ofSystem(update.typeByte()); + maps.put(mapKey, update.getRawSubstateBytes()); + } + } else if (update.isShutDown()) { + if (update.getParsed() instanceof ValidatorData) { + var data = (ValidatorData) update.getParsed(); + var mapKey = + SystemMapKey.ofSystem( + update.typeByte(), data.getValidatorKey().getCompressedBytes()); + maps.remove(mapKey); + } else if (update.getParsed() instanceof SystemData) { + var mapKey = SystemMapKey.ofSystem(update.typeByte()); + maps.remove(mapKey); + } + } + }); + } + } + + @Override + public void storeMetadata(M metadata) { + InMemoryEngineStore.this.metadata = metadata; + } + + @Override + public ByteBuffer verifyVirtualSubstate(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist { + synchronized (lock) { + var parent = substateId.getVirtualParent().orElseThrow(); + var update = storedState.get(parent); + if (update == null || !(update.getParsed() instanceof VirtualParent)) { + throw new VirtualParentStateDoesNotExist(parent); + } + + var inst = storedState.get(substateId); + if (inst != null && inst.isShutDown()) { + throw new VirtualSubstateAlreadyDownException(substateId); + } + + return update.getStateBuf(); + } + } + + @Override + public Optional loadSubstate(SubstateId substateId) { + synchronized (lock) { + var inst = storedState.get(substateId); + if (inst == null || !inst.isBootUp()) { + return Optional.empty(); + } + + return Optional.of(inst.getStateBuf()); + } + } + + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + return InMemoryEngineStore.this.openIndexedCursor(index); + } + + @Override + public Optional loadResource(REAddr addr) { + synchronized (lock) { + var supplier = resources.get(addr); + return supplier == null ? Optional.empty() : Optional.of(supplier.get()); + } + } + }); + } + + @Override + public M getMetadata() { + return metadata; + } + + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + final List substates = new ArrayList<>(); + synchronized (lock) { + for (var i : storedState.values()) { + if (!i.isBootUp()) { + continue; + } + if (!index.test(i.getRawSubstateBytes())) { + continue; + } + substates.add(i.getRawSubstateBytes()); + } + } + substates.sort( + Comparator.comparing( + RawSubstateBytes::getData, UnsignedBytes.lexicographicalComparator().reversed())); + + return CloseableCursor.wrapIterator(substates.iterator()); + } + + @Override + public Optional get(SystemMapKey key) { + return Optional.ofNullable(maps.get(key)); + } + + public boolean contains(SubstateId substateId) { + synchronized (lock) { + var inst = storedState.get(substateId); + return inst != null; + } + } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/store/ResourceStore.java b/radixdlt-engine/src/main/java/com/radixdlt/store/ResourceStore.java index ad81390227..65ecf008a7 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/store/ResourceStore.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/store/ResourceStore.java @@ -65,13 +65,10 @@ package com.radixdlt.store; import com.radixdlt.identifiers.REAddr; - import java.nio.ByteBuffer; import java.util.Optional; -/** - * Index into immutable substates - */ +/** Index into immutable substates */ public interface ResourceStore { - Optional loadResource(REAddr addr); + Optional loadResource(REAddr addr); } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/store/TransientEngineStore.java b/radixdlt-engine/src/main/java/com/radixdlt/store/TransientEngineStore.java index 4fea93c988..cff467c938 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/store/TransientEngineStore.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/store/TransientEngineStore.java @@ -67,90 +67,103 @@ import com.radixdlt.atom.CloseableCursor; import com.radixdlt.atom.SubstateId; import com.radixdlt.constraintmachine.REProcessedTxn; -import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.RawSubstateBytes; +import com.radixdlt.constraintmachine.SubstateIndex; import com.radixdlt.constraintmachine.SystemMapKey; import com.radixdlt.constraintmachine.exceptions.VirtualParentStateDoesNotExist; import com.radixdlt.constraintmachine.exceptions.VirtualSubstateAlreadyDownException; import com.radixdlt.engine.RadixEngineException; import com.radixdlt.identifiers.REAddr; - import java.nio.ByteBuffer; import java.util.Objects; import java.util.Optional; public class TransientEngineStore implements EngineStore { - private final EngineStore base; - private final InMemoryEngineStore transientStore = new InMemoryEngineStore<>(); + private final EngineStore base; + private final InMemoryEngineStore transientStore = new InMemoryEngineStore<>(); - public TransientEngineStore(EngineStore base) { - this.base = Objects.requireNonNull(base); - } + public TransientEngineStore(EngineStore base) { + this.base = Objects.requireNonNull(base); + } - @Override - public R transaction(TransactionEngineStoreConsumer consumer) throws RadixEngineException { - return base.transaction(baseStore -> - transientStore.transaction(tStore -> - consumer.start(new EngineStoreInTransaction() { - @Override - public void storeTxn(REProcessedTxn txn) { - tStore.storeTxn(txn); - } + @Override + public R transaction(TransactionEngineStoreConsumer consumer) + throws RadixEngineException { + return base.transaction( + baseStore -> + transientStore.transaction( + tStore -> + consumer.start( + new EngineStoreInTransaction() { + @Override + public void storeTxn(REProcessedTxn txn) { + tStore.storeTxn(txn); + } - @Override - public void storeMetadata(M metadata) { - // no-op - } + @Override + public void storeMetadata(M metadata) { + // no-op + } - @Override - public ByteBuffer verifyVirtualSubstate(SubstateId substateId) - throws VirtualSubstateAlreadyDownException, VirtualParentStateDoesNotExist { - try { - return tStore.verifyVirtualSubstate(substateId); - } catch (VirtualParentStateDoesNotExist e) { - return baseStore.verifyVirtualSubstate(substateId); - } - } + @Override + public ByteBuffer verifyVirtualSubstate(SubstateId substateId) + throws VirtualSubstateAlreadyDownException, + VirtualParentStateDoesNotExist { + try { + return tStore.verifyVirtualSubstate(substateId); + } catch (VirtualParentStateDoesNotExist e) { + return baseStore.verifyVirtualSubstate(substateId); + } + } - @Override - public Optional loadSubstate(SubstateId substateId) { - if (!transientStore.contains(substateId)) { - return baseStore.loadSubstate(substateId); - } + @Override + public Optional loadSubstate(SubstateId substateId) { + if (!transientStore.contains(substateId)) { + return baseStore.loadSubstate(substateId); + } - return tStore.loadSubstate(substateId); - } + return tStore.loadSubstate(substateId); + } - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return tStore.openIndexedCursor(index) - .concat(() -> baseStore.openIndexedCursor(index) - .filter(s -> !transientStore.contains(SubstateId.fromBytes(s.getId())))); - } + @Override + public CloseableCursor openIndexedCursor( + SubstateIndex index) { + return tStore + .openIndexedCursor(index) + .concat( + () -> + baseStore + .openIndexedCursor(index) + .filter( + s -> + !transientStore.contains( + SubstateId.fromBytes(s.getId())))); + } - @Override - public Optional loadResource(REAddr addr) { - return tStore.loadResource(addr).or(() -> baseStore.loadResource(addr)); - } - }) - ) - ); - } + @Override + public Optional loadResource(REAddr addr) { + return tStore.loadResource(addr).or(() -> baseStore.loadResource(addr)); + } + }))); + } - @Override - public M getMetadata() { - return null; - } + @Override + public M getMetadata() { + return null; + } - @Override - public CloseableCursor openIndexedCursor(SubstateIndex index) { - return transientStore.openIndexedCursor(index) - .concat(() -> base.openIndexedCursor(index) - .filter(s -> !transientStore.contains(SubstateId.fromBytes(s.getId())))); - } + @Override + public CloseableCursor openIndexedCursor(SubstateIndex index) { + return transientStore + .openIndexedCursor(index) + .concat( + () -> + base.openIndexedCursor(index) + .filter(s -> !transientStore.contains(SubstateId.fromBytes(s.getId())))); + } - @Override - public Optional get(SystemMapKey key) { - return transientStore.get(key).or(() -> base.get(key)); - } + @Override + public Optional get(SystemMapKey key) { + return transientStore.get(key).or(() -> base.get(key)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/TestSetupUtils.java b/radixdlt-engine/src/test/java/com/radixdlt/TestSetupUtils.java index 10e986d60b..44bd0ebf31 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/TestSetupUtils.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/TestSetupUtils.java @@ -1,134 +1,129 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt; - -import java.security.Security; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -/** - * Some utilities to help with initialisation of other sub-systems - * that tests use. - */ -public final class TestSetupUtils { - private static final int HEXDUMP_LINESIZE = 0x10; - private static final AtomicBoolean bouncyCastleInstalled = new AtomicBoolean(false); - - private TestSetupUtils() { - throw new IllegalStateException("Can't construct"); - } - - /** - * Install the Bouncy Castle crypto provider used for various hashing - * and symmetric/asymmetric key functions. - */ - public static void installBouncyCastleProvider() { - if (bouncyCastleInstalled.compareAndSet(false, true)) { - Security.insertProviderAt(new BouncyCastleProvider(), 1); - } - } - - /** - * Useful method for discovering why things went wrong - outputs - * a hexdump to {@code System.out}. - * - * @param bytes bytes to dump - */ - public static void hexdump(byte[] bytes) { - for (int index = 0; index < bytes.length; index += HEXDUMP_LINESIZE) { - int thisLen = Math.min(HEXDUMP_LINESIZE, bytes.length - index); - System.out.format("%04X:", index); - int ofs = 0; - for (; ofs < thisLen; ++ofs) { - if (ofs == HEXDUMP_LINESIZE / 2) { - System.out.format("-%02X", bytes[index + ofs] & 0xFF); - } else { - System.out.format(" %02X", bytes[index + ofs] & 0xFF); - } - } - while (ofs < HEXDUMP_LINESIZE) { - System.out.print(" "); - ofs += 1; - } - System.out.print(" |"); - for (ofs = 0; ofs < thisLen; ++ofs) { - System.out.print(toPrintable(bytes[index + ofs])); - } - while (ofs < HEXDUMP_LINESIZE) { - System.out.print(' '); - ofs += 1; - } - System.out.println('|'); - } - } - - private static char toPrintable(byte b) { - if (b >= 0x20 && b < 0x7F) { - return (char) b; - } - return '.'; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt; + +import java.security.Security; +import java.util.concurrent.atomic.AtomicBoolean; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** Some utilities to help with initialisation of other sub-systems that tests use. */ +public final class TestSetupUtils { + private static final int HEXDUMP_LINESIZE = 0x10; + private static final AtomicBoolean bouncyCastleInstalled = new AtomicBoolean(false); + + private TestSetupUtils() { + throw new IllegalStateException("Can't construct"); + } + + /** + * Install the Bouncy Castle crypto provider used for various hashing and symmetric/asymmetric key + * functions. + */ + public static void installBouncyCastleProvider() { + if (bouncyCastleInstalled.compareAndSet(false, true)) { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + } + + /** + * Useful method for discovering why things went wrong - outputs a hexdump to {@code System.out}. + * + * @param bytes bytes to dump + */ + public static void hexdump(byte[] bytes) { + for (int index = 0; index < bytes.length; index += HEXDUMP_LINESIZE) { + int thisLen = Math.min(HEXDUMP_LINESIZE, bytes.length - index); + System.out.format("%04X:", index); + int ofs = 0; + for (; ofs < thisLen; ++ofs) { + if (ofs == HEXDUMP_LINESIZE / 2) { + System.out.format("-%02X", bytes[index + ofs] & 0xFF); + } else { + System.out.format(" %02X", bytes[index + ofs] & 0xFF); + } + } + while (ofs < HEXDUMP_LINESIZE) { + System.out.print(" "); + ofs += 1; + } + System.out.print(" |"); + for (ofs = 0; ofs < thisLen; ++ofs) { + System.out.print(toPrintable(bytes[index + ofs])); + } + while (ofs < HEXDUMP_LINESIZE) { + System.out.print(' '); + ofs += 1; + } + System.out.println('|'); + } + } + + private static char toPrintable(byte b) { + if (b >= 0x20 && b < 0x7F) { + return (char) b; + } + return '.'; + } +} diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/BucketEqualsHashCode.java b/radixdlt-engine/src/test/java/com/radixdlt/application/BucketEqualsHashCode.java index 93b4bacc57..f450767915 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/BucketEqualsHashCode.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/BucketEqualsHashCode.java @@ -64,32 +64,27 @@ package com.radixdlt.application; -import org.junit.Test; -import org.reflections.Reflections; - import com.radixdlt.application.tokens.Bucket; - import java.lang.reflect.Modifier; import java.util.Set; - import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import org.junit.Test; +import org.reflections.Reflections; public class BucketEqualsHashCode { - @Test - public void verify_all_buckets_correctly_override_equals_and_hash_code() { - findSubTypesInPkg("com.radixdlt").stream() - .filter(cls -> !Modifier.isAbstract(cls.getModifiers())) - .forEachOrdered(this::testEquals); - } + @Test + public void verify_all_buckets_correctly_override_equals_and_hash_code() { + findSubTypesInPkg("com.radixdlt").stream() + .filter(cls -> !Modifier.isAbstract(cls.getModifiers())) + .forEachOrdered(this::testEquals); + } - private void testEquals(Class cls) { - EqualsVerifier.forClass(cls) - .suppress(Warning.NONFINAL_FIELDS) - .verify(); - } + private void testEquals(Class cls) { + EqualsVerifier.forClass(cls).suppress(Warning.NONFINAL_FIELDS).verify(); + } - private Set> findSubTypesInPkg(String packagePrefix) { - return new Reflections(packagePrefix).getSubTypesOf(Bucket.class); - } + private Set> findSubTypesInPkg(String packagePrefix) { + return new Reflections(packagePrefix).getSubTypesOf(Bucket.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/ParticlesEqualsHashCode.java b/radixdlt-engine/src/test/java/com/radixdlt/application/ParticlesEqualsHashCode.java index a1a2d5560f..d52ff63925 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/ParticlesEqualsHashCode.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/ParticlesEqualsHashCode.java @@ -64,35 +64,32 @@ package com.radixdlt.application; -import org.junit.Test; -import org.reflections.Reflections; - import com.google.common.hash.HashCode; import com.radixdlt.constraintmachine.Particle; import com.radixdlt.crypto.HashUtils; - import java.lang.reflect.Modifier; import java.util.Set; - import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import org.junit.Test; +import org.reflections.Reflections; public class ParticlesEqualsHashCode { - @Test - public void verify_all_particle_subtypes_correctly_override_equals_and_hash_code() { - findSubTypesInPkg("com.radixdlt").stream() - .filter(cls -> !Modifier.isAbstract(cls.getModifiers())) - .forEachOrdered(this::testEquals); - } + @Test + public void verify_all_particle_subtypes_correctly_override_equals_and_hash_code() { + findSubTypesInPkg("com.radixdlt").stream() + .filter(cls -> !Modifier.isAbstract(cls.getModifiers())) + .forEachOrdered(this::testEquals); + } - private void testEquals(Class cls) { - EqualsVerifier.forClass(cls) - .suppress(Warning.NONFINAL_FIELDS) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } + private void testEquals(Class cls) { + EqualsVerifier.forClass(cls) + .suppress(Warning.NONFINAL_FIELDS) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } - private Set> findSubTypesInPkg(String packagePrefix) { - return new Reflections(packagePrefix).getSubTypesOf(Particle.class); - } + private Set> findSubTypesInPkg(String packagePrefix) { + return new Reflections(packagePrefix).getSubTypesOf(Particle.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/system/FixedFeeTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/system/FixedFeeTest.java index 44d8601662..ba759477da 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/system/FixedFeeTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/system/FixedFeeTest.java @@ -64,6 +64,9 @@ package com.radixdlt.application.system; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.accounting.REResourceAccounting; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.construction.FeeReservePutConstructor; @@ -78,9 +81,9 @@ import com.radixdlt.atom.TxnConstructionRequest; import com.radixdlt.atom.actions.CreateMutableToken; import com.radixdlt.atom.actions.CreateSystem; +import com.radixdlt.atom.actions.FeeReservePut; import com.radixdlt.atom.actions.FeeReserveTake; import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.atom.actions.FeeReservePut; import com.radixdlt.atom.actions.TransferToken; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.constraintmachine.ConstraintMachine; @@ -95,174 +98,170 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; - import java.math.BigInteger; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; public class FixedFeeTest { - private RadixEngine engine; - private EngineStore store; - private final ECKeyPair key = ECKeyPair.generateNew(); - private final REAddr accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); - cmAtomOS.load(new SystemConstraintScrypt()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization(), - FixedFeeMeter.create(UInt256.FIVE) - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(TransferToken.class, new TransferTokensConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(FeeReservePut.class, new FeeReservePutConstructor()) - .put(FeeReserveTake.class, new FeeReserveTakeConstructor()) - .build(), - cm, - store - ); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, UInt256.TEN)) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + private RadixEngine engine; + private EngineStore store; + private final ECKeyPair key = ECKeyPair.generateNew(); + private final REAddr accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - @Test - public void paying_for_fees_should_work() throws Exception { - // Act - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, UInt256.FIVE)) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) - .signAndBuild(key::sign); + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); + cmAtomOS.load(new SystemConstraintScrypt()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization(), + FixedFeeMeter.create(UInt256.FIVE)); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(TransferToken.class, new TransferTokensConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put(FeeReservePut.class, new FeeReservePutConstructor()) + .put(FeeReserveTake.class, new FeeReserveTakeConstructor()) + .build(), + cm, + store); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, UInt256.TEN))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - // Act - var result = this.engine.execute(List.of(transfer)); - var accounting0 = REResourceAccounting.compute( - result.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting0.bucketAccounting()) - .hasSize(1) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), accountAddr), - BigInteger.valueOf(-5) - ); - var accounting1 = REResourceAccounting.compute( - result.getProcessedTxn().getGroupedStateUpdates().get(1).stream() - ); - assertThat(accounting1.bucketAccounting()) - .hasSize(2) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), accountAddr), - BigInteger.valueOf(-5) - ) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), to), - BigInteger.valueOf(5) - ); - } + @Test + public void paying_for_fees_should_work() throws Exception { + // Act + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, UInt256.FIVE)) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) + .signAndBuild(key::sign); + // Act + var result = this.engine.execute(List.of(transfer)); + var accounting0 = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting0.bucketAccounting()) + .hasSize(1) + .containsEntry( + AccountBucket.from(REAddr.ofNativeToken(), accountAddr), BigInteger.valueOf(-5)); + var accounting1 = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(1).stream()); + assertThat(accounting1.bucketAccounting()) + .hasSize(2) + .containsEntry( + AccountBucket.from(REAddr.ofNativeToken(), accountAddr), BigInteger.valueOf(-5)) + .containsEntry(AccountBucket.from(REAddr.ofNativeToken(), to), BigInteger.valueOf(5)); + } - @Test - public void paying_too_little_fees_should_fail() throws Exception { - // Arrange - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, UInt256.THREE)) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) - .signAndBuild(key::sign); + @Test + public void paying_too_little_fees_should_fail() throws Exception { + // Arrange + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, UInt256.THREE)) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) + .signAndBuild(key::sign); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(DefaultedSystemLoanException.class); - } + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(DefaultedSystemLoanException.class); + } - @Test - public void paying_too_much_in_fees_should_fail() throws Exception { - // Arrange - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, UInt256.EIGHT)) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO)) - ).signAndBuild(key::sign); + @Test + public void paying_too_much_in_fees_should_fail() throws Exception { + // Arrange + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, UInt256.EIGHT)) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO))) + .signAndBuild(key::sign); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(ExecutionContextDestroyException.class); - } + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(ExecutionContextDestroyException.class); + } - @Test - public void put_then_take_reserve_should_work() throws Exception { - // Arrange - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, UInt256.EIGHT)) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO)) - .action(new FeeReserveTake(accountAddr, UInt256.THREE))) - .signAndBuild(key::sign); + @Test + public void put_then_take_reserve_should_work() throws Exception { + // Arrange + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, UInt256.EIGHT)) + .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO)) + .action(new FeeReserveTake(accountAddr, UInt256.THREE))) + .signAndBuild(key::sign); - // Act - var result = this.engine.execute(List.of(transfer)); - var accounting0 = REResourceAccounting.compute( - result.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting0.bucketAccounting()) - .hasSize(1) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), accountAddr), - BigInteger.valueOf(-8) - ); - var accounting1 = REResourceAccounting.compute( - result.getProcessedTxn().getGroupedStateUpdates().get(1).stream() - ); - assertThat(accounting1.bucketAccounting()) - .hasSize(2) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), accountAddr), - BigInteger.valueOf(-2) - ) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), to), - BigInteger.valueOf(2) - ); - var accounting2 = REResourceAccounting.compute( - result.getProcessedTxn().getGroupedStateUpdates().get(2).stream() - ); - assertThat(accounting2.bucketAccounting()) - .hasSize(1) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), accountAddr), - BigInteger.valueOf(3) - ); - } + // Act + var result = this.engine.execute(List.of(transfer)); + var accounting0 = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting0.bucketAccounting()) + .hasSize(1) + .containsEntry( + AccountBucket.from(REAddr.ofNativeToken(), accountAddr), BigInteger.valueOf(-8)); + var accounting1 = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(1).stream()); + assertThat(accounting1.bucketAccounting()) + .hasSize(2) + .containsEntry( + AccountBucket.from(REAddr.ofNativeToken(), accountAddr), BigInteger.valueOf(-2)) + .containsEntry(AccountBucket.from(REAddr.ofNativeToken(), to), BigInteger.valueOf(2)); + var accounting2 = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(2).stream()); + assertThat(accounting2.bucketAccounting()) + .hasSize(1) + .containsEntry( + AccountBucket.from(REAddr.ofNativeToken(), accountAddr), BigInteger.valueOf(3)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextEpochV2Test.java b/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextEpochV2Test.java index 40d9647dd4..19719d911c 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextEpochV2Test.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextEpochV2Test.java @@ -64,24 +64,14 @@ package com.radixdlt.application.system; -import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; -import com.radixdlt.application.validators.construction.RegisterValidatorConstructor; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; -import com.radixdlt.atom.REConstructor; -import com.radixdlt.atom.TxnConstructionRequest; -import com.radixdlt.atom.actions.CreateMutableToken; -import com.radixdlt.atom.actions.CreateSystem; -import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.atom.actions.RegisterValidator; -import com.radixdlt.atom.actions.StakeTokens; -import com.radixdlt.atom.actions.NextEpoch; -import com.radixdlt.atom.actions.NextRound; +import static org.assertj.core.api.Assertions.assertThat; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.construction.NextEpochConstructorV3; import com.radixdlt.application.system.construction.NextViewConstructorV3; import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; +import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; import com.radixdlt.application.tokens.Amount; import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; import com.radixdlt.application.tokens.construction.MintTokenConstructor; @@ -90,8 +80,20 @@ import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.application.tokens.state.PreparedStake; import com.radixdlt.application.unique.scrypt.MutexConstraintScrypt; +import com.radixdlt.application.validators.construction.RegisterValidatorConstructor; import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; +import com.radixdlt.atom.REConstructor; +import com.radixdlt.atom.TxnConstructionRequest; +import com.radixdlt.atom.actions.CreateMutableToken; +import com.radixdlt.atom.actions.CreateSystem; +import com.radixdlt.atom.actions.MintToken; +import com.radixdlt.atom.actions.NextEpoch; +import com.radixdlt.atom.actions.NextRound; +import com.radixdlt.atom.actions.RegisterValidator; +import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.constraintmachine.ConstraintMachine; @@ -102,136 +104,136 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.PrivateKeys; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class NextEpochV2Test { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][] { - { - List.of( - new RoundUpdateConstraintScrypt(10), - new EpochUpdateConstraintScrypt( - 10, - Amount.ofTokens(10).toSubunits(), - 9800, - 1, - 10 - ), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new ValidatorConstraintScryptV2(), - new ValidatorUpdateRakeConstraintScrypt(2), - new ValidatorRegisterConstraintScrypt(), - new ValidatorUpdateOwnerConstraintScrypt() - ), - REConstructor.newBuilder() - .put(NextRound.class, new NextViewConstructorV3()) - .put(NextEpoch.class, new NextEpochConstructorV3( - Amount.ofTokens(10).toSubunits(), 9800, 1, 10 - )) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(StakeTokens.class, new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits())) - .put(RegisterValidator.class, new RegisterValidatorConstructor()) - .build() - } - }); - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + { + List.of( + new RoundUpdateConstraintScrypt(10), + new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new ValidatorConstraintScryptV2(), + new ValidatorUpdateRakeConstraintScrypt(2), + new ValidatorRegisterConstraintScrypt(), + new ValidatorUpdateOwnerConstraintScrypt()), + REConstructor.newBuilder() + .put(NextRound.class, new NextViewConstructorV3()) + .put( + NextEpoch.class, + new NextEpochConstructorV3(Amount.ofTokens(10).toSubunits(), 9800, 1, 10)) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put( + StakeTokens.class, + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits())) + .put(RegisterValidator.class, new RegisterValidatorConstructor()) + .build() + } + }); + } - private RadixEngine sut; - private EngineStore store; - private REParser parser; - private final List scrypts; - private final REConstructor constructors; + private RadixEngine sut; + private EngineStore store; + private REParser parser; + private final List scrypts; + private final REConstructor constructors; - public NextEpochV2Test( - List scrypts, - REConstructor constructors - ) { - this.scrypts = scrypts; - this.constructors = constructors; - } + public NextEpochV2Test(List scrypts, REConstructor constructors) { + this.scrypts = scrypts; + this.constructors = constructors; + } - @Before - public void setup() { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - scrypts.forEach(cmAtomOS::load); - cmAtomOS.load(new MutexConstraintScrypt()); // For v1 start - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - this.parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.sut = new RadixEngine<>(parser, serialization, constructors, cm, store); - } + @Before + public void setup() { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + scrypts.forEach(cmAtomOS::load); + cmAtomOS.load(new MutexConstraintScrypt()); // For v1 start + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + this.parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.sut = new RadixEngine<>(parser, serialization, constructors, cm, store); + } - @Test - public void prepared_stake_should_disappear_on_next_epoch() throws Exception { - // Arrange - var key = PrivateKeys.ofNumeric(1).getPublicKey(); - var accountAddr = REAddr.ofPubKeyAccount(key); - var start = sut.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(10).toSubunits())) - .action(new StakeTokens(accountAddr, key, Amount.ofTokens(10).toSubunits())) - ).buildWithoutSignature(); - sut.execute(List.of(start), null, PermissionLevel.SYSTEM); + @Test + public void prepared_stake_should_disappear_on_next_epoch() throws Exception { + // Arrange + var key = PrivateKeys.ofNumeric(1).getPublicKey(); + var accountAddr = REAddr.ofPubKeyAccount(key); + var start = + sut.construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) + .action( + new MintToken( + REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(10).toSubunits())) + .action(new StakeTokens(accountAddr, key, Amount.ofTokens(10).toSubunits()))) + .buildWithoutSignature(); + sut.execute(List.of(start), null, PermissionLevel.SYSTEM); - var request = TxnConstructionRequest.create() - .action(new NextEpoch(1)); + var request = TxnConstructionRequest.create().action(new NextEpoch(1)); - // Act - var txn = sut.construct(request) - .buildWithoutSignature(); - this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER); + // Act + var txn = sut.construct(request).buildWithoutSignature(); + this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER); - // Assert - var map = sut.read(reader -> reader.reduceResources(PreparedStake.class, PreparedStake::getDelegateKey)); - assertThat(map).isEmpty(); - } + // Assert + var map = + sut.read( + reader -> reader.reduceResources(PreparedStake.class, PreparedStake::getDelegateKey)); + assertThat(map).isEmpty(); + } - @Test - public void registered_validator_with_no_stake_should_not_be_in_validatorset() throws Exception { - // Arrange - var key = PrivateKeys.ofNumeric(1).getPublicKey(); - var start = sut.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - .action(new RegisterValidator(key)) - ).buildWithoutSignature(); - sut.execute(List.of(start), null, PermissionLevel.SYSTEM); + @Test + public void registered_validator_with_no_stake_should_not_be_in_validatorset() throws Exception { + // Arrange + var key = PrivateKeys.ofNumeric(1).getPublicKey(); + var start = + sut.construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) + .action(new RegisterValidator(key))) + .buildWithoutSignature(); + sut.execute(List.of(start), null, PermissionLevel.SYSTEM); - var request = TxnConstructionRequest.create() - .action(new NextEpoch(1)); + var request = TxnConstructionRequest.create().action(new NextEpoch(1)); - // Act - var txn = sut.construct(request) - .buildWithoutSignature(); - var result = this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER); - var nextValidatorSet = result.getProcessedTxn().getEvents().stream() - .filter(NextValidatorSetEvent.class::isInstance) - .map(NextValidatorSetEvent.class::cast) - .findFirst().orElseThrow(); - assertThat(nextValidatorSet.nextValidators()).isEmpty(); - } + // Act + var txn = sut.construct(request).buildWithoutSignature(); + var result = this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER); + var nextValidatorSet = + result.getProcessedTxn().getEvents().stream() + .filter(NextValidatorSetEvent.class::isInstance) + .map(NextValidatorSetEvent.class::cast) + .findFirst() + .orElseThrow(); + assertThat(nextValidatorSet.nextValidators()).isEmpty(); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextViewV2Test.java b/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextViewV2Test.java index 37b322f25f..67f098f685 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextViewV2Test.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/system/NextViewV2Test.java @@ -64,24 +64,14 @@ package com.radixdlt.application.system; -import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; -import com.radixdlt.atom.ActionConstructor; -import com.radixdlt.atom.REConstructor; -import com.radixdlt.atom.TxnConstructionRequest; -import com.radixdlt.atom.actions.CreateMutableToken; -import com.radixdlt.atom.actions.CreateSystem; -import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.atom.actions.RegisterValidator; -import com.radixdlt.atom.actions.StakeTokens; -import com.radixdlt.atom.actions.NextEpoch; -import com.radixdlt.atom.actions.NextRound; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.construction.NextEpochConstructorV3; import com.radixdlt.application.system.construction.NextViewConstructorV3; import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; +import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; import com.radixdlt.application.tokens.Amount; import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; import com.radixdlt.application.tokens.construction.MintTokenConstructor; @@ -91,6 +81,18 @@ import com.radixdlt.application.validators.construction.RegisterValidatorConstructor; import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; +import com.radixdlt.atom.ActionConstructor; +import com.radixdlt.atom.REConstructor; +import com.radixdlt.atom.TxnConstructionRequest; +import com.radixdlt.atom.actions.CreateMutableToken; +import com.radixdlt.atom.actions.CreateSystem; +import com.radixdlt.atom.actions.MintToken; +import com.radixdlt.atom.actions.NextEpoch; +import com.radixdlt.atom.actions.NextRound; +import com.radixdlt.atom.actions.RegisterValidator; +import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.constraintmachine.ConstraintMachine; @@ -102,111 +104,124 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class NextViewV2Test { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][] { - { - List.of( - new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), - new RoundUpdateConstraintScrypt(10) - ), - new NextViewConstructorV3() - } - }); - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + { + List.of( + new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), + new RoundUpdateConstraintScrypt(10)), + new NextViewConstructorV3() + } + }); + } - private ECKeyPair key; - private RadixEngine sut; - private EngineStore store; - private final List scrypts; - private final ActionConstructor nextViewConstructor; + private ECKeyPair key; + private RadixEngine sut; + private EngineStore store; + private final List scrypts; + private final ActionConstructor nextViewConstructor; - public NextViewV2Test(List scrypts, ActionConstructor nextViewConstructor) { - this.scrypts = scrypts; - this.nextViewConstructor = nextViewConstructor; - } + public NextViewV2Test( + List scrypts, ActionConstructor nextViewConstructor) { + this.scrypts = scrypts; + this.nextViewConstructor = nextViewConstructor; + } - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - scrypts.forEach(cmAtomOS::load); - cmAtomOS.load(new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits())); - cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); - cmAtomOS.load(new ValidatorConstraintScryptV2()); - cmAtomOS.load(new ValidatorRegisterConstraintScrypt()); - cmAtomOS.load(new ValidatorUpdateRakeConstraintScrypt(2)); - cmAtomOS.load(new ValidatorUpdateOwnerConstraintScrypt()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.sut = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(NextEpoch.class, new NextEpochConstructorV3(Amount.ofTokens(10).toSubunits(), 9800, 1, 10)) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(StakeTokens.class, new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits())) - .put(NextRound.class, nextViewConstructor) - .put(RegisterValidator.class, new RegisterValidatorConstructor()) - .build(), - cm, - store - ); - this.key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var txn = this.sut.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(10).toSubunits())) - .action(new StakeTokens(accountAddr, key.getPublicKey(), Amount.ofTokens(10).toSubunits())) - .action(new RegisterValidator(key.getPublicKey())) - .action(new NextEpoch(0)) - ).buildWithoutSignature(); - this.sut.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + scrypts.forEach(cmAtomOS::load); + cmAtomOS.load(new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits())); + cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); + cmAtomOS.load(new ValidatorConstraintScryptV2()); + cmAtomOS.load(new ValidatorRegisterConstraintScrypt()); + cmAtomOS.load(new ValidatorUpdateRakeConstraintScrypt(2)); + cmAtomOS.load(new ValidatorUpdateOwnerConstraintScrypt()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.sut = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put( + NextEpoch.class, + new NextEpochConstructorV3(Amount.ofTokens(10).toSubunits(), 9800, 1, 10)) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put( + StakeTokens.class, + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits())) + .put(NextRound.class, nextViewConstructor) + .put(RegisterValidator.class, new RegisterValidatorConstructor()) + .build(), + cm, + store); + this.key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var txn = + this.sut + .construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) + .action( + new MintToken( + REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(10).toSubunits())) + .action( + new StakeTokens( + accountAddr, key.getPublicKey(), Amount.ofTokens(10).toSubunits())) + .action(new RegisterValidator(key.getPublicKey())) + .action(new NextEpoch(0))) + .buildWithoutSignature(); + this.sut.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void system_update_should_succeed() throws Exception { - // Arrange - var txn = sut.construct(new NextRound(1, false, 1, i -> key.getPublicKey())) - .buildWithoutSignature(); + @Test + public void system_update_should_succeed() throws Exception { + // Arrange + var txn = + sut.construct(new NextRound(1, false, 1, i -> key.getPublicKey())).buildWithoutSignature(); - // Act and Assert - this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER); - } + // Act and Assert + this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER); + } - @Test - public void including_sigs_in_system_update_should_fail() throws Exception { - // Arrange - var keyPair = ECKeyPair.generateNew(); - var txn = sut.construct(new NextRound(1, false, 1, i -> key.getPublicKey())) - .signAndBuild(keyPair::sign); + @Test + public void including_sigs_in_system_update_should_fail() throws Exception { + // Arrange + var keyPair = ECKeyPair.generateNew(); + var txn = + sut.construct(new NextRound(1, false, 1, i -> key.getPublicKey())) + .signAndBuild(keyPair::sign); - // Act and Assert - assertThatThrownBy(() -> this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER)) - .hasRootCauseInstanceOf(SignedSystemException.class); - } + // Act and Assert + assertThatThrownBy(() -> this.sut.execute(List.of(txn), null, PermissionLevel.SUPER_USER)) + .hasRootCauseInstanceOf(SignedSystemException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/system/ResourceFeeTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/system/ResourceFeeTest.java index 2a4043078d..87690072ed 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/system/ResourceFeeTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/system/ResourceFeeTest.java @@ -64,6 +64,8 @@ package com.radixdlt.application.system; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.construction.FeeReserveCompleteConstructor; import com.radixdlt.application.system.construction.FeeReservePutConstructor; @@ -94,107 +96,116 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; -import org.junit.Before; -import org.junit.Test; - import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; public final class ResourceFeeTest { - private RadixEngine engine; - private EngineStore store; - private final ECKeyPair key = ECKeyPair.generateNew(); - private final REAddr accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + private RadixEngine engine; + private EngineStore store; + private final ECKeyPair key = ECKeyPair.generateNew(); + private final REAddr accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new TokensConstraintScryptV3(Set.of("xrd"), Pattern.compile("[a-z0-9]+"))); - cmAtomOS.load(new SystemConstraintScrypt()); - var feeTable = FeeTable.create( - Amount.zero(), - Map.of(TokenResource.class, Amount.ofTokens(1)) - ); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization(), - UpSubstateFeeMeter.create(feeTable.getPerUpSubstateFee()) - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(TransferToken.class, new TransferTokensConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(FeeReservePut.class, new FeeReservePutConstructor()) - .put(FeeReserveComplete.class, new FeeReserveCompleteConstructor(feeTable)) - .build(), - cm, - store - ); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(4).toSubunits())) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new TokensConstraintScryptV3(Set.of("xrd"), Pattern.compile("[a-z0-9]+"))); + cmAtomOS.load(new SystemConstraintScrypt()); + var feeTable = FeeTable.create(Amount.zero(), Map.of(TokenResource.class, Amount.ofTokens(1))); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization(), + UpSubstateFeeMeter.create(feeTable.getPerUpSubstateFee())); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(TransferToken.class, new TransferTokensConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put(FeeReservePut.class, new FeeReservePutConstructor()) + .put(FeeReserveComplete.class, new FeeReserveCompleteConstructor(feeTable)) + .build(), + cm, + store); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) + .action( + new MintToken( + REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(4).toSubunits()))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void paying_for_fees_should_work() throws Exception { - // Arrange - var tokDef = new MutableTokenDefinition(key.getPublicKey(), "test"); - var create = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, Amount.ofTokens(1).toSubunits())) - .action(new CreateMutableToken(tokDef))) - .signAndBuild(key::sign); + @Test + public void paying_for_fees_should_work() throws Exception { + // Arrange + var tokDef = new MutableTokenDefinition(key.getPublicKey(), "test"); + var create = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, Amount.ofTokens(1).toSubunits())) + .action(new CreateMutableToken(tokDef))) + .signAndBuild(key::sign); - // Act - this.engine.execute(List.of(create)); - } + // Act + this.engine.execute(List.of(create)); + } - @Test - public void paying_for_fees_should_work_2() throws Exception { - // Arrange - var tokDef1 = new MutableTokenDefinition(key.getPublicKey(), "test"); - var tokDef2 = new MutableTokenDefinition(key.getPublicKey(), "testa"); - var tokDef3 = new MutableTokenDefinition(key.getPublicKey(), "testb"); - var create = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, Amount.ofTokens(3).toSubunits())) - .action(new CreateMutableToken(tokDef1)) - .action(new CreateMutableToken(tokDef2)) - .action(new CreateMutableToken(tokDef3))) - .signAndBuild(key::sign); + @Test + public void paying_for_fees_should_work_2() throws Exception { + // Arrange + var tokDef1 = new MutableTokenDefinition(key.getPublicKey(), "test"); + var tokDef2 = new MutableTokenDefinition(key.getPublicKey(), "testa"); + var tokDef3 = new MutableTokenDefinition(key.getPublicKey(), "testb"); + var create = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, Amount.ofTokens(3).toSubunits())) + .action(new CreateMutableToken(tokDef1)) + .action(new CreateMutableToken(tokDef2)) + .action(new CreateMutableToken(tokDef3))) + .signAndBuild(key::sign); - // Act - this.engine.execute(List.of(create)); - } + // Act + this.engine.execute(List.of(create)); + } - @Test - public void paying_too_little_fees_should_fail() throws Exception { - // Arrange - var tokDef = new MutableTokenDefinition(key.getPublicKey(), "test"); - var create = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, Amount.ofMicroTokens(999999).toSubunits())) - .action(new CreateMutableToken(tokDef))) - .signAndBuild(key::sign); + @Test + public void paying_too_little_fees_should_fail() throws Exception { + // Arrange + var tokDef = new MutableTokenDefinition(key.getPublicKey(), "test"); + var create = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new FeeReservePut(accountAddr, Amount.ofMicroTokens(999999).toSubunits())) + .action(new CreateMutableToken(tokDef))) + .signAndBuild(key::sign); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(create))) - .hasRootCauseInstanceOf(DepletedFeeReserveException.class); - } + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(create))) + .hasRootCauseInstanceOf(DepletedFeeReserveException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/system/TxnSizeFeeTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/system/TxnSizeFeeTest.java index 91967579de..aa5c2d967d 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/system/TxnSizeFeeTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/system/TxnSizeFeeTest.java @@ -64,6 +64,9 @@ package com.radixdlt.application.system; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.accounting.REResourceAccounting; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.construction.FeeReserveCompleteConstructor; @@ -105,12 +108,6 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.bouncycastle.util.Arrays; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Collection; @@ -118,253 +115,311 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.bouncycastle.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class TxnSizeFeeTest { - @Parameterized.Parameters - public static Collection parameters() { - return List.of( - new Object[] {Amount.ofSubunits(UInt256.ONE)}, - new Object[] {Amount.ofSubunits(UInt256.TWO)}, - new Object[] {Amount.ofMicroTokens(2)} - ); - } - - private RadixEngine engine; - private EngineStore store; - private final ECKeyPair key = ECKeyPair.generateNew(); - private final REAddr accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - private final Amount costPerByte; - private static final long MAX_SIZE = 507; + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[] {Amount.ofSubunits(UInt256.ONE)}, + new Object[] {Amount.ofSubunits(UInt256.TWO)}, + new Object[] {Amount.ofMicroTokens(2)}); + } - public TxnSizeFeeTest(Amount costPerByte) { - this.costPerByte = costPerByte; - } + private RadixEngine engine; + private EngineStore store; + private final ECKeyPair key = ECKeyPair.generateNew(); + private final REAddr accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + private final Amount costPerByte; + private static final long MAX_SIZE = 507; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); - cmAtomOS.load(new SystemConstraintScrypt()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization(), - TxnSizeFeeMeter.create(costPerByte.toSubunits(), MAX_SIZE) - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(TransferToken.class, new TransferTokensConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(FeeReservePut.class, new FeeReservePutConstructor()) - .put(FeeReserveComplete.class, new FeeReserveCompleteConstructor(FeeTable.create(costPerByte, Map.of()))) - .build(), - cm, - store - ); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(2).toSubunits())) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + public TxnSizeFeeTest(Amount costPerByte) { + this.costPerByte = costPerByte; + } - @Test - public void transaction_thats_over_allowed_size_should_fail() throws Exception { - // Act - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, costPerByte.toSubunits().multiply(UInt256.from(MAX_SIZE + 1)))) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.ONE)) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.ONE)) - ).signAndBuild(key::sign); + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); + cmAtomOS.load(new SystemConstraintScrypt()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization(), + TxnSizeFeeMeter.create(costPerByte.toSubunits(), MAX_SIZE)); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(TransferToken.class, new TransferTokensConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put(FeeReservePut.class, new FeeReservePutConstructor()) + .put( + FeeReserveComplete.class, + new FeeReserveCompleteConstructor(FeeTable.create(costPerByte, Map.of()))) + .build(), + cm, + store); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) + .action( + new MintToken( + REAddr.ofNativeToken(), accountAddr, Amount.ofTokens(2).toSubunits()))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - assertThat(transfer.getPayload().length).isEqualTo(MAX_SIZE + 1); + @Test + public void transaction_thats_over_allowed_size_should_fail() throws Exception { + // Act + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new FeeReservePut( + accountAddr, + costPerByte.toSubunits().multiply(UInt256.from(MAX_SIZE + 1)))) + .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.ONE)) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.ONE))) + .signAndBuild(key::sign); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(DepletedFeeReserveException.class); - } + assertThat(transfer.getPayload().length).isEqualTo(MAX_SIZE + 1); - @Test - public void paying_for_fees_should_work() throws Exception { - var expectedTxnSize = 360; - // Act - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)))) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) - .signAndBuild(key::sign); + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(DepletedFeeReserveException.class); + } - assertThat(transfer.getPayload().length).isEqualTo(expectedTxnSize); + @Test + public void paying_for_fees_should_work() throws Exception { + var expectedTxnSize = 360; + // Act + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new FeeReservePut( + accountAddr, + costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)))) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) + .signAndBuild(key::sign); - // Act - var result = this.engine.execute(List.of(transfer)); - } + assertThat(transfer.getPayload().length).isEqualTo(expectedTxnSize); - @Test - public void pay_just_fees_should_not_fail() throws Exception { - var expectedTxnSize = 212; - // Act - var fee = costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)); - var txn = this.engine.construct(txBuilder -> { - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(accountAddr.getBytes()); - var index = SubstateIndex.create(buf.array(), TokensInAccount.class); - // Take - var remainder = txBuilder.downFungible( - index, - p -> p.getResourceAddr().isNativeToken() && p.getHoldingAddr().equals(accountAddr), - fee, - available -> { - var from = AccountBucket.from(REAddr.ofNativeToken(), accountAddr); - return new NotEnoughResourcesException(from, fee, available); - } - ); - txBuilder.toLowLevelBuilder().syscall(Syscall.FEE_RESERVE_PUT, fee.toByteArray()); - if (!remainder.isZero()) { - txBuilder.up(new TokensInAccount(accountAddr, REAddr.ofNativeToken(), remainder)); - } - txBuilder.end(); - }).signAndBuild(key::sign); - assertThat(txn.getPayload().length).isEqualTo(expectedTxnSize); + // Act + var result = this.engine.execute(List.of(transfer)); + } - // Act - var result = this.engine.execute(List.of(txn)); - assertThat(result.getProcessedTxn().getFeePaid()).isEqualTo(fee); - } + @Test + public void pay_just_fees_should_not_fail() throws Exception { + var expectedTxnSize = 212; + // Act + var fee = costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)); + var txn = + this.engine + .construct( + txBuilder -> { + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(accountAddr.getBytes()); + var index = SubstateIndex.create(buf.array(), TokensInAccount.class); + // Take + var remainder = + txBuilder.downFungible( + index, + p -> + p.getResourceAddr().isNativeToken() + && p.getHoldingAddr().equals(accountAddr), + fee, + available -> { + var from = AccountBucket.from(REAddr.ofNativeToken(), accountAddr); + return new NotEnoughResourcesException(from, fee, available); + }); + txBuilder.toLowLevelBuilder().syscall(Syscall.FEE_RESERVE_PUT, fee.toByteArray()); + if (!remainder.isZero()) { + txBuilder.up( + new TokensInAccount(accountAddr, REAddr.ofNativeToken(), remainder)); + } + txBuilder.end(); + }) + .signAndBuild(key::sign); + assertThat(txn.getPayload().length).isEqualTo(expectedTxnSize); - @Test - public void adding_extra_bytes_to_call_data_should_fail() throws Exception { - var expectedTxnSize = 213; - // Act - var fee = costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)); - var txn = this.engine.construct(txBuilder -> { - var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); - buf.put(SubstateTypeId.TOKENS.id()); - buf.put((byte) 0); - buf.put(accountAddr.getBytes()); - var index = SubstateIndex.create(buf.array(), TokensInAccount.class); - // Take - var remainder = txBuilder.downFungible( - index, - p -> p.getResourceAddr().isNativeToken() && p.getHoldingAddr().equals(accountAddr), - fee, - available -> { - var from = AccountBucket.from(REAddr.ofNativeToken(), accountAddr); - return new NotEnoughResourcesException(from, fee, available); - } - ); + // Act + var result = this.engine.execute(List.of(txn)); + assertThat(result.getProcessedTxn().getFeePaid()).isEqualTo(fee); + } - var data = new byte[Short.BYTES + 1 + UInt256.BYTES + 1]; - data[0] = 0; - data[1] = (byte) (1 + UInt256.BYTES + 1); - data[2] = Syscall.FEE_RESERVE_PUT.id(); - System.arraycopy(Arrays.concatenate(fee.toByteArray(), new byte[1]), 0, data, 3, UInt256.BYTES + 1); - txBuilder.toLowLevelBuilder().instruction(REInstruction.REMicroOp.SYSCALL, data); - if (!remainder.isZero()) { - txBuilder.up(new TokensInAccount(accountAddr, REAddr.ofNativeToken(), remainder)); - } - txBuilder.end(); - }).signAndBuild(key::sign); - assertThat(txn.getPayload().length).isEqualTo(expectedTxnSize); + @Test + public void adding_extra_bytes_to_call_data_should_fail() throws Exception { + var expectedTxnSize = 213; + // Act + var fee = costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)); + var txn = + this.engine + .construct( + txBuilder -> { + var buf = ByteBuffer.allocate(2 + 1 + ECPublicKey.COMPRESSED_BYTES); + buf.put(SubstateTypeId.TOKENS.id()); + buf.put((byte) 0); + buf.put(accountAddr.getBytes()); + var index = SubstateIndex.create(buf.array(), TokensInAccount.class); + // Take + var remainder = + txBuilder.downFungible( + index, + p -> + p.getResourceAddr().isNativeToken() + && p.getHoldingAddr().equals(accountAddr), + fee, + available -> { + var from = AccountBucket.from(REAddr.ofNativeToken(), accountAddr); + return new NotEnoughResourcesException(from, fee, available); + }); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(txn))) - .isInstanceOf(RadixEngineException.class); - } + var data = new byte[Short.BYTES + 1 + UInt256.BYTES + 1]; + data[0] = 0; + data[1] = (byte) (1 + UInt256.BYTES + 1); + data[2] = Syscall.FEE_RESERVE_PUT.id(); + System.arraycopy( + Arrays.concatenate(fee.toByteArray(), new byte[1]), + 0, + data, + 3, + UInt256.BYTES + 1); + txBuilder.toLowLevelBuilder().instruction(REInstruction.REMicroOp.SYSCALL, data); + if (!remainder.isZero()) { + txBuilder.up( + new TokensInAccount(accountAddr, REAddr.ofNativeToken(), remainder)); + } + txBuilder.end(); + }) + .signAndBuild(key::sign); + assertThat(txn.getPayload().length).isEqualTo(expectedTxnSize); - @Test - public void paying_for_fees_should_work_2() throws Exception { - var expectedTxnSize = 360; - // Act - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)))) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE)) - .action(new FeeReserveComplete(accountAddr))) - .signAndBuild(key::sign); + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(txn))) + .isInstanceOf(RadixEngineException.class); + } - assertThat(transfer.getPayload().length).isEqualTo(expectedTxnSize); + @Test + public void paying_for_fees_should_work_2() throws Exception { + var expectedTxnSize = 360; + // Act + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new FeeReservePut( + accountAddr, + costPerByte.toSubunits().multiply(UInt256.from(expectedTxnSize)))) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE)) + .action(new FeeReserveComplete(accountAddr))) + .signAndBuild(key::sign); - // Act - var result = this.engine.execute(List.of(transfer)); - REResourceAccounting.compute(result.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); - } + assertThat(transfer.getPayload().length).isEqualTo(expectedTxnSize); + // Act + var result = this.engine.execute(List.of(transfer)); + REResourceAccounting.compute(result.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + } - @Test - public void paying_too_little_fees_should_fail() throws Exception { - // Arrange - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, UInt256.THREE)) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) - .signAndBuild(key::sign); + @Test + public void paying_too_little_fees_should_fail() throws Exception { + // Arrange + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, UInt256.THREE)) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.FIVE))) + .signAndBuild(key::sign); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(DefaultedSystemLoanException.class); - } + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(DefaultedSystemLoanException.class); + } - @Test - public void paying_too_much_in_fees_should_fail() throws Exception { - // Arrange - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, Amount.ofTokens(1).toSubunits())) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO)) - ).signAndBuild(key::sign); + @Test + public void paying_too_much_in_fees_should_fail() throws Exception { + // Arrange + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, Amount.ofTokens(1).toSubunits())) + .action( + new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO))) + .signAndBuild(key::sign); - // Act - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(ExecutionContextDestroyException.class); - } + // Act + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(ExecutionContextDestroyException.class); + } - @Test - public void put_then_take_reserve_should_work() throws Exception { - // Arrange - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct( - TxnConstructionRequest.create() - .action(new FeeReservePut(accountAddr, Amount.ofTokens(1).toSubunits())) - .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO)) - .action(new FeeReserveComplete(accountAddr))) - .signAndBuild(key::sign); + @Test + public void put_then_take_reserve_should_work() throws Exception { + // Arrange + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new FeeReservePut(accountAddr, Amount.ofTokens(1).toSubunits())) + .action(new TransferToken(REAddr.ofNativeToken(), accountAddr, to, UInt256.TWO)) + .action(new FeeReserveComplete(accountAddr))) + .signAndBuild(key::sign); - var expectedFee = costPerByte.toSubunits().multiply(UInt256.from(transfer.getPayload().length)); - var expectedRefund = new BigInteger(1, Amount.ofTokens(1).toSubunits().subtract(expectedFee).toByteArray()); + var expectedFee = costPerByte.toSubunits().multiply(UInt256.from(transfer.getPayload().length)); + var expectedRefund = + new BigInteger(1, Amount.ofTokens(1).toSubunits().subtract(expectedFee).toByteArray()); - // Act - var result = this.engine.execute(List.of(transfer)); - var refund = REResourceAccounting.compute(result.getProcessedTxn().getGroupedStateUpdates().get(2).stream()) - .bucketAccounting() - .get(AccountBucket.from(REAddr.ofNativeToken(), accountAddr)); - assertThat(refund).isEqualTo(expectedRefund); - } + // Act + var result = this.engine.execute(List.of(transfer)); + var refund = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(2).stream()) + .bucketAccounting() + .get(AccountBucket.from(REAddr.ofNativeToken(), accountAddr)); + assertThat(refund).isEqualTo(expectedRefund); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/BurnTokensV3Test.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/BurnTokensV3Test.java index 2b205dfdb6..fc72e365f6 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/BurnTokensV3Test.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/BurnTokensV3Test.java @@ -64,23 +64,26 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.accounting.REResourceAccounting; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.BurnTokenConstructor; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atom.REConstructor; import com.radixdlt.atom.TxnConstructionRequest; import com.radixdlt.atom.actions.BurnToken; import com.radixdlt.atom.actions.CreateMutableToken; import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.application.tokens.construction.BurnTokenConstructor; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atomos.CMAtomOS; +import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.exceptions.AuthorizationException; -import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.exceptions.ResourceAllocationAndDestructionException; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; @@ -90,150 +93,174 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; - import java.math.BigInteger; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; public class BurnTokensV3Test { - private RadixEngine engine; - - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - EngineStore store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(BurnToken.class, new BurnTokenConstructor()) - .build(), - cm, - store - ); - var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); - } - - @Test - public void can_burn_tokens_if_owner_of_token_resource() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - var account = REAddr.ofPubKeyAccount(key.getPublicKey()); - var mintTxn = this.engine.construct(new MintToken(tokenAddr, account, UInt256.TEN)).signAndBuild(key::sign); - this.engine.execute(List.of(mintTxn)); - - // Act - var burnTxn = this.engine.construct(new BurnToken(tokenAddr, account, UInt256.TEN)) - .signAndBuild(key::sign); - var processed = this.engine.execute(List.of(burnTxn)); - - // Assert - var accounting = REResourceAccounting.compute( - processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting.resourceAccounting()) - .hasSize(1) - .containsEntry(tokenAddr, BigInteger.valueOf(-10)); - } - - @Test - public void can_burn_tokens_of_multiple_utxos() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - var account = REAddr.ofPubKeyAccount(key.getPublicKey()); - var mintTxn = this.engine.construct( - TxnConstructionRequest.create() - .action(new MintToken(tokenAddr, account, UInt256.FIVE)) - .action(new MintToken(tokenAddr, account, UInt256.FIVE)) - ).signAndBuild(key::sign); - this.engine.execute(List.of(mintTxn)); - - // Act - var burnTxn = this.engine.construct(new BurnToken(tokenAddr, account, UInt256.TEN)) - .signAndBuild(key::sign); - var processed = this.engine.execute(List.of(burnTxn)); - - // Assert - var accounting = REResourceAccounting.compute( - processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting.resourceAccounting()) - .hasSize(1) - .containsEntry(tokenAddr, BigInteger.valueOf(-10)); - } - - @Test - public void cannot_burn_tokens_if_deallocation_disabled() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - var account = REAddr.ofPubKeyAccount(key.getPublicKey()); - var mintTxn = this.engine.construct(new MintToken(tokenAddr, account, UInt256.TEN)) - .signAndBuild(key::sign); - this.engine.execute(List.of(mintTxn)); - - // Act - var request = TxnConstructionRequest.create() - .disableResourceAllocAndDestroy() - .action(new BurnToken(tokenAddr, account, UInt256.TEN)); - var burnTxn = this.engine.construct(request).signAndBuild(key::sign); - - // Assert - assertThatThrownBy(() -> this.engine.execute(List.of(burnTxn))) - .hasRootCauseInstanceOf(ResourceAllocationAndDestructionException.class); - } - - @Test - public void cannot_burn_tokens_if_not_owner_of_token_resource() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - var nextKey = ECKeyPair.generateNew(); - var nextAccountAddr = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var mintTxn = this.engine.construct(new MintToken(tokenAddr, nextAccountAddr, UInt256.TEN)).signAndBuild(key::sign); - this.engine.execute(List.of(mintTxn)); - - // Act - var burnTxn = this.engine.construct(new BurnToken(tokenAddr, nextAccountAddr, UInt256.TEN)).signAndBuild(nextKey::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(burnTxn))) - .isInstanceOf(RadixEngineException.class) - .hasRootCauseExactlyInstanceOf(AuthorizationException.class); - } + private RadixEngine engine; + + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+"))); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + EngineStore store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put(BurnToken.class, new BurnTokenConstructor()) + .build(), + cm, + store); + var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); + } + + @Test + public void can_burn_tokens_if_owner_of_token_resource() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + var account = REAddr.ofPubKeyAccount(key.getPublicKey()); + var mintTxn = + this.engine + .construct(new MintToken(tokenAddr, account, UInt256.TEN)) + .signAndBuild(key::sign); + this.engine.execute(List.of(mintTxn)); + + // Act + var burnTxn = + this.engine + .construct(new BurnToken(tokenAddr, account, UInt256.TEN)) + .signAndBuild(key::sign); + var processed = this.engine.execute(List.of(burnTxn)); + + // Assert + var accounting = + REResourceAccounting.compute( + processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting.resourceAccounting()) + .hasSize(1) + .containsEntry(tokenAddr, BigInteger.valueOf(-10)); + } + + @Test + public void can_burn_tokens_of_multiple_utxos() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + var account = REAddr.ofPubKeyAccount(key.getPublicKey()); + var mintTxn = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new MintToken(tokenAddr, account, UInt256.FIVE)) + .action(new MintToken(tokenAddr, account, UInt256.FIVE))) + .signAndBuild(key::sign); + this.engine.execute(List.of(mintTxn)); + + // Act + var burnTxn = + this.engine + .construct(new BurnToken(tokenAddr, account, UInt256.TEN)) + .signAndBuild(key::sign); + var processed = this.engine.execute(List.of(burnTxn)); + + // Assert + var accounting = + REResourceAccounting.compute( + processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting.resourceAccounting()) + .hasSize(1) + .containsEntry(tokenAddr, BigInteger.valueOf(-10)); + } + + @Test + public void cannot_burn_tokens_if_deallocation_disabled() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + var account = REAddr.ofPubKeyAccount(key.getPublicKey()); + var mintTxn = + this.engine + .construct(new MintToken(tokenAddr, account, UInt256.TEN)) + .signAndBuild(key::sign); + this.engine.execute(List.of(mintTxn)); + + // Act + var request = + TxnConstructionRequest.create() + .disableResourceAllocAndDestroy() + .action(new BurnToken(tokenAddr, account, UInt256.TEN)); + var burnTxn = this.engine.construct(request).signAndBuild(key::sign); + + // Assert + assertThatThrownBy(() -> this.engine.execute(List.of(burnTxn))) + .hasRootCauseInstanceOf(ResourceAllocationAndDestructionException.class); + } + + @Test + public void cannot_burn_tokens_if_not_owner_of_token_resource() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + var nextKey = ECKeyPair.generateNew(); + var nextAccountAddr = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var mintTxn = + this.engine + .construct(new MintToken(tokenAddr, nextAccountAddr, UInt256.TEN)) + .signAndBuild(key::sign); + this.engine.execute(List.of(mintTxn)); + + // Act + var burnTxn = + this.engine + .construct(new BurnToken(tokenAddr, nextAccountAddr, UInt256.TEN)) + .signAndBuild(nextKey::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(burnTxn))) + .isInstanceOf(RadixEngineException.class) + .hasRootCauseExactlyInstanceOf(AuthorizationException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/DelegationFlagTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/DelegationFlagTest.java index f36410157c..eb8c98f59f 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/DelegationFlagTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/DelegationFlagTest.java @@ -64,10 +64,20 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.construction.StakeTokensConstructorV3; +import com.radixdlt.application.tokens.scrypt.StakingConstraintScryptV4; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; +import com.radixdlt.application.validators.construction.UpdateAllowDelegationFlagConstructor; +import com.radixdlt.application.validators.construction.UpdateValidatorOwnerConstructor; +import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.REConstructor; @@ -79,14 +89,6 @@ import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atom.actions.UpdateAllowDelegationFlag; import com.radixdlt.atom.actions.UpdateValidatorOwner; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.construction.StakeTokensConstructorV3; -import com.radixdlt.application.tokens.scrypt.StakingConstraintScryptV4; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; -import com.radixdlt.application.validators.construction.UpdateAllowDelegationFlagConstructor; -import com.radixdlt.application.validators.construction.UpdateValidatorOwnerConstructor; -import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.constraintmachine.ConstraintMachine; @@ -99,166 +101,179 @@ import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class DelegationFlagTest { - @Parameterized.Parameters - public static Collection parameters() { - var startAmounts = List.of(10); - var stakeAmounts = List.of(10); - var scrypts = List.of( - Pair.of( - List.of( - new RoundUpdateConstraintScrypt(10), - new EpochUpdateConstraintScrypt(10, UInt256.NINE, 1, 1, 10), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new ValidatorConstraintScryptV2(), - new ValidatorUpdateOwnerConstraintScrypt() - ), - new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()) - ) - ); - - var parameters = new ArrayList(); - for (var scrypt : scrypts) { - for (var startAmount : startAmounts) { - for (var stakeAmount : stakeAmounts) { - var param = new Object[] { - startAmount, - stakeAmount, - scrypt.getFirst(), - scrypt.getSecond() - }; - parameters.add(param); - } - } - } - return parameters; - } - + @Parameterized.Parameters + public static Collection parameters() { + var startAmounts = List.of(10); + var stakeAmounts = List.of(10); + var scrypts = + List.of( + Pair.of( + List.of( + new RoundUpdateConstraintScrypt(10), + new EpochUpdateConstraintScrypt(10, UInt256.NINE, 1, 1, 10), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new ValidatorConstraintScryptV2(), + new ValidatorUpdateOwnerConstraintScrypt()), + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()))); - private RadixEngine engine; - private EngineStore store; - private final UInt256 startAmt; - private final UInt256 stakeAmt; - private final List scrypts; - private final ActionConstructor stakeTokensConstructor; + var parameters = new ArrayList(); + for (var scrypt : scrypts) { + for (var startAmount : startAmounts) { + for (var stakeAmount : stakeAmounts) { + var param = + new Object[] {startAmount, stakeAmount, scrypt.getFirst(), scrypt.getSecond()}; + parameters.add(param); + } + } + } + return parameters; + } - public DelegationFlagTest( - long startAmt, - long stakeAmt, - List scrypts, - ActionConstructor stakeTokensConstructor - ) { - this.startAmt = Amount.ofTokens(startAmt * 10).toSubunits(); - this.stakeAmt = Amount.ofTokens(stakeAmt * 10).toSubunits(); - this.scrypts = scrypts; - this.stakeTokensConstructor = stakeTokensConstructor; - } + private RadixEngine engine; + private EngineStore store; + private final UInt256 startAmt; + private final UInt256 stakeAmt; + private final List scrypts; + private final ActionConstructor stakeTokensConstructor; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - scrypts.forEach(cmAtomOS::load); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(StakeTokens.class, stakeTokensConstructor) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(UpdateAllowDelegationFlag.class, new UpdateAllowDelegationFlagConstructor()) - .put(UpdateValidatorOwner.class, new UpdateValidatorOwnerConstructor()) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .build(), - cm, - store - ); - var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + public DelegationFlagTest( + long startAmt, + long stakeAmt, + List scrypts, + ActionConstructor stakeTokensConstructor) { + this.startAmt = Amount.ofTokens(startAmt * 10).toSubunits(); + this.stakeAmt = Amount.ofTokens(stakeAmt * 10).toSubunits(); + this.scrypts = scrypts; + this.stakeTokensConstructor = stakeTokensConstructor; + } - @Test - public void cannot_construct_stake_tokens_if_delegation_flag_set_to_false() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt)) - ).buildWithoutSignature(); - var validatorKey = ECKeyPair.generateNew(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + scrypts.forEach(cmAtomOS::load); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(StakeTokens.class, stakeTokensConstructor) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put(UpdateAllowDelegationFlag.class, new UpdateAllowDelegationFlagConstructor()) + .put(UpdateValidatorOwner.class, new UpdateValidatorOwnerConstructor()) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .build(), + cm, + store); + var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - // Act - assertThatThrownBy(() -> this.engine.construct(new StakeTokens(accountAddr, validatorKey.getPublicKey(), stakeAmt)) - .signAndBuild(key::sign)).isInstanceOf(TxBuilderException.class); - } + @Test + public void cannot_construct_stake_tokens_if_delegation_flag_set_to_false() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt))) + .buildWithoutSignature(); + var validatorKey = ECKeyPair.generateNew(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - @Test - public void can_stake_tokens_if_delegation_flag_set_to_false_and_am_owner() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt)) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + // Act + assertThatThrownBy( + () -> + this.engine + .construct(new StakeTokens(accountAddr, validatorKey.getPublicKey(), stakeAmt)) + .signAndBuild(key::sign)) + .isInstanceOf(TxBuilderException.class); + } - // Act - var stake = this.engine.construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) - .signAndBuild(key::sign); - this.engine.execute(List.of(stake)); - } + @Test + public void can_stake_tokens_if_delegation_flag_set_to_false_and_am_owner() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + // Act + var stake = + this.engine + .construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) + .signAndBuild(key::sign); + this.engine.execute(List.of(stake)); + } - @Test - public void can_stake_tokens_if_delegation_flag_set_to_false_and_changed_owner() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt)) - ).buildWithoutSignature(); - var validatorKey = ECKeyPair.generateNew(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - var update = this.engine.construct( - TxnConstructionRequest.create() - .action(new UpdateValidatorOwner(validatorKey.getPublicKey(), accountAddr)) - ).signAndBuild(validatorKey::sign); - this.engine.execute(List.of(update)); + @Test + public void can_stake_tokens_if_delegation_flag_set_to_false_and_changed_owner() + throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt))) + .buildWithoutSignature(); + var validatorKey = ECKeyPair.generateNew(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + var update = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new UpdateValidatorOwner(validatorKey.getPublicKey(), accountAddr))) + .signAndBuild(validatorKey::sign); + this.engine.execute(List.of(update)); - // Act - var stake = this.engine.construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) - .signAndBuild(key::sign); - this.engine.execute(List.of(stake)); - } + // Act + var stake = + this.engine + .construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) + .signAndBuild(key::sign); + this.engine.execute(List.of(stake)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/MintTokensTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/MintTokensTest.java index 34a3f9ca29..829417fb5f 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/MintTokensTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/MintTokensTest.java @@ -64,9 +64,16 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.accounting.REResourceAccounting; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.REConstructor; import com.radixdlt.atom.TxnConstructionRequest; @@ -74,15 +81,11 @@ import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.MintToken; import com.radixdlt.atom.actions.TransferToken; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; +import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.exceptions.AuthorizationException; -import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.exceptions.NotAResourceException; import com.radixdlt.constraintmachine.exceptions.ResourceAllocationAndDestructionException; import com.radixdlt.crypto.ECKeyPair; @@ -92,145 +95,157 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.math.BigInteger; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public final class MintTokensTest { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][] { - { - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new TransferTokensConstructorV2() - } - }); - } - - private final ConstraintScrypt scrypt; - private final ActionConstructor transferTokensConstructor; - private RadixEngine engine; - private EngineStore store; - - public MintTokensTest(ConstraintScrypt scrypt, ActionConstructor transferTokensConstructor) { - this.scrypt = scrypt; - this.transferTokensConstructor = transferTokensConstructor; - } - - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(scrypt); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(TransferToken.class, transferTokensConstructor) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .build(), - cm, - store - ); - var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); - } - - @Test - public void mint_tokens_with_no_tokendef() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - - // Act and Assert - var mintTxn = this.engine.construct(new MintToken(tokenAddr, accountAddr, UInt256.TEN)) - .signAndBuild(key::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(mintTxn))) - .hasRootCauseInstanceOf(NotAResourceException.class); - } - - @Test - public void mint_tokens_as_owner() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - - // Act and Assert - var mintTxn = this.engine.construct(new MintToken(tokenAddr, accountAddr, UInt256.TEN)).signAndBuild(key::sign); - var processed = this.engine.execute(List.of(mintTxn)); - var accounting = REResourceAccounting.compute( - processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting.resourceAccounting()) - .hasSize(1) - .containsEntry(tokenAddr, BigInteger.valueOf(10)); - } - - @Test - public void authorization_failure_on_mint() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - - // Act, Assert - var addr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var nextKey = ECKeyPair.generateNew(); - var mintTxn = this.engine.construct( - new MintToken(addr, REAddr.ofPubKeyAccount(key.getPublicKey()), UInt256.ONE) - ).signAndBuild(nextKey::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(mintTxn))) - .hasRootCauseInstanceOf(AuthorizationException.class); - } - - - @Test - public void cannot_mint_on_disable_resource_alloc() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey()) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - - // Act, Assert - var request = TxnConstructionRequest.create() - .disableResourceAllocAndDestroy() - .action(new MintToken(tokenAddr, accountAddr, UInt256.ONE)); - var mintTxn = this.engine.construct(request).signAndBuild(key::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(mintTxn))) - .hasRootCauseInstanceOf(ResourceAllocationAndDestructionException.class); - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + { + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new TransferTokensConstructorV2() + } + }); + } + + private final ConstraintScrypt scrypt; + private final ActionConstructor transferTokensConstructor; + private RadixEngine engine; + private EngineStore store; + + public MintTokensTest( + ConstraintScrypt scrypt, ActionConstructor transferTokensConstructor) { + this.scrypt = scrypt; + this.transferTokensConstructor = transferTokensConstructor; + } + + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(scrypt); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(TransferToken.class, transferTokensConstructor) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .build(), + cm, + store); + var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); + } + + @Test + public void mint_tokens_with_no_tokendef() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + + // Act and Assert + var mintTxn = + this.engine + .construct(new MintToken(tokenAddr, accountAddr, UInt256.TEN)) + .signAndBuild(key::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(mintTxn))) + .hasRootCauseInstanceOf(NotAResourceException.class); + } + + @Test + public void mint_tokens_as_owner() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + + // Act and Assert + var mintTxn = + this.engine + .construct(new MintToken(tokenAddr, accountAddr, UInt256.TEN)) + .signAndBuild(key::sign); + var processed = this.engine.execute(List.of(mintTxn)); + var accounting = + REResourceAccounting.compute( + processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting.resourceAccounting()) + .hasSize(1) + .containsEntry(tokenAddr, BigInteger.valueOf(10)); + } + + @Test + public void authorization_failure_on_mint() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + + // Act, Assert + var addr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var nextKey = ECKeyPair.generateNew(); + var mintTxn = + this.engine + .construct(new MintToken(addr, REAddr.ofPubKeyAccount(key.getPublicKey()), UInt256.ONE)) + .signAndBuild(nextKey::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(mintTxn))) + .hasRootCauseInstanceOf(AuthorizationException.class); + } + + @Test + public void cannot_mint_on_disable_resource_alloc() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + + // Act, Assert + var request = + TxnConstructionRequest.create() + .disableResourceAllocAndDestroy() + .action(new MintToken(tokenAddr, accountAddr, UInt256.ONE)); + var mintTxn = this.engine.construct(request).signAndBuild(key::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(mintTxn))) + .hasRootCauseInstanceOf(ResourceAllocationAndDestructionException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/NativeTokensTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/NativeTokensTest.java index 4890024089..56f0acbcfb 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/NativeTokensTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/NativeTokensTest.java @@ -64,8 +64,14 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.REConstructor; import com.radixdlt.atom.TxnConstructionRequest; @@ -73,10 +79,6 @@ import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.MintToken; import com.radixdlt.atom.actions.TransferToken; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; import com.radixdlt.constraintmachine.ConstraintMachine; @@ -89,82 +91,87 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class NativeTokensTest { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][] { - { - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new TransferTokensConstructorV2() - }, - }); - } - - private final ConstraintScrypt scrypt; - private final ActionConstructor transferTokensConstructor; - private RadixEngine engine; - private EngineStore store; + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + { + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new TransferTokensConstructorV2() + }, + }); + } - public NativeTokensTest(ConstraintScrypt scrypt, ActionConstructor transferTokensConstructor) { - this.scrypt = scrypt; - this.transferTokensConstructor = transferTokensConstructor; - } + private final ConstraintScrypt scrypt; + private final ActionConstructor transferTokensConstructor; + private RadixEngine engine; + private EngineStore store; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(scrypt); - cmAtomOS.load(new SystemConstraintScrypt()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(TransferToken.class, transferTokensConstructor) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .build(), - cm, - store - ); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null)) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + public NativeTokensTest( + ConstraintScrypt scrypt, ActionConstructor transferTokensConstructor) { + this.scrypt = scrypt; + this.transferTokensConstructor = transferTokensConstructor; + } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(scrypt); + cmAtomOS.load(new SystemConstraintScrypt()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(TransferToken.class, transferTokensConstructor) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .build(), + cm, + store); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "xrd", "", "", "", null))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void mint_native_token_as_super_user_should_fail() throws Exception { - // Arrange - var addr = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); - var txn = this.engine.construct( - new MintToken(REAddr.ofNativeToken(), addr, UInt256.TEN) - ).buildWithoutSignature(); - assertThatThrownBy(() -> this.engine.execute(List.of(txn), null, PermissionLevel.SUPER_USER)) - .isInstanceOf(RadixEngineException.class); - } + @Test + public void mint_native_token_as_super_user_should_fail() throws Exception { + // Arrange + var addr = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); + var txn = + this.engine + .construct(new MintToken(REAddr.ofNativeToken(), addr, UInt256.TEN)) + .buildWithoutSignature(); + assertThatThrownBy(() -> this.engine.execute(List.of(txn), null, PermissionLevel.SUPER_USER)) + .isInstanceOf(RadixEngineException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/StakeTokensTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/StakeTokensTest.java index 49cc15f058..25dcc1f5ec 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/StakeTokensTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/StakeTokensTest.java @@ -64,17 +64,12 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.accounting.REResourceAccounting; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; -import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; -import com.radixdlt.atom.ActionConstructor; -import com.radixdlt.atom.REConstructor; -import com.radixdlt.atom.TxnConstructionRequest; -import com.radixdlt.atom.actions.CreateMutableToken; -import com.radixdlt.atom.actions.CreateSystem; -import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; import com.radixdlt.application.tokens.construction.MintTokenConstructor; import com.radixdlt.application.tokens.construction.StakeTokensConstructorV3; @@ -83,11 +78,19 @@ import com.radixdlt.application.tokens.state.AccountBucket; import com.radixdlt.application.tokens.state.PreparedStakeBucket; import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; +import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; +import com.radixdlt.atom.ActionConstructor; +import com.radixdlt.atom.REConstructor; +import com.radixdlt.atom.TxnConstructionRequest; +import com.radixdlt.atom.actions.CreateMutableToken; +import com.radixdlt.atom.actions.CreateSystem; +import com.radixdlt.atom.actions.MintToken; +import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.parser.REParser; @@ -96,155 +99,156 @@ import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class StakeTokensTest { - @Parameterized.Parameters - public static Collection parameters() { - var startAmounts = List.of(10); - var stakeAmounts = List.of(10, 6); - var scrypts = List.of( - Pair.of( - List.of( - new SystemConstraintScrypt(), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new ValidatorConstraintScryptV2(), - new ValidatorUpdateOwnerConstraintScrypt() - ), - new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()) - ) - ); + @Parameterized.Parameters + public static Collection parameters() { + var startAmounts = List.of(10); + var stakeAmounts = List.of(10, 6); + var scrypts = + List.of( + Pair.of( + List.of( + new SystemConstraintScrypt(), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new ValidatorConstraintScryptV2(), + new ValidatorUpdateOwnerConstraintScrypt()), + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()))); - var parameters = new ArrayList(); - for (var scrypt : scrypts) { - for (var startAmount : startAmounts) { - for (var stakeAmount : stakeAmounts) { - var param = new Object[] { - startAmount, - stakeAmount, - scrypt.getFirst(), - scrypt.getSecond() - }; - parameters.add(param); - } - } - } - return parameters; - } + var parameters = new ArrayList(); + for (var scrypt : scrypts) { + for (var startAmount : startAmounts) { + for (var stakeAmount : stakeAmounts) { + var param = + new Object[] {startAmount, stakeAmount, scrypt.getFirst(), scrypt.getSecond()}; + parameters.add(param); + } + } + } + return parameters; + } - private RadixEngine engine; - private EngineStore store; - private final UInt256 startAmt; - private final UInt256 stakeAmt; - private final List scrypts; - private final ActionConstructor stakeTokensConstructor; + private RadixEngine engine; + private EngineStore store; + private final UInt256 startAmt; + private final UInt256 stakeAmt; + private final List scrypts; + private final ActionConstructor stakeTokensConstructor; - public StakeTokensTest( - long startAmt, - long stakeAmt, - List scrypts, - ActionConstructor stakeTokensConstructor - ) { - this.startAmt = Amount.ofTokens(startAmt * 10).toSubunits(); - this.stakeAmt = Amount.ofTokens(stakeAmt * 10).toSubunits(); - this.scrypts = scrypts; - this.stakeTokensConstructor = stakeTokensConstructor; - } + public StakeTokensTest( + long startAmt, + long stakeAmt, + List scrypts, + ActionConstructor stakeTokensConstructor) { + this.startAmt = Amount.ofTokens(startAmt * 10).toSubunits(); + this.stakeAmt = Amount.ofTokens(stakeAmt * 10).toSubunits(); + this.scrypts = scrypts; + this.stakeTokensConstructor = stakeTokensConstructor; + } - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - scrypts.forEach(cmAtomOS::load); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(StakeTokens.class, stakeTokensConstructor) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .build(), - cm, - store - ); - var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + scrypts.forEach(cmAtomOS::load); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(StakeTokens.class, stakeTokensConstructor) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .build(), + cm, + store); + var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); + } - @Test - public void stake_tokens() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt)) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + @Test + public void stake_tokens() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - // Act - var transfer = this.engine.construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) - .signAndBuild(key::sign); - var processed = this.engine.execute(List.of(transfer)); - var accounting = REResourceAccounting.compute( - processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting.bucketAccounting()) - .hasSize(2) - .containsEntry( - AccountBucket.from(REAddr.ofNativeToken(), accountAddr), - new BigInteger(-1, stakeAmt.toByteArray(), 0, UInt256.BYTES) - ) - .containsEntry( - new PreparedStakeBucket(accountAddr, key.getPublicKey()), - new BigInteger(1, stakeAmt.toByteArray(), 0, UInt256.BYTES) - ); - } + // Act + var transfer = + this.engine + .construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) + .signAndBuild(key::sign); + var processed = this.engine.execute(List.of(transfer)); + var accounting = + REResourceAccounting.compute( + processed.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting.bucketAccounting()) + .hasSize(2) + .containsEntry( + AccountBucket.from(REAddr.ofNativeToken(), accountAddr), + new BigInteger(-1, stakeAmt.toByteArray(), 0, UInt256.BYTES)) + .containsEntry( + new PreparedStakeBucket(accountAddr, key.getPublicKey()), + new BigInteger(1, stakeAmt.toByteArray(), 0, UInt256.BYTES)); + } - @Test - public void cannot_stake_others_tokens() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt)) - ).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + @Test + public void cannot_stake_others_tokens() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, startAmt))) + .buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - // Act - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) - .signAndBuild(nextKey::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(AuthorizationException.class); - } + // Act + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct(new StakeTokens(accountAddr, key.getPublicKey(), stakeAmt)) + .signAndBuild(nextKey::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(AuthorizationException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TokenResourceTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TokenResourceTest.java index fb2fc9fa8c..2fa141330a 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TokenResourceTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TokenResourceTest.java @@ -64,9 +64,14 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.Syscall; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.application.tokens.state.TokenResource; import com.radixdlt.application.tokens.state.TokenResourceMetadata; import com.radixdlt.application.tokens.state.TokensInAccount; @@ -78,9 +83,6 @@ import com.radixdlt.atom.actions.CreateMutableToken; import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; @@ -96,171 +98,166 @@ import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; - import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; public class TokenResourceTest { - private RadixEngine engine; - private EngineStore store; - private REParser parser; - private SubstateSerialization serialization; - private Txn genesis; + private RadixEngine engine; + private EngineStore store; + private REParser parser; + private SubstateSerialization serialization; + private Txn genesis; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(new TokensConstraintScryptV3(Set.of("xrd"), Pattern.compile("[a-z0-9]+"))); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - this.parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - this.serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .build(), - cm, - store - ); - this.genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(this.genesis), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(new TokensConstraintScryptV3(Set.of("xrd"), Pattern.compile("[a-z0-9]+"))); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + this.parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + this.serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .build(), + cm, + store); + this.genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(this.genesis), null, PermissionLevel.SYSTEM); + } - @Test - public void create_new_token_with_no_errors() throws Exception { - // Arrange - var keyPair = ECKeyPair.generateNew(); - var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "test"); - var tokenResource = TokenResource.createFixedSupplyResource(addr); - var holdingAddress = REAddr.ofPubKeyAccount(keyPair.getPublicKey()); - var tokensParticle = new TokensInAccount( - holdingAddress, - addr, - UInt256.TEN - ); + @Test + public void create_new_token_with_no_errors() throws Exception { + // Arrange + var keyPair = ECKeyPair.generateNew(); + var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "test"); + var tokenResource = TokenResource.createFixedSupplyResource(addr); + var holdingAddress = REAddr.ofPubKeyAccount(keyPair.getPublicKey()); + var tokensParticle = new TokensInAccount(holdingAddress, addr, UInt256.TEN); - var builder = TxLowLevelBuilder.newBuilder(serialization) - .syscall(Syscall.READDR_CLAIM, "test".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .up(tokenResource) - .up(tokensParticle) - .up(TokenResourceMetadata.empty(addr, "test")) - .end(); - var sig = keyPair.sign(builder.hashToSign().asBytes()); - var txn = builder.sig(sig).build(); + var builder = + TxLowLevelBuilder.newBuilder(serialization) + .syscall(Syscall.READDR_CLAIM, "test".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .up(tokenResource) + .up(tokensParticle) + .up(TokenResourceMetadata.empty(addr, "test")) + .end(); + var sig = keyPair.sign(builder.hashToSign().asBytes()); + var txn = builder.sig(sig).build(); - // Act - // Assert - this.engine.execute(List.of(txn)); - } + // Act + // Assert + this.engine.execute(List.of(txn)); + } - @Test - public void create_token_with_reserved_symbol_should_fail() throws Exception { - // Arrange - var keyPair = PrivateKeys.ofNumeric(1); - var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "xrd"); - var tokenResource = TokenResource.createFixedSupplyResource(addr); - var holdingAddress = REAddr.ofPubKeyAccount(keyPair.getPublicKey()); - var tokensParticle = new TokensInAccount( - holdingAddress, - addr, - UInt256.TEN - ); + @Test + public void create_token_with_reserved_symbol_should_fail() throws Exception { + // Arrange + var keyPair = PrivateKeys.ofNumeric(1); + var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "xrd"); + var tokenResource = TokenResource.createFixedSupplyResource(addr); + var holdingAddress = REAddr.ofPubKeyAccount(keyPair.getPublicKey()); + var tokensParticle = new TokensInAccount(holdingAddress, addr, UInt256.TEN); - var builder = TxLowLevelBuilder.newBuilder(serialization) - .syscall(Syscall.READDR_CLAIM, "xrd".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .up(tokenResource) - .up(tokensParticle) - .up(TokenResourceMetadata.empty(addr, "xrd")) - .end(); - var sig = keyPair.sign(builder.hashToSign().asBytes()); - var txn = builder.sig(sig).build(); + var builder = + TxLowLevelBuilder.newBuilder(serialization) + .syscall(Syscall.READDR_CLAIM, "xrd".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .up(tokenResource) + .up(tokensParticle) + .up(TokenResourceMetadata.empty(addr, "xrd")) + .end(); + var sig = keyPair.sign(builder.hashToSign().asBytes()); + var txn = builder.sig(sig).build(); - // Act - // Assert - assertThatThrownBy(() -> this.engine.execute(List.of(txn))) - .hasRootCauseInstanceOf(ReservedSymbolException.class); - } + // Act + // Assert + assertThatThrownBy(() -> this.engine.execute(List.of(txn))) + .hasRootCauseInstanceOf(ReservedSymbolException.class); + } - @Test - public void create_token_with_reserved_symbol_with_system_permissions_should_pass() throws Exception { - // Arrange - var keyPair = PrivateKeys.ofNumeric(1); - var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "xrd"); - var tokenResource = TokenResource.createFixedSupplyResource(addr); - var holdingAddress = REAddr.ofPubKeyAccount(keyPair.getPublicKey()); - var tokensParticle = new TokensInAccount( - holdingAddress, - addr, - UInt256.TEN - ); + @Test + public void create_token_with_reserved_symbol_with_system_permissions_should_pass() + throws Exception { + // Arrange + var keyPair = PrivateKeys.ofNumeric(1); + var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "xrd"); + var tokenResource = TokenResource.createFixedSupplyResource(addr); + var holdingAddress = REAddr.ofPubKeyAccount(keyPair.getPublicKey()); + var tokensParticle = new TokensInAccount(holdingAddress, addr, UInt256.TEN); - var builder = TxLowLevelBuilder.newBuilder(serialization) - .syscall(Syscall.READDR_CLAIM, "xrd".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .up(tokenResource) - .up(tokensParticle) - .up(TokenResourceMetadata.empty(addr, "xrd")) - .end(); - var txn = builder.build(); + var builder = + TxLowLevelBuilder.newBuilder(serialization) + .syscall(Syscall.READDR_CLAIM, "xrd".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .up(tokenResource) + .up(tokensParticle) + .up(TokenResourceMetadata.empty(addr, "xrd")) + .end(); + var txn = builder.build(); - // Act - // Assert - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + // Act + // Assert + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void create_fixed_token_with_no_tokens_should_error() throws Exception { - // Arrange - var keyPair = ECKeyPair.generateNew(); - var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "test"); - var tokenDefinitionParticle = TokenResource.createFixedSupplyResource(addr); - var builder = TxLowLevelBuilder.newBuilder(serialization) - .syscall(Syscall.READDR_CLAIM, "test".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .up(tokenDefinitionParticle) - .end(); - var sig = keyPair.sign(builder.hashToSign().asBytes()); - var txn = builder.sig(sig).build(); + @Test + public void create_fixed_token_with_no_tokens_should_error() throws Exception { + // Arrange + var keyPair = ECKeyPair.generateNew(); + var addr = REAddr.ofHashedKey(keyPair.getPublicKey(), "test"); + var tokenDefinitionParticle = TokenResource.createFixedSupplyResource(addr); + var builder = + TxLowLevelBuilder.newBuilder(serialization) + .syscall(Syscall.READDR_CLAIM, "test".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .up(tokenDefinitionParticle) + .end(); + var sig = keyPair.sign(builder.hashToSign().asBytes()); + var txn = builder.sig(sig).build(); - // Act - // Assert - assertThatThrownBy(() -> this.engine.execute(List.of(txn))).isInstanceOf(RadixEngineException.class); - } + // Act + // Assert + assertThatThrownBy(() -> this.engine.execute(List.of(txn))) + .isInstanceOf(RadixEngineException.class); + } - @Test - public void using_someone_elses_address_should_fail() throws Exception { - var keyPair = ECKeyPair.generateNew(); - // Arrange - var addr = REAddr.ofHashedKey(ECKeyPair.generateNew().getPublicKey(), "smthng"); - var tokenDefinitionParticle = TokenResource.createMutableSupplyResource(addr, keyPair.getPublicKey()); - var builder = TxBuilder.newBuilder(parser.getSubstateDeserialization(), serialization) - .toLowLevelBuilder() - .syscall(Syscall.READDR_CLAIM, "smthng".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .up(tokenDefinitionParticle) - .end(); - var sig = keyPair.sign(builder.hashToSign()); - var txn = builder.sig(sig).build(); + @Test + public void using_someone_elses_address_should_fail() throws Exception { + var keyPair = ECKeyPair.generateNew(); + // Arrange + var addr = REAddr.ofHashedKey(ECKeyPair.generateNew().getPublicKey(), "smthng"); + var tokenDefinitionParticle = + TokenResource.createMutableSupplyResource(addr, keyPair.getPublicKey()); + var builder = + TxBuilder.newBuilder(parser.getSubstateDeserialization(), serialization) + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, "smthng".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .up(tokenDefinitionParticle) + .end(); + var sig = keyPair.sign(builder.hashToSign()); + var txn = builder.sig(sig).build(); - // Act and Assert - assertThatThrownBy(() -> this.engine.execute(List.of(txn))) - .hasRootCauseInstanceOf(InvalidHashedKeyException.class); - } + // Act and Assert + assertThatThrownBy(() -> this.engine.execute(List.of(txn))) + .hasRootCauseInstanceOf(InvalidHashedKeyException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TransferTokensTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TransferTokensTest.java index 456f113a13..103cd50068 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TransferTokensTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/TransferTokensTest.java @@ -64,9 +64,17 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.accounting.REResourceAccounting; import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; +import com.radixdlt.application.tokens.state.AccountBucket; import com.radixdlt.atom.ActionConstructor; import com.radixdlt.atom.REConstructor; import com.radixdlt.atom.TxnConstructionRequest; @@ -74,16 +82,11 @@ import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.MintToken; import com.radixdlt.atom.actions.TransferToken; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; -import com.radixdlt.application.tokens.state.AccountBucket; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; +import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.exceptions.AuthorizationException; -import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.parser.REParser; @@ -91,133 +94,151 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.math.BigInteger; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class TransferTokensTest { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][] { - {UInt256.TEN, UInt256.TEN, new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), new TransferTokensConstructorV2()}, - {UInt256.TEN, UInt256.SIX, new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), new TransferTokensConstructorV2()}, - }); - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + { + UInt256.TEN, + UInt256.TEN, + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new TransferTokensConstructorV2() + }, + { + UInt256.TEN, + UInt256.SIX, + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new TransferTokensConstructorV2() + }, + }); + } - private RadixEngine engine; - private EngineStore store; - private final UInt256 startAmt; - private final UInt256 transferAmt; - private final ConstraintScrypt scrypt; - private final ActionConstructor transferTokensConstructor; + private RadixEngine engine; + private EngineStore store; + private final UInt256 startAmt; + private final UInt256 transferAmt; + private final ConstraintScrypt scrypt; + private final ActionConstructor transferTokensConstructor; - public TransferTokensTest( - UInt256 startAmt, - UInt256 transferAmount, - ConstraintScrypt scrypt, - ActionConstructor transferTokensConstructor - ) { - this.startAmt = startAmt; - this.transferAmt = transferAmount; - this.scrypt = scrypt; - this.transferTokensConstructor = transferTokensConstructor; - } + public TransferTokensTest( + UInt256 startAmt, + UInt256 transferAmount, + ConstraintScrypt scrypt, + ActionConstructor transferTokensConstructor) { + this.startAmt = startAmt; + this.transferAmt = transferAmount; + this.scrypt = scrypt; + this.transferTokensConstructor = transferTokensConstructor; + } - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(scrypt); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(TransferToken.class, transferTokensConstructor) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .build(), - cm, - store - ); - var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(scrypt); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(TransferToken.class, transferTokensConstructor) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .build(), + cm, + store); + var genesis = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(genesis), null, PermissionLevel.SYSTEM); + } - @Test - public void cannot_transfer_others_tokens() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) - .action(new MintToken(tokenAddr, accountAddr, startAmt)) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); + @Test + public void cannot_transfer_others_tokens() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .action(new MintToken(tokenAddr, accountAddr, startAmt))) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); - // Act - var nextKey = ECKeyPair.generateNew(); - var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); - var transfer = this.engine.construct(new TransferToken(tokenAddr, accountAddr, to, transferAmt)) - .signAndBuild(nextKey::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) - .hasRootCauseInstanceOf(AuthorizationException.class); - } + // Act + var nextKey = ECKeyPair.generateNew(); + var to = REAddr.ofPubKeyAccount(nextKey.getPublicKey()); + var transfer = + this.engine + .construct(new TransferToken(tokenAddr, accountAddr, to, transferAmt)) + .signAndBuild(nextKey::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(transfer))) + .hasRootCauseInstanceOf(AuthorizationException.class); + } - @Test - public void transfer_tokens() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); - var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new CreateMutableToken(tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) - .action(new MintToken(tokenAddr, accountAddr, startAmt)) - ).signAndBuild(key::sign); - this.engine.execute(List.of(txn)); + @Test + public void transfer_tokens() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + var accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var tokenAddr = REAddr.ofHashedKey(key.getPublicKey(), "test"); + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action( + new CreateMutableToken( + tokenAddr, "test", "Name", "", "", "", key.getPublicKey())) + .action(new MintToken(tokenAddr, accountAddr, startAmt))) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); - // Act - var to = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); - var transfer = this.engine.construct(new TransferToken(tokenAddr, accountAddr, to, transferAmt)) - .signAndBuild(key::sign); - var result = this.engine.execute(List.of(transfer)); + // Act + var to = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); + var transfer = + this.engine + .construct(new TransferToken(tokenAddr, accountAddr, to, transferAmt)) + .signAndBuild(key::sign); + var result = this.engine.execute(List.of(transfer)); - // Assert - var accounting = REResourceAccounting.compute( - result.getProcessedTxn().getGroupedStateUpdates().get(0).stream() - ); - assertThat(accounting.bucketAccounting()) - .hasSize(2) - .containsEntry( - AccountBucket.from(tokenAddr, accountAddr), - new BigInteger(-1, transferAmt.toByteArray(), 0, UInt256.BYTES) - ) - .containsEntry( - AccountBucket.from(tokenAddr, to), - new BigInteger(1, transferAmt.toByteArray(), 0, UInt256.BYTES) - ); - assertThat(accounting.resourceAccounting()).isEmpty(); - } + // Assert + var accounting = + REResourceAccounting.compute( + result.getProcessedTxn().getGroupedStateUpdates().get(0).stream()); + assertThat(accounting.bucketAccounting()) + .hasSize(2) + .containsEntry( + AccountBucket.from(tokenAddr, accountAddr), + new BigInteger(-1, transferAmt.toByteArray(), 0, UInt256.BYTES)) + .containsEntry( + AccountBucket.from(tokenAddr, to), + new BigInteger(1, transferAmt.toByteArray(), 0, UInt256.BYTES)); + assertThat(accounting.resourceAccounting()).isEmpty(); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/UnstakeTokensV2Test.java b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/UnstakeTokensV2Test.java index 06736dc6b4..2038abf11c 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/UnstakeTokensV2Test.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/tokens/UnstakeTokensV2Test.java @@ -64,8 +64,24 @@ package com.radixdlt.application.tokens; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.radixdlt.application.system.construction.CreateSystemConstructorV2; +import com.radixdlt.application.system.construction.NextEpochConstructorV3; +import com.radixdlt.application.system.construction.NextViewConstructorV3; +import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; +import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; +import com.radixdlt.application.tokens.construction.MintTokenConstructor; +import com.radixdlt.application.tokens.construction.StakeTokensConstructorV3; +import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; +import com.radixdlt.application.tokens.construction.UnstakeOwnershipConstructor; +import com.radixdlt.application.tokens.scrypt.StakingConstraintScryptV4; +import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; import com.radixdlt.application.validators.construction.RegisterValidatorConstructor; +import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; +import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; import com.radixdlt.application.validators.scrypt.ValidatorUpdateOwnerConstraintScrypt; import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; import com.radixdlt.atom.ActionConstructor; @@ -76,31 +92,17 @@ import com.radixdlt.atom.actions.CreateMutableToken; import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.MintToken; -import com.radixdlt.atom.actions.RegisterValidator; -import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atom.actions.NextEpoch; import com.radixdlt.atom.actions.NextRound; +import com.radixdlt.atom.actions.RegisterValidator; +import com.radixdlt.atom.actions.StakeTokens; import com.radixdlt.atom.actions.TransferToken; import com.radixdlt.atom.actions.UnstakeOwnership; -import com.radixdlt.application.system.construction.CreateSystemConstructorV2; -import com.radixdlt.application.system.construction.NextEpochConstructorV3; -import com.radixdlt.application.system.construction.NextViewConstructorV3; -import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; -import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; -import com.radixdlt.application.tokens.construction.CreateMutableTokenConstructor; -import com.radixdlt.application.tokens.construction.MintTokenConstructor; -import com.radixdlt.application.tokens.construction.StakeTokensConstructorV3; -import com.radixdlt.application.tokens.construction.TransferTokensConstructorV2; -import com.radixdlt.application.tokens.construction.UnstakeOwnershipConstructor; -import com.radixdlt.application.tokens.scrypt.StakingConstraintScryptV4; -import com.radixdlt.application.tokens.scrypt.TokensConstraintScryptV3; -import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; -import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.atomos.ConstraintScrypt; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.parser.REParser; @@ -108,245 +110,246 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class UnstakeTokensV2Test { - @Parameterized.Parameters - public static Collection parameters() { - return List.of(new Object[][] { - { - List.of(10), - 10, - List.of( - new RoundUpdateConstraintScrypt(10), - new EpochUpdateConstraintScrypt( - 10, Amount.ofTokens(10).toSubunits(), 9800, - 1, 10 - ), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new ValidatorConstraintScryptV2(), - new ValidatorRegisterConstraintScrypt(), - new ValidatorUpdateRakeConstraintScrypt(2), - new ValidatorUpdateOwnerConstraintScrypt() - ), - new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), - new UnstakeOwnershipConstructor() - }, - { - List.of(5, 5), - 10, - List.of( - new RoundUpdateConstraintScrypt(10), - new EpochUpdateConstraintScrypt( - 10, Amount.ofTokens(10).toSubunits(), 9800, - 1, 10 - ), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new ValidatorConstraintScryptV2(), - new ValidatorRegisterConstraintScrypt(), - new ValidatorUpdateRakeConstraintScrypt(2), - new ValidatorUpdateOwnerConstraintScrypt() - ), - new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), - new UnstakeOwnershipConstructor() - }, - { - List.of(10), - 6, - List.of( - new RoundUpdateConstraintScrypt(10), - new EpochUpdateConstraintScrypt( - 10, Amount.ofTokens(10).toSubunits(), 9800, - 1, 10 - ), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new ValidatorConstraintScryptV2(), - new ValidatorRegisterConstraintScrypt(), - new ValidatorUpdateRakeConstraintScrypt(2), - new ValidatorUpdateOwnerConstraintScrypt() - ), - new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), - new UnstakeOwnershipConstructor() - }, - { - List.of(5, 5), - 6, - List.of( - new RoundUpdateConstraintScrypt(10), - new EpochUpdateConstraintScrypt( - 10, Amount.ofTokens(10).toSubunits(), 9800, - 1, 10 - ), - new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), - new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), - new ValidatorConstraintScryptV2(), - new ValidatorRegisterConstraintScrypt(), - new ValidatorUpdateRakeConstraintScrypt(2), - new ValidatorUpdateOwnerConstraintScrypt() - ), - new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), - new UnstakeOwnershipConstructor() - }, - }); - } + @Parameterized.Parameters + public static Collection parameters() { + return List.of( + new Object[][] { + { + List.of(10), + 10, + List.of( + new RoundUpdateConstraintScrypt(10), + new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new ValidatorConstraintScryptV2(), + new ValidatorRegisterConstraintScrypt(), + new ValidatorUpdateRakeConstraintScrypt(2), + new ValidatorUpdateOwnerConstraintScrypt()), + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), + new UnstakeOwnershipConstructor() + }, + { + List.of(5, 5), + 10, + List.of( + new RoundUpdateConstraintScrypt(10), + new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new ValidatorConstraintScryptV2(), + new ValidatorRegisterConstraintScrypt(), + new ValidatorUpdateRakeConstraintScrypt(2), + new ValidatorUpdateOwnerConstraintScrypt()), + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), + new UnstakeOwnershipConstructor() + }, + { + List.of(10), + 6, + List.of( + new RoundUpdateConstraintScrypt(10), + new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new ValidatorConstraintScryptV2(), + new ValidatorRegisterConstraintScrypt(), + new ValidatorUpdateRakeConstraintScrypt(2), + new ValidatorUpdateOwnerConstraintScrypt()), + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), + new UnstakeOwnershipConstructor() + }, + { + List.of(5, 5), + 6, + List.of( + new RoundUpdateConstraintScrypt(10), + new EpochUpdateConstraintScrypt(10, Amount.ofTokens(10).toSubunits(), 9800, 1, 10), + new TokensConstraintScryptV3(Set.of(), Pattern.compile("[a-z0-9]+")), + new StakingConstraintScryptV4(Amount.ofTokens(10).toSubunits()), + new ValidatorConstraintScryptV2(), + new ValidatorRegisterConstraintScrypt(), + new ValidatorUpdateRakeConstraintScrypt(2), + new ValidatorUpdateOwnerConstraintScrypt()), + new StakeTokensConstructorV3(Amount.ofTokens(10).toSubunits()), + new UnstakeOwnershipConstructor() + }, + }); + } - private ECKeyPair key; - private REAddr accountAddr; - private RadixEngine sut; - private EngineStore store; - private final List stakes; - private final UInt256 totalStakes; - private final UInt256 unstakeAmt; - private final List scrypts; - private final ActionConstructor stakeTokensConstructor; - private final ActionConstructor unstakeTokensConstructor; + private ECKeyPair key; + private REAddr accountAddr; + private RadixEngine sut; + private EngineStore store; + private final List stakes; + private final UInt256 totalStakes; + private final UInt256 unstakeAmt; + private final List scrypts; + private final ActionConstructor stakeTokensConstructor; + private final ActionConstructor unstakeTokensConstructor; - public UnstakeTokensV2Test( - List stakes, - int unstakeAmt, - List scrypts, - ActionConstructor stakeTokensConstructor, - ActionConstructor unstakeTokensConstructor - ) { - this.stakes = stakes.stream().map(i -> Amount.ofTokens(i * 10).toSubunits()).collect(Collectors.toList()); - this.totalStakes = this.stakes.stream().reduce(UInt256::add).orElseThrow(); - this.unstakeAmt = Amount.ofTokens(unstakeAmt * 10L).toSubunits(); - this.scrypts = scrypts; - this.stakeTokensConstructor = stakeTokensConstructor; - this.unstakeTokensConstructor = unstakeTokensConstructor; - } + public UnstakeTokensV2Test( + List stakes, + int unstakeAmt, + List scrypts, + ActionConstructor stakeTokensConstructor, + ActionConstructor unstakeTokensConstructor) { + this.stakes = + stakes.stream().map(i -> Amount.ofTokens(i * 10).toSubunits()).collect(Collectors.toList()); + this.totalStakes = this.stakes.stream().reduce(UInt256::add).orElseThrow(); + this.unstakeAmt = Amount.ofTokens(unstakeAmt * 10L).toSubunits(); + this.scrypts = scrypts; + this.stakeTokensConstructor = stakeTokensConstructor; + this.unstakeTokensConstructor = unstakeTokensConstructor; + } - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - scrypts.forEach(cmAtomOS::load); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - var serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.sut = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(NextRound.class, new NextViewConstructorV3()) - .put(NextEpoch.class, new NextEpochConstructorV3( - Amount.ofTokens(10).toSubunits(), 9800, 1, 10 - )) - .put(StakeTokens.class, stakeTokensConstructor) - .put(UnstakeOwnership.class, unstakeTokensConstructor) - .put(CreateMutableToken.class, new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) - .put(MintToken.class, new MintTokenConstructor()) - .put(TransferToken.class, new TransferTokensConstructorV2()) - .put(RegisterValidator.class, new RegisterValidatorConstructor()) - .build(), - cm, - store - ); + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + scrypts.forEach(cmAtomOS::load); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + var serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.sut = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(NextRound.class, new NextViewConstructorV3()) + .put( + NextEpoch.class, + new NextEpochConstructorV3(Amount.ofTokens(10).toSubunits(), 9800, 1, 10)) + .put(StakeTokens.class, stakeTokensConstructor) + .put(UnstakeOwnership.class, unstakeTokensConstructor) + .put( + CreateMutableToken.class, + new CreateMutableTokenConstructor(SystemConstraintScrypt.MAX_SYMBOL_LENGTH)) + .put(MintToken.class, new MintTokenConstructor()) + .put(TransferToken.class, new TransferTokensConstructorV2()) + .put(RegisterValidator.class, new RegisterValidatorConstructor()) + .build(), + cm, + store); - this.key = ECKeyPair.generateNew(); - this.accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); - var txn = this.sut.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - .action(new CreateMutableToken(REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) - .action(new MintToken(REAddr.ofNativeToken(), accountAddr, totalStakes)) - ).buildWithoutSignature(); - this.sut.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + this.key = ECKeyPair.generateNew(); + this.accountAddr = REAddr.ofPubKeyAccount(key.getPublicKey()); + var txn = + this.sut + .construct( + TxnConstructionRequest.create() + .action(new CreateSystem(0)) + .action( + new CreateMutableToken( + REAddr.ofNativeToken(), "xrd", "Name", "", "", "", null)) + .action(new MintToken(REAddr.ofNativeToken(), accountAddr, totalStakes))) + .buildWithoutSignature(); + this.sut.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void unstake_tokens_after_epoch() throws Exception { - // Arrange - var stakeActions = this.stakes.stream() - .map(amt -> new StakeTokens(accountAddr, key.getPublicKey(), amt)) - .collect(Collectors.toList()); - var stake = this.sut.construct( - TxnConstructionRequest.create().actions(stakeActions) - ).signAndBuild(key::sign); - this.sut.execute(List.of(stake)); - var nextEpoch = sut.construct(new NextEpoch(1)) - .buildWithoutSignature(); - this.sut.execute(List.of(nextEpoch), null, PermissionLevel.SUPER_USER); + @Test + public void unstake_tokens_after_epoch() throws Exception { + // Arrange + var stakeActions = + this.stakes.stream() + .map(amt -> new StakeTokens(accountAddr, key.getPublicKey(), amt)) + .collect(Collectors.toList()); + var stake = + this.sut + .construct(TxnConstructionRequest.create().actions(stakeActions)) + .signAndBuild(key::sign); + this.sut.execute(List.of(stake)); + var nextEpoch = sut.construct(new NextEpoch(1)).buildWithoutSignature(); + this.sut.execute(List.of(nextEpoch), null, PermissionLevel.SUPER_USER); - // Act - var unstake = this.sut.construct(new UnstakeOwnership(accountAddr, key.getPublicKey(), unstakeAmt)) - .signAndBuild(key::sign); - var parsed = this.sut.execute(List.of(unstake)); - } + // Act + var unstake = + this.sut + .construct(new UnstakeOwnership(accountAddr, key.getPublicKey(), unstakeAmt)) + .signAndBuild(key::sign); + var parsed = this.sut.execute(List.of(unstake)); + } - @Test - public void cannot_unstake_others_tokens() throws Exception { - // Arrange - var stakeActions = this.stakes.stream() - .map(amt -> new StakeTokens(accountAddr, key.getPublicKey(), amt)) - .collect(Collectors.toList()); - var stake = this.sut.construct( - TxnConstructionRequest.create().actions(stakeActions) - ).signAndBuild(key::sign); - this.sut.execute(List.of(stake)); - var nextEpoch = sut.construct(new NextEpoch(1)) - .buildWithoutSignature(); - this.sut.execute(List.of(nextEpoch), null, PermissionLevel.SUPER_USER); + @Test + public void cannot_unstake_others_tokens() throws Exception { + // Arrange + var stakeActions = + this.stakes.stream() + .map(amt -> new StakeTokens(accountAddr, key.getPublicKey(), amt)) + .collect(Collectors.toList()); + var stake = + this.sut + .construct(TxnConstructionRequest.create().actions(stakeActions)) + .signAndBuild(key::sign); + this.sut.execute(List.of(stake)); + var nextEpoch = sut.construct(new NextEpoch(1)).buildWithoutSignature(); + this.sut.execute(List.of(nextEpoch), null, PermissionLevel.SUPER_USER); - // Act - var nextKey = ECKeyPair.generateNew(); - var unstake = this.sut.construct(new UnstakeOwnership(accountAddr, key.getPublicKey(), unstakeAmt)) - .signAndBuild(nextKey::sign); + // Act + var nextKey = ECKeyPair.generateNew(); + var unstake = + this.sut + .construct(new UnstakeOwnership(accountAddr, key.getPublicKey(), unstakeAmt)) + .signAndBuild(nextKey::sign); - assertThatThrownBy(() -> this.sut.execute(List.of(unstake))) - .hasRootCauseInstanceOf(AuthorizationException.class); - } + assertThatThrownBy(() -> this.sut.execute(List.of(unstake))) + .hasRootCauseInstanceOf(AuthorizationException.class); + } - @Test - public void cant_construct_transfer_with_unstaked_tokens_immediately() throws Exception { - // Arrange - var acct2 = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); - var stakeActions = this.stakes.stream() - .map(amt -> new StakeTokens(accountAddr, key.getPublicKey(), amt)) - .collect(Collectors.toList()); - var txn = sut.construct( - TxnConstructionRequest.create() - .actions(stakeActions) - .action(new RegisterValidator(key.getPublicKey())) - ).signAndBuild(key::sign); - sut.execute(List.of(txn)); - var nextEpoch = sut.construct(new NextEpoch(1)) - .buildWithoutSignature(); - this.sut.execute(List.of(nextEpoch), null, PermissionLevel.SUPER_USER); - var unstake = this.sut.construct(new UnstakeOwnership(accountAddr, key.getPublicKey(), unstakeAmt)) - .signAndBuild(key::sign); - sut.execute(List.of(unstake)); - var request = TxnConstructionRequest.create() - .action(new NextRound(10, true, 1, u -> key.getPublicKey())) - .action(new NextEpoch(1)); - var nextEpoch2 = sut.construct(request).buildWithoutSignature(); - this.sut.execute(List.of(nextEpoch2), null, PermissionLevel.SUPER_USER); + @Test + public void cant_construct_transfer_with_unstaked_tokens_immediately() throws Exception { + // Arrange + var acct2 = REAddr.ofPubKeyAccount(ECKeyPair.generateNew().getPublicKey()); + var stakeActions = + this.stakes.stream() + .map(amt -> new StakeTokens(accountAddr, key.getPublicKey(), amt)) + .collect(Collectors.toList()); + var txn = + sut.construct( + TxnConstructionRequest.create() + .actions(stakeActions) + .action(new RegisterValidator(key.getPublicKey()))) + .signAndBuild(key::sign); + sut.execute(List.of(txn)); + var nextEpoch = sut.construct(new NextEpoch(1)).buildWithoutSignature(); + this.sut.execute(List.of(nextEpoch), null, PermissionLevel.SUPER_USER); + var unstake = + this.sut + .construct(new UnstakeOwnership(accountAddr, key.getPublicKey(), unstakeAmt)) + .signAndBuild(key::sign); + sut.execute(List.of(unstake)); + var request = + TxnConstructionRequest.create() + .action(new NextRound(10, true, 1, u -> key.getPublicKey())) + .action(new NextEpoch(1)); + var nextEpoch2 = sut.construct(request).buildWithoutSignature(); + this.sut.execute(List.of(nextEpoch2), null, PermissionLevel.SUPER_USER); - // Act - // Assert - assertThatThrownBy(() -> sut.construct(new TransferToken(REAddr.ofNativeToken(), accountAddr, acct2, unstakeAmt))) - .isInstanceOf(TxBuilderException.class); - } + // Act + // Assert + assertThatThrownBy( + () -> + sut.construct( + new TransferToken(REAddr.ofNativeToken(), accountAddr, acct2, unstakeAmt))) + .isInstanceOf(TxBuilderException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/unique/UniqueTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/unique/UniqueTest.java index e8b72fd167..f65697ee50 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/unique/UniqueTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/unique/UniqueTest.java @@ -64,90 +64,90 @@ package com.radixdlt.application.unique; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.Syscall; +import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.unique.scrypt.MutexConstraintScrypt; +import com.radixdlt.atom.REConstructor; import com.radixdlt.atom.SubstateId; import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.Txn; -import com.radixdlt.constraintmachine.exceptions.InvalidHashedKeyException; -import com.radixdlt.engine.RadixEngine; -import com.radixdlt.application.system.construction.CreateSystemConstructorV2; -import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; -import com.radixdlt.atom.REConstructor; -import com.radixdlt.application.unique.scrypt.MutexConstraintScrypt; import com.radixdlt.atom.TxnConstructionRequest; import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atomos.CMAtomOS; import com.radixdlt.constraintmachine.ConstraintMachine; import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.SubstateSerialization; +import com.radixdlt.constraintmachine.exceptions.InvalidHashedKeyException; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.parser.REParser; import com.radixdlt.identifiers.REAddr; import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; -import org.junit.Before; -import org.junit.Test; - import java.nio.charset.StandardCharsets; import java.util.List; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Before; +import org.junit.Test; public class UniqueTest { - private ECKeyPair keyPair = ECKeyPair.generateNew(); - private RadixEngine sut; - private EngineStore store; - private REParser parser; - private SubstateSerialization serialization; - private Txn genesis; + private ECKeyPair keyPair = ECKeyPair.generateNew(); + private RadixEngine sut; + private EngineStore store; + private REParser parser; + private SubstateSerialization serialization; + private Txn genesis; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new MutexConstraintScrypt()); - cmAtomOS.load(new SystemConstraintScrypt()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - this.parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - this.serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.sut = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .build(), - cm, - store - ); - this.genesis = this.sut.construct( - TxnConstructionRequest.create() - .action(new CreateSystem(0)) - ).buildWithoutSignature(); - this.sut.execute(List.of(genesis), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new MutexConstraintScrypt()); + cmAtomOS.load(new SystemConstraintScrypt()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + this.parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + this.serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.sut = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .build(), + cm, + store); + this.genesis = + this.sut + .construct(TxnConstructionRequest.create().action(new CreateSystem(0))) + .buildWithoutSignature(); + this.sut.execute(List.of(genesis), null, PermissionLevel.SYSTEM); + } - @Test - public void using_own_mutex_should_work() throws Exception { - var txn = this.sut.construct(b -> b.mutex(keyPair.getPublicKey(), "np")) - .signAndBuild(keyPair::sign); - this.sut.execute(List.of(txn)); - } + @Test + public void using_own_mutex_should_work() throws Exception { + var txn = + this.sut.construct(b -> b.mutex(keyPair.getPublicKey(), "np")).signAndBuild(keyPair::sign); + this.sut.execute(List.of(txn)); + } - @Test - public void using_someone_elses_mutex_should_fail() throws Exception { - var addr = REAddr.ofHashedKey(ECKeyPair.generateNew().getPublicKey(), "smthng"); - var builder = TxBuilder.newBuilder(parser.getSubstateDeserialization(), serialization) - .toLowLevelBuilder() - .syscall(Syscall.READDR_CLAIM, "smthng".getBytes(StandardCharsets.UTF_8)) - .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) - .end(); - var sig = keyPair.sign(builder.hashToSign()); - var txn = builder.sig(sig).build(); - assertThatThrownBy(() -> this.sut.execute(List.of(txn))) - .hasRootCauseInstanceOf(InvalidHashedKeyException.class); - } + @Test + public void using_someone_elses_mutex_should_fail() throws Exception { + var addr = REAddr.ofHashedKey(ECKeyPair.generateNew().getPublicKey(), "smthng"); + var builder = + TxBuilder.newBuilder(parser.getSubstateDeserialization(), serialization) + .toLowLevelBuilder() + .syscall(Syscall.READDR_CLAIM, "smthng".getBytes(StandardCharsets.UTF_8)) + .virtualDown(SubstateId.ofSubstate(genesis.getId(), 0), addr.getBytes()) + .end(); + var sig = keyPair.sign(builder.hashToSign()); + var txn = builder.sig(sig).build(); + assertThatThrownBy(() -> this.sut.execute(List.of(txn))) + .hasRootCauseInstanceOf(InvalidHashedKeyException.class); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/RegisterValidatorTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/RegisterValidatorTest.java index cfbd447054..be95b7601c 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/RegisterValidatorTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/RegisterValidatorTest.java @@ -64,115 +64,118 @@ package com.radixdlt.application.validators; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.radixdlt.application.system.construction.CreateSystemConstructorV2; import com.radixdlt.application.system.scrypt.EpochUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.RoundUpdateConstraintScrypt; import com.radixdlt.application.system.scrypt.SystemConstraintScrypt; +import com.radixdlt.application.validators.construction.RegisterValidatorConstructor; import com.radixdlt.application.validators.construction.UpdateRakeConstructor; import com.radixdlt.application.validators.construction.UpdateValidatorMetadataConstructor; +import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; +import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; import com.radixdlt.application.validators.scrypt.ValidatorUpdateRakeConstraintScrypt; import com.radixdlt.atom.REConstructor; import com.radixdlt.atom.TxnConstructionRequest; import com.radixdlt.atom.actions.CreateSystem; import com.radixdlt.atom.actions.RegisterValidator; -import com.radixdlt.application.validators.construction.RegisterValidatorConstructor; -import com.radixdlt.application.validators.scrypt.ValidatorConstraintScryptV2; -import com.radixdlt.application.validators.scrypt.ValidatorRegisterConstraintScrypt; import com.radixdlt.atom.actions.UpdateValidatorFee; import com.radixdlt.atom.actions.UpdateValidatorMetadata; import com.radixdlt.atomos.CMAtomOS; -import com.radixdlt.constraintmachine.PermissionLevel; -import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.constraintmachine.ConstraintMachine; +import com.radixdlt.constraintmachine.PermissionLevel; import com.radixdlt.constraintmachine.SubstateSerialization; +import com.radixdlt.constraintmachine.exceptions.AuthorizationException; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.engine.RadixEngine; import com.radixdlt.engine.parser.REParser; import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; +import java.util.List; import org.junit.Before; import org.junit.Test; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - public class RegisterValidatorTest { - private RadixEngine engine; - private EngineStore store; - private SubstateSerialization serialization; - - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(new RoundUpdateConstraintScrypt(2)); - cmAtomOS.load(new EpochUpdateConstraintScrypt(2, UInt256.NINE, 1, 1, 100)); - cmAtomOS.load(new ValidatorConstraintScryptV2()); - cmAtomOS.load(new ValidatorRegisterConstraintScrypt()); - cmAtomOS.load(new ValidatorUpdateRakeConstraintScrypt(2)); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - this.serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(RegisterValidator.class, new RegisterValidatorConstructor()) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(UpdateValidatorMetadata.class, new UpdateValidatorMetadataConstructor()) - .put(UpdateValidatorFee.class, new UpdateRakeConstructor(2, 2000)) - .build(), - cm, - store - ); - var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + private RadixEngine engine; + private EngineStore store; + private SubstateSerialization serialization; - @Test - public void register_validator() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(new RoundUpdateConstraintScrypt(2)); + cmAtomOS.load(new EpochUpdateConstraintScrypt(2, UInt256.NINE, 1, 1, 100)); + cmAtomOS.load(new ValidatorConstraintScryptV2()); + cmAtomOS.load(new ValidatorRegisterConstraintScrypt()); + cmAtomOS.load(new ValidatorUpdateRakeConstraintScrypt(2)); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + this.serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(RegisterValidator.class, new RegisterValidatorConstructor()) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put(UpdateValidatorMetadata.class, new UpdateValidatorMetadataConstructor()) + .put(UpdateValidatorFee.class, new UpdateRakeConstructor(2, 2000)) + .build(), + cm, + store); + var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - // Act and Assert - var registerTxn = this.engine.construct(new RegisterValidator(key.getPublicKey())) - .signAndBuild(key::sign); - this.engine.execute(List.of(registerTxn)); - } + @Test + public void register_validator() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); - @Test - public void register_other_validator_should_fail() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); + // Act and Assert + var registerTxn = + this.engine.construct(new RegisterValidator(key.getPublicKey())).signAndBuild(key::sign); + this.engine.execute(List.of(registerTxn)); + } - // Act and Assert - var registerTxn = this.engine.construct(new RegisterValidator(ECKeyPair.generateNew().getPublicKey())) - .signAndBuild(key::sign); - assertThatThrownBy(() -> this.engine.execute(List.of(registerTxn))) - .hasRootCauseInstanceOf(AuthorizationException.class); - } + @Test + public void register_other_validator_should_fail() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); + // Act and Assert + var registerTxn = + this.engine + .construct(new RegisterValidator(ECKeyPair.generateNew().getPublicKey())) + .signAndBuild(key::sign); + assertThatThrownBy(() -> this.engine.execute(List.of(registerTxn))) + .hasRootCauseInstanceOf(AuthorizationException.class); + } - @Test - public void multiple_validator_actions() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); + @Test + public void multiple_validator_actions() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); - // Act and Assert - var txn = this.engine.construct( - TxnConstructionRequest.create() - .action(new RegisterValidator(key.getPublicKey())) - .action(new UpdateValidatorMetadata(key.getPublicKey(), "some_name", "http://test.com")) - .action(new UpdateValidatorFee(key.getPublicKey(), 2000)) - ) - .signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - } + // Act and Assert + var txn = + this.engine + .construct( + TxnConstructionRequest.create() + .action(new RegisterValidator(key.getPublicKey())) + .action( + new UpdateValidatorMetadata( + key.getPublicKey(), "some_name", "http://test.com")) + .action(new UpdateValidatorFee(key.getPublicKey(), 2000))) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorFeeTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorFeeTest.java index 649300df83..ab736cdbf9 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorFeeTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorFeeTest.java @@ -84,54 +84,58 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; +import java.util.List; import org.junit.Before; import org.junit.Test; -import java.util.List; - public class UpdateValidatorFeeTest { - private RadixEngine engine; - private EngineStore store; - private SubstateSerialization serialization; + private RadixEngine engine; + private EngineStore store; + private SubstateSerialization serialization; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(new RoundUpdateConstraintScrypt(1)); - cmAtomOS.load(new EpochUpdateConstraintScrypt(1, UInt256.TEN, 0, 1, 100)); - cmAtomOS.load(new ValidatorConstraintScryptV2()); - cmAtomOS.load(new ValidatorUpdateRakeConstraintScrypt(2)); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - this.serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .put(UpdateValidatorFee.class, new UpdateRakeConstructor(2, ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE)) - .build(), - cm, - store - ); - var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(new RoundUpdateConstraintScrypt(1)); + cmAtomOS.load(new EpochUpdateConstraintScrypt(1, UInt256.TEN, 0, 1, 100)); + cmAtomOS.load(new ValidatorConstraintScryptV2()); + cmAtomOS.load(new ValidatorUpdateRakeConstraintScrypt(2)); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + this.serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .put( + UpdateValidatorFee.class, + new UpdateRakeConstructor( + 2, ValidatorUpdateRakeConstraintScrypt.MAX_RAKE_INCREASE)) + .build(), + cm, + store); + var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void update_rake() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); + @Test + public void update_rake() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); - // Act and Assert - var registerTxn = this.engine.construct(new UpdateValidatorFee(key.getPublicKey(), 100)) - .signAndBuild(key::sign); - this.engine.execute(List.of(registerTxn)); - } + // Act and Assert + var registerTxn = + this.engine + .construct(new UpdateValidatorFee(key.getPublicKey(), 100)) + .signAndBuild(key::sign); + this.engine.execute(List.of(registerTxn)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorMetadataTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorMetadataTest.java index 6e123eb0a7..f44eaa4045 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorMetadataTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorMetadataTest.java @@ -83,53 +83,54 @@ import com.radixdlt.store.EngineStore; import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.UInt256; +import java.util.List; import org.junit.Before; import org.junit.Test; -import java.util.List; - public class UpdateValidatorMetadataTest { - private RadixEngine engine; - private EngineStore store; - private SubstateSerialization serialization; + private RadixEngine engine; + private EngineStore store; + private SubstateSerialization serialization; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(new RoundUpdateConstraintScrypt(2)); - cmAtomOS.load(new EpochUpdateConstraintScrypt(2, UInt256.NINE, 1, 1, 100)); - cmAtomOS.load(new ValidatorConstraintScryptV2()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - this.serialization = cmAtomOS.buildSubstateSerialization(); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - serialization, - REConstructor.newBuilder() - .put(UpdateValidatorMetadata.class, new UpdateValidatorMetadataConstructor()) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .build(), - cm, - store - ); - var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(new RoundUpdateConstraintScrypt(2)); + cmAtomOS.load(new EpochUpdateConstraintScrypt(2, UInt256.NINE, 1, 1, 100)); + cmAtomOS.load(new ValidatorConstraintScryptV2()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + this.serialization = cmAtomOS.buildSubstateSerialization(); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + serialization, + REConstructor.newBuilder() + .put(UpdateValidatorMetadata.class, new UpdateValidatorMetadataConstructor()) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .build(), + cm, + store); + var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void update_validator_metadata() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); + @Test + public void update_validator_metadata() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); - // Act and Assert - var registerTxn = this.engine.construct(new UpdateValidatorMetadata(key.getPublicKey(), "name", "http://test.com")) - .signAndBuild(key::sign); - this.engine.execute(List.of(registerTxn)); - } + // Act and Assert + var registerTxn = + this.engine + .construct(new UpdateValidatorMetadata(key.getPublicKey(), "name", "http://test.com")) + .signAndBuild(key::sign); + this.engine.execute(List.of(registerTxn)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorSystemMetadataTest.java b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorSystemMetadataTest.java index 0f90c47a77..cb3bfcbb1a 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorSystemMetadataTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/application/validators/UpdateValidatorSystemMetadataTest.java @@ -84,52 +84,55 @@ import com.radixdlt.store.InMemoryEngineStore; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.UInt256; +import java.util.List; import org.junit.Before; import org.junit.Test; -import java.util.List; - public class UpdateValidatorSystemMetadataTest { - private RadixEngine engine; - private EngineStore store; + private RadixEngine engine; + private EngineStore store; - @Before - public void setup() throws Exception { - var cmAtomOS = new CMAtomOS(); - cmAtomOS.load(new SystemConstraintScrypt()); - cmAtomOS.load(new RoundUpdateConstraintScrypt(2)); - cmAtomOS.load(new EpochUpdateConstraintScrypt(2, UInt256.NINE, 1, 1, 100)); - cmAtomOS.load(new ValidatorConstraintScryptV2()); - var cm = new ConstraintMachine( - cmAtomOS.getProcedures(), - cmAtomOS.buildSubstateDeserialization(), - cmAtomOS.buildVirtualSubstateDeserialization() - ); - var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); - this.store = new InMemoryEngineStore<>(); - this.engine = new RadixEngine<>( - parser, - cmAtomOS.buildSubstateSerialization(), - REConstructor.newBuilder() - .put(UpdateValidatorSystemMetadata.class, new UpdateValidatorSystemMetadataConstructor()) - .put(CreateSystem.class, new CreateSystemConstructorV2()) - .build(), - cm, - store - ); - var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); - this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); - } + @Before + public void setup() throws Exception { + var cmAtomOS = new CMAtomOS(); + cmAtomOS.load(new SystemConstraintScrypt()); + cmAtomOS.load(new RoundUpdateConstraintScrypt(2)); + cmAtomOS.load(new EpochUpdateConstraintScrypt(2, UInt256.NINE, 1, 1, 100)); + cmAtomOS.load(new ValidatorConstraintScryptV2()); + var cm = + new ConstraintMachine( + cmAtomOS.getProcedures(), + cmAtomOS.buildSubstateDeserialization(), + cmAtomOS.buildVirtualSubstateDeserialization()); + var parser = new REParser(cmAtomOS.buildSubstateDeserialization()); + this.store = new InMemoryEngineStore<>(); + this.engine = + new RadixEngine<>( + parser, + cmAtomOS.buildSubstateSerialization(), + REConstructor.newBuilder() + .put( + UpdateValidatorSystemMetadata.class, + new UpdateValidatorSystemMetadataConstructor()) + .put(CreateSystem.class, new CreateSystemConstructorV2()) + .build(), + cm, + store); + var txn = this.engine.construct(new CreateSystem(0)).buildWithoutSignature(); + this.engine.execute(List.of(txn), null, PermissionLevel.SYSTEM); + } - @Test - public void update_validator_metadata() throws Exception { - // Arrange - var key = ECKeyPair.generateNew(); + @Test + public void update_validator_metadata() throws Exception { + // Arrange + var key = ECKeyPair.generateNew(); - var bytes = Bytes.fromHexString(Strings.repeat("0c", 32)); - // Act and Assert - var txn = this.engine.construct(new UpdateValidatorSystemMetadata(key.getPublicKey(), bytes)) - .signAndBuild(key::sign); - this.engine.execute(List.of(txn)); - } + var bytes = Bytes.fromHexString(Strings.repeat("0c", 32)); + // Act and Assert + var txn = + this.engine + .construct(new UpdateValidatorSystemMetadata(key.getPublicKey(), bytes)) + .signAndBuild(key::sign); + this.engine.execute(List.of(txn)); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/atom/LocalSubstateTest.java b/radixdlt-engine/src/test/java/com/radixdlt/atom/LocalSubstateTest.java index 5110f94bba..08d33b8fb7 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/atom/LocalSubstateTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/atom/LocalSubstateTest.java @@ -71,10 +71,10 @@ public class LocalSubstateTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(LocalSubstate.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(LocalSubstate.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-engine/src/test/java/com/radixdlt/atom/SubstateIdTest.java b/radixdlt-engine/src/test/java/com/radixdlt/atom/SubstateIdTest.java index 2e83869308..3d0be0f124 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/atom/SubstateIdTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/atom/SubstateIdTest.java @@ -71,10 +71,10 @@ public class SubstateIdTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(SubstateId.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsContract() { + EqualsVerifier.forClass(SubstateId.class) + .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) + .verify(); + } +} diff --git a/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/OpSignatureTest.java b/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/OpSignatureTest.java index e4f3e2b952..45e780efab 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/OpSignatureTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/OpSignatureTest.java @@ -69,10 +69,8 @@ import org.junit.Test; public class OpSignatureTest { - @Test - public void equalsVerifier() { - EqualsVerifier.forClass(OpSignature.class) - .suppress(Warning.NONFINAL_FIELDS) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsVerifier() { + EqualsVerifier.forClass(OpSignature.class).suppress(Warning.NONFINAL_FIELDS).verify(); + } +} diff --git a/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/ProcedureKeyTest.java b/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/ProcedureKeyTest.java index 2fedb60d6f..9db676b16f 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/ProcedureKeyTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/ProcedureKeyTest.java @@ -69,10 +69,8 @@ import org.junit.Test; public class ProcedureKeyTest { - @Test - public void equalsVerifier() { - EqualsVerifier.forClass(ProcedureKey.class) - .suppress(Warning.NONFINAL_FIELDS) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsVerifier() { + EqualsVerifier.forClass(ProcedureKey.class).suppress(Warning.NONFINAL_FIELDS).verify(); + } +} diff --git a/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/SubstateIndexTest.java b/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/SubstateIndexTest.java index 83b9203e2e..0af8846654 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/SubstateIndexTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/constraintmachine/SubstateIndexTest.java @@ -69,10 +69,8 @@ import org.junit.Test; public class SubstateIndexTest { - @Test - public void equalsVerifier() { - EqualsVerifier.forClass(SubstateIndex.class) - .suppress(Warning.NONFINAL_FIELDS) - .verify(); - } -} \ No newline at end of file + @Test + public void equalsVerifier() { + EqualsVerifier.forClass(SubstateIndex.class).suppress(Warning.NONFINAL_FIELDS).verify(); + } +} diff --git a/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java b/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java index ea7e528cb7..3977d2cc18 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java @@ -64,44 +64,44 @@ package com.radixdlt.serialization; +import static org.junit.Assert.fail; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; - import java.io.IOException; -import static org.junit.Assert.fail; - /** - * N.B. this was copied (duplication :/ ) over from `radixdlt-java-common`, - * we should remove this code duplication in the future, here is Jira ticket - * * relating to this task. + * N.B. this was copied (duplication :/ ) over from `radixdlt-java-common`, we should remove this + * code duplication in the future, here is + * Jira ticket * relating to this task. * - * Utilities for test encoding and decoding with serializers. + *

Utilities for test encoding and decoding with serializers. */ final class SerializationTestUtilsEngine { - private SerializationTestUtilsEngine() { - throw new IllegalStateException("Can't construct"); - } + private SerializationTestUtilsEngine() { + throw new IllegalStateException("Can't construct"); + } - static void testEncodeDecode(T target, Class cls, Serialization serialization, DsonOutput.Output output) - throws Exception { - String json1 = serialization.toJson(target, output); - T newTarget = serialization.fromJson(json1, cls); - String json2 = serialization.toJson(newTarget, output); - compareJson(json1, json2); - } + static void testEncodeDecode( + T target, Class cls, Serialization serialization, DsonOutput.Output output) + throws Exception { + String json1 = serialization.toJson(target, output); + T newTarget = serialization.fromJson(json1, cls); + String json2 = serialization.toJson(newTarget, output); + compareJson(json1, json2); + } - static void compareJson(String s1Json, String s2Json) throws IOException { - ObjectMapper om = new ObjectMapper(); - om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); - om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); - JsonNode s1Tree = om.readTree(s1Json); - JsonNode s2Tree = om.readTree(s2Json); - if (!s1Tree.equals(s2Tree)) { - fail("Not equivalent JSON"); - } - } + static void compareJson(String s1Json, String s2Json) throws IOException { + ObjectMapper om = new ObjectMapper(); + om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); + JsonNode s1Tree = om.readTree(s1Json); + JsonNode s2Tree = om.readTree(s2Json); + if (!s1Tree.equals(s2Tree)) { + fail("Not equivalent JSON"); + } + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java b/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java index 8debfaaa81..2ff03e4a8a 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java @@ -64,71 +64,68 @@ package com.radixdlt.serialization; -import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; -import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; -import org.junit.Test; - -import java.util.function.Supplier; - import static com.radixdlt.serialization.SerializationTestUtilsEngine.testEncodeDecode; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeFalse; +import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; +import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; +import java.util.function.Supplier; +import org.junit.Test; + /** - * N.B. this was copied (duplication :/ ) over from `radixdlt-java-common`, - * we should remove this code duplication in the future, here is Jira ticket - * * relating to this task. + * N.B. this was copied (duplication :/ ) over from `radixdlt-java-common`, we should remove this + * code duplication in the future, here is + * Jira ticket * relating to this task. + * + *

Raft of tests for serialization of objects. * - * Raft of tests for serialization of objects. - *

- * Note that the tests that round-trip types through the serializer - * are not run for {@link Polymorphic} types, as these types do not - * serialize to themselves, but to one of their superclasses. + *

Note that the tests that round-trip types through the serializer are not run for {@link + * Polymorphic} types, as these types do not serialize to themselves, but to one of their + * superclasses. * * @param The type under test. */ public abstract class SerializeObjectEngine { - private final Class cls; - private final Supplier factory; - private final Serialization serialization = Serialization.create( - ClasspathScanningSerializerIds.create(), - ClasspathScanningSerializationPolicy.create() - ); - + private final Class cls; + private final Supplier factory; + private final Serialization serialization = + Serialization.create( + ClasspathScanningSerializerIds.create(), ClasspathScanningSerializationPolicy.create()); - protected SerializeObjectEngine(Class cls, Supplier factory) { - this.cls = cls; - this.factory = factory; - } + protected SerializeObjectEngine(Class cls, Supplier factory) { + this.cls = cls; + this.factory = factory; + } - @Test - public void from_engine___testNONEIsEmpty() throws Exception { - String s2Json = this.serialization.toJson(factory.get(), DsonOutput.Output.NONE); - assertEquals("{}", s2Json); - } + @Test + public void from_engine___testNONEIsEmpty() throws Exception { + String s2Json = this.serialization.toJson(factory.get(), DsonOutput.Output.NONE); + assertEquals("{}", s2Json); + } - @Test - public void from_engine___testEncodeDecodeALL() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.ALL); - } + @Test + public void from_engine___testEncodeDecodeALL() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.ALL); + } - @Test - public void from_engine___testEncodeDecodeAPI() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.API); - } + @Test + public void from_engine___testEncodeDecodeAPI() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.API); + } - @Test - public void from_engine___testEncodeDecodePERSIST() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.PERSIST); - } + @Test + public void from_engine___testEncodeDecodePERSIST() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.PERSIST); + } - @Test - public void from_engine___testEncodeDecodeWIRE() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.WIRE); - } + @Test + public void from_engine___testEncodeDecodeWIRE() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.WIRE); + } } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/test/utils/TypedMocks.java b/radixdlt-engine/src/test/java/com/radixdlt/test/utils/TypedMocks.java index 65d090ce18..a09d1d8d4c 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/test/utils/TypedMocks.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/test/utils/TypedMocks.java @@ -66,28 +66,25 @@ import static org.mockito.Mockito.mock; -/** - * Typed mock wrappers to avoid compiler warnings where they are not - * warranted. - */ +/** Typed mock wrappers to avoid compiler warnings where they are not warranted. */ public final class TypedMocks { - private TypedMocks() { - throw new IllegalStateException("Can't construct"); - } + private TypedMocks() { + throw new IllegalStateException("Can't construct"); + } - /** - * Runtime checked typed mock, primarily for types with type arguments. - * - * @param The type of the mock without type arguments, eg {@code List} - * @param The type of the mock with type arguments, eg {@code List} - * @param cls The raw class for the mocked type - * @return the mock, cast to type {@code U}. If {@code U} is not assignable - * from {@code T}, then a {@code ClassCastException} will occur. - */ - public static U rmock(Class cls) { - @SuppressWarnings("unchecked") - U value = (U) mock(cls); - return value; - } + /** + * Runtime checked typed mock, primarily for types with type arguments. + * + * @param The type of the mock without type arguments, eg {@code List} + * @param The type of the mock with type arguments, eg {@code List} + * @param cls The raw class for the mocked type + * @return the mock, cast to type {@code U}. If {@code U} is not assignable from {@code T}, then a + * {@code ClassCastException} will occur. + */ + public static U rmock(Class cls) { + @SuppressWarnings("unchecked") + U value = (U) mock(cls); + return value; + } } diff --git a/radixdlt-java-common/CHANGELOG.md b/radixdlt-java-common/CHANGELOG.md index 94b5717c70..a4688a81b9 100644 --- a/radixdlt-java-common/CHANGELOG.md +++ b/radixdlt-java-common/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog All notable changes to this project will be documented in this file. - + This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - + ## [Unreleased](#) ## [1.0.0-beta.2] - 2019-08-28 diff --git a/radixdlt-java-common/README.md b/radixdlt-java-common/README.md index 9765653550..d5e2f217f3 100644 --- a/radixdlt-java-common/README.md +++ b/radixdlt-java-common/README.md @@ -1,5 +1,5 @@ # radixdlt-java-common -Java common library with utilities shared by all our Java libraries, such as: -* Serialization -* Hashing -* Crypto +Java common library with utilities shared by all our Java libraries, such as: +* Serialization +* Hashing +* Crypto diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/SecurityCritical.java b/radixdlt-java-common/src/main/java/com/radixdlt/SecurityCritical.java index ac0c8a32f0..2a2176e248 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/SecurityCritical.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/SecurityCritical.java @@ -69,63 +69,40 @@ import java.lang.annotation.Target; /** - * Annotation to be used for classes which require additional scrutiny - * in code review. These classes will be mostly be low level classes whose - * correctness is critical for security. + * Annotation to be used for classes which require additional scrutiny in code review. These classes + * will be mostly be low level classes whose correctness is critical for security. */ @Documented @Target(ElementType.TYPE) public @interface SecurityCritical { - /** - * Returns the kinds of security critical things this annotation is flagging. - * - * @return The kinds of security critical things this annotation is flagging. - */ - SecurityKind[] value(); + /** + * Returns the kinds of security critical things this annotation is flagging. + * + * @return The kinds of security critical things this annotation is flagging. + */ + SecurityKind[] value(); - /** - * Kinds of security critical things that can be flagged. - */ - enum SecurityKind { - /** - * A general security kind, where no other kinds apply. - */ - GENERAL, - /** - * Code calculates or compares hashes. - */ - HASHING, - /** - * Code stores private keys. - */ - KEY_STORE, - /** - * Code generates private keys or computes public keys. - */ - KEY_GENERATION, - /** - * Numeric calculations affecting system security. - */ - NUMERIC, - /** - * Code performs public-key or hybrid decryption, and therefore uses private keys. - */ - PK_DECRYPT, - /** - * Code performs public-key or hybrid encryption. - */ - PK_ENCRYPT, - /** - * Code that ensures randomness is suitable for cryptographic use. - */ - RANDOMNESS, - /** - * Code performs signing, and therefore uses private keys. - */ - SIG_SIGN, - /** - * Code performs signature verification. - */ - SIG_VERIFY, - } + /** Kinds of security critical things that can be flagged. */ + enum SecurityKind { + /** A general security kind, where no other kinds apply. */ + GENERAL, + /** Code calculates or compares hashes. */ + HASHING, + /** Code stores private keys. */ + KEY_STORE, + /** Code generates private keys or computes public keys. */ + KEY_GENERATION, + /** Numeric calculations affecting system security. */ + NUMERIC, + /** Code performs public-key or hybrid decryption, and therefore uses private keys. */ + PK_DECRYPT, + /** Code performs public-key or hybrid encryption. */ + PK_ENCRYPT, + /** Code that ensures randomness is suitable for cryptographic use. */ + RANDOMNESS, + /** Code performs signing, and therefore uses private keys. */ + SIG_SIGN, + /** Code performs signature verification. */ + SIG_VERIFY, + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/application/TokenUnitConversions.java b/radixdlt-java-common/src/main/java/com/radixdlt/application/TokenUnitConversions.java index c110b13c04..4787a48e23 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/application/TokenUnitConversions.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/application/TokenUnitConversions.java @@ -64,135 +64,151 @@ package com.radixdlt.application; -import java.math.BigDecimal; -import java.math.BigInteger; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt256s; import com.radixdlt.utils.UInt384; +import java.math.BigDecimal; +import java.math.BigInteger; -/** - * Utility class for converting token units between UInt256 and BigDecimal - */ +/** Utility class for converting token units between UInt256 and BigDecimal */ public final class TokenUnitConversions { - private TokenUnitConversions() { - throw new IllegalStateException("Not initializable"); - } - - /** - * Number of subunits in a unit as a power of 10, currently {@value #SUB_UNITS_POW_10}. - * In other words, the total number of subunits per unit is 10{@code SUB_UNITS_POW_10}. - */ - private static final int SUB_UNITS_POW_10 = 18; - - /** - * Number of subunits per unit. - * @see #SUB_UNITS_POW_10 - */ - public static final UInt256 SUB_UNITS = UInt256.TEN.pow(SUB_UNITS_POW_10); - - private static final BigDecimal SUB_UNITS_BIG_DECIMAL = new BigDecimal(UInt256s.toBigInteger(SUB_UNITS)); - - private static final BigDecimal MINIMUM_GRANULARITY_BIG_DECIMAL = BigDecimal.ONE.scaleByPowerOfTen(-1 * SUB_UNITS_POW_10); - - public static int getTokenScale() { - return SUB_UNITS_POW_10; - } - - public static BigDecimal getSubunits() { - return SUB_UNITS_BIG_DECIMAL; - } - - public static BigDecimal getMinimumGranularity() { - return MINIMUM_GRANULARITY_BIG_DECIMAL; - } - - /** - * Returns the specified number of subunits as a fractional number - * of units. This method effectively calculates: - *

- * subunits × 10-SUB_UNITS_POW_10 - *
- * - * @param subunits The number of subunits to convert to fractional units - * @return The number of fractional units represented by {@code subunits} - * @see #SUB_UNITS_POW_10 - */ - public static BigDecimal subunitsToUnits(UInt256 subunits) { - return subunitsToUnits(UInt256s.toBigInteger(subunits)); - } - - public static BigDecimal subunitsToUnits(UInt384 subunits) { - var b = new BigInteger(1, subunits.toByteArray()); - return subunitsToUnits(b); - } - - /** - * Returns the specified number of subunits as a fractional number - * of units. This method effectively calculates: - *
- * subunits × 10-SUB_UNITS_POW_10 - *
- * - * @param subunits The number of subunits to convert to fractional units - * @return The number of fractional units represented by {@code subunits} - * @see #SUB_UNITS_POW_10 - */ - public static BigDecimal subunitsToUnits(BigInteger subunits) { - return new BigDecimal(subunits, SUB_UNITS_POW_10); - } - - /** - * Returns the specified number of subunits as a fractional number - * of units. This method effectively calculates: - *
- * subunits × 10-SUB_UNITS_POW_10 - *
- * - * @param subunits The number of subunits to convert to fractional units - * @return The number of fractional units represented by {@code subunits} - * @see #SUB_UNITS_POW_10 - */ - public static BigDecimal subunitsToUnits(long subunits) { - return BigDecimal.valueOf(subunits, SUB_UNITS_POW_10); - } - - /** - * Returns the specified number of units as a {@link UInt256} number of - * of subunits. This method effectively calculates: - *
- * units × 10SUB_UNITS_POW_10 - *
- * - * @param units The number of units to convert to subunits - * @return The integer number of subunits represented by {@code units} - * @throws IllegalArgumentException if {@code units} is less than zero - * @see #SUB_UNITS_POW_10 - */ - public static UInt256 unitsToSubunits(long units) { - if (units < 0) { - throw new IllegalArgumentException("units must be >= 0: " + units); - } - // 10^18 is approximately 60 bits, so a positive long (63 bits) cannot overflow here - return UInt256.from(units).multiply(SUB_UNITS); - } - - /** - * Returns the specified number of units as a {@link UInt256} number of - * of subunits. This method effectively calculates: - *
- * units × 10SUB_UNITS_POW_10 - *
- * - * @param units The number of units to convert to subunits - * @return The integer number of subunits represented by {@code units} - * @throws IllegalArgumentException if {@code units} is less than zero - * or greater than {@link UInt256#MAX_VALUE} - * @throws ArithmeticException if {@code units} × - * 10SUB_UNITS_POW_10 has a nonzero fractional part. - * @see #SUB_UNITS_POW_10 - */ - public static UInt256 unitsToSubunits(BigDecimal units) { - return UInt256s.fromBigDecimal(units.multiply(SUB_UNITS_BIG_DECIMAL)); - } + private TokenUnitConversions() { + throw new IllegalStateException("Not initializable"); + } + + /** + * Number of subunits in a unit as a power of 10, currently {@value #SUB_UNITS_POW_10}. In other + * words, the total number of subunits per unit is 10{@code SUB_UNITS_POW_10}. + */ + private static final int SUB_UNITS_POW_10 = 18; + + /** + * Number of subunits per unit. + * + * @see #SUB_UNITS_POW_10 + */ + public static final UInt256 SUB_UNITS = UInt256.TEN.pow(SUB_UNITS_POW_10); + + private static final BigDecimal SUB_UNITS_BIG_DECIMAL = + new BigDecimal(UInt256s.toBigInteger(SUB_UNITS)); + + private static final BigDecimal MINIMUM_GRANULARITY_BIG_DECIMAL = + BigDecimal.ONE.scaleByPowerOfTen(-1 * SUB_UNITS_POW_10); + + public static int getTokenScale() { + return SUB_UNITS_POW_10; + } + + public static BigDecimal getSubunits() { + return SUB_UNITS_BIG_DECIMAL; + } + + public static BigDecimal getMinimumGranularity() { + return MINIMUM_GRANULARITY_BIG_DECIMAL; + } + + /** + * Returns the specified number of subunits as a fractional number of units. This method + * effectively calculates: + * + *
+ * + * subunits × 10-SUB_UNITS_POW_10 + * + *
+ * + * @param subunits The number of subunits to convert to fractional units + * @return The number of fractional units represented by {@code subunits} + * @see #SUB_UNITS_POW_10 + */ + public static BigDecimal subunitsToUnits(UInt256 subunits) { + return subunitsToUnits(UInt256s.toBigInteger(subunits)); + } + + public static BigDecimal subunitsToUnits(UInt384 subunits) { + var b = new BigInteger(1, subunits.toByteArray()); + return subunitsToUnits(b); + } + + /** + * Returns the specified number of subunits as a fractional number of units. This method + * effectively calculates: + * + *
+ * + * subunits × 10-SUB_UNITS_POW_10 + * + *
+ * + * @param subunits The number of subunits to convert to fractional units + * @return The number of fractional units represented by {@code subunits} + * @see #SUB_UNITS_POW_10 + */ + public static BigDecimal subunitsToUnits(BigInteger subunits) { + return new BigDecimal(subunits, SUB_UNITS_POW_10); + } + + /** + * Returns the specified number of subunits as a fractional number of units. This method + * effectively calculates: + * + *
+ * + * subunits × 10-SUB_UNITS_POW_10 + * + *
+ * + * @param subunits The number of subunits to convert to fractional units + * @return The number of fractional units represented by {@code subunits} + * @see #SUB_UNITS_POW_10 + */ + public static BigDecimal subunitsToUnits(long subunits) { + return BigDecimal.valueOf(subunits, SUB_UNITS_POW_10); + } + + /** + * Returns the specified number of units as a {@link UInt256} number of of subunits. This method + * effectively calculates: + * + *
+ * + * units × 10SUB_UNITS_POW_10 + * + *
+ * + * @param units The number of units to convert to subunits + * @return The integer number of subunits represented by {@code units} + * @throws IllegalArgumentException if {@code units} is less than zero + * @see #SUB_UNITS_POW_10 + */ + public static UInt256 unitsToSubunits(long units) { + if (units < 0) { + throw new IllegalArgumentException("units must be >= 0: " + units); + } + // 10^18 is approximately 60 bits, so a positive long (63 bits) cannot overflow here + return UInt256.from(units).multiply(SUB_UNITS); + } + + /** + * Returns the specified number of units as a {@link UInt256} number of of subunits. This method + * effectively calculates: + * + *
+ * + * units × 10SUB_UNITS_POW_10 + * + *
+ * + * @param units The number of units to convert to subunits + * @return The integer number of subunits represented by {@code units} + * @throws IllegalArgumentException if {@code units} is less than zero or greater than {@link + * UInt256#MAX_VALUE} + * @throws ArithmeticException if {@code units} × 10SUB_UNITS_POW_10 has a + * nonzero fractional part. + * @see #SUB_UNITS_POW_10 + */ + public static UInt256 unitsToSubunits(BigDecimal units) { + return UInt256s.fromBigDecimal(units.multiply(SUB_UNITS_BIG_DECIMAL)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/BouncyCastleKeyHandler.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/BouncyCastleKeyHandler.java index 1b6caae0e3..d7a99f3e22 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/BouncyCastleKeyHandler.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/BouncyCastleKeyHandler.java @@ -64,6 +64,12 @@ package com.radixdlt.crypto; +import com.radixdlt.SecurityCritical; +import com.radixdlt.SecurityCritical.SecurityKind; +import com.radixdlt.crypto.exception.PrivateKeyException; +import com.radixdlt.crypto.exception.PublicKeyException; +import java.math.BigInteger; +import java.security.KeyFactory; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.params.ECDomainParameters; @@ -75,75 +81,76 @@ import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.jce.spec.ECPublicKeySpec; - -import com.radixdlt.SecurityCritical; -import com.radixdlt.SecurityCritical.SecurityKind; -import com.radixdlt.crypto.exception.PrivateKeyException; -import com.radixdlt.crypto.exception.PublicKeyException; import org.bouncycastle.math.ec.ECPoint; -import java.math.BigInteger; -import java.security.KeyFactory; - @SecurityCritical({SecurityKind.KEY_GENERATION, SecurityKind.SIG_SIGN, SecurityKind.SIG_VERIFY}) final class BouncyCastleKeyHandler implements KeyHandler { - private final BigInteger curveOrder; - private final BigInteger halfCurveOrder; - private final ECDomainParameters domain; - private final ECParameterSpec spec; - - BouncyCastleKeyHandler(X9ECParameters curve) { - this.curveOrder = curve.getN(); - this.halfCurveOrder = curve.getN().shiftRight(1); - this.domain = new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); - this.spec = new ECParameterSpec(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); - } - - @Override - public ECDSASignature sign(byte[] hash, byte[] privateKey, byte[] publicKey, boolean enforceLowS, boolean useDeterministicSignatures) { - var signer = new ECDSASigner(useDeterministicSignatures - ? new HMacDSAKCalculator(new SHA256Digest()) - : new RandomDSAKCalculator()); - - signer.init(true, new ECPrivateKeyParameters(new BigInteger(1, privateKey), domain)); - - var components = signer.generateSignature(hash); - var r = components[0]; - var s = components[1]; - - if (enforceLowS) { - s = s.compareTo(this.halfCurveOrder) <= 0 ? s : curveOrder.subtract(s); - } - - return ECDSASignature.create(r, s, ECKeyUtils.calculateV(r, s, publicKey, hash)); - } - - @Override - public boolean verify(byte[] hash, ECDSASignature signature, ECPoint publicKeyPoint) { - var verifier = new ECDSASigner(); - - verifier.init(false, new ECPublicKeyParameters(publicKeyPoint, domain)); - - return verifier.verifySignature(hash, signature.getR(), signature.getS()); - } - - @Override - public byte[] computePublicKey(byte[] privateKey) throws PrivateKeyException, PublicKeyException { - ECKeyUtils.validatePrivate(privateKey); - - var d = new BigInteger(1, privateKey); - - try { - var publicKeySpec = new ECPublicKeySpec(spec.getG().multiply(d), spec); - - // Note that the provider here *must* be "BC" for this to work - // correctly because we are using the bouncy castle ECPublicKeySpec, - // and are casting to a bouncy castle ECPublicKey. - return ((ECPublicKey) KeyFactory.getInstance("EC", "BC").generatePublic(publicKeySpec)) - .getQ().getEncoded(true); - - } catch (Exception e) { - throw new PublicKeyException(e); - } - } + private final BigInteger curveOrder; + private final BigInteger halfCurveOrder; + private final ECDomainParameters domain; + private final ECParameterSpec spec; + + BouncyCastleKeyHandler(X9ECParameters curve) { + this.curveOrder = curve.getN(); + this.halfCurveOrder = curve.getN().shiftRight(1); + this.domain = + new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); + this.spec = new ECParameterSpec(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); + } + + @Override + public ECDSASignature sign( + byte[] hash, + byte[] privateKey, + byte[] publicKey, + boolean enforceLowS, + boolean useDeterministicSignatures) { + var signer = + new ECDSASigner( + useDeterministicSignatures + ? new HMacDSAKCalculator(new SHA256Digest()) + : new RandomDSAKCalculator()); + + signer.init(true, new ECPrivateKeyParameters(new BigInteger(1, privateKey), domain)); + + var components = signer.generateSignature(hash); + var r = components[0]; + var s = components[1]; + + if (enforceLowS) { + s = s.compareTo(this.halfCurveOrder) <= 0 ? s : curveOrder.subtract(s); + } + + return ECDSASignature.create(r, s, ECKeyUtils.calculateV(r, s, publicKey, hash)); + } + + @Override + public boolean verify(byte[] hash, ECDSASignature signature, ECPoint publicKeyPoint) { + var verifier = new ECDSASigner(); + + verifier.init(false, new ECPublicKeyParameters(publicKeyPoint, domain)); + + return verifier.verifySignature(hash, signature.getR(), signature.getS()); + } + + @Override + public byte[] computePublicKey(byte[] privateKey) throws PrivateKeyException, PublicKeyException { + ECKeyUtils.validatePrivate(privateKey); + + var d = new BigInteger(1, privateKey); + + try { + var publicKeySpec = new ECPublicKeySpec(spec.getG().multiply(d), spec); + + // Note that the provider here *must* be "BC" for this to work + // correctly because we are using the bouncy castle ECPublicKeySpec, + // and are casting to a bouncy castle ECPublicKey. + return ((ECPublicKey) KeyFactory.getInstance("EC", "BC").generatePublic(publicKeySpec)) + .getQ() + .getEncoded(true); + + } catch (Exception e) { + throw new PublicKeyException(e); + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ConcatKDFBytesGenerator.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ConcatKDFBytesGenerator.java index 465d3446c3..1bbfb809ff 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ConcatKDFBytesGenerator.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ConcatKDFBytesGenerator.java @@ -72,94 +72,93 @@ import org.bouncycastle.crypto.params.KDFParameters; import org.bouncycastle.util.Pack; -/** - * Basic KDF generator for derived keys and ivs as defined by NIST SP 800-56A. - */ +/** Basic KDF generator for derived keys and ivs as defined by NIST SP 800-56A. */ public class ConcatKDFBytesGenerator implements DigestDerivationFunction { - private int counterStart; - private Digest digest; - private byte[] shared; - private byte[] iv; - - protected ConcatKDFBytesGenerator(int counterStart, Digest digest) { - this.counterStart = counterStart; - this.digest = digest; + private int counterStart; + private Digest digest; + private byte[] shared; + private byte[] iv; + + protected ConcatKDFBytesGenerator(int counterStart, Digest digest) { + this.counterStart = counterStart; + this.digest = digest; + } + + public ConcatKDFBytesGenerator(Digest digest) { + this(1, digest); + } + + public void init(DerivationParameters param) { + if (param instanceof KDFParameters) { + final var p = (KDFParameters) param; + + shared = p.getSharedSecret(); + iv = p.getIV(); + } else if (param instanceof ISO18033KDFParameters) { + final var p = (ISO18033KDFParameters) param; + + shared = p.getSeed(); + iv = null; + } else { + throw new IllegalArgumentException("KDF parameters required for KDF2Generator"); } + } - public ConcatKDFBytesGenerator(Digest digest) { - this(1, digest); - } + public Digest getDigest() { + return digest; + } - public void init(DerivationParameters param) { - if (param instanceof KDFParameters) { - final var p = (KDFParameters) param; + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException { + if ((out.length - len) < outOff) { + throw new DataLengthException("output buffer too small"); + } - shared = p.getSharedSecret(); - iv = p.getIV(); - } else if (param instanceof ISO18033KDFParameters) { - final var p = (ISO18033KDFParameters) param; + long oBytes = len; + int outLen = digest.getDigestSize(); - shared = p.getSeed(); - iv = null; - } else { - throw new IllegalArgumentException("KDF parameters required for KDF2Generator"); - } + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) { + throw new IllegalArgumentException("Output length too large"); } - public Digest getDigest() { - return digest; - } + final var cThreshold = (int) ((oBytes + outLen - 1) / outLen); + final var dig = new byte[digest.getDigestSize()]; + final var c = new byte[4]; + Pack.intToBigEndian(counterStart, c, 0); + + int counterBase = counterStart & ~0xFF; + + for (int i = 0; i < cThreshold; i++) { + digest.update(c, 0, c.length); + digest.update(shared, 0, shared.length); - public int generateBytes(byte[] out, int outOff, int len) throws DataLengthException, IllegalArgumentException { - if ((out.length - len) < outOff) { - throw new DataLengthException("output buffer too small"); - } - - long oBytes = len; - int outLen = digest.getDigestSize(); - - // this is at odds with the standard implementation, the - // maximum value should be hBits * (2^32 - 1) where hBits - // is the digest output size in bits. We can't have an - // array with a long index at the moment... - // - if (oBytes > ((2L << 32) - 1)) { - throw new IllegalArgumentException("Output length too large"); - } - - final var cThreshold = (int) ((oBytes + outLen - 1) / outLen); - final var dig = new byte[digest.getDigestSize()]; - final var c = new byte[4]; - Pack.intToBigEndian(counterStart, c, 0); - - int counterBase = counterStart & ~0xFF; - - for (int i = 0; i < cThreshold; i++) { - digest.update(c, 0, c.length); - digest.update(shared, 0, shared.length); - - if (iv != null) { - digest.update(iv, 0, iv.length); - } - - digest.doFinal(dig, 0); - - if (len > outLen) { - System.arraycopy(dig, 0, out, outOff, outLen); - outOff += outLen; - len -= outLen; - } else { - System.arraycopy(dig, 0, out, outOff, len); - } - - if (++c[3] == 0) { - counterBase += 0x100; - Pack.intToBigEndian(counterBase, c, 0); - } - } - - digest.reset(); - - return (int) oBytes; + if (iv != null) { + digest.update(iv, 0, iv.length); + } + + digest.doFinal(dig, 0); + + if (len > outLen) { + System.arraycopy(dig, 0, out, outOff, outLen); + outOff += outLen; + len -= outLen; + } else { + System.arraycopy(dig, 0, out, outOff, len); + } + + if (++c[3] == 0) { + counterBase += 0x100; + Pack.intToBigEndian(counterBase, c, 0); + } } + + digest.reset(); + + return (int) oBytes; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECDSASignature.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECDSASignature.java index c8e2463296..6aabfc46fa 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECDSASignature.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECDSASignature.java @@ -1,208 +1,208 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; -import com.radixdlt.utils.Bytes; -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.DLSequence; -import org.bouncycastle.util.encoders.Hex; - -import java.io.IOException; -import java.math.BigInteger; -import java.util.Objects; - -import static java.util.Objects.requireNonNull; - -/** - * An
ECDSA signature represented as - * a tuple of {@link BigInteger}s {@code (R, S)}/ - */ -@SerializerId2("sig") -public final class ECDSASignature { - // Placeholder for the serializer ID - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - /* The two components of the signature. */ - private final BigInteger r; - private final BigInteger s; - private final byte v; - - private static final ECDSASignature ZERO_SIGNATURE = new ECDSASignature(BigInteger.ZERO, BigInteger.ZERO, 0); - - private ECDSASignature(BigInteger r, BigInteger s, int v) { - this.r = requireNonNull(r); - this.s = requireNonNull(s); - this.v = ((v & 1) == 0 ? (byte) 0x00 : (byte) 0x01); - } - - @JsonCreator - public static ECDSASignature deserialize( - @JsonProperty(value = "r", required = true) byte[] r, - @JsonProperty(value = "s", required = true) byte[] s, - @JsonProperty(value = "v", required = true) int v - ) { - return create(new BigInteger(1, requireNonNull(r)), new BigInteger(1, requireNonNull(s)), v); - } - - /** - * Constructs a signature with the given components. Does NOT automatically canonicalise the signature. - */ - public static ECDSASignature create(BigInteger r, BigInteger s, int v) { - requireNonNull(r); - requireNonNull(s); - - return new ECDSASignature(r, s, v); - } - - public static ECDSASignature zeroSignature() { - return ZERO_SIGNATURE; - } - - public BigInteger getR() { - return r; - } - - public BigInteger getS() { - return s; - } - - public byte getV() { - return v; - } - - @JsonProperty("r") - @DsonOutput(Output.ALL) - private byte[] getJsonR() { - return Bytes.trimLeadingZeros(r.toByteArray()); - } - - @JsonProperty("s") - @DsonOutput(Output.ALL) - private byte[] getJsonS() { - return Bytes.trimLeadingZeros(s.toByteArray()); - } - - @JsonProperty("v") - @DsonOutput(Output.ALL) - private int getJsonV() { - return v; - } - - @Override - public String toString() { - return toHexString(); - } - - public String toHexString() { - return Bytes.toHexString(getJsonR()) + Bytes.toHexString(getJsonS()); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - return (o instanceof ECDSASignature signature) - && Objects.equals(r, signature.r) - && Objects.equals(s, signature.s) - && Objects.equals(v, signature.v); - } - - @Override - public int hashCode() { - return Objects.hash(r, s, v); - } - - //WARNING: Never ever use this method to restore recoverable signature! It misses 'v' bit necessary for recovery. - public static ECDSASignature decodeFromHexDer(String input) { - return decodeFromDER(Hex.decode(input)); - } - - public static ECDSASignature decodeFromDER(byte[] bytes) { - try (ASN1InputStream decoder = new ASN1InputStream(bytes)) { - var seq = (DLSequence) decoder.readObject(); - var r = (ASN1Integer) seq.getObjectAt(0); - var s = (ASN1Integer) seq.getObjectAt(1); - - return new ECDSASignature(r.getPositiveValue(), s.getPositiveValue(), 0); - } catch (IOException e) { - throw new IllegalArgumentException("Failed to read bytes as ASN1 decode bytes", e); - } catch (ClassCastException e) { - throw new IllegalArgumentException("Failed to cast to ASN1Integer", e); - } - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; +import com.radixdlt.utils.Bytes; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Objects; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.DLSequence; +import org.bouncycastle.util.encoders.Hex; + +/** + * An ECDSA + * signature represented as a tuple of {@link BigInteger}s {@code (R, S)}/ + */ +@SerializerId2("sig") +public final class ECDSASignature { + // Placeholder for the serializer ID + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + /* The two components of the signature. */ + private final BigInteger r; + private final BigInteger s; + private final byte v; + + private static final ECDSASignature ZERO_SIGNATURE = + new ECDSASignature(BigInteger.ZERO, BigInteger.ZERO, 0); + + private ECDSASignature(BigInteger r, BigInteger s, int v) { + this.r = requireNonNull(r); + this.s = requireNonNull(s); + this.v = ((v & 1) == 0 ? (byte) 0x00 : (byte) 0x01); + } + + @JsonCreator + public static ECDSASignature deserialize( + @JsonProperty(value = "r", required = true) byte[] r, + @JsonProperty(value = "s", required = true) byte[] s, + @JsonProperty(value = "v", required = true) int v) { + return create(new BigInteger(1, requireNonNull(r)), new BigInteger(1, requireNonNull(s)), v); + } + + /** + * Constructs a signature with the given components. Does NOT automatically canonicalise the + * signature. + */ + public static ECDSASignature create(BigInteger r, BigInteger s, int v) { + requireNonNull(r); + requireNonNull(s); + + return new ECDSASignature(r, s, v); + } + + public static ECDSASignature zeroSignature() { + return ZERO_SIGNATURE; + } + + public BigInteger getR() { + return r; + } + + public BigInteger getS() { + return s; + } + + public byte getV() { + return v; + } + + @JsonProperty("r") + @DsonOutput(Output.ALL) + private byte[] getJsonR() { + return Bytes.trimLeadingZeros(r.toByteArray()); + } + + @JsonProperty("s") + @DsonOutput(Output.ALL) + private byte[] getJsonS() { + return Bytes.trimLeadingZeros(s.toByteArray()); + } + + @JsonProperty("v") + @DsonOutput(Output.ALL) + private int getJsonV() { + return v; + } + + @Override + public String toString() { + return toHexString(); + } + + public String toHexString() { + return Bytes.toHexString(getJsonR()) + Bytes.toHexString(getJsonS()); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + return (o instanceof ECDSASignature signature) + && Objects.equals(r, signature.r) + && Objects.equals(s, signature.s) + && Objects.equals(v, signature.v); + } + + @Override + public int hashCode() { + return Objects.hash(r, s, v); + } + + // WARNING: Never ever use this method to restore recoverable signature! It misses 'v' bit + // necessary for recovery. + public static ECDSASignature decodeFromHexDer(String input) { + return decodeFromDER(Hex.decode(input)); + } + + public static ECDSASignature decodeFromDER(byte[] bytes) { + try (ASN1InputStream decoder = new ASN1InputStream(bytes)) { + var seq = (DLSequence) decoder.readObject(); + var r = (ASN1Integer) seq.getObjectAt(0); + var s = (ASN1Integer) seq.getObjectAt(1); + + return new ECDSASignature(r.getPositiveValue(), s.getPositiveValue(), 0); + } catch (IOException e) { + throw new IllegalArgumentException("Failed to read bytes as ASN1 decode bytes", e); + } catch (ClassCastException e) { + throw new IllegalArgumentException("Failed to cast to ASN1Integer", e); + } + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECIESCoder.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECIESCoder.java index a0e553a9a1..640f908731 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECIESCoder.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECIESCoder.java @@ -64,6 +64,11 @@ package com.radixdlt.crypto; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; @@ -79,118 +84,115 @@ import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.math.ec.ECPoint; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.security.SecureRandom; - public final class ECIESCoder { - public static final int KEY_SIZE = 128; - public static final int OVERHEAD_SIZE = 65 + KEY_SIZE / 8 + 32; - - private ECIESCoder() { - } - - public static byte[] decrypt(BigInteger privKey, byte[] cipher) throws IOException, InvalidCipherTextException { - return decrypt(privKey, cipher, null); - } - - public static byte[] decrypt(BigInteger privKey, byte[] cipher, byte[] macData) throws InvalidCipherTextException { - final var is = new ByteArrayInputStream(cipher); - try { - final var ephemBytes = new byte[2 * ((ECKeyUtils.domain().getCurve().getFieldSize() + 7) / 8) + 1]; - is.read(ephemBytes); - final var ephem = ECKeyUtils.domain().getCurve().decodePoint(ephemBytes); - final var iv = new byte[KEY_SIZE / 8]; - is.read(iv); - final var cipherBody = new byte[is.available()]; - is.read(cipherBody); - return decrypt(ephem, privKey, iv, cipherBody, macData); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static byte[] decrypt(ECPoint ephem, BigInteger prv, byte[] iv, byte[] cipher, byte[] macData) throws InvalidCipherTextException { - final var iesEngine = new IESEngine( - new ECDHBasicAgreement(), - new ConcatKDFBytesGenerator(new SHA256Digest()), - new HMac(new SHA256Digest()), - new SHA256Digest(), - new BufferedBlockCipher(new SICBlockCipher(new AESEngine())) - ); - - final var d = new byte[] {}; - final var e = new byte[] {}; - - final var p = new IESWithCipherParameters(d, e, KEY_SIZE, KEY_SIZE); - final var parametersWithIV = new ParametersWithIV(p, iv); - iesEngine.init( - false, - new ECPrivateKeyParameters(prv, ECKeyUtils.domain()), - new ECPublicKeyParameters(ephem, ECKeyUtils.domain()), - parametersWithIV - ); - - return iesEngine.processBlock(cipher, 0, cipher.length, macData); - } - - public static byte[] encrypt(ECPoint toPub, byte[] plaintext, byte[] macData) { - final var eGen = new ECKeyPairGenerator(); - final var random = new SecureRandom(); - final var gParam = new ECKeyGenerationParameters(ECKeyUtils.domain(), random); - - eGen.init(gParam); - - final var iv = new byte[KEY_SIZE / 8]; - new SecureRandom().nextBytes(iv); - - final var ephemPair = eGen.generateKeyPair(); - final var prv = ((ECPrivateKeyParameters) ephemPair.getPrivate()).getD(); - final var pub = ((ECPublicKeyParameters) ephemPair.getPublic()).getQ(); - final var iesEngine = makeIESEngine(true, toPub, prv, iv); - - final var keygenParams = new ECKeyGenerationParameters(ECKeyUtils.domain(), random); - final var generator = new ECKeyPairGenerator(); - generator.init(keygenParams); - - final var gen = new ECKeyPairGenerator(); - gen.init(new ECKeyGenerationParameters(ECKeyUtils.domain(), random)); - - byte[] cipher; - try { - cipher = iesEngine.processBlock(plaintext, 0, plaintext.length, macData); - final var bos = new ByteArrayOutputStream(); - bos.write(pub.getEncoded(false)); - bos.write(iv); - bos.write(cipher); - return bos.toByteArray(); - } catch (InvalidCipherTextException | IOException e) { - throw new RuntimeException(e); - } - } - - private static IESEngine makeIESEngine(boolean isEncrypt, ECPoint pub, BigInteger prv, byte[] iv) { - final var iesEngine = new IESEngine( + public static final int KEY_SIZE = 128; + public static final int OVERHEAD_SIZE = 65 + KEY_SIZE / 8 + 32; + + private ECIESCoder() {} + + public static byte[] decrypt(BigInteger privKey, byte[] cipher) + throws IOException, InvalidCipherTextException { + return decrypt(privKey, cipher, null); + } + + public static byte[] decrypt(BigInteger privKey, byte[] cipher, byte[] macData) + throws InvalidCipherTextException { + final var is = new ByteArrayInputStream(cipher); + try { + final var ephemBytes = + new byte[2 * ((ECKeyUtils.domain().getCurve().getFieldSize() + 7) / 8) + 1]; + is.read(ephemBytes); + final var ephem = ECKeyUtils.domain().getCurve().decodePoint(ephemBytes); + final var iv = new byte[KEY_SIZE / 8]; + is.read(iv); + final var cipherBody = new byte[is.available()]; + is.read(cipherBody); + return decrypt(ephem, privKey, iv, cipherBody, macData); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static byte[] decrypt( + ECPoint ephem, BigInteger prv, byte[] iv, byte[] cipher, byte[] macData) + throws InvalidCipherTextException { + final var iesEngine = + new IESEngine( + new ECDHBasicAgreement(), + new ConcatKDFBytesGenerator(new SHA256Digest()), + new HMac(new SHA256Digest()), + new SHA256Digest(), + new BufferedBlockCipher(new SICBlockCipher(new AESEngine()))); + + final var d = new byte[] {}; + final var e = new byte[] {}; + + final var p = new IESWithCipherParameters(d, e, KEY_SIZE, KEY_SIZE); + final var parametersWithIV = new ParametersWithIV(p, iv); + iesEngine.init( + false, + new ECPrivateKeyParameters(prv, ECKeyUtils.domain()), + new ECPublicKeyParameters(ephem, ECKeyUtils.domain()), + parametersWithIV); + + return iesEngine.processBlock(cipher, 0, cipher.length, macData); + } + + public static byte[] encrypt(ECPoint toPub, byte[] plaintext, byte[] macData) { + final var eGen = new ECKeyPairGenerator(); + final var random = new SecureRandom(); + final var gParam = new ECKeyGenerationParameters(ECKeyUtils.domain(), random); + + eGen.init(gParam); + + final var iv = new byte[KEY_SIZE / 8]; + new SecureRandom().nextBytes(iv); + + final var ephemPair = eGen.generateKeyPair(); + final var prv = ((ECPrivateKeyParameters) ephemPair.getPrivate()).getD(); + final var pub = ((ECPublicKeyParameters) ephemPair.getPublic()).getQ(); + final var iesEngine = makeIESEngine(true, toPub, prv, iv); + + final var keygenParams = new ECKeyGenerationParameters(ECKeyUtils.domain(), random); + final var generator = new ECKeyPairGenerator(); + generator.init(keygenParams); + + final var gen = new ECKeyPairGenerator(); + gen.init(new ECKeyGenerationParameters(ECKeyUtils.domain(), random)); + + byte[] cipher; + try { + cipher = iesEngine.processBlock(plaintext, 0, plaintext.length, macData); + final var bos = new ByteArrayOutputStream(); + bos.write(pub.getEncoded(false)); + bos.write(iv); + bos.write(cipher); + return bos.toByteArray(); + } catch (InvalidCipherTextException | IOException e) { + throw new RuntimeException(e); + } + } + + private static IESEngine makeIESEngine( + boolean isEncrypt, ECPoint pub, BigInteger prv, byte[] iv) { + final var iesEngine = + new IESEngine( new ECDHBasicAgreement(), new ConcatKDFBytesGenerator(new SHA256Digest()), new HMac(new SHA256Digest()), new SHA256Digest(), - new BufferedBlockCipher(new SICBlockCipher(new AESEngine())) - ); - - final var d = new byte[] {}; - final var e = new byte[] {}; - - final var p = new IESWithCipherParameters(d, e, KEY_SIZE, KEY_SIZE); - final var parametersWithIV = new ParametersWithIV(p, iv); - iesEngine.init( - isEncrypt, - new ECPrivateKeyParameters(prv, ECKeyUtils.domain()), - new ECPublicKeyParameters(pub, ECKeyUtils.domain()), - parametersWithIV - ); - return iesEngine; - } + new BufferedBlockCipher(new SICBlockCipher(new AESEngine()))); + + final var d = new byte[] {}; + final var e = new byte[] {}; + + final var p = new IESWithCipherParameters(d, e, KEY_SIZE, KEY_SIZE); + final var parametersWithIV = new ParametersWithIV(p, iv); + iesEngine.init( + isEncrypt, + new ECPrivateKeyParameters(prv, ECKeyUtils.domain()), + new ECPublicKeyParameters(pub, ECKeyUtils.domain()), + parametersWithIV); + return iesEngine; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyPair.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyPair.java index a1c66f48b7..d15cef1990 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyPair.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyPair.java @@ -64,157 +64,163 @@ package com.radixdlt.crypto; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.generators.ECKeyPairGenerator; -import org.bouncycastle.crypto.params.ECKeyGenerationParameters; -import org.bouncycastle.crypto.params.ECPrivateKeyParameters; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import org.bouncycastle.math.ec.ECPoint; - import com.radixdlt.SecurityCritical; import com.radixdlt.SecurityCritical.SecurityKind; import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.identifiers.EUID; - import java.math.BigInteger; import java.util.Arrays; import java.util.Objects; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.math.ec.ECPoint; -/** - * Asymmetric EC key pair provider fixed to curve 'secp256k1'. - */ -@SecurityCritical({ SecurityKind.KEY_GENERATION, SecurityKind.SIG_SIGN, SecurityKind.PK_DECRYPT, SecurityKind.PK_ENCRYPT }) +/** Asymmetric EC key pair provider fixed to curve 'secp256k1'. */ +@SecurityCritical({ + SecurityKind.KEY_GENERATION, + SecurityKind.SIG_SIGN, + SecurityKind.PK_DECRYPT, + SecurityKind.PK_ENCRYPT +}) public final class ECKeyPair implements Signing { - public static final int BYTES = 32; - - private final byte[] privateKey; - private final ECPublicKey publicKey; - - private ECKeyPair(final byte[] privateKey, final ECPublicKey publicKey) { - this.privateKey = privateKey; - this.publicKey = publicKey; - } - - /** - * Generates a new private and public key pair based on randomness. - * @return a newly generated private key and it's corresponding {@link ECPublicKey}. - */ - public static ECKeyPair generateNew() { - try { - ECKeyPairGenerator generator = new ECKeyPairGenerator(); - ECKeyGenerationParameters keygenParams = new ECKeyGenerationParameters(ECKeyUtils.domain(), ECKeyUtils.secureRandom()); - generator.init(keygenParams); - AsymmetricCipherKeyPair keypair = generator.generateKeyPair(); - ECPrivateKeyParameters privParams = (ECPrivateKeyParameters) keypair.getPrivate(); - ECPublicKeyParameters pubParams = (ECPublicKeyParameters) keypair.getPublic(); - - final ECPublicKey publicKey = ECPublicKey.fromEcPoint(pubParams.getQ()); - byte[] privateKeyBytes = ECKeyUtils.adjustArray(privParams.getD().toByteArray(), BYTES); - ECKeyUtils.validatePrivate(privateKeyBytes); - - return new ECKeyPair(privateKeyBytes, publicKey); - } catch (Exception e) { - throw new IllegalStateException("Failed to generate ECKeyPair", e); - } - } - - /** - * Generates a new, deterministic {@code ECKeyPair} instance by hashing the - * provided seed. - * - * @param seed The seed to use when deriving the key pair instance, that is hashed (256 bits). - * @return A key pair that corresponds to the hash of the provided seed. - * @throws IllegalArgumentException if the seed is empty. - */ - public static ECKeyPair fromSeed(byte[] seed) { - Objects.requireNonNull(seed, "Seed must not be null"); - - if (seed.length == 0) { - throw new IllegalArgumentException("Seed must not be empty"); - } - - try { - return fromPrivateKey(HashUtils.sha256(seed).asBytes()); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalStateException("Should always be able to create private key from seed", e); - } - } - - /** - * Restore {@link ECKeyPair} instance from given private key by computing corresponding public key. - * @param privateKey byte array which contains private key. - * @return A keypair for provided private key - * @throws PrivateKeyException if input byte array does not represent a private key - * @throws PublicKeyException if public key can't be computed for given private key - */ - public static ECKeyPair fromPrivateKey(byte[] privateKey) throws PrivateKeyException, PublicKeyException { - ECKeyUtils.validatePrivate(privateKey); - - return new ECKeyPair(privateKey, - ECPublicKey.fromBytes(ECKeyUtils.keyHandler.computePublicKey(privateKey))); - } - - public EUID euid() { - return publicKey.euid(); - } - - public byte[] getPrivateKey() { - return privateKey; - } - - public ECPublicKey getPublicKey() { - return publicKey; - } - // TODO move this to new class (yet to be created) `ECPrivateKey`. - - @Override - public ECPoint multiply(ECPoint point) { - BigInteger scalarFromPrivateKey = new BigInteger(1, privateKey); - return point.multiply(scalarFromPrivateKey).normalize(); - } - - @Override - public ECDSASignature sign(byte[] hash) { - return ECKeyUtils.keyHandler.sign(hash, privateKey, publicKey.getBytes()); - } - - /** - * Signs data using the ECPrivateKey resulting in an ECDSA signature. - * - * @param data The data to sign - * @param enforceLowS If signature should enforce low values of signature part `S`, according to - * BIP-62 - * @param beDeterministic If signing should use randomness or be deterministic according to - * RFC6979. - * @return An ECDSA Signature. - */ - public ECDSASignature sign(byte[] data, boolean enforceLowS, boolean beDeterministic) { - return ECKeyUtils.keyHandler.sign(data, privateKey, publicKey.getBytes(), enforceLowS, beDeterministic); - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof ECKeyPair) { - ECKeyPair other = (ECKeyPair) object; - // Comparing private keys should be sufficient - return Arrays.equals(other.getPrivateKey(), getPrivateKey()); - } - return false; - } - - @Override - public int hashCode() { - return Arrays.hashCode(getPrivateKey()); - } - - @Override - public String toString() { - // Not going to print the private key here - return String.format("%s[%s]", - getClass().getSimpleName(), getPublicKey().toHex()); - } + public static final int BYTES = 32; + + private final byte[] privateKey; + private final ECPublicKey publicKey; + + private ECKeyPair(final byte[] privateKey, final ECPublicKey publicKey) { + this.privateKey = privateKey; + this.publicKey = publicKey; + } + + /** + * Generates a new private and public key pair based on randomness. + * + * @return a newly generated private key and it's corresponding {@link ECPublicKey}. + */ + public static ECKeyPair generateNew() { + try { + ECKeyPairGenerator generator = new ECKeyPairGenerator(); + ECKeyGenerationParameters keygenParams = + new ECKeyGenerationParameters(ECKeyUtils.domain(), ECKeyUtils.secureRandom()); + generator.init(keygenParams); + AsymmetricCipherKeyPair keypair = generator.generateKeyPair(); + ECPrivateKeyParameters privParams = (ECPrivateKeyParameters) keypair.getPrivate(); + ECPublicKeyParameters pubParams = (ECPublicKeyParameters) keypair.getPublic(); + + final ECPublicKey publicKey = ECPublicKey.fromEcPoint(pubParams.getQ()); + byte[] privateKeyBytes = ECKeyUtils.adjustArray(privParams.getD().toByteArray(), BYTES); + ECKeyUtils.validatePrivate(privateKeyBytes); + + return new ECKeyPair(privateKeyBytes, publicKey); + } catch (Exception e) { + throw new IllegalStateException("Failed to generate ECKeyPair", e); + } + } + + /** + * Generates a new, deterministic {@code ECKeyPair} instance by hashing the provided seed. + * + * @param seed The seed to use when deriving the key pair instance, that is hashed (256 bits). + * @return A key pair that corresponds to the hash of the provided seed. + * @throws IllegalArgumentException if the seed is empty. + */ + public static ECKeyPair fromSeed(byte[] seed) { + Objects.requireNonNull(seed, "Seed must not be null"); + + if (seed.length == 0) { + throw new IllegalArgumentException("Seed must not be empty"); + } + + try { + return fromPrivateKey(HashUtils.sha256(seed).asBytes()); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalStateException("Should always be able to create private key from seed", e); + } + } + + /** + * Restore {@link ECKeyPair} instance from given private key by computing corresponding public + * key. + * + * @param privateKey byte array which contains private key. + * @return A keypair for provided private key + * @throws PrivateKeyException if input byte array does not represent a private key + * @throws PublicKeyException if public key can't be computed for given private key + */ + public static ECKeyPair fromPrivateKey(byte[] privateKey) + throws PrivateKeyException, PublicKeyException { + ECKeyUtils.validatePrivate(privateKey); + + return new ECKeyPair( + privateKey, ECPublicKey.fromBytes(ECKeyUtils.keyHandler.computePublicKey(privateKey))); + } + + public EUID euid() { + return publicKey.euid(); + } + + public byte[] getPrivateKey() { + return privateKey; + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + // TODO move this to new class (yet to be created) `ECPrivateKey`. + + @Override + public ECPoint multiply(ECPoint point) { + BigInteger scalarFromPrivateKey = new BigInteger(1, privateKey); + return point.multiply(scalarFromPrivateKey).normalize(); + } + + @Override + public ECDSASignature sign(byte[] hash) { + return ECKeyUtils.keyHandler.sign(hash, privateKey, publicKey.getBytes()); + } + + /** + * Signs data using the ECPrivateKey resulting in an ECDSA signature. + * + * @param data The data to sign + * @param enforceLowS If signature should enforce low values of signature part `S`, according to + * BIP-62 + * @param beDeterministic If signing should use randomness or be deterministic according to RFC6979. + * @return An ECDSA Signature. + */ + public ECDSASignature sign(byte[] data, boolean enforceLowS, boolean beDeterministic) { + return ECKeyUtils.keyHandler.sign( + data, privateKey, publicKey.getBytes(), enforceLowS, beDeterministic); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof ECKeyPair) { + ECKeyPair other = (ECKeyPair) object; + // Comparing private keys should be sufficient + return Arrays.equals(other.getPrivateKey(), getPrivateKey()); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(getPrivateKey()); + } + + @Override + public String toString() { + // Not going to print the private key here + return String.format("%s[%s]", getClass().getSimpleName(), getPublicKey().toHex()); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyUtils.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyUtils.java index a6b78a54ff..781a3c744a 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyUtils.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECKeyUtils.java @@ -64,17 +64,7 @@ package com.radixdlt.crypto; -import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.asn1.x9.X9IntegerConverter; -import org.bouncycastle.crypto.ec.CustomNamedCurves; -import org.bouncycastle.crypto.params.ECDomainParameters; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.bouncycastle.math.ec.ECAlgorithms; -import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.math.ec.FixedPointUtil; -import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve; +import static com.radixdlt.errors.ApiErrors.UNABLE_TO_MAKE_SIGNATURE_RECOVERABLE; import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.UnsignedBytes; @@ -83,7 +73,6 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.RuntimeUtils; import com.radixdlt.utils.functional.Result; - import java.math.BigInteger; import java.security.Provider; import java.security.SecureRandom; @@ -91,299 +80,298 @@ import java.util.Arrays; import java.util.Optional; import java.util.function.Predicate; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9IntegerConverter; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.FixedPointUtil; +import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve; -import static com.radixdlt.errors.ApiErrors.UNABLE_TO_MAKE_SIGNATURE_RECOVERABLE; - -/** - * Utilities used by both {@link ECPublicKey} and {@link ECKeyPair}. - */ +/** Utilities used by both {@link ECPublicKey} and {@link ECKeyPair}. */ public class ECKeyUtils { - private ECKeyUtils() { - throw new IllegalStateException("Can't construct"); - } - - private static final String CURVE_NAME = "secp256k1"; - private static SecureRandom secureRandom; - private static X9ECParameters curve; - private static ECDomainParameters domain; - private static ECParameterSpec spec; - private static byte[] order; - - public static SecureRandom secureRandom() { - return secureRandom; - } - - public static X9ECParameters curve() { - return curve; - } - - public static ECParameterSpec spec() { - return spec; - } - - public static ECDomainParameters domain() { - return domain; - } - - private static BigInteger getPrime() { - return ((SecP256K1Curve) curve.getCurve()).getQ(); - } - - static { - install(); - } - - static synchronized void install() { - if (RuntimeUtils.isAndroidRuntime()) { - // Reference class so static initializer is called. - LinuxSecureRandom.class.getName(); - // Ensure the library version of BouncyCastle is used for Android - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } - Provider requiredBouncyCastleProvider = new BouncyCastleProvider(); - Provider currentBouncyCastleProvider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); - - // Check if the currently installed version of BouncyCastle is the version - // we want. NOTE! That Android has a stripped down version of BouncyCastle - // by default. - if (isOfRequiredVersion(currentBouncyCastleProvider, requiredBouncyCastleProvider)) { - Security.insertProviderAt(requiredBouncyCastleProvider, 1); - } - - secureRandom = new SecureRandom(); - - curve = CustomNamedCurves.getByName(CURVE_NAME); - domain = new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); - spec = new ECParameterSpec(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); - order = adjustArray(domain.getN().toByteArray(), ECKeyPair.BYTES); - FixedPointUtil.precompute(curve.getG()); - } - - private static boolean isOfRequiredVersion(Provider currentBouncyCastleProvider, Provider requiredBouncyCastleProvider) { - return currentBouncyCastleProvider == null - || !currentBouncyCastleProvider.getVersionStr().equals(requiredBouncyCastleProvider.getVersionStr()); - } - - // Must be after secureRandom init - static final KeyHandler keyHandler = new BouncyCastleKeyHandler(curve); - - static void validatePrivate(byte[] privateKey) throws PrivateKeyException { - if (privateKey == null) { - throw new PrivateKeyException("Private key is null"); - } - - if (privateKey.length != ECKeyPair.BYTES) { - throw new PrivateKeyException("Private key is invalid length: " + privateKey.length); - } - - if (greaterOrEqualOrder(privateKey)) { - throw new PrivateKeyException("Private key is greater than or equal to curve order"); - } - - int pklen = privateKey.length; - if (allZero(privateKey, 0, pklen - 1)) { - byte lastByte = privateKey[pklen - 1]; - if (lastByte == 0) { - throw new PrivateKeyException("Private key is " + lastByte); - } - } - } - - static void validatePublic(byte[] publicKey) throws PublicKeyException { - if (publicKey == null || publicKey.length == 0) { - throw new PublicKeyException("Public key is empty"); - } - - int pubkey0 = publicKey[0] & 0xFF; - switch (pubkey0) { - case 2: - case 3: - if (publicKey.length != ECPublicKey.COMPRESSED_BYTES) { - throw new PublicKeyException("Public key has invalid compressed size"); - } - break; - case 4: - if (publicKey.length != ECPublicKey.UNCOMPRESSED_BYTES) { - throw new PublicKeyException("Public key has invalid uncompressed size"); - } - break; - default: - throw new PublicKeyException("Public key has invalid format"); - } - } - - private static final X9IntegerConverter CONVERTER = new X9IntegerConverter(); - - private static ECCurve ecCurve() { - return curve.getCurve(); - } - - private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { - byte[] compEnc = CONVERTER.integerToBytes(xBN, 1 + CONVERTER.getByteLength(ecCurve())); - - compEnc[0] = (byte) (yBit ? 0x03 : 0x02); - - try { - return ecCurve().decodePoint(compEnc); - } catch (IllegalArgumentException e) { - // the compressed key was invalid - return null; - } - } - - static int calculateV(BigInteger r, BigInteger s, byte[] publicKey, byte[] hash) { - return tryV(0, r, s, publicKey, hash) - .or(() -> tryV(1, r, s, publicKey, hash)) - .or(() -> tryV(2, r, s, publicKey, hash)) - .or(() -> tryV(3, r, s, publicKey, hash)) - .orElseThrow(() -> new IllegalStateException("Unable to calculate V byte for public key")); - } - - private static Optional tryV(int v, BigInteger r, BigInteger s, byte[] publicKey, byte[] hash) { - return ECKeyUtils.recoverFromSignature(v, r, s, hash) - .filter(q -> Arrays.equals(q.getEncoded(false), publicKey)) - .map(__ -> v); - } - - /** - * Restore public key from recoverable signature. - * - * @param signature recoverable signature (with correct bit 'v') - * @param hash hash from which signature was created - * - * @return recovered public key - */ - static Optional recoverFromSignature(ECDSASignature signature, byte[] hash) { - return recoverFromSignature(signature.getV(), signature.getR(), signature.getS(), hash); - } - - /** - * Restore recoverable signature from non-recoverable signature and public key. - * - * @param signature original non-recoverable signature - * @param hash hash from which signature was created - * @param publicKey corresponding public key from the private key used to sign hash. - * - * @return recoverable signature - */ - public static Result toRecoverable(ECDSASignature signature, byte[] hash, ECPublicKey publicKey) { - return Result.wrap( - UNABLE_TO_MAKE_SIGNATURE_RECOVERABLE, - () -> { - var v = calculateV(signature.getR(), signature.getS(), publicKey.getBytes(), hash); - return ECDSASignature.create(signature.getR(), signature.getS(), v); - } - ); - } - - public static ECDSASignature toRecoverableSig(ECDSASignature signature, byte[] hash, ECPublicKey publicKey) { - var v = calculateV(signature.getR(), signature.getS(), publicKey.getBytes(), hash); - return ECDSASignature.create(signature.getR(), signature.getS(), v); - } - - static Optional recoverFromSignature(int v, BigInteger r, BigInteger s, byte[] hash) { - var curveN = curve().getN(); - var point = r.add(BigInteger.valueOf((long) v / 2).multiply(curveN)); - - if (point.compareTo(getPrime()) >= 0) { - return Optional.empty(); - } - - var decompressedPoint = decompressKey(point, (v & 1) == 1); - - if (decompressedPoint == null || !decompressedPoint.multiply(curveN).isInfinity()) { - return Optional.empty(); - } - - var negModCandidate = BigInteger.ZERO.subtract(new BigInteger(1, hash)).mod(curveN); - var modInverseCurve = r.modInverse(curveN); - - return Optional.of( - ECAlgorithms.sumOfTwoMultiplies( - curve().getG(), - modInverseCurve.multiply(negModCandidate).mod(curveN), - decompressedPoint, - modInverseCurve.multiply(s).mod(curveN) - )) - .filter(Predicate.not(ECPoint::isInfinity)); - } - - /** - * Adjusts the specified array so that is is equal to the specified length. - *
    - *
  • - * If the array is equal to the specified length, it is returned - * without change. - *
  • - *
  • - * If array is shorter than the specified length, a new array that - * is zero padded at the front is returned. The specified array is - * filled with zeros to prevent information leakage. - *
  • - *
  • - * If the array is longer than the specified length, a new array - * with sufficient leading zeros removed is returned. The specified - * array is filled with zeros to prevent information leakage. - * An {@code IllegalArgumentException} is thrown if the specified - * array does not have sufficient leading zeros to allow it to be - * truncated to the specified length. - *
  • - *
- * - * @param array The specified array - * @param length The specified length - * - * @return An array of the specified length as described above - * - * @throws IllegalArgumentException if the specified array is longer than - * the specified length, and does not have sufficient leading zeros - * to allow truncation to the specified length. - * @throws NullPointerException if the specified array is {@code null} - */ - static byte[] adjustArray(byte[] array, int length) { - if (length == array.length) { - // Length is fine - return array; - } - final byte[] result; - if (length > array.length) { - // Needs zero padding at front - result = new byte[length]; - System.arraycopy(array, 0, result, length - array.length, array.length); - } else { - // Must be longer, need to drop zeros at front -> error if dropped bytes are not zero - int offset = 0; - while (array.length - offset > length) { - if (array[offset] != 0) { - throw new IllegalArgumentException(String.format( - "Array is greater than %s bytes: %s", length, Bytes.toHexString(array) - )); - } - offset += 1; - } - // Now copy length bytes from offset within array - result = Arrays.copyOfRange(array, offset, offset + length); - } - // Zero out original array so as to avoid information leaks - Arrays.fill(array, (byte) 0); - return result; - } - - @VisibleForTesting - static boolean greaterOrEqualOrder(byte[] privateKey) { - if (privateKey.length != order.length) { - throw new IllegalArgumentException("Invalid private key"); - } - return UnsignedBytes.lexicographicalComparator().compare(order, privateKey) <= 0; - } - - private static boolean allZero(byte[] bytes, int offset, int len) { - for (int i = 0; i < len; ++i) { - if (bytes[offset + i] != 0) { - return false; - } - } - return true; - } + private ECKeyUtils() { + throw new IllegalStateException("Can't construct"); + } + + private static final String CURVE_NAME = "secp256k1"; + private static SecureRandom secureRandom; + private static X9ECParameters curve; + private static ECDomainParameters domain; + private static ECParameterSpec spec; + private static byte[] order; + + public static SecureRandom secureRandom() { + return secureRandom; + } + + public static X9ECParameters curve() { + return curve; + } + + public static ECParameterSpec spec() { + return spec; + } + + public static ECDomainParameters domain() { + return domain; + } + + private static BigInteger getPrime() { + return ((SecP256K1Curve) curve.getCurve()).getQ(); + } + + static { + install(); + } + + static synchronized void install() { + if (RuntimeUtils.isAndroidRuntime()) { + // Reference class so static initializer is called. + LinuxSecureRandom.class.getName(); + // Ensure the library version of BouncyCastle is used for Android + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + Provider requiredBouncyCastleProvider = new BouncyCastleProvider(); + Provider currentBouncyCastleProvider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); + + // Check if the currently installed version of BouncyCastle is the version + // we want. NOTE! That Android has a stripped down version of BouncyCastle + // by default. + if (isOfRequiredVersion(currentBouncyCastleProvider, requiredBouncyCastleProvider)) { + Security.insertProviderAt(requiredBouncyCastleProvider, 1); + } + + secureRandom = new SecureRandom(); + + curve = CustomNamedCurves.getByName(CURVE_NAME); + domain = new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); + spec = new ECParameterSpec(curve.getCurve(), curve.getG(), curve.getN(), curve.getH()); + order = adjustArray(domain.getN().toByteArray(), ECKeyPair.BYTES); + FixedPointUtil.precompute(curve.getG()); + } + + private static boolean isOfRequiredVersion( + Provider currentBouncyCastleProvider, Provider requiredBouncyCastleProvider) { + return currentBouncyCastleProvider == null + || !currentBouncyCastleProvider + .getVersionStr() + .equals(requiredBouncyCastleProvider.getVersionStr()); + } + + // Must be after secureRandom init + static final KeyHandler keyHandler = new BouncyCastleKeyHandler(curve); + + static void validatePrivate(byte[] privateKey) throws PrivateKeyException { + if (privateKey == null) { + throw new PrivateKeyException("Private key is null"); + } + + if (privateKey.length != ECKeyPair.BYTES) { + throw new PrivateKeyException("Private key is invalid length: " + privateKey.length); + } + + if (greaterOrEqualOrder(privateKey)) { + throw new PrivateKeyException("Private key is greater than or equal to curve order"); + } + + int pklen = privateKey.length; + if (allZero(privateKey, 0, pklen - 1)) { + byte lastByte = privateKey[pklen - 1]; + if (lastByte == 0) { + throw new PrivateKeyException("Private key is " + lastByte); + } + } + } + + static void validatePublic(byte[] publicKey) throws PublicKeyException { + if (publicKey == null || publicKey.length == 0) { + throw new PublicKeyException("Public key is empty"); + } + + int pubkey0 = publicKey[0] & 0xFF; + switch (pubkey0) { + case 2: + case 3: + if (publicKey.length != ECPublicKey.COMPRESSED_BYTES) { + throw new PublicKeyException("Public key has invalid compressed size"); + } + break; + case 4: + if (publicKey.length != ECPublicKey.UNCOMPRESSED_BYTES) { + throw new PublicKeyException("Public key has invalid uncompressed size"); + } + break; + default: + throw new PublicKeyException("Public key has invalid format"); + } + } + + private static final X9IntegerConverter CONVERTER = new X9IntegerConverter(); + + private static ECCurve ecCurve() { + return curve.getCurve(); + } + + private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { + byte[] compEnc = CONVERTER.integerToBytes(xBN, 1 + CONVERTER.getByteLength(ecCurve())); + + compEnc[0] = (byte) (yBit ? 0x03 : 0x02); + + try { + return ecCurve().decodePoint(compEnc); + } catch (IllegalArgumentException e) { + // the compressed key was invalid + return null; + } + } + + static int calculateV(BigInteger r, BigInteger s, byte[] publicKey, byte[] hash) { + return tryV(0, r, s, publicKey, hash) + .or(() -> tryV(1, r, s, publicKey, hash)) + .or(() -> tryV(2, r, s, publicKey, hash)) + .or(() -> tryV(3, r, s, publicKey, hash)) + .orElseThrow(() -> new IllegalStateException("Unable to calculate V byte for public key")); + } + + private static Optional tryV( + int v, BigInteger r, BigInteger s, byte[] publicKey, byte[] hash) { + return ECKeyUtils.recoverFromSignature(v, r, s, hash) + .filter(q -> Arrays.equals(q.getEncoded(false), publicKey)) + .map(__ -> v); + } + + /** + * Restore public key from recoverable signature. + * + * @param signature recoverable signature (with correct bit 'v') + * @param hash hash from which signature was created + * @return recovered public key + */ + static Optional recoverFromSignature(ECDSASignature signature, byte[] hash) { + return recoverFromSignature(signature.getV(), signature.getR(), signature.getS(), hash); + } + + /** + * Restore recoverable signature from non-recoverable signature and public key. + * + * @param signature original non-recoverable signature + * @param hash hash from which signature was created + * @param publicKey corresponding public key from the private key used to sign hash. + * @return recoverable signature + */ + public static Result toRecoverable( + ECDSASignature signature, byte[] hash, ECPublicKey publicKey) { + return Result.wrap( + UNABLE_TO_MAKE_SIGNATURE_RECOVERABLE, + () -> { + var v = calculateV(signature.getR(), signature.getS(), publicKey.getBytes(), hash); + return ECDSASignature.create(signature.getR(), signature.getS(), v); + }); + } + + public static ECDSASignature toRecoverableSig( + ECDSASignature signature, byte[] hash, ECPublicKey publicKey) { + var v = calculateV(signature.getR(), signature.getS(), publicKey.getBytes(), hash); + return ECDSASignature.create(signature.getR(), signature.getS(), v); + } + + static Optional recoverFromSignature(int v, BigInteger r, BigInteger s, byte[] hash) { + var curveN = curve().getN(); + var point = r.add(BigInteger.valueOf((long) v / 2).multiply(curveN)); + + if (point.compareTo(getPrime()) >= 0) { + return Optional.empty(); + } + + var decompressedPoint = decompressKey(point, (v & 1) == 1); + + if (decompressedPoint == null || !decompressedPoint.multiply(curveN).isInfinity()) { + return Optional.empty(); + } + + var negModCandidate = BigInteger.ZERO.subtract(new BigInteger(1, hash)).mod(curveN); + var modInverseCurve = r.modInverse(curveN); + + return Optional.of( + ECAlgorithms.sumOfTwoMultiplies( + curve().getG(), + modInverseCurve.multiply(negModCandidate).mod(curveN), + decompressedPoint, + modInverseCurve.multiply(s).mod(curveN))) + .filter(Predicate.not(ECPoint::isInfinity)); + } + + /** + * Adjusts the specified array so that is is equal to the specified length. + * + *
    + *
  • If the array is equal to the specified length, it is returned without change. + *
  • If array is shorter than the specified length, a new array that is zero padded at the + * front is returned. The specified array is filled with zeros to prevent information + * leakage. + *
  • If the array is longer than the specified length, a new array with sufficient leading + * zeros removed is returned. The specified array is filled with zeros to prevent + * information leakage. An {@code IllegalArgumentException} is thrown if the specified array + * does not have sufficient leading zeros to allow it to be truncated to the specified + * length. + *
+ * + * @param array The specified array + * @param length The specified length + * @return An array of the specified length as described above + * @throws IllegalArgumentException if the specified array is longer than the specified length, + * and does not have sufficient leading zeros to allow truncation to the specified length. + * @throws NullPointerException if the specified array is {@code null} + */ + static byte[] adjustArray(byte[] array, int length) { + if (length == array.length) { + // Length is fine + return array; + } + final byte[] result; + if (length > array.length) { + // Needs zero padding at front + result = new byte[length]; + System.arraycopy(array, 0, result, length - array.length, array.length); + } else { + // Must be longer, need to drop zeros at front -> error if dropped bytes are not zero + int offset = 0; + while (array.length - offset > length) { + if (array[offset] != 0) { + throw new IllegalArgumentException( + String.format( + "Array is greater than %s bytes: %s", length, Bytes.toHexString(array))); + } + offset += 1; + } + // Now copy length bytes from offset within array + result = Arrays.copyOfRange(array, offset, offset + length); + } + // Zero out original array so as to avoid information leaks + Arrays.fill(array, (byte) 0); + return result; + } + + @VisibleForTesting + static boolean greaterOrEqualOrder(byte[] privateKey) { + if (privateKey.length != order.length) { + throw new IllegalArgumentException("Invalid private key"); + } + return UnsignedBytes.lexicographicalComparator().compare(order, privateKey) <= 0; + } + + private static boolean allZero(byte[] bytes, int offset, int len) { + for (int i = 0; i < len; ++i) { + if (bytes[offset + i] != 0) { + return false; + } + } + return true; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECMultiplicationScalar.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECMultiplicationScalar.java index f50daf0b30..3f0de90938 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECMultiplicationScalar.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECMultiplicationScalar.java @@ -67,5 +67,5 @@ import org.bouncycastle.math.ec.ECPoint; public interface ECMultiplicationScalar { - ECPoint multiply(ECPoint point); + ECPoint multiply(ECPoint point); } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECPublicKey.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECPublicKey.java index 6a0e8c5a61..4cd786751d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECPublicKey.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/ECPublicKey.java @@ -64,7 +64,7 @@ package com.radixdlt.crypto; -import org.bouncycastle.math.ec.ECPoint; +import static com.radixdlt.errors.ApiErrors.INVALID_PUBLIC_KEY; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -75,116 +75,111 @@ import com.radixdlt.serialization.DsonOutput; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.functional.Result; - import java.util.Arrays; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; +import org.bouncycastle.math.ec.ECPoint; -import static com.radixdlt.errors.ApiErrors.INVALID_PUBLIC_KEY; - -/** - * Asymmetric EC public key provider fixed to curve 'secp256k1' - */ +/** Asymmetric EC public key provider fixed to curve 'secp256k1' */ public final class ECPublicKey { - public static final int COMPRESSED_BYTES = 33; // 32 + header byte - public static final int UNCOMPRESSED_BYTES = 65; // 64 + header byte - - private final ECPoint ecPoint; - private final Supplier uncompressedBytes; - private final Supplier uid; - private final int hashCode; - private final byte[] compressed; - - private ECPublicKey(ECPoint ecPoint) { - this.ecPoint = Objects.requireNonNull(ecPoint); - this.uncompressedBytes = Suppliers.memoize(() -> this.ecPoint.getEncoded(false)); - this.compressed = this.ecPoint.getEncoded(true); - this.uid = Suppliers.memoize(this::computeUID); - this.hashCode = computeHashCode(); - } - - private int computeHashCode() { - return Arrays.hashCode(compressed); - } - - public static ECPublicKey fromEcPoint(ECPoint ecPoint) { - return new ECPublicKey(ecPoint); - } - - @JsonCreator - public static ECPublicKey fromBytes(byte[] key) throws PublicKeyException { - ECKeyUtils.validatePublic(key); - return new ECPublicKey(ECKeyUtils.spec().getCurve().decodePoint(key)); - } - - @JsonCreator - public static ECPublicKey fromHex(String hex) throws PublicKeyException { - return fromBytes(Bytes.fromHexString(hex)); - } - - public static Optional recoverFrom(HashCode hash, ECDSASignature signature) { - return ECKeyUtils.recoverFromSignature(signature, hash.asBytes()) - .map(ECPublicKey::new); - } - - public static Result fromHexString(String hexadecimal) { - return Result.wrap(INVALID_PUBLIC_KEY, () -> fromHex(hexadecimal)); - } - - public EUID euid() { - return uid.get(); - } - - public ECPoint getEcPoint() { - return ecPoint; - } - - @JsonProperty("publicKey") - @DsonOutput(DsonOutput.Output.ALL) - public byte[] getBytes() { - return uncompressedBytes.get(); - } - - public byte[] getCompressedBytes() { - return compressed; - } - - public boolean verify(HashCode hash, ECDSASignature signature) { - return verify(hash.asBytes(), signature); - } - - public boolean verify(byte[] hash, ECDSASignature signature) { - return signature != null && ECKeyUtils.keyHandler.verify(hash, signature, ecPoint); - } - - public String toHex() { - return Bytes.toHexString(getCompressedBytes()); - } - - @Override - public int hashCode() { - return this.hashCode; - } - - @Override - public boolean equals(Object object) { - if (object == this) { - return true; - } - if (object instanceof ECPublicKey) { - final var that = (ECPublicKey) object; - return Arrays.equals(this.compressed, that.compressed); - } - return false; - } - - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), toHex()); - } - - private EUID computeUID() { - return EUID.sha256(getCompressedBytes()); - } + public static final int COMPRESSED_BYTES = 33; // 32 + header byte + public static final int UNCOMPRESSED_BYTES = 65; // 64 + header byte + + private final ECPoint ecPoint; + private final Supplier uncompressedBytes; + private final Supplier uid; + private final int hashCode; + private final byte[] compressed; + + private ECPublicKey(ECPoint ecPoint) { + this.ecPoint = Objects.requireNonNull(ecPoint); + this.uncompressedBytes = Suppliers.memoize(() -> this.ecPoint.getEncoded(false)); + this.compressed = this.ecPoint.getEncoded(true); + this.uid = Suppliers.memoize(this::computeUID); + this.hashCode = computeHashCode(); + } + + private int computeHashCode() { + return Arrays.hashCode(compressed); + } + + public static ECPublicKey fromEcPoint(ECPoint ecPoint) { + return new ECPublicKey(ecPoint); + } + + @JsonCreator + public static ECPublicKey fromBytes(byte[] key) throws PublicKeyException { + ECKeyUtils.validatePublic(key); + return new ECPublicKey(ECKeyUtils.spec().getCurve().decodePoint(key)); + } + + @JsonCreator + public static ECPublicKey fromHex(String hex) throws PublicKeyException { + return fromBytes(Bytes.fromHexString(hex)); + } + + public static Optional recoverFrom(HashCode hash, ECDSASignature signature) { + return ECKeyUtils.recoverFromSignature(signature, hash.asBytes()).map(ECPublicKey::new); + } + + public static Result fromHexString(String hexadecimal) { + return Result.wrap(INVALID_PUBLIC_KEY, () -> fromHex(hexadecimal)); + } + + public EUID euid() { + return uid.get(); + } + + public ECPoint getEcPoint() { + return ecPoint; + } + + @JsonProperty("publicKey") + @DsonOutput(DsonOutput.Output.ALL) + public byte[] getBytes() { + return uncompressedBytes.get(); + } + + public byte[] getCompressedBytes() { + return compressed; + } + + public boolean verify(HashCode hash, ECDSASignature signature) { + return verify(hash.asBytes(), signature); + } + + public boolean verify(byte[] hash, ECDSASignature signature) { + return signature != null && ECKeyUtils.keyHandler.verify(hash, signature, ecPoint); + } + + public String toHex() { + return Bytes.toHexString(getCompressedBytes()); + } + + @Override + public int hashCode() { + return this.hashCode; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ECPublicKey) { + final var that = (ECPublicKey) object; + return Arrays.equals(this.compressed, that.compressed); + } + return false; + } + + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), toHex()); + } + + private EUID computeUID() { + return EUID.sha256(getCompressedBytes()); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashHandler.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashHandler.java index f5283c606c..7840a18649 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashHandler.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashHandler.java @@ -1,117 +1,116 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto; - -/** - * Interface for abstract 256-bit and 512-bit cryptographic hash functions. - *

- * The intent behind this interface is that the actual hash functions can - * easily be replaced when required. - *

- * Note that all methods must be thread safe. - */ -interface HashHandler { - - /** - * Hashes the specified portion of the array, returning a cryptographically secure 256-bit hash - * - * @param data The data to hash - * @param offset The offset within the array to start hashing data - * @param length The number of bytes in the array to hash - * @return The digest by applying the 256-bit/32-byte hash - */ - byte[] hash256(byte[] data, int offset, int length); - - /** - * Hashes the specified portion of the array, returning a cryptographically secure 512-bit hash. - * - * @param data The data to hash - * @param offset The offset within the array to start hashing data - * @param length The number of bytes in the array to hash - * @return The 512-bit/64-byte hash - */ - byte[] hash512(byte[] data, int offset, int length); - - /** - * Hashes the supplied array, returning a cryptographically secure 256-bit hash - * - * @param data The data to hash - * @return The digest by applying the 256-bit/32-byte hash - */ - default byte[] hash256(byte[] data) { - return hash256(data, 0, data.length); - } - - /** - * Hashes the specified portion of the array, returning a cryptographically secure 512-bit hash. - * - * @param data The data to hash - * @return The 512-bit/64-byte hash - */ - default byte[] hash512(byte[] data) { - return hash512(data, 0, data.length); - } - -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto; + +/** + * Interface for abstract 256-bit and 512-bit cryptographic hash functions. + * + *

The intent behind this interface is that the actual hash functions can easily be replaced when + * required. + * + *

Note that all methods must be thread safe. + */ +interface HashHandler { + + /** + * Hashes the specified portion of the array, returning a cryptographically secure 256-bit hash + * + * @param data The data to hash + * @param offset The offset within the array to start hashing data + * @param length The number of bytes in the array to hash + * @return The digest by applying the 256-bit/32-byte hash + */ + byte[] hash256(byte[] data, int offset, int length); + + /** + * Hashes the specified portion of the array, returning a cryptographically secure 512-bit hash. + * + * @param data The data to hash + * @param offset The offset within the array to start hashing data + * @param length The number of bytes in the array to hash + * @return The 512-bit/64-byte hash + */ + byte[] hash512(byte[] data, int offset, int length); + + /** + * Hashes the supplied array, returning a cryptographically secure 256-bit hash + * + * @param data The data to hash + * @return The digest by applying the 256-bit/32-byte hash + */ + default byte[] hash256(byte[] data) { + return hash256(data, 0, data.length); + } + + /** + * Hashes the specified portion of the array, returning a cryptographically secure 512-bit hash. + * + * @param data The data to hash + * @return The 512-bit/64-byte hash + */ + default byte[] hash512(byte[] data) { + return hash512(data, 0, data.length); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashUtils.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashUtils.java index b214642c0a..e768826a2d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashUtils.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/HashUtils.java @@ -1,204 +1,188 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto; - -import org.bouncycastle.crypto.digests.KeccakDigest; - -import com.google.common.hash.HashCode; -import com.google.common.primitives.UnsignedBytes; -import com.radixdlt.SecurityCritical; -import com.radixdlt.SecurityCritical.SecurityKind; - -import java.nio.ByteBuffer; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Comparator; - -/** - * A class containing a collection of static methods for hashing and hashing-related utils. - */ -@SecurityCritical(SecurityKind.HASHING) -public final class HashUtils { - private static final Comparator hashComparator = new Comparator<>() { - private final Comparator bytesComparator = UnsignedBytes.lexicographicalComparator(); - - @Override - public int compare(HashCode o1, HashCode o2) { - return bytesComparator.compare(o1.asBytes(), o2.asBytes()); - } - }; - - private static final SecureRandom secureRandom = new SecureRandom(); - - private static final HashHandler shaHashHandler = new SHAHashHandler(); - - private static final HashCode ZERO_256 = zero(32); - - /** - * Returns a hash consisting of 32 zero bytes. - */ - public static HashCode zero256() { - return ZERO_256; - } - - /** - * Returns a hash consisting of {@code length} zero bytes. - */ - public static HashCode zero(int length) { - return HashCode.fromBytes(new byte[length]); - } - - /** - * Returns a random hash of length 32 bytes. - */ - public static HashCode random256() { - return random(32); - } - - /** - * Returns a random hash of specified length. - */ - public static HashCode random(int length) { - byte[] randomBytes = new byte[length]; - secureRandom.nextBytes(randomBytes); - return HashCode.fromBytes(shaHashHandler.hash256(randomBytes)); - } - - /** - * Hashes the supplied array, returning a cryptographically secure 256-bit hash. - * - * @param dataToBeHashed The data to hash - * - * @return The digest by applying the 256-bit/32-byte hash function - */ - public static HashCode sha256(byte[] dataToBeHashed) { - return sha256(dataToBeHashed, 0, dataToBeHashed.length); - } - - /** - * Hashes the specified portion of the array, returning a cryptographically secure 256-bit hash. - * - * @param dataToBeHashed The data to hash - * @param offset The offset within the array to start hashing data - * @param length The number of bytes in the array to hash - * - * @return The digest by applying the 256-bit/32-byte hash function. - */ - public static HashCode sha256(byte[] dataToBeHashed, int offset, int length) { - return HashCode.fromBytes(shaHashHandler.hash256(dataToBeHashed, offset, length)); - } - - /** - * Hashes the specified portion of the array, returning a cryptographically secure 512-bit hash. - * - * @param dataToBeHashed The data to hash - * - * @return The 512-bit/64-byte hash - */ - public static HashCode sha512(byte[] dataToBeHashed) { - return HashCode.fromBytes(shaHashHandler.hash512(dataToBeHashed)); - } - - public static byte[] kec256(byte[]... input) { - final var digest = new KeccakDigest(256); - final var output = new byte[digest.getDigestSize()]; - Arrays.stream(input).forEach(i -> digest.update(i, 0, i.length)); - digest.doFinal(output, 0); - return output; - } - - /** - * Compares two HashCode instances using the underlying byte array. - * - * @param fst The first object to be compared - * @param snd The second object to be compared - * - * @return A negative integer, zero, or a positive integer as the - * first argument is less than, equal to, or greater than the - * second. - */ - public static int compare(HashCode fst, HashCode snd) { - return hashComparator.compare(fst, snd); - } - - /** - * Calculate hash which can serve as transaction ID. - * - * @param payload input bytes to hash - * - * @return calculated hash - */ - public static HashCode transactionIdHash(byte[] payload) { - return sha256(payload); - } - - public static HashCode transactionIdHash(ByteBuffer buf) { - return sha256(buf.array(), buf.position(), buf.remaining()); - } - - private HashUtils() { - throw new UnsupportedOperationException(); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto; + +import com.google.common.hash.HashCode; +import com.google.common.primitives.UnsignedBytes; +import com.radixdlt.SecurityCritical; +import com.radixdlt.SecurityCritical.SecurityKind; +import java.nio.ByteBuffer; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Comparator; +import org.bouncycastle.crypto.digests.KeccakDigest; + +/** A class containing a collection of static methods for hashing and hashing-related utils. */ +@SecurityCritical(SecurityKind.HASHING) +public final class HashUtils { + private static final Comparator hashComparator = + new Comparator<>() { + private final Comparator bytesComparator = + UnsignedBytes.lexicographicalComparator(); + + @Override + public int compare(HashCode o1, HashCode o2) { + return bytesComparator.compare(o1.asBytes(), o2.asBytes()); + } + }; + + private static final SecureRandom secureRandom = new SecureRandom(); + + private static final HashHandler shaHashHandler = new SHAHashHandler(); + + private static final HashCode ZERO_256 = zero(32); + + /** Returns a hash consisting of 32 zero bytes. */ + public static HashCode zero256() { + return ZERO_256; + } + + /** Returns a hash consisting of {@code length} zero bytes. */ + public static HashCode zero(int length) { + return HashCode.fromBytes(new byte[length]); + } + + /** Returns a random hash of length 32 bytes. */ + public static HashCode random256() { + return random(32); + } + + /** Returns a random hash of specified length. */ + public static HashCode random(int length) { + byte[] randomBytes = new byte[length]; + secureRandom.nextBytes(randomBytes); + return HashCode.fromBytes(shaHashHandler.hash256(randomBytes)); + } + + /** + * Hashes the supplied array, returning a cryptographically secure 256-bit hash. + * + * @param dataToBeHashed The data to hash + * @return The digest by applying the 256-bit/32-byte hash function + */ + public static HashCode sha256(byte[] dataToBeHashed) { + return sha256(dataToBeHashed, 0, dataToBeHashed.length); + } + + /** + * Hashes the specified portion of the array, returning a cryptographically secure 256-bit hash. + * + * @param dataToBeHashed The data to hash + * @param offset The offset within the array to start hashing data + * @param length The number of bytes in the array to hash + * @return The digest by applying the 256-bit/32-byte hash function. + */ + public static HashCode sha256(byte[] dataToBeHashed, int offset, int length) { + return HashCode.fromBytes(shaHashHandler.hash256(dataToBeHashed, offset, length)); + } + + /** + * Hashes the specified portion of the array, returning a cryptographically secure 512-bit hash. + * + * @param dataToBeHashed The data to hash + * @return The 512-bit/64-byte hash + */ + public static HashCode sha512(byte[] dataToBeHashed) { + return HashCode.fromBytes(shaHashHandler.hash512(dataToBeHashed)); + } + + public static byte[] kec256(byte[]... input) { + final var digest = new KeccakDigest(256); + final var output = new byte[digest.getDigestSize()]; + Arrays.stream(input).forEach(i -> digest.update(i, 0, i.length)); + digest.doFinal(output, 0); + return output; + } + + /** + * Compares two HashCode instances using the underlying byte array. + * + * @param fst The first object to be compared + * @param snd The second object to be compared + * @return A negative integer, zero, or a positive integer as the first argument is less than, + * equal to, or greater than the second. + */ + public static int compare(HashCode fst, HashCode snd) { + return hashComparator.compare(fst, snd); + } + + /** + * Calculate hash which can serve as transaction ID. + * + * @param payload input bytes to hash + * @return calculated hash + */ + public static HashCode transactionIdHash(byte[] payload) { + return sha256(payload); + } + + public static HashCode transactionIdHash(ByteBuffer buf) { + return sha256(buf.array(), buf.position(), buf.remaining()); + } + + private HashUtils() { + throw new UnsupportedOperationException(); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/IESEngine.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/IESEngine.java index f0890ab160..a34f05b7ec 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/IESEngine.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/IESEngine.java @@ -64,372 +64,363 @@ package com.radixdlt.crypto; -import org.bouncycastle.crypto.Digest; +import java.io.ByteArrayInputStream; +import java.io.IOException; import org.bouncycastle.crypto.BasicAgreement; -import org.bouncycastle.crypto.DerivationFunction; -import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.BufferedBlockCipher; -import org.bouncycastle.crypto.KeyParser; import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.DerivationFunction; import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.KeyParser; +import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator; -import org.bouncycastle.crypto.params.IESParameters; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.IESParameters; import org.bouncycastle.crypto.params.IESWithCipherParameters; import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.MGFParameters; +import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.Pack; -import java.io.ByteArrayInputStream; -import java.io.IOException; - /** - * Support class for constructing integrated encryption cipher - * for doing basic message exchanges on top of key agreement ciphers. - * Follows the description given in IEEE Std 1363a with a couple of changes: - * - Hash the MAC key before use - * - Include the encryption IV in the MAC computation + * Support class for constructing integrated encryption cipher for doing basic message exchanges on + * top of key agreement ciphers. Follows the description given in IEEE Std 1363a with a couple of + * changes: - Hash the MAC key before use - Include the encryption IV in the MAC computation */ public class IESEngine { - private final Digest hash; - private BasicAgreement agree; - private DerivationFunction kdf; - private Mac mac; - private BufferedBlockCipher cipher; - - private boolean forEncryption; - private CipherParameters privParam, pubParam; - private IESParameters param; - - private byte[] v; - private EphemeralKeyPairGenerator keyPairGenerator; - private KeyParser keyParser; - private byte[] iv; - boolean hashK2 = true; - - /** - * set up for use with stream mode, where the key derivation function - * is used to provide a stream of bytes to xor with the message. - * @param agree the key agreement used as the basis for the encryption - * @param kdf the key derivation function used for byte generation - * @param mac the message authentication code generator for the message - * @param hash hash ing function - * @param cipher the actual cipher - */ - public IESEngine( - BasicAgreement agree, - DerivationFunction kdf, - Mac mac, - Digest hash, - BufferedBlockCipher cipher - ) { - this.agree = agree; - this.kdf = kdf; - this.mac = mac; - this.hash = hash; - this.cipher = cipher; + private final Digest hash; + private BasicAgreement agree; + private DerivationFunction kdf; + private Mac mac; + private BufferedBlockCipher cipher; + + private boolean forEncryption; + private CipherParameters privParam, pubParam; + private IESParameters param; + + private byte[] v; + private EphemeralKeyPairGenerator keyPairGenerator; + private KeyParser keyParser; + private byte[] iv; + boolean hashK2 = true; + + /** + * set up for use with stream mode, where the key derivation function is used to provide a stream + * of bytes to xor with the message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + * @param hash hash ing function + * @param cipher the actual cipher + */ + public IESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac, + Digest hash, + BufferedBlockCipher cipher) { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.hash = hash; + this.cipher = cipher; + } + + /** + * Initialise the encryptor. + * + * @param forEncryption whether or not this is encryption/decryption. + * @param privParam our private key parameters + * @param pubParam the recipient's/sender's public key parameters + * @param params encoding and derivation parameters, may be wrapped to include an IV for an + * underlying block cipher. + */ + public void init( + boolean forEncryption, + CipherParameters privParam, + CipherParameters pubParam, + CipherParameters params) { + this.forEncryption = forEncryption; + this.privParam = privParam; + this.pubParam = pubParam; + this.v = new byte[0]; + + extractParams(params); + } + + /** + * Initialise the encryptor. + * + * @param publicKey the recipient's/sender's public key parameters + * @param params encoding and derivation parameters, may be wrapped to include an IV for an + * underlying block cipher. + * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use. + */ + public void init( + AsymmetricKeyParameter publicKey, + CipherParameters params, + EphemeralKeyPairGenerator ephemeralKeyPairGenerator) { + this.forEncryption = true; + this.pubParam = publicKey; + this.keyPairGenerator = ephemeralKeyPairGenerator; + + extractParams(params); + } + + /** + * Initialise the encryptor. + * + * @param privateKey the recipient's private key. + * @param params encoding and derivation parameters, may be wrapped to include an IV for an + * underlying block cipher. + * @param publicKeyParser the parser for reading the ephemeral public key. + */ + public void init( + AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser) { + this.forEncryption = false; + this.privParam = privateKey; + this.keyParser = publicKeyParser; + + extractParams(params); + } + + private void extractParams(CipherParameters params) { + if (params instanceof ParametersWithIV) { + this.iv = ((ParametersWithIV) params).getIV(); + this.param = (IESParameters) ((ParametersWithIV) params).getParameters(); + } else { + this.iv = null; + this.param = (IESParameters) params; } - - /** - * Initialise the encryptor. - * - * @param forEncryption whether or not this is encryption/decryption. - * @param privParam our private key parameters - * @param pubParam the recipient's/sender's public key parameters - * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher. - */ - public void init( - boolean forEncryption, - CipherParameters privParam, - CipherParameters pubParam, - CipherParameters params - ) { - this.forEncryption = forEncryption; - this.privParam = privParam; - this.pubParam = pubParam; - this.v = new byte[0]; - - extractParams(params); + } + + private byte[] encryptBlock(byte[] in, int inOff, int inLen, byte[] macData) + throws InvalidCipherTextException { + byte[] c, k, k1, k2; + int len; + + if (cipher == null) { + // Streaming mode. + k1 = new byte[inLen]; + k2 = new byte[param.getMacKeySize() / 8]; + k = new byte[k1.length + k2.length]; + + kdf.generateBytes(k, 0, k.length); + System.arraycopy(k, 0, k1, 0, k1.length); + System.arraycopy(k, inLen, k2, 0, k2.length); + + c = new byte[inLen]; + + for (int i = 0; i != inLen; i++) { + c[i] = (byte) (in[inOff + i] ^ k1[i]); + } + len = inLen; + } else { + // Block cipher mode. + k1 = new byte[((IESWithCipherParameters) param).getCipherKeySize() / 8]; + k2 = new byte[param.getMacKeySize() / 8]; + k = new byte[k1.length + k2.length]; + + kdf.generateBytes(k, 0, k.length); + System.arraycopy(k, 0, k1, 0, k1.length); + System.arraycopy(k, k1.length, k2, 0, k2.length); + + // If iv provided use it to initialise the cipher + if (iv != null) { + cipher.init(true, new ParametersWithIV(new KeyParameter(k1), iv)); + } else { + cipher.init(true, new KeyParameter(k1)); + } + + c = new byte[cipher.getOutputSize(inLen)]; + len = cipher.processBytes(in, inOff, inLen, c, 0); + len += cipher.doFinal(c, len); } - /** - * Initialise the encryptor. - * - * @param publicKey the recipient's/sender's public key parameters - * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher. - * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use. - */ - public void init( - AsymmetricKeyParameter publicKey, - CipherParameters params, - EphemeralKeyPairGenerator ephemeralKeyPairGenerator - ) { - this.forEncryption = true; - this.pubParam = publicKey; - this.keyPairGenerator = ephemeralKeyPairGenerator; - - extractParams(params); - } + // Convert the length of the encoding vector into a byte array. + byte[] p2 = param.getEncodingV(); - /** - * Initialise the encryptor. - * - * @param privateKey the recipient's private key. - * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher. - * @param publicKeyParser the parser for reading the ephemeral public key. - */ - public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser) { - this.forEncryption = false; - this.privParam = privateKey; - this.keyParser = publicKeyParser; - - extractParams(params); - } + // Apply the MAC. + byte[] t = new byte[mac.getMacSize()]; - private void extractParams(CipherParameters params) { - if (params instanceof ParametersWithIV) { - this.iv = ((ParametersWithIV) params).getIV(); - this.param = (IESParameters) ((ParametersWithIV) params).getParameters(); - } else { - this.iv = null; - this.param = (IESParameters) params; - } + byte[] k2a; + if (hashK2) { + k2a = new byte[hash.getDigestSize()]; + hash.reset(); + hash.update(k2, 0, k2.length); + hash.doFinal(k2a, 0); + } else { + k2a = k2; + } + mac.init(new KeyParameter(k2a)); + mac.update(iv, 0, iv.length); + mac.update(c, 0, c.length); + if (p2 != null) { + mac.update(p2, 0, p2.length); + } + if (v.length != 0 && p2 != null) { + byte[] l2 = new byte[4]; + Pack.intToBigEndian(p2.length * 8, l2, 0); + mac.update(l2, 0, l2.length); } - private byte[] encryptBlock( - byte[] in, - int inOff, - int inLen, - byte[] macData) throws InvalidCipherTextException { - byte[] c, k, k1, k2; - int len; - - if (cipher == null) { - // Streaming mode. - k1 = new byte[inLen]; - k2 = new byte[param.getMacKeySize() / 8]; - k = new byte[k1.length + k2.length]; - - kdf.generateBytes(k, 0, k.length); - System.arraycopy(k, 0, k1, 0, k1.length); - System.arraycopy(k, inLen, k2, 0, k2.length); - - c = new byte[inLen]; - - for (int i = 0; i != inLen; i++) { - c[i] = (byte) (in[inOff + i] ^ k1[i]); - } - len = inLen; - } else { - // Block cipher mode. - k1 = new byte[((IESWithCipherParameters) param).getCipherKeySize() / 8]; - k2 = new byte[param.getMacKeySize() / 8]; - k = new byte[k1.length + k2.length]; - - kdf.generateBytes(k, 0, k.length); - System.arraycopy(k, 0, k1, 0, k1.length); - System.arraycopy(k, k1.length, k2, 0, k2.length); - - // If iv provided use it to initialise the cipher - if (iv != null) { - cipher.init(true, new ParametersWithIV(new KeyParameter(k1), iv)); - } else { - cipher.init(true, new KeyParameter(k1)); - } - - c = new byte[cipher.getOutputSize(inLen)]; - len = cipher.processBytes(in, inOff, inLen, c, 0); - len += cipher.doFinal(c, len); - } - - // Convert the length of the encoding vector into a byte array. - byte[] p2 = param.getEncodingV(); - - // Apply the MAC. - byte[] t = new byte[mac.getMacSize()]; + if (macData != null) { + mac.update(macData, 0, macData.length); + } - byte[] k2a; - if (hashK2) { - k2a = new byte[hash.getDigestSize()]; - hash.reset(); - hash.update(k2, 0, k2.length); - hash.doFinal(k2a, 0); - } else { - k2a = k2; - } - mac.init(new KeyParameter(k2a)); - mac.update(iv, 0, iv.length); - mac.update(c, 0, c.length); - if (p2 != null) { - mac.update(p2, 0, p2.length); - } - if (v.length != 0 && p2 != null) { - byte[] l2 = new byte[4]; - Pack.intToBigEndian(p2.length * 8, l2, 0); - mac.update(l2, 0, l2.length); - } + mac.doFinal(t, 0); - if (macData != null) { - mac.update(macData, 0, macData.length); - } + // Output the triple (V,C,T). + final var output = new byte[v.length + len + t.length]; + System.arraycopy(v, 0, output, 0, v.length); + System.arraycopy(c, 0, output, v.length, len); + System.arraycopy(t, 0, output, v.length + len, t.length); + return output; + } - mac.doFinal(t, 0); + private byte[] decryptBlock(byte[] inEnc, int inOff, int inLen, byte[] macData) + throws InvalidCipherTextException { + byte[] m, k, k1, k2; + int len; - // Output the triple (V,C,T). - final var output = new byte[v.length + len + t.length]; - System.arraycopy(v, 0, output, 0, v.length); - System.arraycopy(c, 0, output, v.length, len); - System.arraycopy(t, 0, output, v.length + len, t.length); - return output; + // Ensure that the length of the input is greater than the MAC in bytes + if (inLen <= (param.getMacKeySize() / 8)) { + throw new InvalidCipherTextException("Length of input must be greater than the MAC"); } - private byte[] decryptBlock( - byte[] inEnc, - int inOff, - int inLen, - byte[] macData) throws InvalidCipherTextException { - byte[] m, k, k1, k2; - int len; - - // Ensure that the length of the input is greater than the MAC in bytes - if (inLen <= (param.getMacKeySize() / 8)) { - throw new InvalidCipherTextException("Length of input must be greater than the MAC"); - } - - if (cipher == null) { - // Streaming mode. - k1 = new byte[inLen - v.length - mac.getMacSize()]; - k2 = new byte[param.getMacKeySize() / 8]; - k = new byte[k1.length + k2.length]; - - kdf.generateBytes(k, 0, k.length); - System.arraycopy(k, 0, k1, 0, k1.length); - System.arraycopy(k, k1.length, k2, 0, k2.length); - - m = new byte[k1.length]; - - for (int i = 0; i != k1.length; i++) { - m[i] = (byte) (inEnc[inOff + v.length + i] ^ k1[i]); - } - - len = k1.length; - } else { - // Block cipher mode. - k1 = new byte[((IESWithCipherParameters) param).getCipherKeySize() / 8]; - k2 = new byte[param.getMacKeySize() / 8]; - k = new byte[k1.length + k2.length]; - - kdf.generateBytes(k, 0, k.length); - System.arraycopy(k, 0, k1, 0, k1.length); - System.arraycopy(k, k1.length, k2, 0, k2.length); - - // If IV provide use it to initialize the cipher - if (iv != null) { - cipher.init(false, new ParametersWithIV(new KeyParameter(k1), iv)); - } else { - cipher.init(false, new KeyParameter(k1)); - } - - m = new byte[cipher.getOutputSize(inLen - v.length - mac.getMacSize())]; - len = cipher.processBytes(inEnc, inOff + v.length, inLen - v.length - mac.getMacSize(), m, 0); - len += cipher.doFinal(m, len); - } - - // Convert the length of the encoding vector into a byte array. - byte[] p2 = param.getEncodingV(); - - // Verify the MAC. - int end = inOff + inLen; - byte[] t1 = Arrays.copyOfRange(inEnc, end - mac.getMacSize(), end); - - byte[] t2 = new byte[t1.length]; - byte[] k2a; - if (hashK2) { - k2a = new byte[hash.getDigestSize()]; - hash.reset(); - hash.update(k2, 0, k2.length); - hash.doFinal(k2a, 0); - } else { - k2a = k2; - } - mac.init(new KeyParameter(k2a)); - mac.update(iv, 0, iv.length); - mac.update(inEnc, inOff + v.length, inLen - v.length - t2.length); + if (cipher == null) { + // Streaming mode. + k1 = new byte[inLen - v.length - mac.getMacSize()]; + k2 = new byte[param.getMacKeySize() / 8]; + k = new byte[k1.length + k2.length]; + + kdf.generateBytes(k, 0, k.length); + System.arraycopy(k, 0, k1, 0, k1.length); + System.arraycopy(k, k1.length, k2, 0, k2.length); + + m = new byte[k1.length]; + + for (int i = 0; i != k1.length; i++) { + m[i] = (byte) (inEnc[inOff + v.length + i] ^ k1[i]); + } + + len = k1.length; + } else { + // Block cipher mode. + k1 = new byte[((IESWithCipherParameters) param).getCipherKeySize() / 8]; + k2 = new byte[param.getMacKeySize() / 8]; + k = new byte[k1.length + k2.length]; + + kdf.generateBytes(k, 0, k.length); + System.arraycopy(k, 0, k1, 0, k1.length); + System.arraycopy(k, k1.length, k2, 0, k2.length); + + // If IV provide use it to initialize the cipher + if (iv != null) { + cipher.init(false, new ParametersWithIV(new KeyParameter(k1), iv)); + } else { + cipher.init(false, new KeyParameter(k1)); + } + + m = new byte[cipher.getOutputSize(inLen - v.length - mac.getMacSize())]; + len = cipher.processBytes(inEnc, inOff + v.length, inLen - v.length - mac.getMacSize(), m, 0); + len += cipher.doFinal(m, len); + } - if (p2 != null) { - mac.update(p2, 0, p2.length); - } + // Convert the length of the encoding vector into a byte array. + byte[] p2 = param.getEncodingV(); + + // Verify the MAC. + int end = inOff + inLen; + byte[] t1 = Arrays.copyOfRange(inEnc, end - mac.getMacSize(), end); + + byte[] t2 = new byte[t1.length]; + byte[] k2a; + if (hashK2) { + k2a = new byte[hash.getDigestSize()]; + hash.reset(); + hash.update(k2, 0, k2.length); + hash.doFinal(k2a, 0); + } else { + k2a = k2; + } + mac.init(new KeyParameter(k2a)); + mac.update(iv, 0, iv.length); + mac.update(inEnc, inOff + v.length, inLen - v.length - t2.length); - if (v.length != 0 && p2 != null) { - byte[] l2 = new byte[4]; - Pack.intToBigEndian(p2.length * 8, l2, 0); - mac.update(l2, 0, l2.length); - } + if (p2 != null) { + mac.update(p2, 0, p2.length); + } - if (macData != null) { - mac.update(macData, 0, macData.length); - } + if (v.length != 0 && p2 != null) { + byte[] l2 = new byte[4]; + Pack.intToBigEndian(p2.length * 8, l2, 0); + mac.update(l2, 0, l2.length); + } - mac.doFinal(t2, 0); + if (macData != null) { + mac.update(macData, 0, macData.length); + } - if (!Arrays.constantTimeAreEqual(t1, t2)) { - throw new InvalidCipherTextException("Invalid MAC"); - } + mac.doFinal(t2, 0); - // Output the message. - return Arrays.copyOfRange(m, 0, len); + if (!Arrays.constantTimeAreEqual(t1, t2)) { + throw new InvalidCipherTextException("Invalid MAC"); } - public byte[] processBlock( - byte[] in, - int inOff, - int inLen, - byte[] macData - ) throws InvalidCipherTextException { - if (forEncryption) { - if (keyPairGenerator != null) { - final var ephKeyPair = keyPairGenerator.generate(); - this.privParam = ephKeyPair.getKeyPair().getPrivate(); - this.v = ephKeyPair.getEncodedPublicKey(); - } - } else { - if (keyParser != null) { - final var bIn = new ByteArrayInputStream(in, inOff, inLen); - try { - this.pubParam = keyParser.readKey(bIn); - } catch (IOException e) { - throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e); - } - - final var encLength = (inLen - bIn.available()); - this.v = Arrays.copyOfRange(in, inOff, inOff + encLength); - } + // Output the message. + return Arrays.copyOfRange(m, 0, len); + } + + public byte[] processBlock(byte[] in, int inOff, int inLen, byte[] macData) + throws InvalidCipherTextException { + if (forEncryption) { + if (keyPairGenerator != null) { + final var ephKeyPair = keyPairGenerator.generate(); + this.privParam = ephKeyPair.getKeyPair().getPrivate(); + this.v = ephKeyPair.getEncodedPublicKey(); + } + } else { + if (keyParser != null) { + final var bIn = new ByteArrayInputStream(in, inOff, inLen); + try { + this.pubParam = keyParser.readKey(bIn); + } catch (IOException e) { + throw new InvalidCipherTextException( + "unable to recover ephemeral public key: " + e.getMessage(), e); } - // Compute the common value and convert to byte array. - agree.init(privParam); - byte[] z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), agree.calculateAgreement(pubParam)); - - // Create input to KDF. - byte[] vz; - vz = z; - - // Initialise the KDF. - DerivationParameters kdfParam; - if (kdf instanceof MGF1BytesGeneratorExt) { - kdfParam = new MGFParameters(vz); - } else { - kdfParam = new KDFParameters(vz, param.getDerivationV()); - } - kdf.init(kdfParam); + final var encLength = (inLen - bIn.available()); + this.v = Arrays.copyOfRange(in, inOff, inOff + encLength); + } + } - return forEncryption - ? encryptBlock(in, inOff, inLen, macData) - : decryptBlock(in, inOff, inLen, macData); + // Compute the common value and convert to byte array. + agree.init(privParam); + byte[] z = + BigIntegers.asUnsignedByteArray(agree.getFieldSize(), agree.calculateAgreement(pubParam)); + + // Create input to KDF. + byte[] vz; + vz = z; + + // Initialise the KDF. + DerivationParameters kdfParam; + if (kdf instanceof MGF1BytesGeneratorExt) { + kdfParam = new MGFParameters(vz); + } else { + kdfParam = new KDFParameters(vz, param.getDerivationV()); } + kdf.init(kdfParam); + + return forEncryption + ? encryptBlock(in, inOff, inLen, macData) + : decryptBlock(in, inOff, inLen, macData); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/KeyHandler.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/KeyHandler.java index a668b57316..eccf6bda64 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/KeyHandler.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/KeyHandler.java @@ -1,131 +1,132 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto; - -import com.radixdlt.crypto.exception.PrivateKeyException; -import com.radixdlt.crypto.exception.PublicKeyException; -import org.bouncycastle.math.ec.ECPoint; - -/** - * Interface for signature and public key computation functions. - *

- * The intent behind this interface is that the actual implementations can - * easily be replaced when required. - *

- * Note that all methods must be thread safe. - */ -interface KeyHandler { - - /** - * Sign the specified hash with the specified private key. - * - * @param hash The hash to sign - * @param privateKey The private key to sign the hash with - * @param enforceLowS If signature should enforce low values of signature part {@code S}, according to - * BIP-62 - * @param useDeterministicSignatures If signing should use randomness or be deterministic according to - * RFC6979. - * - * @return An {@link ECDSASignature} with {@code r} and {@code s} values included - */ - ECDSASignature sign(byte[] hash, byte[] privateKey, byte[] publicKey, boolean enforceLowS, boolean useDeterministicSignatures); - - /** - * Verify the specified signature against the specified hash with the - * specified public key. - * - * @param hash The hash to verify against - * @param signature The signature to verify - * @param publicKeyPoint The public key point to verify the signature with - * - * @return An boolean indicating whether the signature could be successfully validated - */ - boolean verify(byte[] hash, ECDSASignature signature, ECPoint publicKeyPoint); - - /** - * Compute a public key for the specified private key. - * - * @param privateKey The private key to compute the public key for - * - * @return A compressed public key - * - * @throws PrivateKeyException If the {@code privateKey} is invalid - * @throws PublicKeyException If computed {@code publicKey} is invalid - */ - byte[] computePublicKey(byte[] privateKey) throws PrivateKeyException, PublicKeyException; - - /** - * Sign the specified hash with the specified private by using randomness and enforced low {@code S} values, - * see documentation of {@link #sign(byte[], byte[], byte[], boolean, boolean)} for more details. - * - * @param hash The hash to sign - * @param privateKey The private key to sign the hash with - * - * @return An {@link ECDSASignature} with {@code r} and {@code s} values included - */ - default ECDSASignature sign(byte[] hash, byte[] privateKey, byte[] publicKey) { - return sign(hash, privateKey, publicKey, true, false); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto; + +import com.radixdlt.crypto.exception.PrivateKeyException; +import com.radixdlt.crypto.exception.PublicKeyException; +import org.bouncycastle.math.ec.ECPoint; + +/** + * Interface for signature and public key computation functions. + * + *

The intent behind this interface is that the actual implementations can easily be replaced + * when required. + * + *

Note that all methods must be thread safe. + */ +interface KeyHandler { + + /** + * Sign the specified hash with the specified private key. + * + * @param hash The hash to sign + * @param privateKey The private key to sign the hash with + * @param enforceLowS If signature should enforce low values of signature part {@code S}, + * according to BIP-62 + * @param useDeterministicSignatures If signing should use randomness or be deterministic + * according to RFC6979. + * @return An {@link ECDSASignature} with {@code r} and {@code s} values included + */ + ECDSASignature sign( + byte[] hash, + byte[] privateKey, + byte[] publicKey, + boolean enforceLowS, + boolean useDeterministicSignatures); + + /** + * Verify the specified signature against the specified hash with the specified public key. + * + * @param hash The hash to verify against + * @param signature The signature to verify + * @param publicKeyPoint The public key point to verify the signature with + * @return An boolean indicating whether the signature could be successfully validated + */ + boolean verify(byte[] hash, ECDSASignature signature, ECPoint publicKeyPoint); + + /** + * Compute a public key for the specified private key. + * + * @param privateKey The private key to compute the public key for + * @return A compressed public key + * @throws PrivateKeyException If the {@code privateKey} is invalid + * @throws PublicKeyException If computed {@code publicKey} is invalid + */ + byte[] computePublicKey(byte[] privateKey) throws PrivateKeyException, PublicKeyException; + + /** + * Sign the specified hash with the specified private by using randomness and enforced low {@code + * S} values, see documentation of {@link #sign(byte[], byte[], byte[], boolean, boolean)} for + * more details. + * + * @param hash The hash to sign + * @param privateKey The private key to sign the hash with + * @return An {@link ECDSASignature} with {@code r} and {@code s} values included + */ + default ECDSASignature sign(byte[] hash, byte[] privateKey, byte[] publicKey) { + return sign(hash, privateKey, publicKey, true, false); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/LinuxSecureRandom.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/LinuxSecureRandom.java index 55f417e6d9..6d845e5bd7 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/LinuxSecureRandom.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/LinuxSecureRandom.java @@ -1,21 +1,71 @@ -/* - * Copyright 2013 Google Inc. +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: * - * http://www.apache.org/licenses/LICENSE-2.0 + * radixfoundation.org/licenses/LICENSE-v1 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. */ package com.radixdlt.crypto; +import com.radixdlt.SecurityCritical; +import com.radixdlt.SecurityCritical.SecurityKind; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; @@ -25,12 +75,9 @@ import java.security.SecureRandomSpi; import java.security.Security; -import com.radixdlt.SecurityCritical; -import com.radixdlt.SecurityCritical.SecurityKind; - /** - * Implementation from - * + * Implementation from * BitcoinJ implementation * *

A SecureRandom implementation that is able to override the standard JVM provided @@ -41,65 +88,67 @@ */ @SecurityCritical(SecurityKind.RANDOMNESS) public class LinuxSecureRandom extends SecureRandomSpi { - private static final FileInputStream URANDOM; + private static final FileInputStream URANDOM; - private static class LinuxSecureRandomProvider extends Provider { - LinuxSecureRandomProvider() { - super("LinuxSecureRandom", "1.0", - "A Linux specific random number provider that uses /dev/urandom"); - put("SecureRandom.LinuxSecureRandom", LinuxSecureRandom.class.getName()); - } + private static class LinuxSecureRandomProvider extends Provider { + LinuxSecureRandomProvider() { + super( + "LinuxSecureRandom", + "1.0", + "A Linux specific random number provider that uses /dev/urandom"); + put("SecureRandom.LinuxSecureRandom", LinuxSecureRandom.class.getName()); } + } - static { - try { - File file = new File("/dev/urandom"); - // This stream is deliberately leaked. - URANDOM = new FileInputStream(file); - if (URANDOM.read() == -1) { - throw new RuntimeException("/dev/urandom not readable?"); - } - // Now override the default SecureRandom implementation with this one. - int position = Security.insertProviderAt(new LinuxSecureRandomProvider(), 1); + static { + try { + File file = new File("/dev/urandom"); + // This stream is deliberately leaked. + URANDOM = new FileInputStream(file); + if (URANDOM.read() == -1) { + throw new RuntimeException("/dev/urandom not readable?"); + } + // Now override the default SecureRandom implementation with this one. + int position = Security.insertProviderAt(new LinuxSecureRandomProvider(), 1); - if (position != -1) { - System.out.println("Secure randomness will be read from {} only. " + file.getPath()); - } else { - System.out.println("Randomness is already secure."); - } - } catch (FileNotFoundException e) { - // Should never happen. - throw new RuntimeException("/dev/urandom does not appear to exist or is not openable", e); - } catch (IOException e) { - throw new RuntimeException("/dev/urandom does not appear to be readable", e); - } + if (position != -1) { + System.out.println("Secure randomness will be read from {} only. " + file.getPath()); + } else { + System.out.println("Randomness is already secure."); + } + } catch (FileNotFoundException e) { + // Should never happen. + throw new RuntimeException("/dev/urandom does not appear to exist or is not openable", e); + } catch (IOException e) { + throw new RuntimeException("/dev/urandom does not appear to be readable", e); } + } - private final DataInputStream dis; + private final DataInputStream dis; - public LinuxSecureRandom() { - // DataInputStream is not thread safe, so each random object has its own. - dis = new DataInputStream(URANDOM); - } + public LinuxSecureRandom() { + // DataInputStream is not thread safe, so each random object has its own. + dis = new DataInputStream(URANDOM); + } - @Override - protected void engineSetSeed(byte[] bytes) { - // Ignore. - } + @Override + protected void engineSetSeed(byte[] bytes) { + // Ignore. + } - @Override - protected void engineNextBytes(byte[] bytes) { - try { - dis.readFully(bytes); // This will block until all the bytes can be read. - } catch (IOException e) { - throw new RuntimeException(e); // Fatal error. Do not attempt to recover from this. - } + @Override + protected void engineNextBytes(byte[] bytes) { + try { + dis.readFully(bytes); // This will block until all the bytes can be read. + } catch (IOException e) { + throw new RuntimeException(e); // Fatal error. Do not attempt to recover from this. } + } - @Override - protected byte[] engineGenerateSeed(int i) { - byte[] bits = new byte[i]; - engineNextBytes(bits); - return bits; - } + @Override + protected byte[] engineGenerateSeed(int i) { + byte[] bits = new byte[i]; + engineNextBytes(bits); + return bits; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/MGF1BytesGeneratorExt.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/MGF1BytesGeneratorExt.java index 7fe221a999..3f08e585e7 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/MGF1BytesGeneratorExt.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/MGF1BytesGeneratorExt.java @@ -71,70 +71,70 @@ import org.bouncycastle.crypto.params.MGFParameters; /** - * This class is borrowed from spongycastle project - * The only change made is addition of 'counterStart' parameter to - * conform to Crypto++ capabilities + * This class is borrowed from spongycastle project The only change made is addition of + * 'counterStart' parameter to conform to Crypto++ capabilities */ public final class MGF1BytesGeneratorExt implements DerivationFunction { - private Digest digest; - private byte[] seed; - private int hLen; - private int counterStart; + private Digest digest; + private byte[] seed; + private int hLen; + private int counterStart; - public MGF1BytesGeneratorExt(Digest digest, int counterStart) { - this.digest = digest; - this.hLen = digest.getDigestSize(); - this.counterStart = counterStart; - } + public MGF1BytesGeneratorExt(Digest digest, int counterStart) { + this.digest = digest; + this.hLen = digest.getDigestSize(); + this.counterStart = counterStart; + } - public void init(DerivationParameters param) { - if (!(param instanceof MGFParameters)) { - throw new IllegalArgumentException("MGF parameters required for MGF1Generator"); - } else { - this.seed = ((MGFParameters) param).getSeed(); - } + public void init(DerivationParameters param) { + if (!(param instanceof MGFParameters)) { + throw new IllegalArgumentException("MGF parameters required for MGF1Generator"); + } else { + this.seed = ((MGFParameters) param).getSeed(); } + } - public Digest getDigest() { - return this.digest; - } + public Digest getDigest() { + return this.digest; + } - private void iToOsp(int i, byte[] sp) { - sp[0] = (byte) (i >>> 24); - sp[1] = (byte) (i >>> 16); - sp[2] = (byte) (i >>> 8); - sp[3] = (byte) (i >>> 0); - } + private void iToOsp(int i, byte[] sp) { + sp[0] = (byte) (i >>> 24); + sp[1] = (byte) (i >>> 16); + sp[2] = (byte) (i >>> 8); + sp[3] = (byte) (i >>> 0); + } - public int generateBytes(byte[] out, int outOff, int len) throws DataLengthException, IllegalArgumentException { - if (out.length - len < outOff) { - throw new DataLengthException("output buffer too small"); - } else { - final var hashBuf = new byte[this.hLen]; - final var c = new byte[4]; - int counter = 0; - int hashCounter = counterStart; - this.digest.reset(); - if (len > this.hLen) { - do { - this.iToOsp(hashCounter++, c); - this.digest.update(this.seed, 0, this.seed.length); - this.digest.update(c, 0, c.length); - this.digest.doFinal(hashBuf, 0); - System.arraycopy(hashBuf, 0, out, outOff + counter * this.hLen, this.hLen); - ++counter; - } while (counter < len / this.hLen); - } + public int generateBytes(byte[] out, int outOff, int len) + throws DataLengthException, IllegalArgumentException { + if (out.length - len < outOff) { + throw new DataLengthException("output buffer too small"); + } else { + final var hashBuf = new byte[this.hLen]; + final var c = new byte[4]; + int counter = 0; + int hashCounter = counterStart; + this.digest.reset(); + if (len > this.hLen) { + do { + this.iToOsp(hashCounter++, c); + this.digest.update(this.seed, 0, this.seed.length); + this.digest.update(c, 0, c.length); + this.digest.doFinal(hashBuf, 0); + System.arraycopy(hashBuf, 0, out, outOff + counter * this.hLen, this.hLen); + ++counter; + } while (counter < len / this.hLen); + } - if (counter * this.hLen < len) { - this.iToOsp(hashCounter, c); - this.digest.update(this.seed, 0, this.seed.length); - this.digest.update(c, 0, c.length); - this.digest.doFinal(hashBuf, 0); - System.arraycopy(hashBuf, 0, out, outOff + counter * this.hLen, len - counter * this.hLen); - } + if (counter * this.hLen < len) { + this.iToOsp(hashCounter, c); + this.digest.update(this.seed, 0, this.seed.length); + this.digest.update(c, 0, c.length); + this.digest.doFinal(hashBuf, 0); + System.arraycopy(hashBuf, 0, out, outOff + counter * this.hLen, len - counter * this.hLen); + } - return len; - } + return len; } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/RadixKeyStore.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/RadixKeyStore.java index 6baabc7973..dc4544a5ea 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/RadixKeyStore.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/RadixKeyStore.java @@ -64,21 +64,6 @@ package com.radixdlt.crypto; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.asn1.sec.ECPrivateKey; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; - import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; import com.radixdlt.SecurityCritical; @@ -86,7 +71,6 @@ import com.radixdlt.crypto.exception.KeyStoreException; import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; - import java.io.Closeable; import java.io.File; import java.io.FileInputStream; @@ -110,320 +94,338 @@ import java.util.Date; import java.util.Objects; import java.util.Set; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.sec.ECPrivateKey; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; /** - * A key store that provide basic integrity checks, plus an optional level - * of security. - *

- * Each store can hold multiple names, and keys are identified by their - * unique name within the store, - *

- * Implementation note:
- * This store uses a PKCS#12 representation for the underlying storage, and - * the store requires a non-empty password to protect it. - * In order to ease unattended use, note that where a password is required, - * a {@code null}, or zero length password may be provided, in which case the - * default 5 character password, "radix" is used. Clearly this is insecure, - * and clients should make an effort to specify passwords in a secure way. + * A key store that provide basic integrity checks, plus an optional level of security. + * + *

Each store can hold multiple names, and keys are identified by their unique name within the + * store, + * + *

Implementation note:
+ * This store uses a PKCS#12 representation for the underlying storage, and the store requires a + * non-empty password to protect it. In order to ease unattended use, note that where a password is + * required, a {@code null}, or zero length password may be provided, in which case the default 5 + * character password, "radix" is used. Clearly this is insecure, and clients should make an effort + * to specify passwords in a secure way. */ @SecurityCritical(SecurityKind.KEY_STORE) -public final class RadixKeyStore implements Closeable { - // ASN.1 Object Identifiers for various things we use - private static final ASN1ObjectIdentifier OID_EC_ENCRYPTION = new ASN1ObjectIdentifier("1.2.840.10045.2.1"); - private static final ASN1ObjectIdentifier OID_SECP256K1_CURVE = new ASN1ObjectIdentifier("1.3.132.0.10"); - private static final ASN1ObjectIdentifier OID_BASIC_CONSTRAINTS = new ASN1ObjectIdentifier("2.5.29.19"); +public final class RadixKeyStore implements Closeable { + // ASN.1 Object Identifiers for various things we use + private static final ASN1ObjectIdentifier OID_EC_ENCRYPTION = + new ASN1ObjectIdentifier("1.2.840.10045.2.1"); + private static final ASN1ObjectIdentifier OID_SECP256K1_CURVE = + new ASN1ObjectIdentifier("1.3.132.0.10"); + private static final ASN1ObjectIdentifier OID_BASIC_CONSTRAINTS = + new ASN1ObjectIdentifier("2.5.29.19"); - private static final String CERT_SIGNATURE_ALG = "SHA256withECDSA"; - private static final String DEFAULT_SUBJECT_DN = "CN=Radix DLT Network, OU=Network, O=Radix DLT, C=UK"; + private static final String CERT_SIGNATURE_ALG = "SHA256withECDSA"; + private static final String DEFAULT_SUBJECT_DN = + "CN=Radix DLT Network, OU=Network, O=Radix DLT, C=UK"; - // Default key to use for key store when none is provided. - private static final char[] defaultKey = "radix".toCharArray(); + // Default key to use for key store when none is provided. + private static final char[] defaultKey = "radix".toCharArray(); - // PKCS12 has no mechanism for per-key passwords, but the JCE KeyStore requires a password - // for a PrivateKeyEntry. We work around this by providing an empty password. - private static KeyStore.PasswordProtection emptyPassword = new KeyStore.PasswordProtection(new char[0]); + // PKCS12 has no mechanism for per-key passwords, but the JCE KeyStore requires a password + // for a PrivateKeyEntry. We work around this by providing an empty password. + private static KeyStore.PasswordProtection emptyPassword = + new KeyStore.PasswordProtection(new char[0]); - // Unexpectedly, the BouncyCastleProvider.getPrivateKey(...) and getPublicKey(...) static methods - // require the BouncyCastleProvider() constructor to be called at least once, otherwise they fail. - // This looks like an implementation issue with Bouncy Castle (static data initialisation in - // constructor), but we work around it here by creating a dummy provider and then discarding it. - // This is as at BC 1.64. - static { - @SuppressWarnings("unused") - Object unused = new BouncyCastleProvider(); - } + // Unexpectedly, the BouncyCastleProvider.getPrivateKey(...) and getPublicKey(...) static methods + // require the BouncyCastleProvider() constructor to be called at least once, otherwise they fail. + // This looks like an implementation issue with Bouncy Castle (static data initialisation in + // constructor), but we work around it here by creating a dummy provider and then discarding it. + // This is as at BC 1.64. + static { + @SuppressWarnings("unused") + Object unused = new BouncyCastleProvider(); + } - /** - * Load a private key from file, and compute the public key. - *

- * Note that if {@code create} is set to {@code true}, then the file will - * be created if it does not exist. If the file is created, then it's - * permissions will be set to just {@link PosixFilePermission#OWNER_READ} - * and {@link PosixFilePermission#OWNER_WRITE} on Posix filesystems. - * - * @param file The file to load the private key from - * @param storePassword The password to use for securing the store. - * Set to {@code null} if a default password should be used. - * Note: using {@code null} effectively means there is - * no security on the underlying key store. - * @param create Set to {@code true} if the file should be created if it doesn't exist. - * @return A {@link RadixKeyStore} - * @throws IOException If reading or writing the file fails - * @throws KeyStoreException If the key read from the file is invalid - */ - public static RadixKeyStore fromFile(File file, char[] storePassword, boolean create) - throws IOException, KeyStoreException { - try { - var ks = KeyStore.getInstance("pkcs12"); - var usedStorePassword = (storePassword == null || storePassword.length == 0) - ? defaultKey - : storePassword; - initializeKeyStore(ks, file, usedStorePassword, create); - return new RadixKeyStore(file, ks, usedStorePassword.clone()); - } catch (GeneralSecurityException ex) { - throw new KeyStoreException("Can't load key store", ex); - } - } + /** + * Load a private key from file, and compute the public key. + * + *

Note that if {@code create} is set to {@code true}, then the file will be created if it does + * not exist. If the file is created, then it's permissions will be set to just {@link + * PosixFilePermission#OWNER_READ} and {@link PosixFilePermission#OWNER_WRITE} on Posix + * filesystems. + * + * @param file The file to load the private key from + * @param storePassword The password to use for securing the store. Set to {@code null} if a + * default password should be used. Note: using {@code null} effectively means there is + * no security on the underlying key store. + * @param create Set to {@code true} if the file should be created if it doesn't exist. + * @return A {@link RadixKeyStore} + * @throws IOException If reading or writing the file fails + * @throws KeyStoreException If the key read from the file is invalid + */ + public static RadixKeyStore fromFile(File file, char[] storePassword, boolean create) + throws IOException, KeyStoreException { + try { + var ks = KeyStore.getInstance("pkcs12"); + var usedStorePassword = + (storePassword == null || storePassword.length == 0) ? defaultKey : storePassword; + initializeKeyStore(ks, file, usedStorePassword, create); + return new RadixKeyStore(file, ks, usedStorePassword.clone()); + } catch (GeneralSecurityException ex) { + throw new KeyStoreException("Can't load key store", ex); + } + } - private final File file; - private final KeyStore keyStore; - private final char[] storePassword; + private final File file; + private final KeyStore keyStore; + private final char[] storePassword; - @VisibleForTesting - RadixKeyStore(File file, KeyStore keyStore, char[] storePassword) { - this.file = Objects.requireNonNull(file); - this.keyStore = Objects.requireNonNull(keyStore); - this.storePassword = Objects.requireNonNull(storePassword); - } + @VisibleForTesting + RadixKeyStore(File file, KeyStore keyStore, char[] storePassword) { + this.file = Objects.requireNonNull(file); + this.keyStore = Objects.requireNonNull(keyStore); + this.storePassword = Objects.requireNonNull(storePassword); + } - @VisibleForTesting - char[] storePassword() { - return storePassword; - } + @VisibleForTesting + char[] storePassword() { + return storePassword; + } - /** - * Reads a key pair with the specified name from the key store. - * - * @param name The name of the key in the key store. If no key with the - * given name is present, it will be created if specified, otherwise - * a {@link KeyStoreException} will be raised. - * @param create Set to {@code true} to create a new random key if the - * specified key does not exist in the store. - * @return The {@link ECKeyPair} read from the key store - * @throws KeyStoreException If an error occurs while reading the key - * @throws PrivateKeyException If an error occurs while validating the private key - * @throws PublicKeyException If an error occurs while computing the public key - */ - public ECKeyPair readKeyPair(String name, boolean create) - throws KeyStoreException, PrivateKeyException, PublicKeyException { - try { - var entry = this.keyStore.getEntry(name, emptyPassword); - if (entry == null) { - if (create) { - var newKeyPair = ECKeyPair.generateNew(); - writeKeyPair(name, newKeyPair); - return newKeyPair; - } else { - throw new KeyStoreException("No such entry: " + name); - } - } else { - return processEntry(name, entry); - } - } catch (GeneralSecurityException | IOException e) { - throw new KeyStoreException("Key store error while reading key", e); - } - } + /** + * Reads a key pair with the specified name from the key store. + * + * @param name The name of the key in the key store. If no key with the given name is present, it + * will be created if specified, otherwise a {@link KeyStoreException} will be raised. + * @param create Set to {@code true} to create a new random key if the specified key does not + * exist in the store. + * @return The {@link ECKeyPair} read from the key store + * @throws KeyStoreException If an error occurs while reading the key + * @throws PrivateKeyException If an error occurs while validating the private key + * @throws PublicKeyException If an error occurs while computing the public key + */ + public ECKeyPair readKeyPair(String name, boolean create) + throws KeyStoreException, PrivateKeyException, PublicKeyException { + try { + var entry = this.keyStore.getEntry(name, emptyPassword); + if (entry == null) { + if (create) { + var newKeyPair = ECKeyPair.generateNew(); + writeKeyPair(name, newKeyPair); + return newKeyPair; + } else { + throw new KeyStoreException("No such entry: " + name); + } + } else { + return processEntry(name, entry); + } + } catch (GeneralSecurityException | IOException e) { + throw new KeyStoreException("Key store error while reading key", e); + } + } - /** - * Writes the specified key pair identified by the specified name to the - * key store. - *

- * Implementation note:
Note that this method will create a new - * file and set it's permissions to just - * {@link PosixFilePermission#OWNER_READ} and - * {@link PosixFilePermission#OWNER_WRITE} on Posix filesystems. - * - * @param name The name the key will have in the key store. If a key - * already exists with this name, it will be overwritten. - * @param ecKeyPair The {@link ECKeyPair} to write to the store. - * @throws KeyStoreException If an error occurs writing the key - * @throws IOException If an error occurs persisting the key store - */ - public void writeKeyPair(String name, ECKeyPair ecKeyPair) throws KeyStoreException, IOException { - byte[] encodedPrivKey = null; - try { - encodedPrivKey = encodePrivKey(ecKeyPair.getPrivateKey()); - var pki = PrivateKeyInfo.getInstance(encodedPrivKey); - // Note that while PrivateKey objects are in theory destroyable, - // the BCEC implementation has not yet been updated to accommodate. - var privateKey = BouncyCastleProvider.getPrivateKey(pki); + /** + * Writes the specified key pair identified by the specified name to the key store. + * + *

Implementation note:
+ * Note that this method will create a new file and set it's permissions to just {@link + * PosixFilePermission#OWNER_READ} and {@link PosixFilePermission#OWNER_WRITE} on Posix + * filesystems. + * + * @param name The name the key will have in the key store. If a key already exists with this + * name, it will be overwritten. + * @param ecKeyPair The {@link ECKeyPair} to write to the store. + * @throws KeyStoreException If an error occurs writing the key + * @throws IOException If an error occurs persisting the key store + */ + public void writeKeyPair(String name, ECKeyPair ecKeyPair) throws KeyStoreException, IOException { + byte[] encodedPrivKey = null; + try { + encodedPrivKey = encodePrivKey(ecKeyPair.getPrivateKey()); + var pki = PrivateKeyInfo.getInstance(encodedPrivKey); + // Note that while PrivateKey objects are in theory destroyable, + // the BCEC implementation has not yet been updated to accommodate. + var privateKey = BouncyCastleProvider.getPrivateKey(pki); - var encodedPubKey = encodePubKey(ecKeyPair.getPublicKey().getBytes()); - var spki = SubjectPublicKeyInfo.getInstance(encodedPubKey); - var publicKey = BouncyCastleProvider.getPublicKey(spki); + var encodedPubKey = encodePubKey(ecKeyPair.getPublicKey().getBytes()); + var spki = SubjectPublicKeyInfo.getInstance(encodedPubKey); + var publicKey = BouncyCastleProvider.getPublicKey(spki); - var keyPair = new KeyPair(publicKey, privateKey); + var keyPair = new KeyPair(publicKey, privateKey); - Certificate[] chain = { - selfSignedCert(keyPair, 1000, DEFAULT_SUBJECT_DN) - }; - KeyStore.PrivateKeyEntry pke = new KeyStore.PrivateKeyEntry(privateKey, chain); - this.keyStore.setEntry(name, pke, emptyPassword); - writeKeyStore(this.file, this.keyStore, this.storePassword); - } catch (GeneralSecurityException ex) { - throw new KeyStoreException("Error while writing key", ex); - } finally { - if (encodedPrivKey != null) { - Arrays.fill(encodedPrivKey, (byte) 0); - } - } - } + Certificate[] chain = {selfSignedCert(keyPair, 1000, DEFAULT_SUBJECT_DN)}; + KeyStore.PrivateKeyEntry pke = new KeyStore.PrivateKeyEntry(privateKey, chain); + this.keyStore.setEntry(name, pke, emptyPassword); + writeKeyStore(this.file, this.keyStore, this.storePassword); + } catch (GeneralSecurityException ex) { + throw new KeyStoreException("Error while writing key", ex); + } finally { + if (encodedPrivKey != null) { + Arrays.fill(encodedPrivKey, (byte) 0); + } + } + } - @Override - public void close() throws IOException { - Arrays.fill(this.storePassword, ' '); - } + @Override + public void close() throws IOException { + Arrays.fill(this.storePassword, ' '); + } - @Override - public String toString() { - return String.format("%s[%s]", getClass().getSimpleName(), file.toString()); - } + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), file.toString()); + } - private ECKeyPair processEntry(String name, Entry entry) - throws KeyStoreException, PrivateKeyException, IOException, PublicKeyException { + private ECKeyPair processEntry(String name, Entry entry) + throws KeyStoreException, PrivateKeyException, IOException, PublicKeyException { - // Should not be possible to have a non-private key entry in a PKCS#12 store. - // Still do the check here anyway. - if (entry instanceof KeyStore.PrivateKeyEntry) { - KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) entry; - // Note that while PrivateKey objects are in theory destroyable, - // the BCEC implementation has not yet been updated to accommodate. - PrivateKey pk = pkEntry.getPrivateKey(); - byte[] encoded = pk.getEncoded(); - try { - return ECKeyPair.fromPrivateKey(decodePrivKey(encoded)); - } finally { - Arrays.fill(encoded, (byte) 0); - } - } else { - throw new KeyStoreException(String.format("Entry %s is not a private key: %s", name, entry.getClass())); - } - } + // Should not be possible to have a non-private key entry in a PKCS#12 store. + // Still do the check here anyway. + if (entry instanceof KeyStore.PrivateKeyEntry) { + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) entry; + // Note that while PrivateKey objects are in theory destroyable, + // the BCEC implementation has not yet been updated to accommodate. + PrivateKey pk = pkEntry.getPrivateKey(); + byte[] encoded = pk.getEncoded(); + try { + return ECKeyPair.fromPrivateKey(decodePrivKey(encoded)); + } finally { + Arrays.fill(encoded, (byte) 0); + } + } else { + throw new KeyStoreException( + String.format("Entry %s is not a private key: %s", name, entry.getClass())); + } + } - private static void initializeKeyStore(KeyStore ks, File file, char[] storePassword, boolean create) - throws GeneralSecurityException, IOException { - try (var is = new FileInputStream(file)) { - ks.load(is, storePassword); - } catch (FileNotFoundException ex) { - if (create) { - // Create a new keystore - ks.load(null, storePassword); - writeKeyStore(file, ks, storePassword); - } else { - throw ex; - } - } - } + private static void initializeKeyStore( + KeyStore ks, File file, char[] storePassword, boolean create) + throws GeneralSecurityException, IOException { + try (var is = new FileInputStream(file)) { + ks.load(is, storePassword); + } catch (FileNotFoundException ex) { + if (create) { + // Create a new keystore + ks.load(null, storePassword); + writeKeyStore(file, ks, storePassword); + } else { + throw ex; + } + } + } - /** - * Creates a self-signed certificate from the specified key-pair. - *

- * The validity period for the certificate extends from the current - * local clock time for the specified validity period in years. - * - * @param keyPair The key-pair to create the self-signed certificate for. - * @param validityYears Years The validity period in years. - * @param subjectDN The key's distinguished subject name. - * @return A self-signed certificate - * @throws KeyStoreException If an error occurs while building the certificate - * @throws IOException If an I/O error occurs while building the certificate - */ - @VisibleForTesting - static Certificate selfSignedCert(KeyPair keyPair, int validityYears, String subjectDN) throws KeyStoreException, IOException { - X500Name dnName = new X500Name(subjectDN); - LocalDateTime startDate = LocalDateTime.now(); - LocalDateTime endDate = startDate.plusYears(validityYears); - BigInteger certSerialNumber = BigInteger.valueOf(toDate(startDate).getTime()); + /** + * Creates a self-signed certificate from the specified key-pair. + * + *

The validity period for the certificate extends from the current local clock time for the + * specified validity period in years. + * + * @param keyPair The key-pair to create the self-signed certificate for. + * @param validityYears Years The validity period in years. + * @param subjectDN The key's distinguished subject name. + * @return A self-signed certificate + * @throws KeyStoreException If an error occurs while building the certificate + * @throws IOException If an I/O error occurs while building the certificate + */ + @VisibleForTesting + static Certificate selfSignedCert(KeyPair keyPair, int validityYears, String subjectDN) + throws KeyStoreException, IOException { + X500Name dnName = new X500Name(subjectDN); + LocalDateTime startDate = LocalDateTime.now(); + LocalDateTime endDate = startDate.plusYears(validityYears); + BigInteger certSerialNumber = BigInteger.valueOf(toDate(startDate).getTime()); - var certBuilder = new JcaX509v3CertificateBuilder( - dnName, - certSerialNumber, - toDate(startDate), - toDate(endDate), - dnName, - keyPair.getPublic() - ); - BasicConstraints basicConstraints = new BasicConstraints(true); - certBuilder.addExtension(OID_BASIC_CONSTRAINTS, true, basicConstraints); + var certBuilder = + new JcaX509v3CertificateBuilder( + dnName, + certSerialNumber, + toDate(startDate), + toDate(endDate), + dnName, + keyPair.getPublic()); + BasicConstraints basicConstraints = new BasicConstraints(true); + certBuilder.addExtension(OID_BASIC_CONSTRAINTS, true, basicConstraints); - try { - ContentSigner contentSigner = new JcaContentSignerBuilder(CERT_SIGNATURE_ALG).build(keyPair.getPrivate()); - return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner)); - } catch (OperatorCreationException | CertificateException ex) { - throw new KeyStoreException("Error creating certificate", ex); - } - } + try { + ContentSigner contentSigner = + new JcaContentSignerBuilder(CERT_SIGNATURE_ALG).build(keyPair.getPrivate()); + return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner)); + } catch (OperatorCreationException | CertificateException ex) { + throw new KeyStoreException("Error creating certificate", ex); + } + } - private static Date toDate(LocalDateTime dateToConvert) { - return Date.from(dateToConvert.atZone(ZoneOffset.UTC).toInstant()); - } + private static Date toDate(LocalDateTime dateToConvert) { + return Date.from(dateToConvert.atZone(ZoneOffset.UTC).toInstant()); + } - /** - * Decodes the specified ASN.1 encoded - * Private Key Info - * structure into a raw 256-bit secp256k1 private key. - * - * @param encoded The encoded private key - * @return The raw {@link ECKeyPair#BYTES} length private key. - * @throws IOException If an error occurred decoding the ASN.1 key. - * @throws KeyStoreException If the decoded key is not for the secp256k1 curve. - */ - private static byte[] decodePrivKey(byte[] encoded) throws IOException, KeyStoreException { - PrivateKeyInfo pki = PrivateKeyInfo.getInstance(encoded); - ECPrivateKey ecpk = ECPrivateKey.getInstance(pki.parsePrivateKey()); - ASN1Primitive params = ecpk.getParameters(); - if (!OID_SECP256K1_CURVE.equals(params)) { - throw new KeyStoreException("Unknown curve: " + params.toString()); - } - return ECKeyUtils.adjustArray(ecpk.getKey().toByteArray(), ECKeyPair.BYTES); - } + /** + * Decodes the specified ASN.1 encoded Private Key Info structure into a raw + * 256-bit secp256k1 private key. + * + * @param encoded The encoded private key + * @return The raw {@link ECKeyPair#BYTES} length private key. + * @throws IOException If an error occurred decoding the ASN.1 key. + * @throws KeyStoreException If the decoded key is not for the secp256k1 curve. + */ + private static byte[] decodePrivKey(byte[] encoded) throws IOException, KeyStoreException { + PrivateKeyInfo pki = PrivateKeyInfo.getInstance(encoded); + ECPrivateKey ecpk = ECPrivateKey.getInstance(pki.parsePrivateKey()); + ASN1Primitive params = ecpk.getParameters(); + if (!OID_SECP256K1_CURVE.equals(params)) { + throw new KeyStoreException("Unknown curve: " + params.toString()); + } + return ECKeyUtils.adjustArray(ecpk.getKey().toByteArray(), ECKeyPair.BYTES); + } - /** - * Encodes the specified raw secp256k1 private key into an ASN.1 encoded - * Private Key Info - * structure. - *

- * Note that the encoded key will not include a public key. - * - * @param rawkey The raw secp256k1 private key. - * @return The ASN.1 encoded private key. - * @throws IOException If an error occurs encoding the ASN.1 key. - */ - private static byte[] encodePrivKey(byte[] rawkey) throws IOException { - AlgorithmIdentifier ecSecp256k1 = new AlgorithmIdentifier(OID_EC_ENCRYPTION, OID_SECP256K1_CURVE); - BigInteger key = new BigInteger(1, rawkey); - ECPrivateKey privateKey = new ECPrivateKey(ECKeyPair.BYTES * Byte.SIZE, key, OID_SECP256K1_CURVE); - PrivateKeyInfo pki = new PrivateKeyInfo(ecSecp256k1, privateKey); - return pki.getEncoded(); - } + /** + * Encodes the specified raw secp256k1 private key into an ASN.1 encoded Private Key Info structure. + * + *

Note that the encoded key will not include a public key. + * + * @param rawkey The raw secp256k1 private key. + * @return The ASN.1 encoded private key. + * @throws IOException If an error occurs encoding the ASN.1 key. + */ + private static byte[] encodePrivKey(byte[] rawkey) throws IOException { + AlgorithmIdentifier ecSecp256k1 = + new AlgorithmIdentifier(OID_EC_ENCRYPTION, OID_SECP256K1_CURVE); + BigInteger key = new BigInteger(1, rawkey); + ECPrivateKey privateKey = + new ECPrivateKey(ECKeyPair.BYTES * Byte.SIZE, key, OID_SECP256K1_CURVE); + PrivateKeyInfo pki = new PrivateKeyInfo(ecSecp256k1, privateKey); + return pki.getEncoded(); + } - private static byte[] encodePubKey(byte[] rawkey) throws IOException { - AlgorithmIdentifier ecSecp256k1 = new AlgorithmIdentifier(OID_EC_ENCRYPTION, OID_SECP256K1_CURVE); - SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo(ecSecp256k1, rawkey); - return spki.getEncoded(); - } + private static byte[] encodePubKey(byte[] rawkey) throws IOException { + AlgorithmIdentifier ecSecp256k1 = + new AlgorithmIdentifier(OID_EC_ENCRYPTION, OID_SECP256K1_CURVE); + SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo(ecSecp256k1, rawkey); + return spki.getEncoded(); + } - private static void writeKeyStore(File file, KeyStore ks, char[] storePassword) throws GeneralSecurityException, IOException { - try (OutputStream os = new FileOutputStream(file)) { - try { - // Make some effort to make file read/writable only by owner - Set perms = - ImmutableSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE); - Files.setPosixFilePermissions(file.toPath(), perms); - } catch (UnsupportedOperationException ignoredException) { - // probably windows - } - ks.store(os, storePassword); - } - } + private static void writeKeyStore(File file, KeyStore ks, char[] storePassword) + throws GeneralSecurityException, IOException { + try (OutputStream os = new FileOutputStream(file)) { + try { + // Make some effort to make file read/writable only by owner + Set perms = + ImmutableSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(file.toPath(), perms); + } catch (UnsupportedOperationException ignoredException) { + // probably windows + } + ks.store(os, storePassword); + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SHAHashHandler.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SHAHashHandler.java index 872c191130..e23f9c758c 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SHAHashHandler.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SHAHashHandler.java @@ -64,70 +64,70 @@ package com.radixdlt.crypto; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - import com.radixdlt.SecurityCritical; import com.radixdlt.SecurityCritical.SecurityKind; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; -/** - * Collection of Hashing methods using SHA-2 family and invoking each hash function twice. - */ +/** Collection of Hashing methods using SHA-2 family and invoking each hash function twice. */ @SecurityCritical(SecurityKind.HASHING) class SHAHashHandler implements HashHandler { - // Note that default provide around 20-25% faster than Bouncy Castle. - // See jmh/org.radix.benchmark.HashBenchmark - private final ThreadLocal hash256DigesterInner = ThreadLocal.withInitial(() -> getDigester("SHA-256")); - private final ThreadLocal hash256DigesterOuter = ThreadLocal.withInitial(() -> getDigester("SHA-256")); - private final ThreadLocal hash512Digester = ThreadLocal.withInitial(() -> getDigester("SHA-512")); + // Note that default provide around 20-25% faster than Bouncy Castle. + // See jmh/org.radix.benchmark.HashBenchmark + private final ThreadLocal hash256DigesterInner = + ThreadLocal.withInitial(() -> getDigester("SHA-256")); + private final ThreadLocal hash256DigesterOuter = + ThreadLocal.withInitial(() -> getDigester("SHA-256")); + private final ThreadLocal hash512Digester = + ThreadLocal.withInitial(() -> getDigester("SHA-512")); - SHAHashHandler() { - // Nothing to do here - } + SHAHashHandler() { + // Nothing to do here + } - /** - * {@inheritDoc} - *

- * This implementation uses two rounds of SHA-2 with 256 bits of length, - * i.e. {@code SHA-256(SHA-256(data))}, in order to avoid length-extension attack - */ - @Override - public byte[] hash256(byte[] data, int offset, int length) { - return hash256Twice(data, offset, length); - } + /** + * {@inheritDoc} + * + *

This implementation uses two rounds of SHA-2 with 256 bits of length, i.e. {@code + * SHA-256(SHA-256(data))}, in order to avoid length-extension attack + */ + @Override + public byte[] hash256(byte[] data, int offset, int length) { + return hash256Twice(data, offset, length); + } - /** - * {@inheritDoc} - *

- * This implementation uses two rounds of SHA-2 with 512 bits of length, - * i.e. {@code SHA-512(SHA-512(data))}, in order to avoid length-extension attack - */ - @Override - public byte[] hash512(byte[] data, int offset, int length) { - return hash512Twice(data, offset, length); - } + /** + * {@inheritDoc} + * + *

This implementation uses two rounds of SHA-2 with 512 bits of length, i.e. {@code + * SHA-512(SHA-512(data))}, in order to avoid length-extension attack + */ + @Override + public byte[] hash512(byte[] data, int offset, int length) { + return hash512Twice(data, offset, length); + } - private byte[] hash256Twice(byte[] data, int offset, int length) { - final MessageDigest hash256DigesterOuterLocal = hash256DigesterOuter.get(); - final MessageDigest hash256DigesterInnerLocal = hash256DigesterInner.get(); - hash256DigesterOuterLocal.reset(); - hash256DigesterInnerLocal.reset(); - hash256DigesterInnerLocal.update(data, offset, length); - return hash256DigesterOuterLocal.digest(hash256DigesterInnerLocal.digest()); - } + private byte[] hash256Twice(byte[] data, int offset, int length) { + final MessageDigest hash256DigesterOuterLocal = hash256DigesterOuter.get(); + final MessageDigest hash256DigesterInnerLocal = hash256DigesterInner.get(); + hash256DigesterOuterLocal.reset(); + hash256DigesterInnerLocal.reset(); + hash256DigesterInnerLocal.update(data, offset, length); + return hash256DigesterOuterLocal.digest(hash256DigesterInnerLocal.digest()); + } - private byte[] hash512Twice(byte[] data, int offset, int length) { - final MessageDigest hash512DigesterLocal = hash512Digester.get(); - hash512DigesterLocal.reset(); - hash512DigesterLocal.update(data, offset, length); - return hash512DigesterLocal.digest(hash512DigesterLocal.digest()); - } + private byte[] hash512Twice(byte[] data, int offset, int length) { + final MessageDigest hash512DigesterLocal = hash512Digester.get(); + hash512DigesterLocal.reset(); + hash512DigesterLocal.update(data, offset, length); + return hash512DigesterLocal.digest(hash512DigesterLocal.digest()); + } - private static MessageDigest getDigester(String algorithm) { - try { - return MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException("No such algorithm: " + algorithm, e); - } - } + private static MessageDigest getDigester(String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("No such algorithm: " + algorithm, e); + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SignatureScheme.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SignatureScheme.java index cee363a144..81f28cf354 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SignatureScheme.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/SignatureScheme.java @@ -66,14 +66,15 @@ public enum SignatureScheme { - /** - * Elliptic Curve Digital Signature Algorithm, or ECDSA for short. A good introduction is to be found - * here on wikipedia. - */ - ECDSA; + /** + * Elliptic Curve Digital Signature Algorithm, or ECDSA for short. A good introduction is to be + * found here + * on wikipedia. + */ + ECDSA; - @Override - public String toString() { - return this.name().toLowerCase(); - } + @Override + public String toString() { + return this.name().toLowerCase(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/Signing.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/Signing.java index da191525d9..64a7c78951 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/Signing.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/Signing.java @@ -66,23 +66,23 @@ import com.google.common.hash.HashCode; -/** - * An entity capable of producing a signature of type {@code }. - */ +/** An entity capable of producing a signature of type {@code }. */ public interface Signing extends ECMultiplicationScalar { - /** - * Produces a signature of type {@code } of {@code hash}. - * @param hash A hash of some message to sign as a byte array. - * @return A signature of type {@code } of {@code hash}. - */ - T sign(byte[] hash); + /** + * Produces a signature of type {@code } of {@code hash}. + * + * @param hash A hash of some message to sign as a byte array. + * @return A signature of type {@code } of {@code hash}. + */ + T sign(byte[] hash); - /** - * Produces a signature of type {@code } of {@code hash}. - * @param hash A hash of some message to sign. - * @return A signature of type {@code } of {@code hash}. - */ - default T sign(HashCode hash) { - return sign(hash.asBytes()); - } + /** + * Produces a signature of type {@code } of {@code hash}. + * + * @param hash A hash of some message to sign. + * @return A signature of type {@code } of {@code hash}. + */ + default T sign(HashCode hash) { + return sign(hash.asBytes()); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/CryptoException.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/CryptoException.java index cbffd44ef7..97f3bbb51d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/CryptoException.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/CryptoException.java @@ -1,83 +1,82 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto.exception; - -@SuppressWarnings("serial") -public class CryptoException extends Exception { - public CryptoException() { - } - - public CryptoException(Throwable throwable) { - super(throwable); - } - - public CryptoException(String errorMessage) { - super(errorMessage); - } - - public CryptoException(String errorMessage, Throwable throwable) { - super(errorMessage, throwable); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto.exception; + +@SuppressWarnings("serial") +public class CryptoException extends Exception { + public CryptoException() {} + + public CryptoException(Throwable throwable) { + super(throwable); + } + + public CryptoException(String errorMessage) { + super(errorMessage); + } + + public CryptoException(String errorMessage, Throwable throwable) { + super(errorMessage, throwable); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/KeyStoreException.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/KeyStoreException.java index 979c7a96e1..c5aa092c5b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/KeyStoreException.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/KeyStoreException.java @@ -1,75 +1,75 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto.exception; - -public class KeyStoreException extends CryptoException { - public KeyStoreException(String errorMessage) { - super(errorMessage); - } - - public KeyStoreException(String errorMessage, Throwable throwable) { - super(errorMessage, throwable); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto.exception; + +public class KeyStoreException extends CryptoException { + public KeyStoreException(String errorMessage) { + super(errorMessage); + } + + public KeyStoreException(String errorMessage, Throwable throwable) { + super(errorMessage, throwable); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PrivateKeyException.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PrivateKeyException.java index 81a3298729..f785c81d62 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PrivateKeyException.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PrivateKeyException.java @@ -1,71 +1,71 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto.exception; - -public class PrivateKeyException extends CryptoException { - public PrivateKeyException(String errorMessage) { - super(errorMessage); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto.exception; + +public class PrivateKeyException extends CryptoException { + public PrivateKeyException(String errorMessage) { + super(errorMessage); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PublicKeyException.java b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PublicKeyException.java index d71b84c291..66d26baf48 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PublicKeyException.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/crypto/exception/PublicKeyException.java @@ -1,75 +1,75 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto.exception; - -public class PublicKeyException extends CryptoException { - public PublicKeyException(Throwable throwable) { - super(throwable); - } - - public PublicKeyException(String errorMessage) { - super(errorMessage); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto.exception; + +public class PublicKeyException extends CryptoException { + public PublicKeyException(Throwable throwable) { + super(throwable); + } + + public PublicKeyException(String errorMessage) { + super(errorMessage); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/errors/ApiErrors.java b/radixdlt-java-common/src/main/java/com/radixdlt/errors/ApiErrors.java index 190c7bd223..0672fffc45 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/errors/ApiErrors.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/errors/ApiErrors.java @@ -1,5 +1,4 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: @@ -67,83 +66,81 @@ import com.radixdlt.utils.functional.Failure; -/** - * Full list of API error codes. - */ +/** Full list of API error codes. */ public enum ApiErrors implements Failure { - ERROR_CALL_DATA(100, "{0}"), - ERROR_CONSTRAINT_VIOLATION(101, "{0}"), - ERROR_DEFAULT_SYSTEM_LOAN(102, "{0}"), - ERROR_DELEGATION_NOT_ALLOWED(103, "{0}"), - ERROR_INVALID_HASHED_KEY(104, "{0}"), - ERROR_INVALID_PERMISSION(105, "{0}"), - ERROR_INVALID_RESOURCE(106, "{0}"), - ERROR_INVALID_VIRTUAL_SUBSTATE(107, "{0}"), - ERROR_LOCAL_SUBSTATE_NOT_FOUND(108, "{0}"), - ERROR_MINIMUM_STAKE(109, "{0}"), - ERROR_MISMATCH(110, "{0}"), - ERROR_MISSING_PROCEDURE(111, "{0}"), - ERROR_MULTIPLE_FEE_RESERVE_DEPOSIT(112, "Multiple fee reserve deposit"), - ERROR_NOT_AUTHORIZED(113, "{0}"), - ERROR_NOT_A_RESOURCE(114, "{0}"), - ERROR_NOT_ENOUGH_RESERVE(115, "{0}"), - ERROR_NOT_ENOUGH_RESOURCES(116, "{0}"), - ERROR_PROCEDURE(117, "{0}"), - ERROR_RESERVED_SYMBOL(118, "{0}"), - ERROR_RESERVE_NOT_EMPTY(119, "{0}"), - ERROR_RESOURCE_ALLOCATION_AND_DESTRUCTION(120, "{0}"), - ERROR_SIGNED_SYSTEM(121, "{0}"), - ERROR_SUBSTATE_NOT_FOUND(122, "{0}"), - ERROR_VIRTUAL_PARENT_STATE_DOES_NOT_EXIST(123, "{0}"), - ERROR_VIRTUAL_SUBSTATE_ALREADY_DOWN(124, "{0}"), + ERROR_CALL_DATA(100, "{0}"), + ERROR_CONSTRAINT_VIOLATION(101, "{0}"), + ERROR_DEFAULT_SYSTEM_LOAN(102, "{0}"), + ERROR_DELEGATION_NOT_ALLOWED(103, "{0}"), + ERROR_INVALID_HASHED_KEY(104, "{0}"), + ERROR_INVALID_PERMISSION(105, "{0}"), + ERROR_INVALID_RESOURCE(106, "{0}"), + ERROR_INVALID_VIRTUAL_SUBSTATE(107, "{0}"), + ERROR_LOCAL_SUBSTATE_NOT_FOUND(108, "{0}"), + ERROR_MINIMUM_STAKE(109, "{0}"), + ERROR_MISMATCH(110, "{0}"), + ERROR_MISSING_PROCEDURE(111, "{0}"), + ERROR_MULTIPLE_FEE_RESERVE_DEPOSIT(112, "Multiple fee reserve deposit"), + ERROR_NOT_AUTHORIZED(113, "{0}"), + ERROR_NOT_A_RESOURCE(114, "{0}"), + ERROR_NOT_ENOUGH_RESERVE(115, "{0}"), + ERROR_NOT_ENOUGH_RESOURCES(116, "{0}"), + ERROR_PROCEDURE(117, "{0}"), + ERROR_RESERVED_SYMBOL(118, "{0}"), + ERROR_RESERVE_NOT_EMPTY(119, "{0}"), + ERROR_RESOURCE_ALLOCATION_AND_DESTRUCTION(120, "{0}"), + ERROR_SIGNED_SYSTEM(121, "{0}"), + ERROR_SUBSTATE_NOT_FOUND(122, "{0}"), + ERROR_VIRTUAL_PARENT_STATE_DOES_NOT_EXIST(123, "{0}"), + ERROR_VIRTUAL_SUBSTATE_ALREADY_DOWN(124, "{0}"), - INVALID_ACCOUNT_ADDRESS(200, "Invalid account address {0}"), - INVALID_ACTION_DATA(201, "Invalid action data {0}"), - INVALID_AID_LENGTH(202, "AID string has incorrect length {0}"), - INVALID_AID_STRING(203, "AID string is 'null'"), - INVALID_PAGE_SIZE(204, "Size {0} must be > 0"), - INVALID_PUBLIC_KEY(205, "Invalid public key {0}"), - INVALID_RESOURCE_ADDRESS(206, "Invalid resource address {0}"), - INVALID_SIGNATURE_DER(207, "Invalid signature DER {0}"), - INVALID_TX_ID(208, "Invalid TX ID {0}"), - INVALID_VALIDATOR_ADDRESS(209, "Invalid validator address {0}"), - INVALID_VALUE_OUT_OF_RANGE(210, "Parameter {0} must be between {1} and {2}"), + INVALID_ACCOUNT_ADDRESS(200, "Invalid account address {0}"), + INVALID_ACTION_DATA(201, "Invalid action data {0}"), + INVALID_AID_LENGTH(202, "AID string has incorrect length {0}"), + INVALID_AID_STRING(203, "AID string is 'null'"), + INVALID_PAGE_SIZE(204, "Size {0} must be > 0"), + INVALID_PUBLIC_KEY(205, "Invalid public key {0}"), + INVALID_RESOURCE_ADDRESS(206, "Invalid resource address {0}"), + INVALID_SIGNATURE_DER(207, "Invalid signature DER {0}"), + INVALID_TX_ID(208, "Invalid TX ID {0}"), + INVALID_VALIDATOR_ADDRESS(209, "Invalid validator address {0}"), + INVALID_VALUE_OUT_OF_RANGE(210, "Parameter {0} must be between {1} and {2}"), - MISSING_ACTION_FIELD(300, "Required field {0} is not present in action definition"), - MISSING_PARAMETER(301, "The parameter {0} is missing"), + MISSING_ACTION_FIELD(300, "Required field {0} is not present in action definition"), + MISSING_PARAMETER(301, "The parameter {0} is missing"), - MUST_MATCH_TX_ID(400, "Provided txID does not match provided transaction"), + MUST_MATCH_TX_ID(400, "Provided txID does not match provided transaction"), - UNABLE_TO_ADD_TO_MEMPOOL(500, "Unable to add transaction to mempool: mempool is full"), - UNABLE_TO_MAKE_SIGNATURE_RECOVERABLE(502, "Unable to convert signature to recoverable {0}"), - UNABLE_TO_PARSE_BOOLEAN(503, "Unable to parse boolean value: {0}"), - UNABLE_TO_PARSE_FLOAT(504, "Unable to parse float number: {0}"), - UNABLE_TO_PARSE_HEX_STRING(505, "The value {0} is not a correct hexadecimal string"), - UNABLE_TO_PARSE_INT(506, "Unable to parse integer number: {0}"), - UNABLE_TO_PARSE_UINT(507, "Unable to parse unsigned integer number: {0}"), - UNABLE_TO_PREPARE_TX(508, "Unable to prepare transaction"), - UNABLE_TO_SUBMIT_TX(510, "Transaction submission failed: {0}"), + UNABLE_TO_ADD_TO_MEMPOOL(500, "Unable to add transaction to mempool: mempool is full"), + UNABLE_TO_MAKE_SIGNATURE_RECOVERABLE(502, "Unable to convert signature to recoverable {0}"), + UNABLE_TO_PARSE_BOOLEAN(503, "Unable to parse boolean value: {0}"), + UNABLE_TO_PARSE_FLOAT(504, "Unable to parse float number: {0}"), + UNABLE_TO_PARSE_HEX_STRING(505, "The value {0} is not a correct hexadecimal string"), + UNABLE_TO_PARSE_INT(506, "Unable to parse integer number: {0}"), + UNABLE_TO_PARSE_UINT(507, "Unable to parse unsigned integer number: {0}"), + UNABLE_TO_PREPARE_TX(508, "Unable to prepare transaction"), + UNABLE_TO_SUBMIT_TX(510, "Transaction submission failed: {0}"), - UNKNOWN_ACTION(511, "Unknown action {0}"), - UNKNOWN_RRI(513, "Unknown RRI {0}"), - UNKNOWN_TX_ID(514, "Transaction with id {0} not found"), - UNSUPPORTED_ACTION(515, "Action type {0} is not supported"); + UNKNOWN_ACTION(511, "Unknown action {0}"), + UNKNOWN_RRI(513, "Unknown RRI {0}"), + UNKNOWN_TX_ID(514, "Transaction with id {0} not found"), + UNSUPPORTED_ACTION(515, "Action type {0} is not supported"); - private final int code; - private final String message; + private final int code; + private final String message; - ApiErrors(int code, String message) { - this.code = code; - this.message = message; - } + ApiErrors(int code, String message) { + this.code = code; + this.message = message; + } - @Override - public String message() { - return message; - } + @Override + public String message() { + return message; + } - @Override - public int code() { - return Category.API.forId(code); - } + @Override + public int code() { + return Category.API.forId(code); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/errors/Category.java b/radixdlt-java-common/src/main/java/com/radixdlt/errors/Category.java index 5e9d718cd7..0a62b8e5a6 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/errors/Category.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/errors/Category.java @@ -1,5 +1,4 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: @@ -65,26 +64,24 @@ package com.radixdlt.errors; -/** - * General error categories. - */ +/** General error categories. */ public enum Category { - API(1), - INTERNAL(2), - CLIENT(3), - PROTOCOL(32); + API(1), + INTERNAL(2), + CLIENT(3), + PROTOCOL(32); - private final int code; + private final int code; - Category(int code) { - this.code = code; - } + Category(int code) { + this.code = code; + } - public int code() { - return code; - } + public int code() { + return code; + } - public int forId(int id) { - return -(code * 1000 + id); - } + public int forId(int id) { + return -(code * 1000 + id); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/errors/ClientErrors.java b/radixdlt-java-common/src/main/java/com/radixdlt/errors/ClientErrors.java index a892b883d2..b357c05497 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/errors/ClientErrors.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/errors/ClientErrors.java @@ -66,38 +66,36 @@ import com.radixdlt.utils.functional.Failure; -/** - * Full list of Java client error codes. - */ +/** Full list of Java client error codes. */ public enum ClientErrors implements Failure { - INTERRUPTED_OPERATION(200, "Operation interrupted with InterruptedException. Details: {0} {1}"), + INTERRUPTED_OPERATION(200, "Operation interrupted with InterruptedException. Details: {0} {1}"), - IO_ERROR(300, "I/O Error: {0} {1}"), + IO_ERROR(300, "I/O Error: {0} {1}"), - MISSING_BASE_URL(400, "Base URL is mandatory"), + MISSING_BASE_URL(400, "Base URL is mandatory"), - SSL_ALGORITHM_ERROR(500, "SSL algorithm error: {0}"), - SSL_GENERAL_ERROR(501, "SSL algorithm error: {0}"), - SSL_KEY_ERROR(502, "SSL Key error: {0}"), + SSL_ALGORITHM_ERROR(500, "SSL algorithm error: {0}"), + SSL_GENERAL_ERROR(501, "SSL algorithm error: {0}"), + SSL_KEY_ERROR(502, "SSL Key error: {0}"), - UNABLE_TO_DESERIALIZE(600, "Unable to deserialize: {0}"), - UNABLE_TO_SERIALIZE(601, "Unable to serialize: {0}"); + UNABLE_TO_DESERIALIZE(600, "Unable to deserialize: {0}"), + UNABLE_TO_SERIALIZE(601, "Unable to serialize: {0}"); - private final int code; - private final String message; + private final int code; + private final String message; - ClientErrors(int code, String message) { - this.code = code; - this.message = message; - } + ClientErrors(int code, String message) { + this.code = code; + this.message = message; + } - @Override - public String message() { - return message; - } + @Override + public String message() { + return message; + } - @Override - public int code() { - return Category.CLIENT.forId(code); - } + @Override + public int code() { + return Category.CLIENT.forId(code); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/errors/InternalErrors.java b/radixdlt-java-common/src/main/java/com/radixdlt/errors/InternalErrors.java index f27c7a9d55..d79f57e155 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/errors/InternalErrors.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/errors/InternalErrors.java @@ -66,35 +66,33 @@ import com.radixdlt.utils.functional.Failure; -/** - * Full list of internal error codes. - */ +/** Full list of internal error codes. */ public enum InternalErrors implements Failure { - GENERAL(0, "General error (used for testing only)"), - UNKNOWN(100, "Unknown error of type {0} with message {1}"), + GENERAL(0, "General error (used for testing only)"), + UNKNOWN(100, "Unknown error of type {0} with message {1}"), - ASYNC_PROCESSING_ERROR(200, "Async processing error {0}"), + ASYNC_PROCESSING_ERROR(200, "Async processing error {0}"), - MISSING_KEYSTORE_FILE(300, "keystore file '{0}' does not exist or is not accessible"), + MISSING_KEYSTORE_FILE(300, "keystore file '{0}' does not exist or is not accessible"), - UNABLE_TO_LOAD_KEYSTORE(400, "Unable to load keystore: {0}"), - UNABLE_TO_PARSE_COMMAND_LINE(401, "Error parsing command line parameters: {0}"); + UNABLE_TO_LOAD_KEYSTORE(400, "Unable to load keystore: {0}"), + UNABLE_TO_PARSE_COMMAND_LINE(401, "Error parsing command line parameters: {0}"); - private final int code; - private final String message; + private final int code; + private final String message; - InternalErrors(int code, String message) { - this.code = code; - this.message = message; - } + InternalErrors(int code, String message) { + this.code = code; + this.message = message; + } - @Override - public String message() { - return message; - } + @Override + public String message() { + return message; + } - @Override - public int code() { - return Category.INTERNAL.forId(code); - } + @Override + public int code() { + return Category.INTERNAL.forId(code); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/errors/ProtocolErrors.java b/radixdlt-java-common/src/main/java/com/radixdlt/errors/ProtocolErrors.java index b70f091373..1168aaea3f 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/errors/ProtocolErrors.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/errors/ProtocolErrors.java @@ -1,5 +1,4 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: @@ -69,30 +68,30 @@ /** * Errors defined by JSON-RPC specification. - *

- * WARNING: New errors should be added to the end of the list to preserve error codes. + * + *

WARNING: New errors should be added to the end of the list to preserve error codes. */ public enum ProtocolErrors implements Failure { - INVALID_REQUEST(600, "Invalid request: {0}"), - INVALID_PARAMETERS(602, "Invalid parameters: {0} {1}"), - PARSE_ERROR(700, "Parse error: {0}"), - METHOD_NOT_FOUND(601, "Method {0} not found"); + INVALID_REQUEST(600, "Invalid request: {0}"), + INVALID_PARAMETERS(602, "Invalid parameters: {0} {1}"), + PARSE_ERROR(700, "Parse error: {0}"), + METHOD_NOT_FOUND(601, "Method {0} not found"); - private final int code; - private final String message; + private final int code; + private final String message; - ProtocolErrors(int code, String message) { - this.code = code; - this.message = message; - } + ProtocolErrors(int code, String message) { + this.code = code; + this.message = message; + } - @Override - public String message() { - return message; - } + @Override + public String message() { + return message; + } - @Override - public int code() { - return Category.PROTOCOL.forId(code); - } + @Override + public int code() { + return Category.PROTOCOL.forId(code); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AID.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AID.java index 017ae8cad0..8fb48a3b67 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AID.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AID.java @@ -1,250 +1,243 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.identifiers; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import com.google.common.hash.HashCode; -import com.google.common.primitives.UnsignedBytes; -import com.radixdlt.utils.Bytes; -import com.radixdlt.utils.functional.Result; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; - -import static com.radixdlt.errors.ApiErrors.INVALID_AID_LENGTH; -import static com.radixdlt.errors.ApiErrors.INVALID_AID_STRING; -import static com.radixdlt.utils.functional.Result.fromOptional; - -import static java.util.Optional.ofNullable; - -/** - * An Atom ID, made up of 256 bits of a hash. - * The Atom ID is used so that Atoms can be located using just their hid. - */ -public final class AID implements Comparable { - static final int HASH_BYTES = 32; - public static final int BYTES = HASH_BYTES; - - public static final AID ZERO = new AID(new byte[BYTES]); - - private final byte[] value; - - private AID(byte[] bytes) { - assert (bytes != null && bytes.length == HASH_BYTES); - this.value = bytes; - } - - /** - * Copies this AID to a byte array with some offset. - * Note that the array must fit the offset + AID.BYTES. - * - * @param array The array - * @param offset The offset into that array - */ - public void copyTo(byte[] array, int offset) { - Objects.requireNonNull(array, "array is required"); - if (array.length - offset < BYTES) { - throw new IllegalArgumentException( - String.format("Array must be bigger than offset + %d but was %d", BYTES, array.length) - ); - } - System.arraycopy(this.value, 0, array, offset, BYTES); - } - - public HashCode asHashCode() { - return HashCode.fromBytes(value); - } - - @JsonValue - public String toJson() { - return Bytes.toHexString(this.value); - } - - @Override - public String toString() { - return Bytes.toHexString(this.value); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof AID)) { - return false; - } - if (hashCode() != o.hashCode()) { - return false; - } - return Arrays.equals(this.value, ((AID) o).value); - } - - @Override - public int hashCode() { - return Arrays.hashCode(value); - } - - /** - * Gets the underlying bytes of this AID. - * Note that this is NOT a copy and is the actual underlying byte array. - */ - public byte[] getBytes() { - return this.value; - } - - /** - * Create an AID from its bytes - * - * @param bytes The bytes (must be of length AID.BYTES) - * - * @return An AID with those bytes - */ - public static AID from(byte[] bytes) { - return from(bytes, 0); - } - - /** - * Create an AID from a portion of a byte array - * - * @param bytes The bytes (must be of length AID.BYTES) - * @param offset The offset into the bytes array - * - * @return An AID with those bytes - */ - public static AID from(byte[] bytes, int offset) { - Objects.requireNonNull(bytes, "AID decoding error: input must not be null"); - if (offset < 0) { - throw new IllegalArgumentException("AID decoding error: offset must be >= 0: " + offset); - } - if (offset + BYTES > bytes.length) { - throw new IllegalArgumentException( - String.format("AID decoding error: length must be %d but is %d", offset + BYTES, bytes.length) - ); - } - return new AID(Arrays.copyOfRange(bytes, offset, offset + BYTES)); - } - - /** - * Create an AID from its hex bytes - * - * @param hexBytes The bytes in hex (must be of length AID.BYTES * 2) - * - * @return An AID with those bytes - */ - @JsonCreator - public static AID from(String hexBytes) { - Objects.requireNonNull(hexBytes, "hexBytes is required"); - if (hexBytes.length() != BYTES * 2) { - throw new IllegalArgumentException( - String.format("Hex bytes string length must be %d but is %d", BYTES * 2, hexBytes.length()) - ); - } - - return new AID(Bytes.fromHexString(hexBytes)); - } - - /** - * Functional style friendly version of {@link #from(String)}. - * - * @param input The string to parse - * - * @return Success {@link Result} if value can be successfully parsed and failure {@link Result} otherwise. - */ - public static Result fromString(String input) { - return fromOptional(INVALID_AID_STRING, ofNullable(input)) - .filter(bytes -> bytes.length() == BYTES * 2, INVALID_AID_LENGTH) - .map(Bytes::fromHexString) - .flatMap(AID::fromBytes); - } - - /** - * Create an AID from bytes. Unlike {@link #from(byte[])} this method does not throw an exception. - * - * @param input The bytes (must be of length AID.BYTES) - * - * @return Success result in case of successful conversion and failure result in case of error. - */ - public static Result fromBytes(byte[] input) { - return fromOptional(INVALID_AID_STRING, ofNullable(input)) - .filter(bytes -> bytes.length == HASH_BYTES, INVALID_AID_LENGTH) - .map(AID::new); - } - - @Override - public int compareTo(AID o) { - return lexicalComparator().compare(this, o); - } - - private static final class LexicalComparatorHolder { - private static final Comparator BYTES_COMPARATOR = UnsignedBytes.lexicographicalComparator(); - private static final Comparator INSTANCE = (o1, o2) -> BYTES_COMPARATOR.compare(o1.value, o2.value); - } - - /** - * Get a lexical comparator for this type. - */ - public static Comparator lexicalComparator() { - return LexicalComparatorHolder.INSTANCE; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.identifiers; + +import static com.radixdlt.errors.ApiErrors.INVALID_AID_LENGTH; +import static com.radixdlt.errors.ApiErrors.INVALID_AID_STRING; +import static com.radixdlt.utils.functional.Result.fromOptional; +import static java.util.Optional.ofNullable; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.hash.HashCode; +import com.google.common.primitives.UnsignedBytes; +import com.radixdlt.utils.Bytes; +import com.radixdlt.utils.functional.Result; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Objects; + +/** + * An Atom ID, made up of 256 bits of a hash. The Atom ID is used so that Atoms can be located using + * just their hid. + */ +public final class AID implements Comparable { + static final int HASH_BYTES = 32; + public static final int BYTES = HASH_BYTES; + + public static final AID ZERO = new AID(new byte[BYTES]); + + private final byte[] value; + + private AID(byte[] bytes) { + assert (bytes != null && bytes.length == HASH_BYTES); + this.value = bytes; + } + + /** + * Copies this AID to a byte array with some offset. Note that the array must fit the offset + + * AID.BYTES. + * + * @param array The array + * @param offset The offset into that array + */ + public void copyTo(byte[] array, int offset) { + Objects.requireNonNull(array, "array is required"); + if (array.length - offset < BYTES) { + throw new IllegalArgumentException( + String.format("Array must be bigger than offset + %d but was %d", BYTES, array.length)); + } + System.arraycopy(this.value, 0, array, offset, BYTES); + } + + public HashCode asHashCode() { + return HashCode.fromBytes(value); + } + + @JsonValue + public String toJson() { + return Bytes.toHexString(this.value); + } + + @Override + public String toString() { + return Bytes.toHexString(this.value); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof AID)) { + return false; + } + if (hashCode() != o.hashCode()) { + return false; + } + return Arrays.equals(this.value, ((AID) o).value); + } + + @Override + public int hashCode() { + return Arrays.hashCode(value); + } + + /** + * Gets the underlying bytes of this AID. Note that this is NOT a copy and is the actual + * underlying byte array. + */ + public byte[] getBytes() { + return this.value; + } + + /** + * Create an AID from its bytes + * + * @param bytes The bytes (must be of length AID.BYTES) + * @return An AID with those bytes + */ + public static AID from(byte[] bytes) { + return from(bytes, 0); + } + + /** + * Create an AID from a portion of a byte array + * + * @param bytes The bytes (must be of length AID.BYTES) + * @param offset The offset into the bytes array + * @return An AID with those bytes + */ + public static AID from(byte[] bytes, int offset) { + Objects.requireNonNull(bytes, "AID decoding error: input must not be null"); + if (offset < 0) { + throw new IllegalArgumentException("AID decoding error: offset must be >= 0: " + offset); + } + if (offset + BYTES > bytes.length) { + throw new IllegalArgumentException( + String.format( + "AID decoding error: length must be %d but is %d", offset + BYTES, bytes.length)); + } + return new AID(Arrays.copyOfRange(bytes, offset, offset + BYTES)); + } + + /** + * Create an AID from its hex bytes + * + * @param hexBytes The bytes in hex (must be of length AID.BYTES * 2) + * @return An AID with those bytes + */ + @JsonCreator + public static AID from(String hexBytes) { + Objects.requireNonNull(hexBytes, "hexBytes is required"); + if (hexBytes.length() != BYTES * 2) { + throw new IllegalArgumentException( + String.format( + "Hex bytes string length must be %d but is %d", BYTES * 2, hexBytes.length())); + } + + return new AID(Bytes.fromHexString(hexBytes)); + } + + /** + * Functional style friendly version of {@link #from(String)}. + * + * @param input The string to parse + * @return Success {@link Result} if value can be successfully parsed and failure {@link Result} + * otherwise. + */ + public static Result fromString(String input) { + return fromOptional(INVALID_AID_STRING, ofNullable(input)) + .filter(bytes -> bytes.length() == BYTES * 2, INVALID_AID_LENGTH) + .map(Bytes::fromHexString) + .flatMap(AID::fromBytes); + } + + /** + * Create an AID from bytes. Unlike {@link #from(byte[])} this method does not throw an exception. + * + * @param input The bytes (must be of length AID.BYTES) + * @return Success result in case of successful conversion and failure result in case of error. + */ + public static Result fromBytes(byte[] input) { + return fromOptional(INVALID_AID_STRING, ofNullable(input)) + .filter(bytes -> bytes.length == HASH_BYTES, INVALID_AID_LENGTH) + .map(AID::new); + } + + @Override + public int compareTo(AID o) { + return lexicalComparator().compare(this, o); + } + + private static final class LexicalComparatorHolder { + private static final Comparator BYTES_COMPARATOR = + UnsignedBytes.lexicographicalComparator(); + private static final Comparator INSTANCE = + (o1, o2) -> BYTES_COMPARATOR.compare(o1.value, o2.value); + } + + /** Get a lexical comparator for this type. */ + public static Comparator lexicalComparator() { + return LexicalComparatorHolder.INSTANCE; + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AccountAddressing.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AccountAddressing.java index cf9a18cce0..432f921efc 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AccountAddressing.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/AccountAddressing.java @@ -64,81 +64,81 @@ package com.radixdlt.identifiers; -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.Bech32; - import com.radixdlt.utils.Bits; import com.radixdlt.utils.Pair; - import java.util.Objects; import java.util.function.Function; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Bech32; /** * Bech-32 encoding/decoding of account addresses. - *

- * The human-readable part is "rdx" for mainnet, "brx" for betanet. - *

- * The data part is a conversion of the 1-34 byte Radix Engine address - * {@link com.radixdlt.identifiers.REAddr} to Base32 similar to specification described - * in BIP_0173 for converting witness programs. + * + *

The human-readable part is "rdx" for mainnet, "brx" for betanet. + * + *

The data part is a conversion of the 1-34 byte Radix Engine address {@link + * com.radixdlt.identifiers.REAddr} to Base32 similar to specification described in BIP_0173 for + * converting witness programs. */ public final class AccountAddressing { - private final String hrp; - - private AccountAddressing(String hrp) { - this.hrp = hrp; - } - - public String getHrp() { - return hrp; - } - - public static AccountAddressing bech32(String hrp) { - Objects.requireNonNull(hrp); - return new AccountAddressing(hrp); - } - - public static Pair parseUnknownHrp(String v, Function exceptionSupplier) throws X { - Bech32.Bech32Data bech32Data; - try { - bech32Data = Bech32.decode(v); - } catch (AddressFormatException e) { - throw exceptionSupplier.apply("Could not decode"); - } - - final REAddr reAddr; - try { - var addrBytes = fromBech32Data(bech32Data.data); - reAddr = REAddr.of(addrBytes); - } catch (IllegalArgumentException e) { - throw exceptionSupplier.apply("Invalid address"); - } - - if (!reAddr.isAccount()) { - throw exceptionSupplier.apply("Address is not an account"); - } - - return Pair.of(bech32Data.hrp, reAddr); - } - - private static byte[] toBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); - } - - private static byte[] fromBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); - } - - public String of(REAddr addr) { - var convert = toBech32Data(addr.getBytes()); - return Bech32.encode(hrp, convert); - } - - public REAddr parseOrThrow(String v, Function exceptionSupplier) throws X { - var p = parseUnknownHrp(v, exceptionSupplier); - if (!p.getFirst().equals(hrp)) { - throw exceptionSupplier.apply("hrp must be " + hrp + " but was " + p.getFirst()); - } - return p.getSecond(); - } + private final String hrp; + + private AccountAddressing(String hrp) { + this.hrp = hrp; + } + + public String getHrp() { + return hrp; + } + + public static AccountAddressing bech32(String hrp) { + Objects.requireNonNull(hrp); + return new AccountAddressing(hrp); + } + + public static Pair parseUnknownHrp( + String v, Function exceptionSupplier) throws X { + Bech32.Bech32Data bech32Data; + try { + bech32Data = Bech32.decode(v); + } catch (AddressFormatException e) { + throw exceptionSupplier.apply("Could not decode"); + } + + final REAddr reAddr; + try { + var addrBytes = fromBech32Data(bech32Data.data); + reAddr = REAddr.of(addrBytes); + } catch (IllegalArgumentException e) { + throw exceptionSupplier.apply("Invalid address"); + } + + if (!reAddr.isAccount()) { + throw exceptionSupplier.apply("Address is not an account"); + } + + return Pair.of(bech32Data.hrp, reAddr); + } + + private static byte[] toBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); + } + + private static byte[] fromBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); + } + + public String of(REAddr addr) { + var convert = toBech32Data(addr.getBytes()); + return Bech32.encode(hrp, convert); + } + + public REAddr parseOrThrow(String v, Function exceptionSupplier) + throws X { + var p = parseUnknownHrp(v, exceptionSupplier); + if (!p.getFirst().equals(hrp)) { + throw exceptionSupplier.apply("hrp must be " + hrp + " but was " + p.getFirst()); + } + return p.getSecond(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/EUID.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/EUID.java index f943d54f70..506c1c4f34 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/EUID.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/EUID.java @@ -1,314 +1,312 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.identifiers; - -import com.google.common.hash.HashCode; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.utils.Bytes; -import com.radixdlt.utils.UInt128; -import java.util.Arrays; -import java.util.Objects; - -public final class EUID implements Comparable { - public static EUID valueOf(String string) { - return new EUID(string); - } - - public static final EUID ZERO = new EUID(0); - public static final EUID ONE = new EUID(1); - public static final EUID TWO = new EUID(2); - - /** Size in bytes. */ - public static final int BYTES = UInt128.BYTES; - - private final UInt128 value; - - /** - * Construct {@link EUID} from a {@code String} value. - * Hex conversion is performed. - * - * @param s The string to convert to an EUID. - * @throws IllegalArgumentException If {@code s} does not contain a valid - * {@code EUID}. - */ - public EUID(String s) { - final byte[] bytes = Bytes.fromHexString(s); - - if (bytes.length != BYTES) { - throw new IllegalArgumentException("Byte length must be " + BYTES + " but was " + bytes.length); - } - - this.value = UInt128.from(bytes); - } - - public EUID(int value) { - this((long) value); - } - - public EUID(HashCode value) { - this(value.asBytes()); - } - - public EUID(long value) { - long extend = (value < 0) ? -1L : 0L; - this.value = UInt128.from(extend, value); - } - - public EUID(UInt128 value) { - this.value = Objects.requireNonNull(value); - } - - /** - * Constructor for creating an {@link EUID} from an array of bytes. - * The array is most-significant byte first, and must not be - * zero length. - *

If the array is smaller than {@link #BYTES}, then it is effectively - * padded with leading bytes with the correct sign. - *

If the array is longer than {@link #BYTES}, then values at index - * {@link #BYTES} and beyond are ignored. - * - * @param bytes The array of bytes to be used. - * @throws IllegalArgumentException if {@code bytes} is 0 bytes in length. - * @see #toByteArray() - */ - public EUID(byte[] bytes) { - Objects.requireNonNull(bytes); - if (bytes.length == 0) { - throw new IllegalArgumentException("Invalid byte length of " + bytes.length); - } - byte[] newBytes = extend(bytes); - this.value = UInt128.from(newBytes); - } - - public static EUID fromHash(HashCode hash) { - return new EUID(hash); - } - - /** - * Performs hashing on {@code bytes} and creates an EUID - * using the 256-bit digest of those bytes. - * @param bytes - * @return An EUID by taking the 256-bit digest of provided {@code bytes}. - */ - public static EUID sha256(byte[] bytes) { - return new EUID(HashUtils.sha256(bytes)); - } - - /** - * Constructor for creating an {@link EUID} from an array of bytes. - * The array is most-significant byte first. - * - * @param bytes The array of bytes to be used. - * @param offset The offset of the bytes to be used. - * @see #toByteArray() - */ - public EUID(byte[] bytes, int offset) { - Objects.requireNonNull(bytes); - this.value = UInt128.from(bytes, offset); - } - - /** - * Return {@code true} if {@code this} is equal to {@link EUID.ZERO}. - * - * @return {@code true} if {@code this} is {@code EUID.ZERO}. - */ - public boolean isZero() { - return this.value.isZero(); - } - - - /** - * Retrieve the lower 64-bit word of this {@link EUID}. - * - * @return The lower 64-bit word of this {@link EUID}. - */ - public long getLow() { - return value.getLow(); - } - - /** - * Retrieve the underlying value of this {@link EUID} - * - * @return The underlying {@link UInt128} value of this {@link EUID} - */ - public UInt128 getValue() { - return value; - } - - /** - * Calculate the routing difference between this {@link EUID} and the - * specified {@link EUID}. - *

- * Currently the algorithm is {@code numberOfLeadingZeros(this ^ other)}. - * - * @param other The {@link EUID} for which to calculate the routing - * distance from. - * @return The routing distance. - */ - public int routingDistanceFrom(EUID other) { - return this.value.xor(other.value).numberOfLeadingZeros(); - } - - /** - * Compare the distances of {@code first} and {@code second} from - * {@code this}, treating our unsigned 128-bit number system as a - * ring with values 0 and 2128-1 adjacent. - *

- * The algorithm for calculating distance between two nodes is: - *

- * distance = minunsigned((a - b) mod 2128, (b - a) mod 2128) - *
- * - * @param first The first ID to calculate distance - * @param second The second ID to calculate the distance - * @return 0 if both distances are equal, 1 if first is further - * away and -1 if first is closer. - */ - public int compareDistances(EUID first, EUID second) { - // Trivial check now for early exit. - if (first.equals(second)) { - return 0; - } - UInt128 dFirst = ringClosest(this.value, first.value); - UInt128 dSecond = ringClosest(this.value, second.value); - return dFirst.compareTo(dSecond); - } - - public int compareXorDistances(EUID id1, EUID id2) { - UInt128 d1 = this.value.xor(id1.value); - UInt128 d2 = this.value.xor(id2.value); - - int cmp = Integer.compare(d1.getLowestSetBit(), d2.getLowestSetBit()); - return (cmp == 0) ? d1.compareTo(d2) : cmp; - } - - public int xorDistance(EUID id) { - return this.value.xor(id.value).getLowestSetBit(); - } - - public byte[] toByteArray() { - return value.toByteArray(); - } - - public byte[] toByteArray(byte[] bytes, int offset) { - return value.toByteArray(bytes, offset); - } - - @Override - public int compareTo(EUID euid) { - return this.value.compareTo(euid.value); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - if (o instanceof EUID) { - EUID other = (EUID) o; - return Objects.equals(value, other.value); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hashCode(value); - } - - @Override - public String toString() { - return Bytes.toHexString(value.toByteArray()); - } - - private static UInt128 ringClosest(UInt128 a, UInt128 b) { - // Here we assume that our (unsigned) number system forms a ring, - // with 0 and 2^128-1 adjacent. - // Given this, there are two ways to get from a to b, one clockwise - // around the ring, the other anti-clockwise (for some suitable - // definition of cw/acw). - // These distances are given by (a - b) mod 2^128 - // and (b - a) mod 2^128 - // Where mod 2^128 is implicit given that we are working with - // 128 bit integers. - UInt128 d1 = b.subtract(a); - UInt128 d2 = a.subtract(b); - return d1.compareTo(d2) <= 0 ? d1 : d2; - } - - // Pad short (< BYTES length) array with appropriate lead bytes. - private static byte[] extend(byte[] bytes) { - if (bytes.length >= BYTES) { - return bytes; - } - byte[] newBytes = new byte[BYTES]; - int newPos = BYTES - bytes.length; - // Zero extension - Arrays.fill(newBytes, 0, newPos, (bytes[0] < 0) ? (byte) 0xff : (byte) 0); - System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); - return newBytes; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.identifiers; + +import com.google.common.hash.HashCode; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.utils.Bytes; +import com.radixdlt.utils.UInt128; +import java.util.Arrays; +import java.util.Objects; + +public final class EUID implements Comparable { + public static EUID valueOf(String string) { + return new EUID(string); + } + + public static final EUID ZERO = new EUID(0); + public static final EUID ONE = new EUID(1); + public static final EUID TWO = new EUID(2); + + /** Size in bytes. */ + public static final int BYTES = UInt128.BYTES; + + private final UInt128 value; + + /** + * Construct {@link EUID} from a {@code String} value. Hex conversion is performed. + * + * @param s The string to convert to an EUID. + * @throws IllegalArgumentException If {@code s} does not contain a valid {@code EUID}. + */ + public EUID(String s) { + final byte[] bytes = Bytes.fromHexString(s); + + if (bytes.length != BYTES) { + throw new IllegalArgumentException( + "Byte length must be " + BYTES + " but was " + bytes.length); + } + + this.value = UInt128.from(bytes); + } + + public EUID(int value) { + this((long) value); + } + + public EUID(HashCode value) { + this(value.asBytes()); + } + + public EUID(long value) { + long extend = (value < 0) ? -1L : 0L; + this.value = UInt128.from(extend, value); + } + + public EUID(UInt128 value) { + this.value = Objects.requireNonNull(value); + } + + /** + * Constructor for creating an {@link EUID} from an array of bytes. The array is most-significant + * byte first, and must not be zero length. + * + *

If the array is smaller than {@link #BYTES}, then it is effectively padded with leading + * bytes with the correct sign. + * + *

If the array is longer than {@link #BYTES}, then values at index {@link #BYTES} and beyond + * are ignored. + * + * @param bytes The array of bytes to be used. + * @throws IllegalArgumentException if {@code bytes} is 0 bytes in length. + * @see #toByteArray() + */ + public EUID(byte[] bytes) { + Objects.requireNonNull(bytes); + if (bytes.length == 0) { + throw new IllegalArgumentException("Invalid byte length of " + bytes.length); + } + byte[] newBytes = extend(bytes); + this.value = UInt128.from(newBytes); + } + + public static EUID fromHash(HashCode hash) { + return new EUID(hash); + } + + /** + * Performs hashing on {@code bytes} and creates an EUID using the 256-bit digest of those bytes. + * + * @param bytes + * @return An EUID by taking the 256-bit digest of provided {@code bytes}. + */ + public static EUID sha256(byte[] bytes) { + return new EUID(HashUtils.sha256(bytes)); + } + + /** + * Constructor for creating an {@link EUID} from an array of bytes. The array is most-significant + * byte first. + * + * @param bytes The array of bytes to be used. + * @param offset The offset of the bytes to be used. + * @see #toByteArray() + */ + public EUID(byte[] bytes, int offset) { + Objects.requireNonNull(bytes); + this.value = UInt128.from(bytes, offset); + } + + /** + * Return {@code true} if {@code this} is equal to {@link EUID.ZERO}. + * + * @return {@code true} if {@code this} is {@code EUID.ZERO}. + */ + public boolean isZero() { + return this.value.isZero(); + } + + /** + * Retrieve the lower 64-bit word of this {@link EUID}. + * + * @return The lower 64-bit word of this {@link EUID}. + */ + public long getLow() { + return value.getLow(); + } + + /** + * Retrieve the underlying value of this {@link EUID} + * + * @return The underlying {@link UInt128} value of this {@link EUID} + */ + public UInt128 getValue() { + return value; + } + + /** + * Calculate the routing difference between this {@link EUID} and the specified {@link EUID}. + * + *

Currently the algorithm is {@code numberOfLeadingZeros(this ^ other)}. + * + * @param other The {@link EUID} for which to calculate the routing distance from. + * @return The routing distance. + */ + public int routingDistanceFrom(EUID other) { + return this.value.xor(other.value).numberOfLeadingZeros(); + } + + /** + * Compare the distances of {@code first} and {@code second} from {@code this}, treating our + * unsigned 128-bit number system as a ring with values 0 and 2128-1 adjacent. + * + *

The algorithm for calculating distance between two nodes is: + * + *

+ * + * distance = minunsigned((a - b) mod 2128, (b - a) mod 2128) + * + *
+ * + * @param first The first ID to calculate distance + * @param second The second ID to calculate the distance + * @return 0 if both distances are equal, 1 if first is further away and -1 if first is closer. + */ + public int compareDistances(EUID first, EUID second) { + // Trivial check now for early exit. + if (first.equals(second)) { + return 0; + } + UInt128 dFirst = ringClosest(this.value, first.value); + UInt128 dSecond = ringClosest(this.value, second.value); + return dFirst.compareTo(dSecond); + } + + public int compareXorDistances(EUID id1, EUID id2) { + UInt128 d1 = this.value.xor(id1.value); + UInt128 d2 = this.value.xor(id2.value); + + int cmp = Integer.compare(d1.getLowestSetBit(), d2.getLowestSetBit()); + return (cmp == 0) ? d1.compareTo(d2) : cmp; + } + + public int xorDistance(EUID id) { + return this.value.xor(id.value).getLowestSetBit(); + } + + public byte[] toByteArray() { + return value.toByteArray(); + } + + public byte[] toByteArray(byte[] bytes, int offset) { + return value.toByteArray(bytes, offset); + } + + @Override + public int compareTo(EUID euid) { + return this.value.compareTo(euid.value); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof EUID) { + EUID other = (EUID) o; + return Objects.equals(value, other.value); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return Bytes.toHexString(value.toByteArray()); + } + + private static UInt128 ringClosest(UInt128 a, UInt128 b) { + // Here we assume that our (unsigned) number system forms a ring, + // with 0 and 2^128-1 adjacent. + // Given this, there are two ways to get from a to b, one clockwise + // around the ring, the other anti-clockwise (for some suitable + // definition of cw/acw). + // These distances are given by (a - b) mod 2^128 + // and (b - a) mod 2^128 + // Where mod 2^128 is implicit given that we are working with + // 128 bit integers. + UInt128 d1 = b.subtract(a); + UInt128 d2 = a.subtract(b); + return d1.compareTo(d2) <= 0 ? d1 : d2; + } + + // Pad short (< BYTES length) array with appropriate lead bytes. + private static byte[] extend(byte[] bytes) { + if (bytes.length >= BYTES) { + return bytes; + } + byte[] newBytes = new byte[BYTES]; + int newPos = BYTES - bytes.length; + // Zero extension + Arrays.fill(newBytes, 0, newPos, (bytes[0] < 0) ? (byte) 0xff : (byte) 0); + System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); + return newBytes; + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/NodeAddressing.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/NodeAddressing.java index a73ca47756..87c4bf0cad 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/NodeAddressing.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/NodeAddressing.java @@ -69,87 +69,86 @@ import com.radixdlt.serialization.DeserializeException; import com.radixdlt.utils.Bits; import com.radixdlt.utils.Pair; +import java.util.Objects; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Bech32; -import java.util.Objects; - /** * Bech-32 encoding/decoding of a node addresses. - *

- * The human-readable part is "rdn" ("radix node") for mainnet, "brn" ("betanet radix node") for betanet. - *

- * The data part is a conversion of the 1-34 byte Radix Engine address - * {@link REAddr} to Base32 similar to specification described - * in BIP_0173 for converting witness programs. + * + *

The human-readable part is "rdn" ("radix node") for mainnet, "brn" ("betanet radix node") for + * betanet. + * + *

The data part is a conversion of the 1-34 byte Radix Engine address {@link REAddr} to Base32 + * similar to specification described in BIP_0173 for converting witness programs. */ public final class NodeAddressing { - private final String hrp; - - private NodeAddressing(String hrp) { - this.hrp = hrp; - } - - public static NodeAddressing bech32(String hrp) { - Objects.requireNonNull(hrp); - return new NodeAddressing(hrp); - } - - public String getHrp() { - return hrp; - } - - private static byte[] toBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); - } - - private static byte[] fromBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); - } - - public String of(ECPublicKey publicKey) { - var convert = toBech32Data(publicKey.getCompressedBytes()); - return Bech32.encode(hrp, convert); - } - - public static String of(String hrp, ECPublicKey publicKey) { - var convert = toBech32Data(publicKey.getCompressedBytes()); - return Bech32.encode(hrp, convert); - } - - public ECPublicKey parse(String v) throws DeserializeException { - Bech32.Bech32Data bech32Data; - try { - bech32Data = Bech32.decode(v); - } catch (AddressFormatException e) { - throw new DeserializeException("Could not decode string: " + v, e); - } - - if (!bech32Data.hrp.equals(hrp)) { - throw new DeserializeException("hrp must be " + hrp + " but was " + bech32Data.hrp); - } - - try { - var pubKeyBytes = fromBech32Data(bech32Data.data); - return ECPublicKey.fromBytes(pubKeyBytes); - } catch (IllegalArgumentException | PublicKeyException e) { - throw new DeserializeException("Invalid address", e); - } - } - - public static Pair parseUnknownHrp(String v) throws DeserializeException { - Bech32.Bech32Data bech32Data; - try { - bech32Data = Bech32.decode(v); - } catch (AddressFormatException e) { - throw new DeserializeException("Could not decode string: " + v, e); - } - - try { - var pubKeyBytes = fromBech32Data(bech32Data.data); - return Pair.of(bech32Data.hrp, ECPublicKey.fromBytes(pubKeyBytes)); - } catch (IllegalArgumentException | PublicKeyException e) { - throw new DeserializeException("Invalid address", e); - } - } + private final String hrp; + + private NodeAddressing(String hrp) { + this.hrp = hrp; + } + + public static NodeAddressing bech32(String hrp) { + Objects.requireNonNull(hrp); + return new NodeAddressing(hrp); + } + + public String getHrp() { + return hrp; + } + + private static byte[] toBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); + } + + private static byte[] fromBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); + } + + public String of(ECPublicKey publicKey) { + var convert = toBech32Data(publicKey.getCompressedBytes()); + return Bech32.encode(hrp, convert); + } + + public static String of(String hrp, ECPublicKey publicKey) { + var convert = toBech32Data(publicKey.getCompressedBytes()); + return Bech32.encode(hrp, convert); + } + + public ECPublicKey parse(String v) throws DeserializeException { + Bech32.Bech32Data bech32Data; + try { + bech32Data = Bech32.decode(v); + } catch (AddressFormatException e) { + throw new DeserializeException("Could not decode string: " + v, e); + } + + if (!bech32Data.hrp.equals(hrp)) { + throw new DeserializeException("hrp must be " + hrp + " but was " + bech32Data.hrp); + } + + try { + var pubKeyBytes = fromBech32Data(bech32Data.data); + return ECPublicKey.fromBytes(pubKeyBytes); + } catch (IllegalArgumentException | PublicKeyException e) { + throw new DeserializeException("Invalid address", e); + } + } + + public static Pair parseUnknownHrp(String v) throws DeserializeException { + Bech32.Bech32Data bech32Data; + try { + bech32Data = Bech32.decode(v); + } catch (AddressFormatException e) { + throw new DeserializeException("Could not decode string: " + v, e); + } + + try { + var pubKeyBytes = fromBech32Data(bech32Data.data); + return Pair.of(bech32Data.hrp, ECPublicKey.fromBytes(pubKeyBytes)); + } catch (IllegalArgumentException | PublicKeyException e) { + throw new DeserializeException("Invalid address", e); + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/REAddr.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/REAddr.java index cc9361f627..a7a6fa079d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/REAddr.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/REAddr.java @@ -64,12 +64,9 @@ package com.radixdlt.identifiers; -import org.bouncycastle.util.encoders.Hex; - import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.HashUtils; import com.radixdlt.crypto.exception.PublicKeyException; - import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -77,266 +74,273 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import org.bouncycastle.util.encoders.Hex; /** - * A Radix Engine Address. A 1-34 byte array describing a resource or component - * in the Radix Engine. + * A Radix Engine Address. A 1-34 byte array describing a resource or component in the Radix Engine. * - * The first byte of the address describes the type of address followed by - * additional data depending on the type. + *

The first byte of the address describes the type of address followed by additional data + * depending on the type. * - * Type (first byte) - * 0x01 : Native Token, 0 data bytes - * 0x03 : Hashed Key+Nonce, append lower_26_bytes(sha_256_twice(33_byte_compressed_pubkey | arg_nonce)) - * 0x04 : Public Key, append 33 bytes of a compressed EC public key + *

Type (first byte) 0x01 : Native Token, 0 data bytes 0x03 : Hashed Key+Nonce, append + * lower_26_bytes(sha_256_twice(33_byte_compressed_pubkey | arg_nonce)) 0x04 : Public Key, append 33 + * bytes of a compressed EC public key */ public final class REAddr { - public enum REAddrType { - SYSTEM((byte) 0) { - public REAddr parse(ByteBuffer buf) { - return REAddr.ofSystem(); - } - - public Optional verify(ByteBuffer buf) { - if (buf.hasRemaining()) { - return Optional.of("System must not have bytes"); - } - return Optional.empty(); - } - }, - NATIVE_TOKEN((byte) 1) { - public REAddr parse(ByteBuffer buf) { - return REAddr.ofNativeToken(); - } - - public Optional verify(ByteBuffer buf) { - if (buf.hasRemaining()) { - return Optional.of("Native token must not have bytes"); - } - return Optional.empty(); - } - }, - HASHED_KEY((byte) 3) { - public REAddr parse(ByteBuffer buf) { - var addr = new byte[REAddr.HASHED_KEY_BYTES + 1]; - addr[0] = type; - buf.get(addr, 1, REAddr.HASHED_KEY_BYTES); - return new REAddr(addr); - } - - public Optional verify(ByteBuffer buf) { - if (buf.remaining() != REAddr.HASHED_KEY_BYTES) { - return Optional.of("Hashed key must have " + HASHED_KEY_BYTES + " bytes"); - } - return Optional.empty(); - } - }, - PUB_KEY((byte) 4) { - public REAddr parse(ByteBuffer buf) { - var addr = new byte[ECPublicKey.COMPRESSED_BYTES + 1]; - addr[0] = type; - buf.get(addr, 1, ECPublicKey.COMPRESSED_BYTES); - return new REAddr(addr); - } - - public Optional verify(ByteBuffer buf) { - if (buf.remaining() != ECPublicKey.COMPRESSED_BYTES) { - return Optional.of("Pub key address must have " + ECPublicKey.COMPRESSED_BYTES + " bytes"); - } - return Optional.empty(); - } - }; - - static Map opMap; - static { - opMap = Arrays.stream(REAddrType.values()) - .collect(Collectors.toMap(REAddrType::byteValue, r -> r)); - } - - final byte type; - - REAddrType(byte type) { - this.type = type; - } - - public byte byteValue() { - return type; - } - - public abstract REAddr parse(ByteBuffer buf); - - public abstract Optional verify(ByteBuffer buf); - - public static Optional parse(byte b) { - return Optional.ofNullable(opMap.get(b)); - } - } - - public static final int PUB_KEY_BYTES = ECPublicKey.COMPRESSED_BYTES + 1; - public static final int HASHED_KEY_BYTES = 26; - private final byte[] addr; - - REAddr(byte[] addr) { - this.addr = addr; - } - - private static REAddr create(byte[] hash) { - Objects.requireNonNull(hash); - if (hash.length == 0) { - throw new IllegalArgumentException(); - } - if (hash[0] == REAddrType.NATIVE_TOKEN.type) { - if (hash.length != 1) { - throw new IllegalArgumentException(); - } - } else if (hash[0] == REAddrType.HASHED_KEY.type) { - if (hash.length != 1 + HASHED_KEY_BYTES) { - throw new IllegalArgumentException(); - } - } - - return new REAddr(hash); - } - - public static byte[] pkToHash(String name, ECPublicKey publicKey) { - var nameBytes = name.getBytes(StandardCharsets.UTF_8); - var dataToHash = new byte[33 + nameBytes.length]; - System.arraycopy(publicKey.getCompressedBytes(), 0, dataToHash, 0, 33); - System.arraycopy(nameBytes, 0, dataToHash, 33, nameBytes.length); - var hash = HashUtils.sha256(dataToHash); - return Arrays.copyOfRange(hash.asBytes(), 32 - HASHED_KEY_BYTES, 32); - } - - public boolean allowToClaimAddress(ECPublicKey publicKey, byte[] arg) { - if (addr[0] == REAddrType.HASHED_KEY.type) { - var hash = REAddr.pkToHash(new String(arg), publicKey); - return Arrays.equals(addr, 1, HASHED_KEY_BYTES + 1, hash, 0, HASHED_KEY_BYTES); - } - - return false; - } - - public boolean isAccount() { - return getType() == REAddrType.PUB_KEY; - } - - public Optional publicKey() { - if (!isAccount()) { - return Optional.empty(); - } - - try { - return Optional.of(ECPublicKey.fromBytes(Arrays.copyOfRange(addr, 1, addr.length))); - } catch (PublicKeyException e) { - return Optional.empty(); - } - } - - // FIXME: Should use AuthorizationException instead but packages a bit of a mess at the moment - public static class BucketWithdrawAuthorizationException extends Exception { - private BucketWithdrawAuthorizationException(String msg) { - super(msg); - } - } - - public void verifyWithdrawAuthorization(Optional publicKey) throws BucketWithdrawAuthorizationException { - if (getType() != REAddrType.PUB_KEY) { - throw new BucketWithdrawAuthorizationException(this + " is not an account address."); - } - - if (publicKey.isEmpty()) { - throw new BucketWithdrawAuthorizationException("No key present."); - } - - if (!Arrays.equals( - addr, 1, 1 + ECPublicKey.COMPRESSED_BYTES, - publicKey.get().getCompressedBytes(), 0, ECPublicKey.COMPRESSED_BYTES - )) { - throw new BucketWithdrawAuthorizationException("Invalid key."); - } - } - - public REAddrType getType() { - return REAddrType.parse(addr[0]).orElseThrow(); - } - - public boolean isSystem() { - return addr[0] == REAddrType.SYSTEM.type; - } - - public boolean isNativeToken() { - return addr[0] == REAddrType.NATIVE_TOKEN.type; - } - - public byte[] getBytes() { - return addr; - } - - public static REAddr of(byte[] addr) { - if (addr.length == 0) { - throw new IllegalArgumentException("Address cannot be empty."); - } - var buf = ByteBuffer.wrap(addr); - var type = REAddrType.parse(buf.get()); - if (type.isEmpty()) { - throw new IllegalArgumentException("Unknown address type: " + type); - } - var error = type.get().verify(buf); - error.ifPresent(str -> { - throw new IllegalArgumentException(str); - }); - return new REAddr(addr); - } - - public static REAddr ofHashedKey(ECPublicKey key, String name) { - Objects.requireNonNull(key); - var hash = pkToHash(name, key); - var buf = ByteBuffer.allocate(HASHED_KEY_BYTES + 1); - buf.put(REAddrType.HASHED_KEY.type); - buf.put(hash); - return create(buf.array()); - } - - public static REAddr ofHashedKey(ByteBuffer readBuf) { - var buf = ByteBuffer.allocate(HASHED_KEY_BYTES + 1); - buf.put(REAddrType.HASHED_KEY.type); - buf.put(readBuf.get(HASHED_KEY_BYTES)); - return create(buf.array()); - } - - public static REAddr ofPubKeyAccount(ECPublicKey key) { - Objects.requireNonNull(key); - var buf = ByteBuffer.allocate(ECPublicKey.COMPRESSED_BYTES + 1); - buf.put(REAddrType.PUB_KEY.type); - buf.put(key.getCompressedBytes()); - return create(buf.array()); - } - - public static REAddr ofSystem() { - return create(new byte[] {REAddrType.SYSTEM.type}); - } - - public static REAddr ofNativeToken() { - return create(new byte[] {REAddrType.NATIVE_TOKEN.type}); - } - - @Override - public String toString() { - return Hex.toHexString(this.addr); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof REAddr)) { - return false; - } - - var rri = (REAddr) o; - return Arrays.equals(rri.addr, addr); - } - - @Override - public int hashCode() { - return Arrays.hashCode(addr); - } -} \ No newline at end of file + public enum REAddrType { + SYSTEM((byte) 0) { + public REAddr parse(ByteBuffer buf) { + return REAddr.ofSystem(); + } + + public Optional verify(ByteBuffer buf) { + if (buf.hasRemaining()) { + return Optional.of("System must not have bytes"); + } + return Optional.empty(); + } + }, + NATIVE_TOKEN((byte) 1) { + public REAddr parse(ByteBuffer buf) { + return REAddr.ofNativeToken(); + } + + public Optional verify(ByteBuffer buf) { + if (buf.hasRemaining()) { + return Optional.of("Native token must not have bytes"); + } + return Optional.empty(); + } + }, + HASHED_KEY((byte) 3) { + public REAddr parse(ByteBuffer buf) { + var addr = new byte[REAddr.HASHED_KEY_BYTES + 1]; + addr[0] = type; + buf.get(addr, 1, REAddr.HASHED_KEY_BYTES); + return new REAddr(addr); + } + + public Optional verify(ByteBuffer buf) { + if (buf.remaining() != REAddr.HASHED_KEY_BYTES) { + return Optional.of("Hashed key must have " + HASHED_KEY_BYTES + " bytes"); + } + return Optional.empty(); + } + }, + PUB_KEY((byte) 4) { + public REAddr parse(ByteBuffer buf) { + var addr = new byte[ECPublicKey.COMPRESSED_BYTES + 1]; + addr[0] = type; + buf.get(addr, 1, ECPublicKey.COMPRESSED_BYTES); + return new REAddr(addr); + } + + public Optional verify(ByteBuffer buf) { + if (buf.remaining() != ECPublicKey.COMPRESSED_BYTES) { + return Optional.of( + "Pub key address must have " + ECPublicKey.COMPRESSED_BYTES + " bytes"); + } + return Optional.empty(); + } + }; + + static Map opMap; + + static { + opMap = + Arrays.stream(REAddrType.values()) + .collect(Collectors.toMap(REAddrType::byteValue, r -> r)); + } + + final byte type; + + REAddrType(byte type) { + this.type = type; + } + + public byte byteValue() { + return type; + } + + public abstract REAddr parse(ByteBuffer buf); + + public abstract Optional verify(ByteBuffer buf); + + public static Optional parse(byte b) { + return Optional.ofNullable(opMap.get(b)); + } + } + + public static final int PUB_KEY_BYTES = ECPublicKey.COMPRESSED_BYTES + 1; + public static final int HASHED_KEY_BYTES = 26; + private final byte[] addr; + + REAddr(byte[] addr) { + this.addr = addr; + } + + private static REAddr create(byte[] hash) { + Objects.requireNonNull(hash); + if (hash.length == 0) { + throw new IllegalArgumentException(); + } + if (hash[0] == REAddrType.NATIVE_TOKEN.type) { + if (hash.length != 1) { + throw new IllegalArgumentException(); + } + } else if (hash[0] == REAddrType.HASHED_KEY.type) { + if (hash.length != 1 + HASHED_KEY_BYTES) { + throw new IllegalArgumentException(); + } + } + + return new REAddr(hash); + } + + public static byte[] pkToHash(String name, ECPublicKey publicKey) { + var nameBytes = name.getBytes(StandardCharsets.UTF_8); + var dataToHash = new byte[33 + nameBytes.length]; + System.arraycopy(publicKey.getCompressedBytes(), 0, dataToHash, 0, 33); + System.arraycopy(nameBytes, 0, dataToHash, 33, nameBytes.length); + var hash = HashUtils.sha256(dataToHash); + return Arrays.copyOfRange(hash.asBytes(), 32 - HASHED_KEY_BYTES, 32); + } + + public boolean allowToClaimAddress(ECPublicKey publicKey, byte[] arg) { + if (addr[0] == REAddrType.HASHED_KEY.type) { + var hash = REAddr.pkToHash(new String(arg), publicKey); + return Arrays.equals(addr, 1, HASHED_KEY_BYTES + 1, hash, 0, HASHED_KEY_BYTES); + } + + return false; + } + + public boolean isAccount() { + return getType() == REAddrType.PUB_KEY; + } + + public Optional publicKey() { + if (!isAccount()) { + return Optional.empty(); + } + + try { + return Optional.of(ECPublicKey.fromBytes(Arrays.copyOfRange(addr, 1, addr.length))); + } catch (PublicKeyException e) { + return Optional.empty(); + } + } + + // FIXME: Should use AuthorizationException instead but packages a bit of a mess at the moment + public static class BucketWithdrawAuthorizationException extends Exception { + private BucketWithdrawAuthorizationException(String msg) { + super(msg); + } + } + + public void verifyWithdrawAuthorization(Optional publicKey) + throws BucketWithdrawAuthorizationException { + if (getType() != REAddrType.PUB_KEY) { + throw new BucketWithdrawAuthorizationException(this + " is not an account address."); + } + + if (publicKey.isEmpty()) { + throw new BucketWithdrawAuthorizationException("No key present."); + } + + if (!Arrays.equals( + addr, + 1, + 1 + ECPublicKey.COMPRESSED_BYTES, + publicKey.get().getCompressedBytes(), + 0, + ECPublicKey.COMPRESSED_BYTES)) { + throw new BucketWithdrawAuthorizationException("Invalid key."); + } + } + + public REAddrType getType() { + return REAddrType.parse(addr[0]).orElseThrow(); + } + + public boolean isSystem() { + return addr[0] == REAddrType.SYSTEM.type; + } + + public boolean isNativeToken() { + return addr[0] == REAddrType.NATIVE_TOKEN.type; + } + + public byte[] getBytes() { + return addr; + } + + public static REAddr of(byte[] addr) { + if (addr.length == 0) { + throw new IllegalArgumentException("Address cannot be empty."); + } + var buf = ByteBuffer.wrap(addr); + var type = REAddrType.parse(buf.get()); + if (type.isEmpty()) { + throw new IllegalArgumentException("Unknown address type: " + type); + } + var error = type.get().verify(buf); + error.ifPresent( + str -> { + throw new IllegalArgumentException(str); + }); + return new REAddr(addr); + } + + public static REAddr ofHashedKey(ECPublicKey key, String name) { + Objects.requireNonNull(key); + var hash = pkToHash(name, key); + var buf = ByteBuffer.allocate(HASHED_KEY_BYTES + 1); + buf.put(REAddrType.HASHED_KEY.type); + buf.put(hash); + return create(buf.array()); + } + + public static REAddr ofHashedKey(ByteBuffer readBuf) { + var buf = ByteBuffer.allocate(HASHED_KEY_BYTES + 1); + buf.put(REAddrType.HASHED_KEY.type); + buf.put(readBuf.get(HASHED_KEY_BYTES)); + return create(buf.array()); + } + + public static REAddr ofPubKeyAccount(ECPublicKey key) { + Objects.requireNonNull(key); + var buf = ByteBuffer.allocate(ECPublicKey.COMPRESSED_BYTES + 1); + buf.put(REAddrType.PUB_KEY.type); + buf.put(key.getCompressedBytes()); + return create(buf.array()); + } + + public static REAddr ofSystem() { + return create(new byte[] {REAddrType.SYSTEM.type}); + } + + public static REAddr ofNativeToken() { + return create(new byte[] {REAddrType.NATIVE_TOKEN.type}); + } + + @Override + public String toString() { + return Hex.toHexString(this.addr); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof REAddr)) { + return false; + } + + var rri = (REAddr) o; + return Arrays.equals(rri.addr, addr); + } + + @Override + public int hashCode() { + return Arrays.hashCode(addr); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ResourceAddressing.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ResourceAddressing.java index 41094a0a92..a839d969d8 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ResourceAddressing.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ResourceAddressing.java @@ -64,73 +64,72 @@ package com.radixdlt.identifiers; -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.Bech32; - import com.radixdlt.utils.Bits; import com.radixdlt.utils.Pair; - import java.util.function.Function; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Bech32; /** - * A Radix resource identifier which encodes addresses with a resource behind them in - * Bech-32 encoding. + * A Radix resource identifier which encodes addresses with a resource behind them in Bech-32 + * encoding. * - * The human-readable part is the alphanumeric argument provided when creating the - * resource followed by "_rb" for betanet and "_rr" for mainnet. + *

The human-readable part is the alphanumeric argument provided when creating the resource + * followed by "_rb" for betanet and "_rr" for mainnet. * - * The data part is a conversion of the 1-34 byte Radix Engine address - * {@link com.radixdlt.identifiers.REAddr} to Base32 similar to specification described - * in BIP_0173 for converting witness programs. + *

The data part is a conversion of the 1-34 byte Radix Engine address {@link + * com.radixdlt.identifiers.REAddr} to Base32 similar to specification described in BIP_0173 for + * converting witness programs. */ public final class ResourceAddressing { - private final String hrpSuffix; + private final String hrpSuffix; - private ResourceAddressing(String hrpSuffix) { - this.hrpSuffix = hrpSuffix; - } + private ResourceAddressing(String hrpSuffix) { + this.hrpSuffix = hrpSuffix; + } - public static ResourceAddressing bech32(String hrpSuffix) { - return new ResourceAddressing(hrpSuffix); - } + public static ResourceAddressing bech32(String hrpSuffix) { + return new ResourceAddressing(hrpSuffix); + } - public String getHrpSuffix() { - return hrpSuffix; - } + public String getHrpSuffix() { + return hrpSuffix; + } - private static byte[] toBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); - } + private static byte[] toBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); + } - private static byte[] fromBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); - } + private static byte[] fromBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); + } - public Pair parseOrThrow(String rri, Function exceptionSupplier) throws X { - Bech32.Bech32Data bech32Data; - try { - bech32Data = Bech32.decode(rri); - } catch (AddressFormatException e) { - throw exceptionSupplier.apply("Could not decode"); - } + public Pair parseOrThrow( + String rri, Function exceptionSupplier) throws X { + Bech32.Bech32Data bech32Data; + try { + bech32Data = Bech32.decode(rri); + } catch (AddressFormatException e) { + throw exceptionSupplier.apply("Could not decode"); + } - if (!bech32Data.hrp.endsWith(hrpSuffix)) { - throw exceptionSupplier.apply("Address hrp suffix must be " + hrpSuffix + "(" + rri + ")"); - } + if (!bech32Data.hrp.endsWith(hrpSuffix)) { + throw exceptionSupplier.apply("Address hrp suffix must be " + hrpSuffix + "(" + rri + ")"); + } - var symbol = bech32Data.hrp.substring(0, bech32Data.hrp.length() - hrpSuffix.length()); - var addrBytes = fromBech32Data(bech32Data.data); - try { - var readdr = REAddr.of(addrBytes); - return Pair.of(symbol, readdr); - } catch (IllegalArgumentException e) { - throw exceptionSupplier.apply(e.getMessage()); - } - } + var symbol = bech32Data.hrp.substring(0, bech32Data.hrp.length() - hrpSuffix.length()); + var addrBytes = fromBech32Data(bech32Data.data); + try { + var readdr = REAddr.of(addrBytes); + return Pair.of(symbol, readdr); + } catch (IllegalArgumentException e) { + throw exceptionSupplier.apply(e.getMessage()); + } + } - public String of(String symbol, REAddr addr) { - var addrBytes = addr.getBytes(); - var bech32Data = toBech32Data(addrBytes); - return Bech32.encode(symbol + hrpSuffix, bech32Data); - } -} \ No newline at end of file + public String of(String symbol, REAddr addr) { + var addrBytes = addr.getBytes(); + var bech32Data = toBech32Data(addrBytes); + return Bech32.encode(symbol + hrpSuffix, bech32Data); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ValidatorAddressing.java b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ValidatorAddressing.java index d6331d6b42..f7d8c85b59 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ValidatorAddressing.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/identifiers/ValidatorAddressing.java @@ -64,69 +64,69 @@ package com.radixdlt.identifiers; -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.Bech32; - import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.utils.Bits; - import java.util.Objects; import java.util.function.Function; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Bech32; /** - * Bech-32 encoding/decoding of validators. Validators are represented as 33-byte - * compressed EC Public Keys. - *

- * The data part is a conversion of the 33 byte compressed EC public key to Base32 - * similar to specification described in BIP_0173 for converting witness programs. + * Bech-32 encoding/decoding of validators. Validators are represented as 33-byte compressed EC + * Public Keys. + * + *

The data part is a conversion of the 33 byte compressed EC public key to Base32 similar to + * specification described in BIP_0173 for converting witness programs. */ public final class ValidatorAddressing { - private final String hrp; - private ValidatorAddressing(String hrp) { - this.hrp = hrp; - } + private final String hrp; + + private ValidatorAddressing(String hrp) { + this.hrp = hrp; + } - public String getHrp() { - return hrp; - } + public String getHrp() { + return hrp; + } - public static ValidatorAddressing bech32(String hrp) { - Objects.requireNonNull(hrp); - return new ValidatorAddressing(hrp); - } + public static ValidatorAddressing bech32(String hrp) { + Objects.requireNonNull(hrp); + return new ValidatorAddressing(hrp); + } - private static byte[] toBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); - } + private static byte[] toBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 8, 5, true); + } - private static byte[] fromBech32Data(byte[] bytes) { - return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); - } + private static byte[] fromBech32Data(byte[] bytes) { + return Bits.convertBits(bytes, 0, bytes.length, 5, 8, false); + } - public String of(ECPublicKey key) { - var bytes = key.getCompressedBytes(); - var convert = toBech32Data(bytes); - return Bech32.encode(hrp, convert); - } + public String of(ECPublicKey key) { + var bytes = key.getCompressedBytes(); + var convert = toBech32Data(bytes); + return Bech32.encode(hrp, convert); + } - public ECPublicKey parseOrThrow(String v, Function exceptionSupplier) throws X { - Bech32.Bech32Data bech32Data; - try { - bech32Data = Bech32.decode(v); - } catch (AddressFormatException e) { - throw exceptionSupplier.apply("Could not decode"); - } + public ECPublicKey parseOrThrow( + String v, Function exceptionSupplier) throws X { + Bech32.Bech32Data bech32Data; + try { + bech32Data = Bech32.decode(v); + } catch (AddressFormatException e) { + throw exceptionSupplier.apply("Could not decode"); + } - if (!bech32Data.hrp.equals(hrp)) { - throw exceptionSupplier.apply("hrp must be " + this.hrp + " but was " + bech32Data.hrp); - } + if (!bech32Data.hrp.equals(hrp)) { + throw exceptionSupplier.apply("hrp must be " + this.hrp + " but was " + bech32Data.hrp); + } - var keyBytes = fromBech32Data(bech32Data.data); - try { - return ECPublicKey.fromBytes(keyBytes); - } catch (PublicKeyException e) { - throw exceptionSupplier.apply("Validator address does not contain a valid public key"); - } - } + var keyBytes = fromBech32Data(bech32Data.data); + try { + return ECPublicKey.fromBytes(keyBytes); + } catch (PublicKeyException e) { + throw exceptionSupplier.apply("Validator address does not contain a valid public key"); + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/networks/AddressType.java b/radixdlt-java-common/src/main/java/com/radixdlt/networks/AddressType.java index 47c472da33..9a75e0abc0 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/networks/AddressType.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/networks/AddressType.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -64,8 +65,8 @@ package com.radixdlt.networks; public enum AddressType { - ACCOUNT, - VALIDATOR, - RESOURCE, - NODE; + ACCOUNT, + VALIDATOR, + RESOURCE, + NODE; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/networks/Addressing.java b/radixdlt-java-common/src/main/java/com/radixdlt/networks/Addressing.java index c52fe8fb0f..5ab7b1dd5e 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/networks/Addressing.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/networks/Addressing.java @@ -68,99 +68,100 @@ import com.radixdlt.identifiers.NodeAddressing; import com.radixdlt.identifiers.ResourceAddressing; import com.radixdlt.identifiers.ValidatorAddressing; +import java.util.Optional; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Bech32; -import java.util.Optional; - public final class Addressing { - public static String accountHrp(int networkId) { - return Network.ofId(networkId).map(Network::getAccountHrp) - .orElse(Network.STOKENET.getAccountHrp() + networkId); - } - - public static String validatorHrp(int networkId) { - return Network.ofId(networkId).map(Network::getValidatorHrp) - .orElse(Network.STOKENET.getValidatorHrp() + networkId); - } - - public static String resourceHrpSuffix(int networkId) { - return Network.ofId(networkId).map(Network::getResourceHrpSuffix) - .orElse(Network.STOKENET.getResourceHrpSuffix() + networkId); - } - - public static String nodeHrp(int networkId) { - return Network.ofId(networkId).map(Network::getNodeHrp) - .orElse(Network.STOKENET.getNodeHrp() + networkId); - } - - private final ValidatorAddressing validatorAddressing; - private final AccountAddressing accountAddressing; - private final ResourceAddressing resourceAddressing; - private final NodeAddressing nodeAddressing; - - private Addressing( - ValidatorAddressing validatorAddressing, - AccountAddressing accountAddressing, - ResourceAddressing resourceAddressing, - NodeAddressing nodeAddressing - ) { - this.validatorAddressing = validatorAddressing; - this.accountAddressing = accountAddressing; - this.resourceAddressing = resourceAddressing; - this.nodeAddressing = nodeAddressing; - } - - public static Addressing ofNetwork(Network network) { - return ofNetworkId(network.getId()); - } - - public static Addressing ofNetworkId(int networkId) { - return new Addressing( - ValidatorAddressing.bech32(validatorHrp(networkId)), - AccountAddressing.bech32(accountHrp(networkId)), - ResourceAddressing.bech32(resourceHrpSuffix(networkId)), - NodeAddressing.bech32(nodeHrp(networkId)) - ); - } - - public Optional getAddressType(String address) { - Bech32.Bech32Data data; - try { - data = Bech32.decode(address); - } catch (AddressFormatException e) { - return Optional.empty(); - } - - if (data.hrp.startsWith(validatorAddressing.getHrp())) { - return Optional.of(AddressType.VALIDATOR); - } - if (data.hrp.startsWith(accountAddressing.getHrp())) { - return Optional.of(AddressType.ACCOUNT); - } - if (data.hrp.endsWith(resourceAddressing.getHrpSuffix())) { - return Optional.of(AddressType.RESOURCE); - } - if (data.hrp.startsWith(nodeAddressing.getHrp())) { - return Optional.of(AddressType.NODE); - } - return Optional.empty(); - } - - public ValidatorAddressing forValidators() { - return validatorAddressing; - } - - public AccountAddressing forAccounts() { - return accountAddressing; - } - - public ResourceAddressing forResources() { - return resourceAddressing; - } - - public NodeAddressing forNodes() { - return nodeAddressing; - } + public static String accountHrp(int networkId) { + return Network.ofId(networkId) + .map(Network::getAccountHrp) + .orElse(Network.STOKENET.getAccountHrp() + networkId); + } + + public static String validatorHrp(int networkId) { + return Network.ofId(networkId) + .map(Network::getValidatorHrp) + .orElse(Network.STOKENET.getValidatorHrp() + networkId); + } + + public static String resourceHrpSuffix(int networkId) { + return Network.ofId(networkId) + .map(Network::getResourceHrpSuffix) + .orElse(Network.STOKENET.getResourceHrpSuffix() + networkId); + } + + public static String nodeHrp(int networkId) { + return Network.ofId(networkId) + .map(Network::getNodeHrp) + .orElse(Network.STOKENET.getNodeHrp() + networkId); + } + + private final ValidatorAddressing validatorAddressing; + private final AccountAddressing accountAddressing; + private final ResourceAddressing resourceAddressing; + private final NodeAddressing nodeAddressing; + + private Addressing( + ValidatorAddressing validatorAddressing, + AccountAddressing accountAddressing, + ResourceAddressing resourceAddressing, + NodeAddressing nodeAddressing) { + this.validatorAddressing = validatorAddressing; + this.accountAddressing = accountAddressing; + this.resourceAddressing = resourceAddressing; + this.nodeAddressing = nodeAddressing; + } + + public static Addressing ofNetwork(Network network) { + return ofNetworkId(network.getId()); + } + + public static Addressing ofNetworkId(int networkId) { + return new Addressing( + ValidatorAddressing.bech32(validatorHrp(networkId)), + AccountAddressing.bech32(accountHrp(networkId)), + ResourceAddressing.bech32(resourceHrpSuffix(networkId)), + NodeAddressing.bech32(nodeHrp(networkId))); + } + + public Optional getAddressType(String address) { + Bech32.Bech32Data data; + try { + data = Bech32.decode(address); + } catch (AddressFormatException e) { + return Optional.empty(); + } + + if (data.hrp.startsWith(validatorAddressing.getHrp())) { + return Optional.of(AddressType.VALIDATOR); + } + if (data.hrp.startsWith(accountAddressing.getHrp())) { + return Optional.of(AddressType.ACCOUNT); + } + if (data.hrp.endsWith(resourceAddressing.getHrpSuffix())) { + return Optional.of(AddressType.RESOURCE); + } + if (data.hrp.startsWith(nodeAddressing.getHrp())) { + return Optional.of(AddressType.NODE); + } + return Optional.empty(); + } + + public ValidatorAddressing forValidators() { + return validatorAddressing; + } + + public AccountAddressing forAccounts() { + return accountAddressing; + } + + public ResourceAddressing forResources() { + return resourceAddressing; + } + + public NodeAddressing forNodes() { + return nodeAddressing; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/networks/Network.java b/radixdlt-java-common/src/main/java/com/radixdlt/networks/Network.java index c597d9192e..61dca53df4 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/networks/Network.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/networks/Network.java @@ -70,84 +70,89 @@ import java.util.stream.Stream; public enum Network { - MAINNET(1, "rdx", "rv", "_rr", "rn", mainnetGenesis()), - STOKENET(2, "tdx", "tv", "_tr", "tn", stokenetGenesis()), - RELEASENET(3, "tdx3", "tv3", "_tr3", "tn3"), - RCNET(4, "tdx4", "tv4", "_tr4", "tn4"), - MILESTONENET(5, "tdx5", "tv5", "_tr5", "tn5"), - DEVOPSNET(6, "tdx6", "tv6", "_tr6", "tn6"), - SANDPITNET(7, "tdx7", "tv7", "_tr7", "tn7"), - LOCALNET(99, "ddx", "dv", "_dr", "dn"); - - private static String mainnetGenesis() { - return "02000300000100010004027379730a000300000002000a03000000000000000000020012020000000000000000000000017ae473072002000300000b02000300000c02000300000e02000300000f02000300001002000300001102000300001200010004027872640a000300000102004604000100000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000000000020102050001000378726400055261646978009e546865205261646978205075626c6963204e6574776f726b2773206e617469766520746f6b656e2c207573656420746f2070617920746865206e6574776f726b2773207265717569726564207472616e73616374696f6e206665657320616e6420746f2073656375726520746865206e6574776f726b207468726f756768207374616b696e6720746f206974732076616c696461746f72206e6f6465732e001b68747470733a2f2f746f6b656e732e7261646978646c742e636f6d003468747470733a2f2f6173736574732e7261646978646c742e636f6d2f69636f6e732f69636f6e2d7872642d33327833322e706e670002004506000402aa1c49af92a39d15257d7dc43f805f18f9f0ea712450c53e75255f5a0d1d93b601000000000000000000000000000000000000000007c13bc4b2c133c56000000000020045060004022e258dc77f18775c12ff7aad1575fc0d172a62687b6451438a29d8e0b7e2ca0b01000000000000000000000000000000000000000005c8a6bc554c114a748000000002004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f010000000000000000000000000000000000000000000000821ab0d441498000000002004506000403385b5d3a9e7934c11bc8fac83260188a26d6763950465eb0fc152f34d1c5323d010000000000000000000000000000000000000000019d971e4fe8401e740000000002004506000403d865a0c06935c72d89f322aedbb2773e05648c96f48c823712a612c02d8feef4010000000000000000000000000000000000000000019d971e4fe8401e740000000002004506000403d672ccf7644bc62fcad284433b162593b5b627f6f967f2f46ea956de21c6c48301000000000000000000000000000000000000000007c13bc4b2c133c5600000000002004506000403741961b0ce6771d638cd37b9b00c032817857970d24eb9111fba6c8d56d2e9f30100000000000000000000000000000000000000000e3fddd3087635877a000000000a00230006038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee04000102002d0f00010000000000000001038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee01000a0023000602e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a04000102002d0f0001000000000000000102e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a01000a002300060383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6804000102002d0f000100000000000000010383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6801000a00230006026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203004000102002d0f00010000000000000001026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203001000a002300060249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c04000102002d0f000100000000000000010249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c01000a0023000602d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898604000102002d0f0001000000000000000102d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898601000a00230006020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce04000102002d0f00010000000000000001020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce01000a0023000602d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496704000102002d0f0001000000000000000102d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496701000a00230006037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619504000102002d0f00010000000000000001037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619501000a0023000603ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f104000102002d0f0001000000000000000103ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f101000a0023000602059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b504000102002d0f0001000000000000000102059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b501000a0023000602a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e404000102002d0f0001000000000000000102a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e401000a0023000602546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109704000102002d0f0001000000000000000102546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109701000a00230006028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed04000102002d0f00010000000000000001028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed01000a00230006021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b04000102002d0f00010000000000000001021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b01000a0023000603f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c04000102002d0f0001000000000000000103f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c01000a002300060219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d804000102002d0f000100000000000000010219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d801000a002300060256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e204000102002d0f000100000000000000010256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e201000a0023000603d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db604000102002d0f0001000000000000000103d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db601000a0023000603c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227604000102002d0f0001000000000000000103c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227601000a002300060343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d04000102002d0f000100000000000000010343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d01000a00230006038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9604000102002d0f00010000000000000001038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9601000a0023000603d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89704000102002d0f0001000000000000000103d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89701000a002300060230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f04000102002d0f000100000000000000010230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f01000a00230005038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0200240e00038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee01000a0023000502e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0200240e0002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a01000a002300050383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680200240e000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6801000a00230005026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300200240e00026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203001000a002300050249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0200240e000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c01000a0023000502d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860200240e0002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898601000a00230005020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0200240e00020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce01000a0023000502d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670200240e0002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496701000a00230005037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950200240e00037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619501000a0023000503ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10200240e0003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f101000a0023000502059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50200240e0002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b501000a0023000502a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40200240e0002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e401000a0023000502546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970200240e0002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109701000a00230005028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0200240e00028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed01000a00230005021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0200240e00021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b01000a0023000503f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0200240e0003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c01000a002300050219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80200240e000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d801000a002300050256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20200240e000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e201000a0023000503d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60200240e0003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db601000a0023000503c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760200240e0003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227601000a002300050343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0200240e000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d01000a00230005038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960200240e00038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9601000a0023000503d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970200240e0003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89701000a002300050230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0200240e000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f010008000e02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000007caee97613e670000004002b0200650700038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004302004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000077432217e68360000004002c020065070002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004502004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000071d75ab9b92050000004002d02006507000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004702004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000006c6b935b8bbd40000004002e0200650700026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004902004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000066ffcbfd5e5a30000004002f02006507000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004b02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000006194049f30f7200000040030020065070002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004d02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000005c283d4103941000000400310200650700020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004f02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000056bc75e2d631000000040032020065070002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005102004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000005150ae84a8cdf000000400330200650700037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005302004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000004be4e7267b6ae00000040034020065070003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005502004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000046791fc84e07d00000040035020065070002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005702004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f010000000000000000000000000000000000000000000000410d586a20a4c00000040036020065070002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005902004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000003ba1910bf341b00000040037020065070002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005b02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000003635c9adc5dea000000400380200650700028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005d02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000030ca024f987b9000000400390200650700021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005f02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000002b5e3af16b1880000004003a020065070003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006102004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000025f273933db570000004003b02006507000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006302004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000002086ac35105260000004003c02006507000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006502004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000001b1ae4d6e2ef50000004003d020065070003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006702004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000015af1d78b58c40000004003e020065070003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006902004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000001043561a882930000004003f02006507000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006b02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000000ad78ebc5ac62000000400400200650700038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006d02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f010000000000000000000000000000000000000000000000056bc75e2d63100000040041020065070003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006f04004202006507000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008002b0200240e00038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee000008002c0200240e0002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a000008002d0200240e000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f68000008002e0200240e00026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef02030000008002f0200240e000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c00000800300200240e0002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898600000800310200240e00020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce00000800320200240e0002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496700000800330200240e00037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619500000800340200240e0003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f100000800350200240e0002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b500000800360200240e0002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e400000800370200240e0002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109700000800380200240e00028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed00000800390200240e00021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b000008003a0200240e0003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c000008003b0200240e000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d8000008003c0200240e000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e2000008003d0200240e0003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db6000008003e0200240e0003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa12276000008003f0200240e000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d00000800400200240e00038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9600000800410200240e0003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89700000800420200240e000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f00000800020800010f000a0a0000000000000000010f00010d0f0001090f0001070a0023000402059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b5020065080002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0200650800020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d802006508000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0200650800021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f02006508000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c02006508000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c1097020065080002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e202006508000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300200650800026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0200650800028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e4020065080002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc9168654967020065080002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a8986020065080002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a020065080002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d02006508000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950200650800037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6802006508000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0200650800038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960200650800038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa12276020065080003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db6020065080003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae897020065080003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f1020065080003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c020065080003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000f000b10000100000000000000010f000b11000100000000000000010f000b0f0001000000000000000102002d0f0000000000000000000002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50102002d0f00000000000000000000020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0102002d0f000000000000000000000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80102002d0f00000000000000000000021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0102002d0f000000000000000000000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0102002d0f000000000000000000000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0102002d0f0000000000000000000002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970102002d0f000000000000000000000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20102002d0f00000000000000000000026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300102002d0f00000000000000000000028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0102002d0f0000000000000000000002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40102002d0f0000000000000000000002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670102002d0f0000000000000000000002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860102002d0f0000000000000000000002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0102002d0f000000000000000000000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0102002d0f00000000000000000000037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950102002d0f000000000000000000000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680102002d0f00000000000000000000038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0102002d0f00000000000000000000038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960102002d0f0000000000000000000003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760102002d0f0000000000000000000003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60102002d0f0000000000000000000003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970102002d0f0000000000000000000003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10102002d0f0000000000000000000003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0102008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50000000000000000000000000000000000000000000000056bc75e2d63100000000027100402059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b502008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0000000000000000000000000000000000000000000000056bc75e2d631000000000271004020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80000000000000000000000000000000000000000000000056bc75e2d6310000000002710040219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d802008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0000000000000000000000000000000000000000000000056bc75e2d631000000000271004021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0000000000000000000000000000000000000000000000056bc75e2d6310000000002710040230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0000000000000000000000000000000000000000000000056bc75e2d6310000000002710040249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c02008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970000000000000000000000000000000000000000000000056bc75e2d63100000000027100402546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109702008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20000000000000000000000000000000000000000000000056bc75e2d6310000000002710040256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e202008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300000000000000000000000000000000000000000000000056bc75e2d631000000000271004026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203002008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0000000000000000000000000000000000000000000000056bc75e2d631000000000271004028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed02008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40000000000000000000000000000000000000000000000056bc75e2d63100000000027100402a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e402008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670000000000000000000000000000000000000000000000056bc75e2d63100000000027100402d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496702008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860000000000000000000000000000000000000000000000056bc75e2d63100000000027100402d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0000000000000000000000000000000000000000000000056bc75e2d63100000000027100402e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0000000000000000000000000000000000000000000000056bc75e2d6310000000002710040343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d02008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950000000000000000000000000000000000000000000000056bc75e2d631000000000271004037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619502008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680000000000000000000000000000000000000000000000056bc75e2d6310000000002710040383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6802008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0000000000000000000000000000000000000000000000056bc75e2d631000000000271004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee02008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960000000000000000000000000000000000000000000000056bc75e2d631000000000271004038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760000000000000000000000000000000000000000000000056bc75e2d63100000000027100403c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60000000000000000000000000000000000000000000000056bc75e2d63100000000027100403d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970000000000000000000000000000000000000000000000056bc75e2d63100000000027100403d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89702008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10000000000000000000000000000000000000000000000056bc75e2d63100000000027100403ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f102008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0000000000000000000000000000000000000000000000056bc75e2d63100000000027100403f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0e00030c00010200330d0003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c000000000000000000000000000000000200330d0003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f1000000000000000000000000000000000200330d0003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae897000000000000000000000000000000000200330d0003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db6000000000000000000000000000000000200330d0003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa12276000000000000000000000000000000000200330d00038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a96000000000000000000000000000000000200330d00038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee000000000000000000000000000000000200330d000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f68000000000000000000000000000000000200330d00037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c334396195000000000000000000000000000000000200330d000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d000000000000000000000000000000000200330d0002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a000000000000000000000000000000000200330d0002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a8986000000000000000000000000000000000200330d0002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc9168654967000000000000000000000000000000000200330d0002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e4000000000000000000000000000000000200330d00028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed000000000000000000000000000000000200330d00026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef02030000000000000000000000000000000000200330d000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e2000000000000000000000000000000000200330d0002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c1097000000000000000000000000000000000200330d000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c000000000000000000000000000000000200330d000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f000000000000000000000000000000000200330d00021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b000000000000000000000000000000000200330d000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d8000000000000000000000000000000000200330d00020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce000000000000000000000000000000000200330d0002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50000000000000000000000000000000002000a03000000000000000001020012020000000000000000000000017ae4730720000c0083546865206f6e6c7920776179206f6620646973636f766572696e6720746865206c696d697473206f662074686520706f737369626c6520697320746f2076656e747572652061206c6974746c65207761792070617374207468656d20696e746f2074686520696d706f737369626c652e202d2041727468757220432e20436c61726b65"; // genesis_transaction - } - - private static String stokenetGenesis() { - return "02000300000100010004027379730a000300000002000a03000000000000000000020012020000000000000000000000017ae460063f02000300000b02000300000c02000300000e02000300000f02000300001002000300001102000300001200010004027872640a0003000001020046040001000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000200f1050001000378726400105261646978202853746f6b656e657429007653746f6b656e65742d6f6e6c79206e617469766520746f6b656e732c207573656420746f20706179207468652052616469782053746f6b656e6574206e6574776f726b2773207265717569726564207472616e73616374696f6e206665657320616e6420666f72206f746865722074657374696e672e002768747470733a2f2f73746f6b656e65742d6578706c6f7265722e7261646978646c742e636f6d2f003468747470733a2f2f6173736574732e7261646978646c742e636f6d2f69636f6e732f69636f6e2d7872642d33327833322e706e6700020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204fce5e3e2502611000000000020045060004030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd6148010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f9010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a1010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af307010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000a00230006030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614804000102002d0f00010000000000000001030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614801000a00230006030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde04000102002d0f00010000000000000001030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde01000a0023000602293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d04000102002d0f0001000000000000000102293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d01000a0023000602af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5904000102002d0f0001000000000000000102af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5901000a0023000602ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040304000102002d0f0001000000000000000102ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040301000a0023000602a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819404000102002d0f0001000000000000000102a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819401000a002300060316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a04000102002d0f000100000000000000010316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a01000a0023000603e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b04000102002d0f0001000000000000000103e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b01000a0023000603434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d04000102002d0f0001000000000000000103434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d01000a0023000603ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb04000102002d0f0001000000000000000103ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb01000a0023000603628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c04000102002d0f0001000000000000000103628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c01000a0023000603fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f904000102002d0f0001000000000000000103fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f901000a002300060393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce04000102002d0f000100000000000000010393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce01000a0023000602cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d04000102002d0f0001000000000000000102cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d01000a002300060339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f904000102002d0f000100000000000000010339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f901000a0023000603a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff904000102002d0f0001000000000000000103a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff901000a00230006025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a104000102002d0f00010000000000000001025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a101000a00230006037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed04000102002d0f00010000000000000001037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed01000a0023000602c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560504000102002d0f0001000000000000000102c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560501000a002300060384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a04000102002d0f000100000000000000010384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a01000a002300060202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb04000102002d0f000100000000000000010202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb01000a00230006038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30704000102002d0f00010000000000000001038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30701000a0023000603aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22504000102002d0f0001000000000000000103aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22501000a002300060250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330004000102002d0f000100000000000000010250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330001000a00230005030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd61480200240e00030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614801000a00230005030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde0200240e00030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde01000a0023000502293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d0200240e0002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d01000a0023000502af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be590200240e0002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5901000a0023000502ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd04030200240e0002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040301000a0023000502a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af2281940200240e0002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819401000a002300050316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a0200240e000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a01000a0023000503e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b0200240e0003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b01000a0023000503434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d0200240e0003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d01000a0023000503ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb0200240e0003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb01000a0023000503628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c0200240e0003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c01000a0023000503fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f90200240e0003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f901000a002300050393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce0200240e000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce01000a0023000502cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d0200240e0002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d01000a002300050339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f90200240e000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f901000a0023000503a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff90200240e0003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff901000a00230005025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a10200240e00025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a101000a00230005037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed0200240e00037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed01000a0023000502c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c56050200240e0002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560501000a002300050384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a0200240e000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a01000a002300050202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb0200240e000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb01000a00230005038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af3070200240e00038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30701000a0023000503aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a2250200240e0003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22501000a002300050250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f27833000200240e000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300010008000c020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204fa40438953fcb2300000004003d0200650700030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614804020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080055020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204f79aa33057d353600000004003e0200650700030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080057020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204f4f502d75ba9f4900000004003f020065070002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080059020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204f24f627e5f8095c000000040040020065070002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008005b020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204efa9c225635736f000000040041020065070002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040304020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008005d020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204ed0421cc672dd82000000040042020065070002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819404020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008005f020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204ea5e81736b0479500000004004302006507000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080061020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204e7b8e11a6edb1a8000000040044020065070003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080063020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204e51340c172b1bbb000000040045020065070003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080065020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204e26da06876885ce000000040046020065070003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080067020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204dfc8000f7a5efe1000000040047020065070003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080069020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204dd225fb67e359f4000000040048020065070003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008006b020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204da7cbf5d820c40700000004004902006507000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008006d020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204d7d71f0485e2e1a00000004004a020065070002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008006f020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204d5317eab89b982d00000004004b02006507000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080071020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204d28bde528d9024000000004004c020065070003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080073020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204cfe63df99166c5300000004004d0200650700025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a104020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080075020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204cd409da0953d66600000004004e0200650700037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080077020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204ca9afd47991407900000004004f020065070002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080079020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c7f55cee9ceaa8c00000004005002006507000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008007b020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c54fbc95a0c149f00000004005102006507000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008007d020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c2aa1c3ca497eb20000000400520200650700038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30704020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008007f020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c0047be3a86e8c5000000040053020065070003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080081020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204bd5edb8aac452d800000004005402006507000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed000000000800020800010f000a0a0000000000000000010f00010d0f0001090f0001070a002300040202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb02006508000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d020065080002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330002006508000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a10200650800025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a104020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194020065080002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819404020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59020065080002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605020065080002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d020065080002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403020065080002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040304020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde0200650800030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd61480200650800030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614804020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a02006508000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f902006508000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d020065080003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c020065080003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed0200650800037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a02006508000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af3070200650800038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30704020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce02006508000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9020065080003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225020065080003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb020065080003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b020065080003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9020065080003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000f000b10000100000000000000010f000b11000100000000000000010f000b0f0001000000000000000102002d0f000000000000000000000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb0102002d0f0000000000000000000002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d0102002d0f000000000000000000000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f27833000102002d0f00000000000000000000025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a10102002d0f0000000000000000000002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af2281940102002d0f0000000000000000000002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be590102002d0f0000000000000000000002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c56050102002d0f0000000000000000000002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d0102002d0f0000000000000000000002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd04030102002d0f00000000000000000000030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde0102002d0f00000000000000000000030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd61480102002d0f000000000000000000000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a0102002d0f000000000000000000000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f90102002d0f0000000000000000000003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d0102002d0f0000000000000000000003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c0102002d0f00000000000000000000037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed0102002d0f000000000000000000000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a0102002d0f00000000000000000000038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af3070102002d0f000000000000000000000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce0102002d0f0000000000000000000003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff90102002d0f0000000000000000000003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a2250102002d0f0000000000000000000003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb0102002d0f0000000000000000000003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b0102002d0f0000000000000000000003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f90102008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330002008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a1000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a102008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819402008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5902008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560502008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040302008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd6148000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614802008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f9000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f902008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af307000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30702008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff902008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22502008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f90e00030c00010200330d0003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9000000000000000000000000000000000200330d0003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b000000000000000000000000000000000200330d0003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb000000000000000000000000000000000200330d0003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225000000000000000000000000000000000200330d0003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9000000000000000000000000000000000200330d000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce000000000000000000000000000000000200330d00038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af307000000000000000000000000000000000200330d000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a000000000000000000000000000000000200330d00037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed000000000000000000000000000000000200330d0003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c000000000000000000000000000000000200330d0003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d000000000000000000000000000000000200330d000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f9000000000000000000000000000000000200330d000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a000000000000000000000000000000000200330d00030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd6148000000000000000000000000000000000200330d00030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde000000000000000000000000000000000200330d0002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403000000000000000000000000000000000200330d0002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d000000000000000000000000000000000200330d0002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605000000000000000000000000000000000200330d0002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59000000000000000000000000000000000200330d0002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194000000000000000000000000000000000200330d00025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a1000000000000000000000000000000000200330d000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300000000000000000000000000000000000200330d0002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d000000000000000000000000000000000200330d000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb0000000000000000000000000000000002000a03000000000000000001020012020000000000000000000000017ae460063f000c007157656c636f6d6520746f2053746f6b656e65742c20612070657273697374616e742052616469782074657374206e6574776f726b2e2053746f6b652069732074686520686f6d6520746f776e206f662044616e204875676865732c2074686520666f756e646572206f662052616469782e"; // genesis_transaction - } - - private final int id; - private final String accountHrp; - private final String validatorHrp; - private final String resourceHrpSuffix; - private final String nodeHrp; - private final String genesisTxn; - - Network(int id, String accountHrp, String validatorHrp, String resourceHrpSuffix, String nodeHrp) { - this.id = id; - this.accountHrp = accountHrp; - this.validatorHrp = validatorHrp; - this.resourceHrpSuffix = resourceHrpSuffix; - this.nodeHrp = nodeHrp; - this.genesisTxn = null; - } - - Network(int id, String accountHrp, String validatorHrp, String resourceHrpSuffix, String nodeHrp, String genesisTxn) { - this.id = id; - this.accountHrp = accountHrp; - this.validatorHrp = validatorHrp; - this.resourceHrpSuffix = resourceHrpSuffix; - this.nodeHrp = nodeHrp; - this.genesisTxn = genesisTxn; - } - - public String getAccountHrp() { - return accountHrp; - } - - public String getValidatorHrp() { - return validatorHrp; - } - - public String getResourceHrpSuffix() { - return resourceHrpSuffix; - } - - public String getNodeHrp() { - return nodeHrp; - } - - public int getId() { - return id; - } - - public Optional genesisTxn() { - return Optional.ofNullable(genesisTxn); - } - - public static Optional ofId(int id) { - return find(network -> network.id == id); - } - - public static Optional ofName(String name) { - var upperCaseName = name.toUpperCase(Locale.US); - return find(network -> network.name().equals(upperCaseName)); - } - - private static Optional find(Predicate predicate) { - return Stream.of(values()) - .filter(predicate) - .findAny(); - } + MAINNET(1, "rdx", "rv", "_rr", "rn", mainnetGenesis()), + STOKENET(2, "tdx", "tv", "_tr", "tn", stokenetGenesis()), + RELEASENET(3, "tdx3", "tv3", "_tr3", "tn3"), + RCNET(4, "tdx4", "tv4", "_tr4", "tn4"), + MILESTONENET(5, "tdx5", "tv5", "_tr5", "tn5"), + DEVOPSNET(6, "tdx6", "tv6", "_tr6", "tn6"), + SANDPITNET(7, "tdx7", "tv7", "_tr7", "tn7"), + LOCALNET(99, "ddx", "dv", "_dr", "dn"); + + private static String mainnetGenesis() { + return "02000300000100010004027379730a000300000002000a03000000000000000000020012020000000000000000000000017ae473072002000300000b02000300000c02000300000e02000300000f02000300001002000300001102000300001200010004027872640a000300000102004604000100000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000000000020102050001000378726400055261646978009e546865205261646978205075626c6963204e6574776f726b2773206e617469766520746f6b656e2c207573656420746f2070617920746865206e6574776f726b2773207265717569726564207472616e73616374696f6e206665657320616e6420746f2073656375726520746865206e6574776f726b207468726f756768207374616b696e6720746f206974732076616c696461746f72206e6f6465732e001b68747470733a2f2f746f6b656e732e7261646978646c742e636f6d003468747470733a2f2f6173736574732e7261646978646c742e636f6d2f69636f6e732f69636f6e2d7872642d33327833322e706e670002004506000402aa1c49af92a39d15257d7dc43f805f18f9f0ea712450c53e75255f5a0d1d93b601000000000000000000000000000000000000000007c13bc4b2c133c56000000000020045060004022e258dc77f18775c12ff7aad1575fc0d172a62687b6451438a29d8e0b7e2ca0b01000000000000000000000000000000000000000005c8a6bc554c114a748000000002004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f010000000000000000000000000000000000000000000000821ab0d441498000000002004506000403385b5d3a9e7934c11bc8fac83260188a26d6763950465eb0fc152f34d1c5323d010000000000000000000000000000000000000000019d971e4fe8401e740000000002004506000403d865a0c06935c72d89f322aedbb2773e05648c96f48c823712a612c02d8feef4010000000000000000000000000000000000000000019d971e4fe8401e740000000002004506000403d672ccf7644bc62fcad284433b162593b5b627f6f967f2f46ea956de21c6c48301000000000000000000000000000000000000000007c13bc4b2c133c5600000000002004506000403741961b0ce6771d638cd37b9b00c032817857970d24eb9111fba6c8d56d2e9f30100000000000000000000000000000000000000000e3fddd3087635877a000000000a00230006038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee04000102002d0f00010000000000000001038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee01000a0023000602e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a04000102002d0f0001000000000000000102e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a01000a002300060383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6804000102002d0f000100000000000000010383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6801000a00230006026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203004000102002d0f00010000000000000001026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203001000a002300060249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c04000102002d0f000100000000000000010249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c01000a0023000602d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898604000102002d0f0001000000000000000102d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898601000a00230006020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce04000102002d0f00010000000000000001020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce01000a0023000602d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496704000102002d0f0001000000000000000102d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496701000a00230006037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619504000102002d0f00010000000000000001037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619501000a0023000603ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f104000102002d0f0001000000000000000103ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f101000a0023000602059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b504000102002d0f0001000000000000000102059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b501000a0023000602a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e404000102002d0f0001000000000000000102a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e401000a0023000602546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109704000102002d0f0001000000000000000102546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109701000a00230006028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed04000102002d0f00010000000000000001028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed01000a00230006021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b04000102002d0f00010000000000000001021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b01000a0023000603f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c04000102002d0f0001000000000000000103f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c01000a002300060219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d804000102002d0f000100000000000000010219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d801000a002300060256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e204000102002d0f000100000000000000010256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e201000a0023000603d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db604000102002d0f0001000000000000000103d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db601000a0023000603c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227604000102002d0f0001000000000000000103c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227601000a002300060343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d04000102002d0f000100000000000000010343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d01000a00230006038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9604000102002d0f00010000000000000001038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9601000a0023000603d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89704000102002d0f0001000000000000000103d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89701000a002300060230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f04000102002d0f000100000000000000010230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f01000a00230005038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0200240e00038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee01000a0023000502e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0200240e0002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a01000a002300050383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680200240e000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6801000a00230005026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300200240e00026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203001000a002300050249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0200240e000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c01000a0023000502d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860200240e0002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898601000a00230005020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0200240e00020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce01000a0023000502d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670200240e0002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496701000a00230005037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950200240e00037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619501000a0023000503ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10200240e0003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f101000a0023000502059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50200240e0002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b501000a0023000502a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40200240e0002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e401000a0023000502546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970200240e0002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109701000a00230005028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0200240e00028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed01000a00230005021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0200240e00021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b01000a0023000503f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0200240e0003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c01000a002300050219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80200240e000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d801000a002300050256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20200240e000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e201000a0023000503d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60200240e0003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db601000a0023000503c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760200240e0003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227601000a002300050343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0200240e000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d01000a00230005038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960200240e00038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9601000a0023000503d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970200240e0003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89701000a002300050230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0200240e000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f010008000e02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000007caee97613e670000004002b0200650700038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004302004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000077432217e68360000004002c020065070002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004502004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000071d75ab9b92050000004002d02006507000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004702004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000006c6b935b8bbd40000004002e0200650700026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004902004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000066ffcbfd5e5a30000004002f02006507000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004b02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000006194049f30f7200000040030020065070002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004d02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000005c283d4103941000000400310200650700020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008004f02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000056bc75e2d631000000040032020065070002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005102004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000005150ae84a8cdf000000400330200650700037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005302004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000004be4e7267b6ae00000040034020065070003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005502004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000046791fc84e07d00000040035020065070002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005702004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f010000000000000000000000000000000000000000000000410d586a20a4c00000040036020065070002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005902004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000003ba1910bf341b00000040037020065070002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005b02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000003635c9adc5dea000000400380200650700028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005d02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000030ca024f987b9000000400390200650700021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008005f02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000002b5e3af16b1880000004003a020065070003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006102004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000025f273933db570000004003b02006507000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006302004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000002086ac35105260000004003c02006507000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006502004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000001b1ae4d6e2ef50000004003d020065070003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006702004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f01000000000000000000000000000000000000000000000015af1d78b58c40000004003e020065070003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006902004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000001043561a882930000004003f02006507000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006b02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0100000000000000000000000000000000000000000000000ad78ebc5ac62000000400400200650700038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006d02004506000403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f010000000000000000000000000000000000000000000000056bc75e2d63100000040041020065070003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008006f04004202006507000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000008002b0200240e00038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee000008002c0200240e0002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a000008002d0200240e000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f68000008002e0200240e00026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef02030000008002f0200240e000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c00000800300200240e0002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898600000800310200240e00020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce00000800320200240e0002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496700000800330200240e00037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619500000800340200240e0003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f100000800350200240e0002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b500000800360200240e0002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e400000800370200240e0002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109700000800380200240e00028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed00000800390200240e00021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b000008003a0200240e0003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c000008003b0200240e000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d8000008003c0200240e000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e2000008003d0200240e0003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db6000008003e0200240e0003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa12276000008003f0200240e000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d00000800400200240e00038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9600000800410200240e0003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89700000800420200240e000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f00000800020800010f000a0a0000000000000000010f00010d0f0001090f0001070a0023000402059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b5020065080002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0200650800020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d802006508000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0200650800021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f02006508000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c02006508000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c1097020065080002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e202006508000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300200650800026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0200650800028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e4020065080002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc9168654967020065080002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a8986020065080002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000402e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a020065080002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d02006508000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950200650800037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a002300040383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6802006508000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0200650800038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a00230004038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960200650800038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa12276020065080003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db6020065080003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae897020065080003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f1020065080003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000a0023000403f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c020065080003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0403f6c2eb33e0afa8789f40b004023b1c18289a696197b80aa18682abfad6361d6f0000000000000000000000000000000000000000000000056bc75e2d631000000f000b10000100000000000000010f000b11000100000000000000010f000b0f0001000000000000000102002d0f0000000000000000000002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50102002d0f00000000000000000000020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0102002d0f000000000000000000000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80102002d0f00000000000000000000021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0102002d0f000000000000000000000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0102002d0f000000000000000000000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0102002d0f0000000000000000000002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970102002d0f000000000000000000000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20102002d0f00000000000000000000026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300102002d0f00000000000000000000028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0102002d0f0000000000000000000002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40102002d0f0000000000000000000002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670102002d0f0000000000000000000002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860102002d0f0000000000000000000002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0102002d0f000000000000000000000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0102002d0f00000000000000000000037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950102002d0f000000000000000000000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680102002d0f00000000000000000000038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0102002d0f00000000000000000000038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960102002d0f0000000000000000000003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760102002d0f0000000000000000000003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60102002d0f0000000000000000000003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970102002d0f0000000000000000000003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10102002d0f0000000000000000000003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0102008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50000000000000000000000000000000000000000000000056bc75e2d63100000000027100402059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b502008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce0000000000000000000000000000000000000000000000056bc75e2d631000000000271004020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d80000000000000000000000000000000000000000000000056bc75e2d6310000000002710040219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d802008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b0000000000000000000000000000000000000000000000056bc75e2d631000000000271004021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f0000000000000000000000000000000000000000000000056bc75e2d6310000000002710040230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c0000000000000000000000000000000000000000000000056bc75e2d6310000000002710040249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c02008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c10970000000000000000000000000000000000000000000000056bc75e2d63100000000027100402546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c109702008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e20000000000000000000000000000000000000000000000056bc75e2d6310000000002710040256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e202008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef020300000000000000000000000000000000000000000000000056bc75e2d631000000000271004026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef0203002008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed0000000000000000000000000000000000000000000000056bc75e2d631000000000271004028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed02008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e40000000000000000000000000000000000000000000000056bc75e2d63100000000027100402a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e402008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc91686549670000000000000000000000000000000000000000000000056bc75e2d63100000000027100402d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc916865496702008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a89860000000000000000000000000000000000000000000000056bc75e2d63100000000027100402d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a898602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a0000000000000000000000000000000000000000000000056bc75e2d63100000000027100402e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a02008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d0000000000000000000000000000000000000000000000056bc75e2d6310000000002710040343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d02008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c3343961950000000000000000000000000000000000000000000000056bc75e2d631000000000271004037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c33439619502008a0c00010000000000000000000000000000000000000000000000056bc75e2d631000000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f680000000000000000000000000000000000000000000000056bc75e2d6310000000002710040383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f6802008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee0000000000000000000000000000000000000000000000056bc75e2d631000000000271004038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee02008a0c00010000000000000000000000000000000000000000000000056bc75e2d63100000038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a960000000000000000000000000000000000000000000000056bc75e2d631000000000271004038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a9602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa122760000000000000000000000000000000000000000000000056bc75e2d63100000000027100403c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa1227602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db60000000000000000000000000000000000000000000000056bc75e2d63100000000027100403d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db602008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae8970000000000000000000000000000000000000000000000056bc75e2d63100000000027100403d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae89702008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f10000000000000000000000000000000000000000000000056bc75e2d63100000000027100403ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f102008a0c00010000000000000000000000000000000000000000000000056bc75e2d6310000003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0000000000000000000000000000000000000000000000056bc75e2d63100000000027100403f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c0e00030c00010200330d0003f0f48e764719024d17fb8f549d534dcead847a6a926eeff89d438088e282303c000000000000000000000000000000000200330d0003ec5b9bfdd06f8eef1eff4dd7cb9d720ab2b17009b5c32a0f9f7eff18fcb509f1000000000000000000000000000000000200330d0003d3398553c6d762105548b40d14d1b867c2d06794da2be84435df6194ec3ae897000000000000000000000000000000000200330d0003d13db98f4ef01f670078a694f8821afc843861afa1d28610411839ffd45b0db6000000000000000000000000000000000200330d0003c4d19b1ee6b2a2e8920adb5201bf8eca8a4ed5158be5f6ba1ad49e132fa12276000000000000000000000000000000000200330d00038c979160e9dbddee2c1aa4803eb3ccb2c7d626251e503d961ddb7c5e1e9f8a96000000000000000000000000000000000200330d00038639dc49e14a94448045a3c595996282d8419eb9ae949dfb84b7bc48ba3d4fee000000000000000000000000000000000200330d000383aadb507d2d8fbf07d639bc8899bd56e113d96f1c2ae6698d38dba6e9667f68000000000000000000000000000000000200330d00037b60a0b02be17ee8450787c4563cc34983aba076aff3612352ce31c334396195000000000000000000000000000000000200330d000343d60d8260e1f97efee7b664444080b04e948309ac56fbc81dd7bae0c875ff7d000000000000000000000000000000000200330d0002e2b7af226240d248562643b95e236069be47e9e9d5518356fc64a37ee50aec0a000000000000000000000000000000000200330d0002d4a63cb0f942b6f24b11d628a22dc57658d6259101a9255272699eeb471a8986000000000000000000000000000000000200330d0002d4222b1c4c61032a82806c2428a017dc98af0dbf1b12bce439a8cc9168654967000000000000000000000000000000000200330d0002a8c7d5f9937ee3129fe57473ffbad38ff7d81789e21813bd680343cb7017a8e4000000000000000000000000000000000200330d00028b23f91483e8c49ffecff25a5623d4eb06f5f6952a794e7836a408607fabd8ed000000000000000000000000000000000200330d00026edfd458e45735f7a5687b546e6dc2103717a5f67353d21074162318aef02030000000000000000000000000000000000200330d000256a0ff7344ed3d25460a858f96b55b84427ef5851fdea80a9e9d78f91302d2e2000000000000000000000000000000000200330d0002546d4406ac9bd348f1b59ca21179bb19be686a37ff44603ac147d9e3634c1097000000000000000000000000000000000200330d000249638d4f905c696c5665ac6e2abe66d291d5bf8b2c091510de021a275b30506c000000000000000000000000000000000200330d000230ed095a00e3b87d32b08ea8780bda013e2005eee13f9e4e008d758fa6aec16f000000000000000000000000000000000200330d00021bcc9e2e4644e1b61354d009803df573c25b9f9a2003647315858806c1e7fb5b000000000000000000000000000000000200330d000219dbf0acfe930f98fbd416dbe472b304b01b7a7a6fbc2e5f7f3204dff96b68d8000000000000000000000000000000000200330d00020d38e73f6ca31b34a7bf74eb4916adbde168db7b59edba63b65369cb00832fce000000000000000000000000000000000200330d0002059ef930aa4b234a30a538e29d7067242ca75ce3a460af3da889d5108eb231b50000000000000000000000000000000002000a03000000000000000001020012020000000000000000000000017ae4730720000c0083546865206f6e6c7920776179206f6620646973636f766572696e6720746865206c696d697473206f662074686520706f737369626c6520697320746f2076656e747572652061206c6974746c65207761792070617374207468656d20696e746f2074686520696d706f737369626c652e202d2041727468757220432e20436c61726b65"; // genesis_transaction + } + + private static String stokenetGenesis() { + return "02000300000100010004027379730a000300000002000a03000000000000000000020012020000000000000000000000017ae460063f02000300000b02000300000c02000300000e02000300000f02000300001002000300001102000300001200010004027872640a0003000001020046040001000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000200f1050001000378726400105261646978202853746f6b656e657429007653746f6b656e65742d6f6e6c79206e617469766520746f6b656e732c207573656420746f20706179207468652052616469782053746f6b656e6574206e6574776f726b2773207265717569726564207472616e73616374696f6e206665657320616e6420666f72206f746865722074657374696e672e002768747470733a2f2f73746f6b656e65742d6578706c6f7265722e7261646978646c742e636f6d2f003468747470733a2f2f6173736574732e7261646978646c742e636f6d2f69636f6e732f69636f6e2d7872642d33327833322e706e6700020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204fce5e3e2502611000000000020045060004030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd6148010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f9010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a1010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000402c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb010000000000000000000000000000000000000000033b2e3c9fd0803ce800000000020045060004038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af307010000000000000000000000000000000000000000033b2e3c9fd0803ce80000000002004506000403aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000200450600040250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300010000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000a00230006030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614804000102002d0f00010000000000000001030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614801000a00230006030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde04000102002d0f00010000000000000001030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde01000a0023000602293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d04000102002d0f0001000000000000000102293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d01000a0023000602af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5904000102002d0f0001000000000000000102af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5901000a0023000602ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040304000102002d0f0001000000000000000102ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040301000a0023000602a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819404000102002d0f0001000000000000000102a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819401000a002300060316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a04000102002d0f000100000000000000010316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a01000a0023000603e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b04000102002d0f0001000000000000000103e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b01000a0023000603434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d04000102002d0f0001000000000000000103434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d01000a0023000603ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb04000102002d0f0001000000000000000103ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb01000a0023000603628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c04000102002d0f0001000000000000000103628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c01000a0023000603fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f904000102002d0f0001000000000000000103fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f901000a002300060393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce04000102002d0f000100000000000000010393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce01000a0023000602cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d04000102002d0f0001000000000000000102cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d01000a002300060339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f904000102002d0f000100000000000000010339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f901000a0023000603a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff904000102002d0f0001000000000000000103a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff901000a00230006025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a104000102002d0f00010000000000000001025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a101000a00230006037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed04000102002d0f00010000000000000001037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed01000a0023000602c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560504000102002d0f0001000000000000000102c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560501000a002300060384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a04000102002d0f000100000000000000010384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a01000a002300060202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb04000102002d0f000100000000000000010202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb01000a00230006038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30704000102002d0f00010000000000000001038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30701000a0023000603aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22504000102002d0f0001000000000000000103aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22501000a002300060250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330004000102002d0f000100000000000000010250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330001000a00230005030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd61480200240e00030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614801000a00230005030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde0200240e00030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde01000a0023000502293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d0200240e0002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d01000a0023000502af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be590200240e0002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5901000a0023000502ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd04030200240e0002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040301000a0023000502a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af2281940200240e0002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819401000a002300050316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a0200240e000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a01000a0023000503e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b0200240e0003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b01000a0023000503434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d0200240e0003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d01000a0023000503ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb0200240e0003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb01000a0023000503628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c0200240e0003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c01000a0023000503fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f90200240e0003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f901000a002300050393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce0200240e000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce01000a0023000502cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d0200240e0002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d01000a002300050339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f90200240e000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f901000a0023000503a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff90200240e0003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff901000a00230005025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a10200240e00025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a101000a00230005037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed0200240e00037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed01000a0023000502c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c56050200240e0002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560501000a002300050384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a0200240e000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a01000a002300050202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb0200240e000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb01000a00230005038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af3070200240e00038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30701000a0023000503aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a2250200240e0003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22501000a002300050250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f27833000200240e000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300010008000c020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204fa40438953fcb2300000004003d0200650700030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614804020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080055020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204f79aa33057d353600000004003e0200650700030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080057020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204f4f502d75ba9f4900000004003f020065070002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080059020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204f24f627e5f8095c000000040040020065070002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008005b020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204efa9c225635736f000000040041020065070002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040304020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008005d020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204ed0421cc672dd82000000040042020065070002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819404020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008005f020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204ea5e81736b0479500000004004302006507000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080061020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204e7b8e11a6edb1a8000000040044020065070003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080063020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204e51340c172b1bbb000000040045020065070003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080065020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204e26da06876885ce000000040046020065070003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080067020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204dfc8000f7a5efe1000000040047020065070003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080069020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204dd225fb67e359f4000000040048020065070003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008006b020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204da7cbf5d820c40700000004004902006507000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008006d020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204d7d71f0485e2e1a00000004004a020065070002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008006f020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204d5317eab89b982d00000004004b02006507000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080071020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204d28bde528d9024000000004004c020065070003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080073020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204cfe63df99166c5300000004004d0200650700025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a104020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080075020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204cd409da0953d66600000004004e0200650700037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080077020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204ca9afd47991407900000004004f020065070002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080079020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c7f55cee9ceaa8c00000004005002006507000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008007b020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c54fbc95a0c149f00000004005102006507000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008007d020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c2aa1c3ca497eb20000000400520200650700038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30704020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000008007f020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204c0047be3a86e8c5000000040053020065070003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed00000000080081020045060004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4010000000000000000000000000000000000000000204bd5edb8aac452d800000004005402006507000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed000000000800020800010f000a0a0000000000000000010f00010d0f0001090f0001070a002300040202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb02006508000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d020065080002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330002006508000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330004020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a10200650800025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a104020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194020065080002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819404020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59020065080002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605020065080002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d020065080002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000402ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403020065080002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040304020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde0200650800030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd61480200650800030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614804020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a02006508000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f902006508000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d020065080003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c020065080003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed0200650800037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a02006508000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a00230004038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af3070200650800038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30704020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a002300040393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce02006508000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9020065080003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225020065080003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22504020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb020065080003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b020065080003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b04020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000a0023000403fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9020065080003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f904020ede6f45b54b1b3da8ca6a074d4f3cbce3823e628abc084d7ce6d9fec4df3ef4000000000000000000000000000000000000000000002a5a058fc295ed0000000f000b10000100000000000000010f000b11000100000000000000010f000b0f0001000000000000000102002d0f000000000000000000000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb0102002d0f0000000000000000000002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d0102002d0f000000000000000000000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f27833000102002d0f00000000000000000000025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a10102002d0f0000000000000000000002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af2281940102002d0f0000000000000000000002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be590102002d0f0000000000000000000002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c56050102002d0f0000000000000000000002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d0102002d0f0000000000000000000002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd04030102002d0f00000000000000000000030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde0102002d0f00000000000000000000030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd61480102002d0f000000000000000000000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a0102002d0f000000000000000000000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f90102002d0f0000000000000000000003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d0102002d0f0000000000000000000003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c0102002d0f00000000000000000000037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed0102002d0f000000000000000000000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a0102002d0f00000000000000000000038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af3070102002d0f000000000000000000000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce0102002d0f0000000000000000000003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff90102002d0f0000000000000000000003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a2250102002d0f0000000000000000000003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb0102002d0f0000000000000000000003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b0102002d0f0000000000000000000003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f90102008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f278330002008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a1000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a102008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af22819402008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be5902008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c560502008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403000000000000000000000000000000000000000000002a5a058fc295ed000000000027100402ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd040302008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd6148000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd614802008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f9000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f902008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed000000038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af307000000000000000000000000000000000000000000002a5a058fc295ed0000000000271004038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af30702008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed0000000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce000000000000000000000000000000000000000000002a5a058fc295ed00000000002710040393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff902008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a22502008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b02008a0c0001000000000000000000000000000000000000000000002a5a058fc295ed00000003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9000000000000000000000000000000000000000000002a5a058fc295ed000000000027100403fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f90e00030c00010200330d0003fb3042f5cab468e2b1de0c89cf2acc299f1e2af06d73699654a6aca75b0084f9000000000000000000000000000000000200330d0003e8f237becdbdf662f375cb6914b4a6c45809f4dc9d45df022bc477898c96391b000000000000000000000000000000000200330d0003ba34802b8f28552e3bb034e3453c85cf00c575a50e7e3f7930f51e5cd35e0ceb000000000000000000000000000000000200330d0003aec13763151a9b4dbfc8fe1d02e98e5d16cff002a264d2a9d2be1dd959e6a225000000000000000000000000000000000200330d0003a781cb7945377345e83cc04ff2fa64a51fdf3db491fbb012da3865b50a965ff9000000000000000000000000000000000200330d000393754e6f118f8e2e890245a9f83de293e6349f397640fdb39d52efdfb94296ce000000000000000000000000000000000200330d00038c42629c559aaff804d3803aa95c7262c26c112197f7c3676f981fb3284af307000000000000000000000000000000000200330d000384a8fac06870ba7e2495f73eeb5c34baa26c6989b57bd1479a2285b26e9b573a000000000000000000000000000000000200330d00037d30a82c39cd327588a3b91b747fd1dca449c03cbb43a71c0353dd4e360bafed000000000000000000000000000000000200330d0003628c154dd29bea4dd7586b7cef5c33cda461fa31cab16b0e609aef35bad8782c000000000000000000000000000000000200330d0003434bbe775a976367f599678221787c043e6f56185cd33f701117045933d32c3d000000000000000000000000000000000200330d000339ea428ab88f65341a0e6196bfdb70f838a4607ed8c6f0adf3deb304c77c85f9000000000000000000000000000000000200330d000316d2a52db9888f6f204206ac8a0d4a243cd3a086a4eb53bb6de483e8d7e0d13a000000000000000000000000000000000200330d00030e5221ffeaa4baa8ac004f032c282d2f2d47c8e8db0d79014008a6718ecd6148000000000000000000000000000000000200330d00030a93921eeb735b5e441aa1a17efc3480cb88512d99285b81ec8bf65ff1fb8dde000000000000000000000000000000000200330d0002ff6807685ae76823a467916b65b5b54a4b981fe921320781b32484813ebd0403000000000000000000000000000000000200330d0002cd1a66d39cf29e66ae68fa594788fb5f2a5fe14ee638ea61911a3ecc516d490d000000000000000000000000000000000200330d0002c755c0e948877a3b5aa85867bc8e9c73dc0764a72ece3147d337de72c44c5605000000000000000000000000000000000200330d0002af0d0dd241e50c7fe2d86adea33a5cb99071d76b073618fa605d4468e221be59000000000000000000000000000000000200330d0002a559bf082f567e04a6c60918d25614f886332aa6481782f1ccd06d19af228194000000000000000000000000000000000200330d00025a857f0778f01d8b5f55d9cb85075910b0804e4ab98cd7413678de3e80ed27a1000000000000000000000000000000000200330d000250888d0d4543c60bae816d48d9038f50bbeb05117312077aa8919f23f2783300000000000000000000000000000000000200330d0002293686eaa58fa084cc7551f69aab6f6e428ba52021b6610e8e7506923fab4c8d000000000000000000000000000000000200330d000202917a1d22076683650109925ea10c613dafb476cd34ac38d323c4225d8ee1cb0000000000000000000000000000000002000a03000000000000000001020012020000000000000000000000017ae460063f000c007157656c636f6d6520746f2053746f6b656e65742c20612070657273697374616e742052616469782074657374206e6574776f726b2e2053746f6b652069732074686520686f6d6520746f776e206f662044616e204875676865732c2074686520666f756e646572206f662052616469782e"; // genesis_transaction + } + + private final int id; + private final String accountHrp; + private final String validatorHrp; + private final String resourceHrpSuffix; + private final String nodeHrp; + private final String genesisTxn; + + Network( + int id, String accountHrp, String validatorHrp, String resourceHrpSuffix, String nodeHrp) { + this.id = id; + this.accountHrp = accountHrp; + this.validatorHrp = validatorHrp; + this.resourceHrpSuffix = resourceHrpSuffix; + this.nodeHrp = nodeHrp; + this.genesisTxn = null; + } + + Network( + int id, + String accountHrp, + String validatorHrp, + String resourceHrpSuffix, + String nodeHrp, + String genesisTxn) { + this.id = id; + this.accountHrp = accountHrp; + this.validatorHrp = validatorHrp; + this.resourceHrpSuffix = resourceHrpSuffix; + this.nodeHrp = nodeHrp; + this.genesisTxn = genesisTxn; + } + + public String getAccountHrp() { + return accountHrp; + } + + public String getValidatorHrp() { + return validatorHrp; + } + + public String getResourceHrpSuffix() { + return resourceHrpSuffix; + } + + public String getNodeHrp() { + return nodeHrp; + } + + public int getId() { + return id; + } + + public Optional genesisTxn() { + return Optional.ofNullable(genesisTxn); + } + + public static Optional ofId(int id) { + return find(network -> network.id == id); + } + + public static Optional ofName(String name) { + var upperCaseName = name.toUpperCase(Locale.US); + return find(network -> network.name().equals(upperCaseName)); + } + + private static Optional find(Predicate predicate) { + return Stream.of(values()).filter(predicate).findAny(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/networks/NetworkId.java b/radixdlt-java-common/src/main/java/com/radixdlt/networks/NetworkId.java index a2d7f8050f..432e7a9ce1 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/networks/NetworkId.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/networks/NetworkId.java @@ -64,21 +64,17 @@ package com.radixdlt.networks; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import javax.inject.Qualifier; - import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Marker for network ID. - */ +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** Marker for network ID. */ @Qualifier -@Target({ FIELD, PARAMETER, METHOD }) +@Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) -public @interface NetworkId { -} +public @interface NetworkId {} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ApiSerializationModifier.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ApiSerializationModifier.java index 8b29d72476..09ae0324b7 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ApiSerializationModifier.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ApiSerializationModifier.java @@ -73,50 +73,51 @@ import com.google.common.hash.HashCode; import com.radixdlt.crypto.HashUtils; import com.radixdlt.serialization.mapper.JacksonCborMapper; - import java.io.IOException; /** - * A Jackson bean serialization modifier that handles adding the "hid" property to output data - * for classes annotated with @SerializeWithHid annotation. + * A Jackson bean serialization modifier that handles adding the "hid" property to output data for + * classes annotated with @SerializeWithHid annotation. */ public class ApiSerializationModifier extends BeanSerializerModifier { - private final JacksonCborMapper hashDsonMapper; + private final JacksonCborMapper hashDsonMapper; - public ApiSerializationModifier(JacksonCborMapper hashDsonMapper) { - this.hashDsonMapper = hashDsonMapper; - } + public ApiSerializationModifier(JacksonCborMapper hashDsonMapper) { + this.hashDsonMapper = hashDsonMapper; + } - @Override - @SuppressWarnings("unchecked") - public JsonSerializer modifySerializer( - final SerializationConfig serializationConfig, - final BeanDescription beanDescription, - final JsonSerializer jsonSerializer) { - return new SerializerWithHid((JsonSerializer) jsonSerializer); - } + @Override + @SuppressWarnings("unchecked") + public JsonSerializer modifySerializer( + final SerializationConfig serializationConfig, + final BeanDescription beanDescription, + final JsonSerializer jsonSerializer) { + return new SerializerWithHid((JsonSerializer) jsonSerializer); + } - private class SerializerWithHid extends JsonSerializer { + private class SerializerWithHid extends JsonSerializer { - private final JsonSerializer serializer; + private final JsonSerializer serializer; - SerializerWithHid(JsonSerializer jsonSerializer) { - this.serializer = jsonSerializer; - } + SerializerWithHid(JsonSerializer jsonSerializer) { + this.serializer = jsonSerializer; + } - @Override - public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - if (o.getClass().isAnnotationPresent(SerializeWithHid.class)) { - jsonGenerator.writeStartObject(); - serializer.unwrappingSerializer(null).serialize(o, jsonGenerator, serializerProvider); - byte[] bytesToHash = hashDsonMapper.writeValueAsBytes(o); - HashCode hash = HashUtils.sha256(bytesToHash); - jsonGenerator.writeObjectField("hid", hash); - jsonGenerator.writeEndObject(); - } else { - serializer.serialize(o, jsonGenerator, serializerProvider); - } - } + @Override + public void serialize( + Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { + if (o.getClass().isAnnotationPresent(SerializeWithHid.class)) { + jsonGenerator.writeStartObject(); + serializer.unwrappingSerializer(null).serialize(o, jsonGenerator, serializerProvider); + byte[] bytesToHash = hashDsonMapper.writeValueAsBytes(o); + HashCode hash = HashUtils.sha256(bytesToHash); + jsonGenerator.writeObjectField("hid", hash); + jsonGenerator.writeEndObject(); + } else { + serializer.serialize(o, jsonGenerator, serializerProvider); + } } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializationPolicy.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializationPolicy.java index 82c47039cc..226ae5054d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializationPolicy.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializationPolicy.java @@ -1,245 +1,270 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.serialization; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.radixdlt.serialization.DsonOutput.Output; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Class that maintains a map of {@link DsonOutput.Output} types to - * a set of pairs of classes and field/method names to output for that - * serialization type. - *

- * This {@link SerializationPolicy} operates by scanning a supplied list of classes. - */ -public abstract class ClassScanningSerializationPolicy implements SerializationPolicy { - - private final EnumMap, ImmutableSet>> outputs = new EnumMap<>(Output.class); - - /** - * Scan for all classes with an {@code SerializerId} annotation. - * The entire classpath is scanned, including JAR files. - * - * @param classes The list of classes to scan for serialization annotations - * @throws IllegalStateException If issues with serialization configuration - * are found while scanning. - */ - protected ClassScanningSerializationPolicy(Collection> classes) { - Map, Set>> tempOutputs = new EnumMap<>(Output.class); - // These are the outputs we will be collecting. - // ALL and NONE are replaced with the complete set and empty set respectively - tempOutputs.put(Output.HASH, new HashMap<>()); - tempOutputs.put(Output.API, new HashMap<>()); - tempOutputs.put(Output.WIRE, new HashMap<>()); - tempOutputs.put(Output.PERSIST, new HashMap<>()); - - // First fields - for (Class outerCls : classes) { - for (Class cls = outerCls; !Object.class.equals(cls); cls = cls.getSuperclass()) { - for (Field field : cls.getDeclaredFields()) { - DsonOutput dsonOutput = field.getDeclaredAnnotation(DsonOutput.class); - JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class); - if (dsonOutput == null && jsonProperty != null) { - throw new IllegalStateException( - String.format("Field %s#%s has a %s annotation, but no %s annotation", - outerCls.getName(), field.getName(), - JsonProperty.class.getSimpleName(), - DsonOutput.class.getSimpleName())); - } - if (dsonOutput != null && jsonProperty == null) { - throw new IllegalStateException( - String.format("Field %s#%s has a %s annotation, but no %s annotation", - outerCls.getName(), field.getName(), - DsonOutput.class.getSimpleName(), - JsonProperty.class.getSimpleName())); - } - if (dsonOutput != null && jsonProperty != null) { - String fieldName = jsonProperty.value(); - for (DsonOutput.Output out : DsonOutput.Output.toEnumSet(dsonOutput.value(), dsonOutput.include())) { - if (!tempOutputs.get(out).computeIfAbsent(outerCls, k -> new HashSet<>()).add(fieldName)) { - throw new IllegalStateException( - String.format("Duplicate property %s in class %s", - fieldName, outerCls.getName())); - } - } - } - } - // Now methods - for (Method method : cls.getDeclaredMethods()) { - DsonOutput dsonOutput = method.getDeclaredAnnotation(DsonOutput.class); - JsonProperty jsonProperty = method.getDeclaredAnnotation(JsonProperty.class); - JsonAnyGetter jsonAnyGetter = method.getDeclaredAnnotation(JsonAnyGetter.class); - if (dsonOutput == null && jsonProperty != null) { - if (method.getParameterCount() == 1) { - // Ignore setter - continue; - } - throw new IllegalStateException( - String.format("Method %s#%s has a %s annotation, but no %s annotation", - outerCls.getName(), method.getName(), - JsonProperty.class.getSimpleName(), - DsonOutput.class.getSimpleName())); - } - if (dsonOutput != null && jsonProperty == null && jsonAnyGetter == null) { - throw new IllegalStateException( - String.format("Method %s#%s has a %s annotation, but no %s or %s annotation", - outerCls.getName(), method.getName(), - DsonOutput.class.getSimpleName(), - JsonProperty.class.getSimpleName(), - JsonAnyGetter.class.getSimpleName())); - } - if (dsonOutput != null && jsonProperty != null) { - String fieldName = jsonProperty.value(); - if (method.getParameterCount() != 0) { - throw new IllegalStateException( - String.format("Property %s in class %s not a getter", - fieldName, outerCls.getName())); - } - for (DsonOutput.Output out : DsonOutput.Output.toEnumSet(dsonOutput.value(), dsonOutput.include())) { - if (!tempOutputs.get(out).computeIfAbsent(outerCls, k -> new HashSet<>()).add(fieldName)) { - throw new IllegalStateException( - String.format("Duplicate property %s in class %s", - fieldName, outerCls.getName())); - } - } - } - if (dsonOutput != null && jsonAnyGetter != null) { - DsonAnyProperties properties = method.getDeclaredAnnotation(DsonAnyProperties.class); - if (properties == null) { - throw new IllegalStateException( - String.format("Found %s annotation without %s annotation in class %s", - JsonAnyGetter.class.getSimpleName(), - DsonAnyProperties.class.getSimpleName(), - cls.getName())); - } - Set fieldNames = ImmutableSet.copyOf(properties.value()); - for (DsonOutput.Output out : DsonOutput.Output.toEnumSet(dsonOutput.value(), dsonOutput.include())) { - Set fields = tempOutputs.get(out).computeIfAbsent(outerCls, k -> new HashSet<>()); - for (String fieldName : fieldNames) { - if (!fields.add(fieldName)) { - throw new IllegalStateException( - String.format("Duplicate property %s in class %s", - fieldName, outerCls.getName())); - } - } - } - } - } - } - } - Map, ImmutableSet>> newOutputs = new EnumMap<>(Output.class); - Map, Set> classFields = new HashMap<>(); - for (Map.Entry, Set>> output : tempOutputs.entrySet()) { - newOutputs.put(output.getKey(), toImmutableMap(output.getValue())); - for (Map.Entry, Set> fields : output.getValue().entrySet()) { - classFields.computeIfAbsent(fields.getKey(), k -> new HashSet<>()).addAll(fields.getValue()); - } - } - List classesWithMissingSerializer = classFields.entrySet().stream() - .filter(e -> !e.getValue().contains(SerializerConstants.SERIALIZER_NAME)) - .map(Map.Entry::getKey) - .map(Class::getName) - .sorted() - .collect(Collectors.toList()); - if (!classesWithMissingSerializer.isEmpty()) { - throw new IllegalStateException(String.format( - "The following class%s missing the '%s' field: %s", - classesWithMissingSerializer.size() == 1 ? " is" : "es are", - SerializerConstants.SERIALIZER_NAME, - String.join(", ", classesWithMissingSerializer))); - } - outputs.putAll(newOutputs); - } - - @Override - public ImmutableMap, ImmutableSet> getIncludedFields(Output output) { - ImmutableMap, ImmutableSet> includedFields = outputs.get(output); - if (includedFields == null) { - throw new IllegalArgumentException("No such output selection: " + output); - } - return includedFields; - } - - private static ImmutableMap, ImmutableSet> toImmutableMap(Map, Set> value) { - ImmutableMap.Builder, ImmutableSet> mapBuilder = ImmutableMap.builder(); - for (Map.Entry, Set> e : value.entrySet()) { - mapBuilder.put(e.getKey(), ImmutableSet.copyOf(e.getValue())); - } - return mapBuilder.build(); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.serialization; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.radixdlt.serialization.DsonOutput.Output; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Class that maintains a map of {@link DsonOutput.Output} types to a set of pairs of classes and + * field/method names to output for that serialization type. + * + *

This {@link SerializationPolicy} operates by scanning a supplied list of classes. + */ +public abstract class ClassScanningSerializationPolicy implements SerializationPolicy { + + private final EnumMap, ImmutableSet>> outputs = + new EnumMap<>(Output.class); + + /** + * Scan for all classes with an {@code SerializerId} annotation. The entire classpath is scanned, + * including JAR files. + * + * @param classes The list of classes to scan for serialization annotations + * @throws IllegalStateException If issues with serialization configuration are found while + * scanning. + */ + protected ClassScanningSerializationPolicy(Collection> classes) { + Map, Set>> tempOutputs = new EnumMap<>(Output.class); + // These are the outputs we will be collecting. + // ALL and NONE are replaced with the complete set and empty set respectively + tempOutputs.put(Output.HASH, new HashMap<>()); + tempOutputs.put(Output.API, new HashMap<>()); + tempOutputs.put(Output.WIRE, new HashMap<>()); + tempOutputs.put(Output.PERSIST, new HashMap<>()); + + // First fields + for (Class outerCls : classes) { + for (Class cls = outerCls; !Object.class.equals(cls); cls = cls.getSuperclass()) { + for (Field field : cls.getDeclaredFields()) { + DsonOutput dsonOutput = field.getDeclaredAnnotation(DsonOutput.class); + JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class); + if (dsonOutput == null && jsonProperty != null) { + throw new IllegalStateException( + String.format( + "Field %s#%s has a %s annotation, but no %s annotation", + outerCls.getName(), + field.getName(), + JsonProperty.class.getSimpleName(), + DsonOutput.class.getSimpleName())); + } + if (dsonOutput != null && jsonProperty == null) { + throw new IllegalStateException( + String.format( + "Field %s#%s has a %s annotation, but no %s annotation", + outerCls.getName(), + field.getName(), + DsonOutput.class.getSimpleName(), + JsonProperty.class.getSimpleName())); + } + if (dsonOutput != null && jsonProperty != null) { + String fieldName = jsonProperty.value(); + for (DsonOutput.Output out : + DsonOutput.Output.toEnumSet(dsonOutput.value(), dsonOutput.include())) { + if (!tempOutputs + .get(out) + .computeIfAbsent(outerCls, k -> new HashSet<>()) + .add(fieldName)) { + throw new IllegalStateException( + String.format( + "Duplicate property %s in class %s", fieldName, outerCls.getName())); + } + } + } + } + // Now methods + for (Method method : cls.getDeclaredMethods()) { + DsonOutput dsonOutput = method.getDeclaredAnnotation(DsonOutput.class); + JsonProperty jsonProperty = method.getDeclaredAnnotation(JsonProperty.class); + JsonAnyGetter jsonAnyGetter = method.getDeclaredAnnotation(JsonAnyGetter.class); + if (dsonOutput == null && jsonProperty != null) { + if (method.getParameterCount() == 1) { + // Ignore setter + continue; + } + throw new IllegalStateException( + String.format( + "Method %s#%s has a %s annotation, but no %s annotation", + outerCls.getName(), + method.getName(), + JsonProperty.class.getSimpleName(), + DsonOutput.class.getSimpleName())); + } + if (dsonOutput != null && jsonProperty == null && jsonAnyGetter == null) { + throw new IllegalStateException( + String.format( + "Method %s#%s has a %s annotation, but no %s or %s annotation", + outerCls.getName(), + method.getName(), + DsonOutput.class.getSimpleName(), + JsonProperty.class.getSimpleName(), + JsonAnyGetter.class.getSimpleName())); + } + if (dsonOutput != null && jsonProperty != null) { + String fieldName = jsonProperty.value(); + if (method.getParameterCount() != 0) { + throw new IllegalStateException( + String.format( + "Property %s in class %s not a getter", fieldName, outerCls.getName())); + } + for (DsonOutput.Output out : + DsonOutput.Output.toEnumSet(dsonOutput.value(), dsonOutput.include())) { + if (!tempOutputs + .get(out) + .computeIfAbsent(outerCls, k -> new HashSet<>()) + .add(fieldName)) { + throw new IllegalStateException( + String.format( + "Duplicate property %s in class %s", fieldName, outerCls.getName())); + } + } + } + if (dsonOutput != null && jsonAnyGetter != null) { + DsonAnyProperties properties = method.getDeclaredAnnotation(DsonAnyProperties.class); + if (properties == null) { + throw new IllegalStateException( + String.format( + "Found %s annotation without %s annotation in class %s", + JsonAnyGetter.class.getSimpleName(), + DsonAnyProperties.class.getSimpleName(), + cls.getName())); + } + Set fieldNames = ImmutableSet.copyOf(properties.value()); + for (DsonOutput.Output out : + DsonOutput.Output.toEnumSet(dsonOutput.value(), dsonOutput.include())) { + Set fields = + tempOutputs.get(out).computeIfAbsent(outerCls, k -> new HashSet<>()); + for (String fieldName : fieldNames) { + if (!fields.add(fieldName)) { + throw new IllegalStateException( + String.format( + "Duplicate property %s in class %s", fieldName, outerCls.getName())); + } + } + } + } + } + } + } + Map, ImmutableSet>> newOutputs = + new EnumMap<>(Output.class); + Map, Set> classFields = new HashMap<>(); + for (Map.Entry, Set>> output : tempOutputs.entrySet()) { + newOutputs.put(output.getKey(), toImmutableMap(output.getValue())); + for (Map.Entry, Set> fields : output.getValue().entrySet()) { + classFields + .computeIfAbsent(fields.getKey(), k -> new HashSet<>()) + .addAll(fields.getValue()); + } + } + List classesWithMissingSerializer = + classFields.entrySet().stream() + .filter(e -> !e.getValue().contains(SerializerConstants.SERIALIZER_NAME)) + .map(Map.Entry::getKey) + .map(Class::getName) + .sorted() + .collect(Collectors.toList()); + if (!classesWithMissingSerializer.isEmpty()) { + throw new IllegalStateException( + String.format( + "The following class%s missing the '%s' field: %s", + classesWithMissingSerializer.size() == 1 ? " is" : "es are", + SerializerConstants.SERIALIZER_NAME, + String.join(", ", classesWithMissingSerializer))); + } + outputs.putAll(newOutputs); + } + + @Override + public ImmutableMap, ImmutableSet> getIncludedFields(Output output) { + ImmutableMap, ImmutableSet> includedFields = outputs.get(output); + if (includedFields == null) { + throw new IllegalArgumentException("No such output selection: " + output); + } + return includedFields; + } + + private static ImmutableMap, ImmutableSet> toImmutableMap( + Map, Set> value) { + ImmutableMap.Builder, ImmutableSet> mapBuilder = ImmutableMap.builder(); + for (Map.Entry, Set> e : value.entrySet()) { + mapBuilder.put(e.getKey(), ImmutableSet.copyOf(e.getValue())); + } + return mapBuilder.build(); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializerIds.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializerIds.java index a6c40ac9eb..8872320b8d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializerIds.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/ClassScanningSerializerIds.java @@ -1,205 +1,207 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.serialization; - -import static com.radixdlt.serialization.SerializerConstants.SERIALIZER_ID_ANNOTATION; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Maps; -import com.radixdlt.identifiers.EUID; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Class that maintains a map of serializer IDs to {@code Class} objects - * and vice versa. - *

- * This {@link SerializerIds} operates by scanning a supplied list of classes. - */ -public abstract class ClassScanningSerializerIds implements SerializerIds { - private static final Logger log = LogManager.getLogger(ClassScanningSerializerIds.class); - - // Assuming that lookups from class to ID will be more identifiers - private final Map, String> classIdMap = Maps.newHashMap(); - // Inverse view of same data - private final BiMap> idClassMap = HashBiMap.create(); - - private final HashSet> serializableSupertypes = new HashSet<>(); - - /** - * Scan for all classes with an {@code SerializerId} annotation - * in the specified set of classes. - * - * @param classes The list of classes to scan for serialization annotations - * @throws SerializerIdsException If two or more classes are - * found with the same {@code SerializerId} - */ - protected ClassScanningSerializerIds(Collection> classes) { - Map>> polymorphicMap = new HashMap<>(); - - for (Class cls : classes) { - SerializerId2 sid = cls.getDeclaredAnnotation(SERIALIZER_ID_ANNOTATION); - if (sid == null) { - // For some reason, Reflections returns classes without SerializerId, but - // that inherit from classes with the (non-inheritable) annotation. Sad. - log.debug("Skipping unannotated class " + cls.getName()); - continue; - } - - if (cls.isInterface()) { - // Interfaces should not be marked with @SerializerId - log.warn(String.format("Skipping interface %s with unexpected %s annotation", - cls.getName(), SERIALIZER_ID_ANNOTATION.getSimpleName())); - continue; - } - - String id = sid.value(); - - if (Polymorphic.class.isAssignableFrom(cls)) { - // Polymorphic class hierarchy checked later - log.debug("Polymorphic class:" + cls.getName() + " with ID:" + id); - polymorphicMap.computeIfAbsent(id, k -> new ArrayList<>()).add(cls); - } else { - // Check for duplicates - Class dupClass = idClassMap.put(id, cls); - if (dupClass != null) { - throw new SerializerIdsException( - String.format("Aborting, duplicate ID %s discovered in classes: [%s, %s]", - id, cls.getName(), dupClass.getName())); - } - log.debug("Putting Class:" + cls.getName() + " with ID:" + id); - collectSupertypes(cls); - collectInterfaces(cls); - } - } - - classIdMap.putAll(idClassMap.inverse()); - Map idNumericMap = new HashMap<>(); - // Check polymorphic hierarchy consistency - for (Map.Entry>> entry : polymorphicMap.entrySet()) { - String id = entry.getKey(); - if (!idClassMap.containsKey(id)) { - throw new SerializerIdsException(String.format( - "No concrete class with ID '%s' for polymorphic classes %s", entry.getKey(), entry.getValue() - )); - } - EUID numericId = SerializationUtils.stringToNumericID(id); - String dupNumericId = idNumericMap.put(numericId, id); - if (dupNumericId != null) { - throw new SerializerIdsException(String.format("Aborting, numeric id %s of %s clashes with %s", - numericId, id, dupNumericId)); - } - for (Class cls : entry.getValue()) { - String dupId = classIdMap.put(cls, id); - if (dupId != null) { - throw new SerializerIdsException( - String.format("Aborting, class %s has duplicate IDs %s and %s", - cls.getName(), id, dupId)); - } - } - } - } - - private void collectSupertypes(Class cls) { - while (!Object.class.equals(cls)) { - serializableSupertypes.add(cls); - cls = cls.getSuperclass(); - } - } - - private void collectInterfaces(Class cls) { - Stream.of(cls.getInterfaces()) - .filter(this::isSerializerRoot) - .forEachOrdered(serializableSupertypes::add); - } - - private boolean isSerializerRoot(Class clazz) { - return clazz.isAnnotationPresent(SerializerConstants.SERIALIZER_ROOT_ANNOTATION); - } - - @Override - public String getIdForClass(Class cls) { - return classIdMap.get(cls); - } - - @Override - public Class getClassForId(String id) { - return idClassMap.get(id); - } - - @Override - public boolean isSerializableSuper(Class cls) { - return serializableSupertypes.contains(cls); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.serialization; + +import static com.radixdlt.serialization.SerializerConstants.SERIALIZER_ID_ANNOTATION; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; +import com.radixdlt.identifiers.EUID; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Class that maintains a map of serializer IDs to {@code Class} objects and vice versa. + * + *

This {@link SerializerIds} operates by scanning a supplied list of classes. + */ +public abstract class ClassScanningSerializerIds implements SerializerIds { + private static final Logger log = LogManager.getLogger(ClassScanningSerializerIds.class); + + // Assuming that lookups from class to ID will be more identifiers + private final Map, String> classIdMap = Maps.newHashMap(); + // Inverse view of same data + private final BiMap> idClassMap = HashBiMap.create(); + + private final HashSet> serializableSupertypes = new HashSet<>(); + + /** + * Scan for all classes with an {@code SerializerId} annotation in the specified set of classes. + * + * @param classes The list of classes to scan for serialization annotations + * @throws SerializerIdsException If two or more classes are found with the same {@code + * SerializerId} + */ + protected ClassScanningSerializerIds(Collection> classes) { + Map>> polymorphicMap = new HashMap<>(); + + for (Class cls : classes) { + SerializerId2 sid = cls.getDeclaredAnnotation(SERIALIZER_ID_ANNOTATION); + if (sid == null) { + // For some reason, Reflections returns classes without SerializerId, but + // that inherit from classes with the (non-inheritable) annotation. Sad. + log.debug("Skipping unannotated class " + cls.getName()); + continue; + } + + if (cls.isInterface()) { + // Interfaces should not be marked with @SerializerId + log.warn( + String.format( + "Skipping interface %s with unexpected %s annotation", + cls.getName(), SERIALIZER_ID_ANNOTATION.getSimpleName())); + continue; + } + + String id = sid.value(); + + if (Polymorphic.class.isAssignableFrom(cls)) { + // Polymorphic class hierarchy checked later + log.debug("Polymorphic class:" + cls.getName() + " with ID:" + id); + polymorphicMap.computeIfAbsent(id, k -> new ArrayList<>()).add(cls); + } else { + // Check for duplicates + Class dupClass = idClassMap.put(id, cls); + if (dupClass != null) { + throw new SerializerIdsException( + String.format( + "Aborting, duplicate ID %s discovered in classes: [%s, %s]", + id, cls.getName(), dupClass.getName())); + } + log.debug("Putting Class:" + cls.getName() + " with ID:" + id); + collectSupertypes(cls); + collectInterfaces(cls); + } + } + + classIdMap.putAll(idClassMap.inverse()); + Map idNumericMap = new HashMap<>(); + // Check polymorphic hierarchy consistency + for (Map.Entry>> entry : polymorphicMap.entrySet()) { + String id = entry.getKey(); + if (!idClassMap.containsKey(id)) { + throw new SerializerIdsException( + String.format( + "No concrete class with ID '%s' for polymorphic classes %s", + entry.getKey(), entry.getValue())); + } + EUID numericId = SerializationUtils.stringToNumericID(id); + String dupNumericId = idNumericMap.put(numericId, id); + if (dupNumericId != null) { + throw new SerializerIdsException( + String.format( + "Aborting, numeric id %s of %s clashes with %s", numericId, id, dupNumericId)); + } + for (Class cls : entry.getValue()) { + String dupId = classIdMap.put(cls, id); + if (dupId != null) { + throw new SerializerIdsException( + String.format( + "Aborting, class %s has duplicate IDs %s and %s", cls.getName(), id, dupId)); + } + } + } + } + + private void collectSupertypes(Class cls) { + while (!Object.class.equals(cls)) { + serializableSupertypes.add(cls); + cls = cls.getSuperclass(); + } + } + + private void collectInterfaces(Class cls) { + Stream.of(cls.getInterfaces()) + .filter(this::isSerializerRoot) + .forEachOrdered(serializableSupertypes::add); + } + + private boolean isSerializerRoot(Class clazz) { + return clazz.isAnnotationPresent(SerializerConstants.SERIALIZER_ROOT_ANNOTATION); + } + + @Override + public String getIdForClass(Class cls) { + return classIdMap.get(cls); + } + + @Override + public Class getClassForId(String id) { + return idClassMap.get(id); + } + + @Override + public boolean isSerializableSuper(Class cls) { + return serializableSupertypes.contains(cls); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DeserializeException.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DeserializeException.java index 6b8869fe6b..e1d234e4b1 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DeserializeException.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DeserializeException.java @@ -66,40 +66,34 @@ import java.io.IOException; -/** - * General exception raised for failures which may happen during deserialization. - */ +/** General exception raised for failures which may happen during deserialization. */ public class DeserializeException extends IOException { - private static final long serialVersionUID = -4590727289547107895L; + private static final long serialVersionUID = -4590727289547107895L; - /** - * Constructs a new exception with the specified detail message. The - * cause is not initialized, and may subsequently be initialized by - * a call to {@link #initCause}. - * - * @param message the detail message. The detail message is saved for - * later retrieval by the {@link #getMessage()} method. - */ - public DeserializeException(String message) { - super(message); - } + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and + * may subsequently be initialized by a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public DeserializeException(String message) { + super(message); + } - /** - * Constructs a new exception with the specified detail message and - * cause. - *

- * Note that the detail message associated with {@code cause} is - * not automatically incorporated in this exception's detail - * message. - * - * @param message the detail message (which is saved for later retrieval - * by the {@link #getMessage()} method). - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method). (A {@code null} value is - * permitted, and indicates that the cause is nonexistent or - * unknown.) - */ - public DeserializeException(String message, Throwable cause) { - super(message, cause); - } + /** + * Constructs a new exception with the specified detail message and cause. + * + *

Note that the detail message associated with {@code cause} is not automatically + * incorporated in this exception's detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link + * #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). + * (A {@code null} value is permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public DeserializeException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonAnyProperties.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonAnyProperties.java index a977f2d7f8..9b526826bb 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonAnyProperties.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonAnyProperties.java @@ -74,5 +74,5 @@ @Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DsonAnyProperties { - String[] value(); + String[] value(); } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonOutput.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonOutput.java index 853efcbea9..fb829978d5 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonOutput.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/DsonOutput.java @@ -73,20 +73,21 @@ import java.util.EnumSet; /** - * Annotation used to indicate which fields are included in coded DSON - * for which output requirements. As an example, the "serializer" field - * and any signature fields, are not included in "HASH" output, as they - * are not included in any hash computation. - *

- * As an example, to include a field only in data persisted to disk, the - * following annotation might be used: + * Annotation used to indicate which fields are included in coded DSON for which output + * requirements. As an example, the "serializer" field and any signature fields, are not included in + * "HASH" output, as they are not included in any hash computation. + * + *

As an example, to include a field only in data persisted to disk, the following annotation + * might be used: + * *

  *         @DsonOutput(Output.PERSIST)
  *         @JsonProperty("diskTimestamp")
  *         private long diskTimestamp;
  * 
- * To exclude data from being included in a hash, the following annotation - * could be used: + * + * To exclude data from being included in a hash, the following annotation could be used: + * *
  *         @DsonOutput(value = Output.HASH, include = false)
  *         @JsonProperty("signature")
@@ -98,95 +99,81 @@
 @Retention(RetentionPolicy.RUNTIME)
 public @interface DsonOutput {
 
-	/**
-	 * The serialization output modes for which this field should be
-	 * included or excluded, depending on the value of {@link #include()}.
-	 *
-	 * @return The output modes for which this field should be
-	 * 		included or excluded.
-	 * @see #include()
-	 */
-	Output[] value();
+  /**
+   * The serialization output modes for which this field should be included or excluded, depending
+   * on the value of {@link #include()}.
+   *
+   * @return The output modes for which this field should be included or excluded.
+   * @see #include()
+   */
+  Output[] value();
 
-	/**
-	 * {@code true} if {@link #value()} specified output modes where this
-	 * field is to be included.  Otherwise {@link #value()} specifies modes
-	 * where this field is to be excluded.
-	 *
-	 * @return {@code true} if field to be included for specified modes,
-	 * 		{@code false} otherwise.
-	 */
-	boolean include() default true;
+  /**
+   * {@code true} if {@link #value()} specified output modes where this field is to be included.
+   * Otherwise {@link #value()} specifies modes where this field is to be excluded.
+   *
+   * @return {@code true} if field to be included for specified modes, {@code false} otherwise.
+   */
+  boolean include() default true;
 
-	/**
-	 * Output modes for serialization.
-	 * 

- * There are four concrete output modes, {@link #HASH}, {@link #API}, - * {@link #WIRE} and {@link #PERSIST}. Two additional modes are - * provided for ease of use {@link #ALL} and {@link #NONE}, representing - * the union of all the concrete modes, and the empty set respectively. - *

- * Note that the output mode {@link #NONE} is of limited use. - */ - enum Output { - /** - * An output mode that never results in output. Of limited use. - */ - NONE, - /** - * An output mode for calculating hashes. - */ - HASH, - /** - * An output mode for use with application interfaces. - */ - API, - /** - * An output mode for use when communicating to other nodes. - */ - WIRE, - /** - * An output mode for use when writing data to persistent storage. - */ - PERSIST, - /** - * An output mode that always results in output. - */ - ALL; + /** + * Output modes for serialization. + * + *

There are four concrete output modes, {@link #HASH}, {@link #API}, {@link #WIRE} and {@link + * #PERSIST}. Two additional modes are provided for ease of use {@link #ALL} and {@link #NONE}, + * representing the union of all the concrete modes, and the empty set respectively. + * + *

Note that the output mode {@link #NONE} is of limited use. + */ + enum Output { + /** An output mode that never results in output. Of limited use. */ + NONE, + /** An output mode for calculating hashes. */ + HASH, + /** An output mode for use with application interfaces. */ + API, + /** An output mode for use when communicating to other nodes. */ + WIRE, + /** An output mode for use when writing data to persistent storage. */ + PERSIST, + /** An output mode that always results in output. */ + ALL; - private static final EnumSet NONE_OF = EnumSet.noneOf(Output.class); - private static final EnumSet ALL_OF = EnumSet.allOf(Output.class); + private static final EnumSet NONE_OF = EnumSet.noneOf(Output.class); + private static final EnumSet ALL_OF = EnumSet.allOf(Output.class); - /** - * Convert enclosing annotation values to an {@link EnumSet}. - * - * @param value The values from the annotation - * @param include The include flag from the annotation - * @return An {@link EnumSet} identifying the {@link DsonOutput.Output} - * modes to output fields for. - */ - public static EnumSet toEnumSet(Output[] value, boolean include) { - EnumSet set = EnumSet.copyOf(Arrays.asList(value)); - if (set.contains(NONE)) { - if (value.length == 1) { - set = NONE_OF; - } else { - throw new IllegalArgumentException("Can't include additional outputs with NONE: " + Arrays.toString(value)); - } - } - if (set.contains(ALL)) { - if (value.length == 1) { - set = ALL_OF; - } else { - throw new IllegalArgumentException("Can't include additional outputs with ALL: " + Arrays.toString(value)); - } - } - if (!include) { - set = EnumSet.complementOf(set); - } - set.remove(ALL); - set.remove(NONE); - return set; - } - } + /** + * Convert enclosing annotation values to an {@link EnumSet}. + * + * @param value The values from the annotation + * @param include The include flag from the annotation + * @return An {@link EnumSet} identifying the {@link DsonOutput.Output} modes to output fields + * for. + */ + public static EnumSet toEnumSet(Output[] value, boolean include) { + EnumSet set = EnumSet.copyOf(Arrays.asList(value)); + if (set.contains(NONE)) { + if (value.length == 1) { + set = NONE_OF; + } else { + throw new IllegalArgumentException( + "Can't include additional outputs with NONE: " + Arrays.toString(value)); + } + } + if (set.contains(ALL)) { + if (value.length == 1) { + set = ALL_OF; + } else { + throw new IllegalArgumentException( + "Can't include additional outputs with ALL: " + Arrays.toString(value)); + } + } + if (!include) { + set = EnumSet.complementOf(set); + } + set.remove(ALL); + set.remove(NONE); + return set; + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/JsonJavaType.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/JsonJavaType.java index 192c28feca..8191841fd7 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/JsonJavaType.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/JsonJavaType.java @@ -67,18 +67,15 @@ import com.fasterxml.jackson.databind.JavaType; import java.util.Objects; -/** - * Opaque wrapper for {@link JavaType} to allow distinguishing between - * JSON and DSON mapper. - */ +/** Opaque wrapper for {@link JavaType} to allow distinguishing between JSON and DSON mapper. */ public class JsonJavaType { - private final JavaType type; + private final JavaType type; - JsonJavaType(JavaType type) { - this.type = Objects.requireNonNull(type); - } + JsonJavaType(JavaType type) { + this.type = Objects.requireNonNull(type); + } - JavaType javaType() { - return this.type; - } + JavaType javaType() { + return this.type; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/MapHelper.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/MapHelper.java index 6c0cab9193..0c640e4938 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/MapHelper.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/MapHelper.java @@ -67,239 +67,324 @@ import java.util.HashMap; import java.util.Map; -/** - * Helpers for creating maps used by the serializer for ephemeral data. - */ +/** Helpers for creating maps used by the serializer for ephemeral data. */ public final class MapHelper { - private MapHelper() { - throw new IllegalStateException("Can't construct"); - } + private MapHelper() { + throw new IllegalStateException("Can't construct"); + } - /** - * Create a new mutable map with contents {@code (k1, v1)}. - * - * @param k1 The key of the element to add to the new map - * @param v1 The value of the element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1) { - Map newMap = new HashMap<>(); - newMap.put(k1, v1); - return newMap; - } + /** + * Create a new mutable map with contents {@code (k1, v1)}. + * + * @param k1 The key of the element to add to the new map + * @param v1 The value of the element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf(String k1, Object v1) { + Map newMap = new HashMap<>(); + newMap.put(k1, v1); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2) { - Map newMap = mapOf(k1, v1); - newMap.put(k2, v2); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf(String k1, Object v1, String k2, Object v2) { + Map newMap = mapOf(k1, v1); + newMap.put(k2, v2); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3) { - Map newMap = mapOf(k1, v1, k2, v2); - newMap.put(k3, v3); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, Object v1, String k2, Object v2, String k3, Object v3) { + Map newMap = mapOf(k1, v1, k2, v2); + newMap.put(k3, v3); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3); - newMap.put(k4, v4); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4) { + Map newMap = mapOf(k1, v1, k2, v2, k3, v3); + newMap.put(k4, v4); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @param k5 The key of the fifth element to add to the new map - * @param v5 The value of the fifth element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4, - String k5, Object v5) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4); - newMap.put(k5, v5); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @param k5 The key of the fifth element to add to the new map + * @param v5 The value of the fifth element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, + Object v1, + String k2, + Object v2, + String k3, + Object v3, + String k4, + Object v4, + String k5, + Object v5) { + Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4); + newMap.put(k5, v5); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @param k5 The key of the fifth element to add to the new map - * @param v5 The value of the fifth element to add to the new map - * @param k6 The key of the sixth element to add to the new map - * @param v6 The value of the sixth element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4, - String k5, Object v5, String k6, Object v6) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); - newMap.put(k6, v6); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @param k5 The key of the fifth element to add to the new map + * @param v5 The value of the fifth element to add to the new map + * @param k6 The key of the sixth element to add to the new map + * @param v6 The value of the sixth element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, + Object v1, + String k2, + Object v2, + String k3, + Object v3, + String k4, + Object v4, + String k5, + Object v5, + String k6, + Object v6) { + Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + newMap.put(k6, v6); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @param k5 The key of the fifth element to add to the new map - * @param v5 The value of the fifth element to add to the new map - * @param k6 The key of the sixth element to add to the new map - * @param v6 The value of the sixth element to add to the new map - * @param k7 The key of the seventh element to add to the new map - * @param v7 The value of the seventh element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4, - String k5, Object v5, String k6, Object v6, String k7, Object v7) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); - newMap.put(k7, v7); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @param k5 The key of the fifth element to add to the new map + * @param v5 The value of the fifth element to add to the new map + * @param k6 The key of the sixth element to add to the new map + * @param v6 The value of the sixth element to add to the new map + * @param k7 The key of the seventh element to add to the new map + * @param v7 The value of the seventh element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, + Object v1, + String k2, + Object v2, + String k3, + Object v3, + String k4, + Object v4, + String k5, + Object v5, + String k6, + Object v6, + String k7, + Object v7) { + Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); + newMap.put(k7, v7); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @param k5 The key of the fifth element to add to the new map - * @param v5 The value of the fifth element to add to the new map - * @param k6 The key of the sixth element to add to the new map - * @param v6 The value of the sixth element to add to the new map - * @param k7 The key of the seventh element to add to the new map - * @param v7 The value of the seventh element to add to the new map - * @param k8 The key of the eighth element to add to the new map - * @param v8 The value of the eighth element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4, - String k5, Object v5, String k6, Object v6, String k7, Object v7, String k8, Object v8) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); - newMap.put(k8, v8); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @param k5 The key of the fifth element to add to the new map + * @param v5 The value of the fifth element to add to the new map + * @param k6 The key of the sixth element to add to the new map + * @param v6 The value of the sixth element to add to the new map + * @param k7 The key of the seventh element to add to the new map + * @param v7 The value of the seventh element to add to the new map + * @param k8 The key of the eighth element to add to the new map + * @param v8 The value of the eighth element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, + Object v1, + String k2, + Object v2, + String k3, + Object v3, + String k4, + Object v4, + String k5, + Object v5, + String k6, + Object v6, + String k7, + Object v7, + String k8, + Object v8) { + Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); + newMap.put(k8, v8); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @param k5 The key of the fifth element to add to the new map - * @param v5 The value of the fifth element to add to the new map - * @param k6 The key of the sixth element to add to the new map - * @param v6 The value of the sixth element to add to the new map - * @param k7 The key of the seventh element to add to the new map - * @param v7 The value of the seventh element to add to the new map - * @param k8 The key of the eighth element to add to the new map - * @param v8 The value of the eighth element to add to the new map - * @param k9 The key of the ninth element to add to the new map - * @param v9 The value of the ninth element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4, - String k5, Object v5, String k6, Object v6, String k7, Object v7, String k8, Object v8, String k9, Object v9) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); - newMap.put(k9, v9); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @param k5 The key of the fifth element to add to the new map + * @param v5 The value of the fifth element to add to the new map + * @param k6 The key of the sixth element to add to the new map + * @param v6 The value of the sixth element to add to the new map + * @param k7 The key of the seventh element to add to the new map + * @param v7 The value of the seventh element to add to the new map + * @param k8 The key of the eighth element to add to the new map + * @param v8 The value of the eighth element to add to the new map + * @param k9 The key of the ninth element to add to the new map + * @param v9 The value of the ninth element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, + Object v1, + String k2, + Object v2, + String k3, + Object v3, + String k4, + Object v4, + String k5, + Object v5, + String k6, + Object v6, + String k7, + Object v7, + String k8, + Object v8, + String k9, + Object v9) { + Map newMap = + mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); + newMap.put(k9, v9); + return newMap; + } - /** - * Create a new mutable map with the specified contents. - * - * @param k1 The key of the first element to add to the new map - * @param v1 The value of the first element to add to the new map - * @param k2 The key of the second element to add to the new map - * @param v2 The value of the second element to add to the new map - * @param k3 The key of the third element to add to the new map - * @param v3 The value of the third element to add to the new map - * @param k4 The key of the fourth element to add to the new map - * @param v4 The value of the fourth element to add to the new map - * @param k5 The key of the fifth element to add to the new map - * @param v5 The value of the fifth element to add to the new map - * @param k6 The key of the sixth element to add to the new map - * @param v6 The value of the sixth element to add to the new map - * @param k7 The key of the seventh element to add to the new map - * @param v7 The value of the seventh element to add to the new map - * @param k8 The key of the eighth element to add to the new map - * @param v8 The value of the eighth element to add to the new map - * @param k9 The key of the ninth element to add to the new map - * @param v9 The value of the ninth element to add to the new map - * @param k10 The key of the tenth element to add to the new map - * @param v10 The value of the tenth element to add to the new map - * @return A freshly created mutable map with the specified contents - */ - public static Map mapOf(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4, Object v4, - String k5, Object v5, String k6, Object v6, String k7, Object v7, String k8, Object v8, String k9, Object v9, - String k10, Object v10) { - Map newMap = mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); - newMap.put(k10, v10); - return newMap; - } + /** + * Create a new mutable map with the specified contents. + * + * @param k1 The key of the first element to add to the new map + * @param v1 The value of the first element to add to the new map + * @param k2 The key of the second element to add to the new map + * @param v2 The value of the second element to add to the new map + * @param k3 The key of the third element to add to the new map + * @param v3 The value of the third element to add to the new map + * @param k4 The key of the fourth element to add to the new map + * @param v4 The value of the fourth element to add to the new map + * @param k5 The key of the fifth element to add to the new map + * @param v5 The value of the fifth element to add to the new map + * @param k6 The key of the sixth element to add to the new map + * @param v6 The value of the sixth element to add to the new map + * @param k7 The key of the seventh element to add to the new map + * @param v7 The value of the seventh element to add to the new map + * @param k8 The key of the eighth element to add to the new map + * @param v8 The value of the eighth element to add to the new map + * @param k9 The key of the ninth element to add to the new map + * @param v9 The value of the ninth element to add to the new map + * @param k10 The key of the tenth element to add to the new map + * @param v10 The value of the tenth element to add to the new map + * @return A freshly created mutable map with the specified contents + */ + public static Map mapOf( + String k1, + Object v1, + String k2, + Object v2, + String k3, + Object v3, + String k4, + Object v4, + String k5, + Object v5, + String k6, + Object v6, + String k7, + Object v7, + String k8, + Object v8, + String k9, + Object v9, + String k10, + Object v10) { + Map newMap = + mapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); + newMap.put(k10, v10); + return newMap; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Polymorphic.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Polymorphic.java index 2e017a82df..dad40e3737 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Polymorphic.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Polymorphic.java @@ -64,6 +64,4 @@ package com.radixdlt.serialization; -public interface Polymorphic { - -} +public interface Polymorphic {} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Serialization.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Serialization.java index 2a520a66df..4256ebb32b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Serialization.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/Serialization.java @@ -64,6 +64,8 @@ package com.radixdlt.serialization; +import static com.radixdlt.serialization.mapper.DsonFieldFilter.filterProviderFor; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ser.FilterProvider; @@ -74,8 +76,6 @@ import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.mapper.JacksonCborMapper; import com.radixdlt.serialization.mapper.JacksonJsonMapper; -import org.json.JSONObject; - import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -87,273 +87,285 @@ import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.json.JSONObject; -import static com.radixdlt.serialization.mapper.DsonFieldFilter.filterProviderFor; - -/** - * Serialization class that handles conversion to/from DSON and JSON. - */ -//TODO: get rid of JSON support +/** Serialization class that handles conversion to/from DSON and JSON. */ +// TODO: get rid of JSON support public class Serialization { - /** - * Create a new instance of {@link Serialization} with the specified IDs and policy. - * - * @param idLookup The {@link SerializerIds} to use for class and property lookup. - * @param policy The {@link SerializationPolicy} to use for determining serialization outputs. - * @return A new instance of {@link Serialization}. - */ - public static Serialization create(SerializerIds idLookup, SerializationPolicy policy) { - return new Serialization(idLookup, policy); - } - - private final ImmutableMap dsonMappers; - private final ImmutableMap jsonMappers; - - private final SerializerIds idLookup; - - private final FilterProvider allProvider; - private final FilterProvider noneProvider; - - // Constructor set up to be dependency injection capable at some future date - @VisibleForTesting - Serialization(SerializerIds idLookup, SerializationPolicy policy) { - this.idLookup = idLookup; - - EnumSet availableOutputs = EnumSet.allOf(Output.class); - availableOutputs.remove(Output.ALL); - availableOutputs.remove(Output.NONE); - - noneProvider = filterProviderFor(ImmutableMap.of()); - Map, ImmutableSet> allFields = availableOutputs.stream() - .flatMap(output -> policy.getIncludedFields(output).entrySet().stream()) - .collect(Collectors.groupingBy( - Map.Entry::getKey, - flatMapping(e -> e.getValue().stream(), ImmutableSet.toImmutableSet()) - )); - allProvider = filterProviderFor(ImmutableMap.copyOf(allFields)); - - ImmutableMap.Builder dsonBuilder = ImmutableMap.builder(); - - JacksonCborMapper hashDsonMapper = JacksonCborMapper.create(idLookup, filterProviderFor(policy.getIncludedFields(Output.HASH)), true); - - JacksonCborMapper apiDsonMapper = JacksonCborMapper.create(idLookup, filterProviderFor(policy.getIncludedFields(Output.API)), - true, Optional.of(new ApiSerializationModifier(hashDsonMapper))); - - dsonBuilder.put(Output.HASH, hashDsonMapper); - dsonBuilder.put(Output.API, apiDsonMapper); - dsonBuilder.put(Output.NONE, JacksonCborMapper.create(idLookup, noneProvider, true)); - dsonBuilder.put(Output.ALL, JacksonCborMapper.create(idLookup, allProvider, true)); - for (Output e : Arrays.asList(Output.PERSIST, Output.WIRE)) { - dsonBuilder.put(e, JacksonCborMapper.create(idLookup, filterProviderFor(policy.getIncludedFields(e)), true)); - } - - dsonMappers = dsonBuilder.build(); - - ImmutableMap.Builder jsonBuilder = ImmutableMap.builder(); - - var hashJsonMapper = JacksonJsonMapper.create( - idLookup, - filterProviderFor(policy.getIncludedFields(Output.HASH)), - false - ); - var apiJsonMapper = JacksonJsonMapper.create( - idLookup, - filterProviderFor(policy.getIncludedFields(Output.API)), - false, - Optional.of(new ApiSerializationModifier(hashDsonMapper)) - ); - - jsonBuilder.put(Output.HASH, hashJsonMapper); - jsonBuilder.put(Output.API, apiJsonMapper); - jsonBuilder.put(Output.NONE, JacksonJsonMapper.create(idLookup, noneProvider, false)); - jsonBuilder.put(Output.ALL, JacksonJsonMapper.create(idLookup, allProvider, false)); - for (Output e : Lists.newArrayList(Output.PERSIST, Output.WIRE)) { - jsonBuilder.put(e, JacksonJsonMapper.create(idLookup, filterProviderFor(policy.getIncludedFields(e)), false)); - } - - jsonMappers = jsonBuilder.build(); - } - - /** - * Convert the specified object to DSON encoded bytes for the specified - * output mode. - * - * @param o The object to serialize - * @param output The output mode to serialize for - * @return The serialized object as a DSON byte array - */ - public byte[] toDson(Object o, DsonOutput.Output output) { - try { - return dsonMapper(output).writeValueAsBytes(o); - } catch (JsonProcessingException ex) { - throw new IllegalStateException(assembleMessage(o, "DSON"), ex); - } - } - - /** - * Convert the specified object to a JSON encoded string for the specified - * output mode. - * - * @param o The object to serialize - * @param output The output mode to serialize for - * @return The serialized object as a JSON string - */ - public String toJson(Object o, DsonOutput.Output output) { - try { - return jsonMapper(output).writeValueAsString(o); - } catch (JsonProcessingException ex) { - throw new IllegalStateException(assembleMessage(o, "JSON"), ex); - } - } - - /** - * Convert the specified object to a {@link JSONObject} encoded string for the specified - * output mode. - * - * @param o The object to serialize - * @param output The output mode to serialize for - * @return The serialized object as a JSON string - */ - public JSONObject toJsonObject(Object o, DsonOutput.Output output) { - return jsonMapper(output).convertValue(o, JSONObject.class); - } - - /** - * Convert the specified DSON encoded byte array to an instance of the - * specified class. - * - * @param bytes The DSON encoded object to deserialize - * @param valueType The class of the object to deserialize - * @return The deserialized object - * @throws DeserializeException if something goes wrong with serialization - */ - public T fromDson(byte[] bytes, int offset, int len, Class valueType) throws DeserializeException { - try { - return dsonMapper(Output.ALL).readValue(bytes, offset, len, valueType); - } catch (IOException ex) { - throw new DeserializeException("Error converting from DSON", ex); - } - } - - /** - * Convert the specified DSON encoded byte array to an instance of the - * specified class. - * - * @param bytes The DSON encoded object to deserialize - * @param valueType The class of the object to deserialize - * @return The deserialized object - * @throws DeserializeException if something goes wrong with serialization - */ - public T fromDson(byte[] bytes, Class valueType) throws DeserializeException { - try { - return dsonMapper(Output.ALL).readValue(bytes, valueType); - } catch (IOException ex) { - throw new DeserializeException("Error converting from DSON", ex); - } - } - - /** - * Convert the specified JSON encoded string to an instance of the - * specified class. - * - * @param json The JSON encoded object to deserialize - * @param valueType The class of the object to deserialize - * @return The deserialized object - * @throws DeserializeException if something goes wrong with serialization - */ - public T fromJson(String json, Class valueType) throws DeserializeException { - try { - return jsonMapper(Output.ALL).readValue(json, valueType); - } catch (IOException ex) { - throw new DeserializeException("Error converting from JSON", ex); - } - } - - /** - * Convert the specified JSON encoded string to an instance of the - * specified class. - * - * @param json The JSON encoded object to deserialize - * @param valueType The class of the object to deserialize - * @return The deserialized object - * @throws DeserializeException if something goes wrong with serialization - */ - public T fromJson(String json, JsonJavaType valueType) throws DeserializeException { - try { - return jsonMapper(Output.ALL).readValue(json, valueType.javaType()); - } catch (IOException ex) { - throw new DeserializeException("Error converting from JSON", ex); - } - } - - /** - * Convert the specified JSONObject to an instance of the - * specified class. - * - * @param json The {@link JSONObject} object to convert - * @param valueType The class of the object to convert to - * @return The converted object - */ - public T fromJsonObject(JSONObject json, Class valueType) { - return jsonMapper(Output.ALL).convertValue(json, valueType); - } - - /** - * Return a collection type for use with the {@link #fromJson(String, JsonJavaType)} method. - * - * @param collectionClass The collection class to deserialize. - * @param elementClass The collection element type. - * @return The deserialized collection. - */ - @SuppressWarnings("rawtypes") - public JsonJavaType jsonCollectionType(Class collectionClass, Class elementClass) { - JavaType type = jsonMappers.get(Output.ALL).getTypeFactory().constructCollectionType(collectionClass, elementClass); - return new JsonJavaType(type); - } - - /** - * Retrieve serializer ID from class. - * - * @param cls The class to look up the ID for - * @return The serializer ID, or {@code null} if no serializer for the specified class. - */ - public String getIdForClass(Class cls) { - return idLookup.getIdForClass(cls); - } - - /** - * Retrieve class given a serializer ID. - * - * @param id The ID to look up the class for - * @return The class, or {@code null} if serializer ID unknown. - */ - public Class getClassForId(String id) { - return idLookup.getClassForId(id); - } - - private JacksonCborMapper dsonMapper(Output output) { - return dsonMappers.get(output); - } - - private JacksonJsonMapper jsonMapper(Output output) { - return jsonMappers.get(output); - } - - private static Collector flatMapping(Function> mapper, - Collector downstream) { - BiConsumer acc = downstream.accumulator(); - return Collector.of(downstream.supplier(), (a, t) -> { - try (Stream s = mapper.apply(t)) { - if (s != null) { - s.forEachOrdered(u -> acc.accept(a, u)); - } - } - }, downstream.combiner(), downstream.finisher(), downstream.characteristics().toArray(new Collector.Characteristics[0])); - } - - private static String assembleMessage(Object o, String type) { - String className = o == null ? "(null)" : o.getClass().getName(); - return "Error converting to " + type + ". Check registration for " + className; - } + /** + * Create a new instance of {@link Serialization} with the specified IDs and policy. + * + * @param idLookup The {@link SerializerIds} to use for class and property lookup. + * @param policy The {@link SerializationPolicy} to use for determining serialization outputs. + * @return A new instance of {@link Serialization}. + */ + public static Serialization create(SerializerIds idLookup, SerializationPolicy policy) { + return new Serialization(idLookup, policy); + } + + private final ImmutableMap dsonMappers; + private final ImmutableMap jsonMappers; + + private final SerializerIds idLookup; + + private final FilterProvider allProvider; + private final FilterProvider noneProvider; + + // Constructor set up to be dependency injection capable at some future date + @VisibleForTesting + Serialization(SerializerIds idLookup, SerializationPolicy policy) { + this.idLookup = idLookup; + + EnumSet availableOutputs = EnumSet.allOf(Output.class); + availableOutputs.remove(Output.ALL); + availableOutputs.remove(Output.NONE); + + noneProvider = filterProviderFor(ImmutableMap.of()); + Map, ImmutableSet> allFields = + availableOutputs.stream() + .flatMap(output -> policy.getIncludedFields(output).entrySet().stream()) + .collect( + Collectors.groupingBy( + Map.Entry::getKey, + flatMapping(e -> e.getValue().stream(), ImmutableSet.toImmutableSet()))); + allProvider = filterProviderFor(ImmutableMap.copyOf(allFields)); + + ImmutableMap.Builder dsonBuilder = ImmutableMap.builder(); + + JacksonCborMapper hashDsonMapper = + JacksonCborMapper.create( + idLookup, filterProviderFor(policy.getIncludedFields(Output.HASH)), true); + + JacksonCborMapper apiDsonMapper = + JacksonCborMapper.create( + idLookup, + filterProviderFor(policy.getIncludedFields(Output.API)), + true, + Optional.of(new ApiSerializationModifier(hashDsonMapper))); + + dsonBuilder.put(Output.HASH, hashDsonMapper); + dsonBuilder.put(Output.API, apiDsonMapper); + dsonBuilder.put(Output.NONE, JacksonCborMapper.create(idLookup, noneProvider, true)); + dsonBuilder.put(Output.ALL, JacksonCborMapper.create(idLookup, allProvider, true)); + for (Output e : Arrays.asList(Output.PERSIST, Output.WIRE)) { + dsonBuilder.put( + e, + JacksonCborMapper.create(idLookup, filterProviderFor(policy.getIncludedFields(e)), true)); + } + + dsonMappers = dsonBuilder.build(); + + ImmutableMap.Builder jsonBuilder = ImmutableMap.builder(); + + var hashJsonMapper = + JacksonJsonMapper.create( + idLookup, filterProviderFor(policy.getIncludedFields(Output.HASH)), false); + var apiJsonMapper = + JacksonJsonMapper.create( + idLookup, + filterProviderFor(policy.getIncludedFields(Output.API)), + false, + Optional.of(new ApiSerializationModifier(hashDsonMapper))); + + jsonBuilder.put(Output.HASH, hashJsonMapper); + jsonBuilder.put(Output.API, apiJsonMapper); + jsonBuilder.put(Output.NONE, JacksonJsonMapper.create(idLookup, noneProvider, false)); + jsonBuilder.put(Output.ALL, JacksonJsonMapper.create(idLookup, allProvider, false)); + for (Output e : Lists.newArrayList(Output.PERSIST, Output.WIRE)) { + jsonBuilder.put( + e, + JacksonJsonMapper.create( + idLookup, filterProviderFor(policy.getIncludedFields(e)), false)); + } + + jsonMappers = jsonBuilder.build(); + } + + /** + * Convert the specified object to DSON encoded bytes for the specified output mode. + * + * @param o The object to serialize + * @param output The output mode to serialize for + * @return The serialized object as a DSON byte array + */ + public byte[] toDson(Object o, DsonOutput.Output output) { + try { + return dsonMapper(output).writeValueAsBytes(o); + } catch (JsonProcessingException ex) { + throw new IllegalStateException(assembleMessage(o, "DSON"), ex); + } + } + + /** + * Convert the specified object to a JSON encoded string for the specified output mode. + * + * @param o The object to serialize + * @param output The output mode to serialize for + * @return The serialized object as a JSON string + */ + public String toJson(Object o, DsonOutput.Output output) { + try { + return jsonMapper(output).writeValueAsString(o); + } catch (JsonProcessingException ex) { + throw new IllegalStateException(assembleMessage(o, "JSON"), ex); + } + } + + /** + * Convert the specified object to a {@link JSONObject} encoded string for the specified output + * mode. + * + * @param o The object to serialize + * @param output The output mode to serialize for + * @return The serialized object as a JSON string + */ + public JSONObject toJsonObject(Object o, DsonOutput.Output output) { + return jsonMapper(output).convertValue(o, JSONObject.class); + } + + /** + * Convert the specified DSON encoded byte array to an instance of the specified class. + * + * @param bytes The DSON encoded object to deserialize + * @param valueType The class of the object to deserialize + * @return The deserialized object + * @throws DeserializeException if something goes wrong with serialization + */ + public T fromDson(byte[] bytes, int offset, int len, Class valueType) + throws DeserializeException { + try { + return dsonMapper(Output.ALL).readValue(bytes, offset, len, valueType); + } catch (IOException ex) { + throw new DeserializeException("Error converting from DSON", ex); + } + } + + /** + * Convert the specified DSON encoded byte array to an instance of the specified class. + * + * @param bytes The DSON encoded object to deserialize + * @param valueType The class of the object to deserialize + * @return The deserialized object + * @throws DeserializeException if something goes wrong with serialization + */ + public T fromDson(byte[] bytes, Class valueType) throws DeserializeException { + try { + return dsonMapper(Output.ALL).readValue(bytes, valueType); + } catch (IOException ex) { + throw new DeserializeException("Error converting from DSON", ex); + } + } + + /** + * Convert the specified JSON encoded string to an instance of the specified class. + * + * @param json The JSON encoded object to deserialize + * @param valueType The class of the object to deserialize + * @return The deserialized object + * @throws DeserializeException if something goes wrong with serialization + */ + public T fromJson(String json, Class valueType) throws DeserializeException { + try { + return jsonMapper(Output.ALL).readValue(json, valueType); + } catch (IOException ex) { + throw new DeserializeException("Error converting from JSON", ex); + } + } + + /** + * Convert the specified JSON encoded string to an instance of the specified class. + * + * @param json The JSON encoded object to deserialize + * @param valueType The class of the object to deserialize + * @return The deserialized object + * @throws DeserializeException if something goes wrong with serialization + */ + public T fromJson(String json, JsonJavaType valueType) throws DeserializeException { + try { + return jsonMapper(Output.ALL).readValue(json, valueType.javaType()); + } catch (IOException ex) { + throw new DeserializeException("Error converting from JSON", ex); + } + } + + /** + * Convert the specified JSONObject to an instance of the specified class. + * + * @param json The {@link JSONObject} object to convert + * @param valueType The class of the object to convert to + * @return The converted object + */ + public T fromJsonObject(JSONObject json, Class valueType) { + return jsonMapper(Output.ALL).convertValue(json, valueType); + } + + /** + * Return a collection type for use with the {@link #fromJson(String, JsonJavaType)} method. + * + * @param collectionClass The collection class to deserialize. + * @param elementClass The collection element type. + * @return The deserialized collection. + */ + @SuppressWarnings("rawtypes") + public JsonJavaType jsonCollectionType( + Class collectionClass, Class elementClass) { + JavaType type = + jsonMappers + .get(Output.ALL) + .getTypeFactory() + .constructCollectionType(collectionClass, elementClass); + return new JsonJavaType(type); + } + + /** + * Retrieve serializer ID from class. + * + * @param cls The class to look up the ID for + * @return The serializer ID, or {@code null} if no serializer for the specified class. + */ + public String getIdForClass(Class cls) { + return idLookup.getIdForClass(cls); + } + + /** + * Retrieve class given a serializer ID. + * + * @param id The ID to look up the class for + * @return The class, or {@code null} if serializer ID unknown. + */ + public Class getClassForId(String id) { + return idLookup.getClassForId(id); + } + + private JacksonCborMapper dsonMapper(Output output) { + return dsonMappers.get(output); + } + + private JacksonJsonMapper jsonMapper(Output output) { + return jsonMappers.get(output); + } + + private static Collector flatMapping( + Function> mapper, + Collector downstream) { + BiConsumer acc = downstream.accumulator(); + return Collector.of( + downstream.supplier(), + (a, t) -> { + try (Stream s = mapper.apply(t)) { + if (s != null) { + s.forEachOrdered(u -> acc.accept(a, u)); + } + } + }, + downstream.combiner(), + downstream.finisher(), + downstream.characteristics().toArray(new Collector.Characteristics[0])); + } + + private static String assembleMessage(Object o, String type) { + String className = o == null ? "(null)" : o.getClass().getName(); + return "Error converting to " + type + ". Check registration for " + className; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationPolicy.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationPolicy.java index 68c873efad..d471fa1284 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationPolicy.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationPolicy.java @@ -69,17 +69,16 @@ import com.radixdlt.serialization.DsonOutput.Output; /** - * Serialization policy that returns a set of classes and field names for - * a specified {@link DsonOutput.Output} mode. + * Serialization policy that returns a set of classes and field names for a specified {@link + * DsonOutput.Output} mode. */ public interface SerializationPolicy { - /** - * Retrieve the fields to output for the given output mode. - * - * @param output The output mode - * @return The set of pairs of {@code Class} and field names to output - */ - ImmutableMap, ImmutableSet> getIncludedFields(Output output); - -} \ No newline at end of file + /** + * Retrieve the fields to output for the given output mode. + * + * @param output The output mode + * @return The set of pairs of {@code Class} and field names to output + */ + ImmutableMap, ImmutableSet> getIncludedFields(Output output); +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationUtils.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationUtils.java index 146c0bcfe3..8705c5a802 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationUtils.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializationUtils.java @@ -1,87 +1,84 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.serialization; - -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; -import com.radixdlt.identifiers.EUID; - -import java.nio.charset.StandardCharsets; - -/** - * Collection of Serialization-related utilities - */ -public class SerializationUtils { - private static final HashFunction murmur3_128 = Hashing.murmur3_128(); - - private SerializationUtils() { - throw new IllegalStateException("Cannot instantiate."); - } - - public static EUID stringToNumericID(String id) { - var h = murmur3_128.hashBytes(id.getBytes(StandardCharsets.UTF_8)); - return new EUID(h.asBytes()); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.serialization; + +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import com.radixdlt.identifiers.EUID; +import java.nio.charset.StandardCharsets; + +/** Collection of Serialization-related utilities */ +public class SerializationUtils { + private static final HashFunction murmur3_128 = Hashing.murmur3_128(); + + private SerializationUtils() { + throw new IllegalStateException("Cannot instantiate."); + } + + public static EUID stringToNumericID(String id) { + var h = murmur3_128.hashBytes(id.getBytes(StandardCharsets.UTF_8)); + return new EUID(h.asBytes()); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializeWithHid.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializeWithHid.java index 9dba10fc73..25434b111a 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializeWithHid.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializeWithHid.java @@ -65,18 +65,15 @@ package com.radixdlt.serialization; import java.lang.annotation.Documented; -import java.lang.annotation.Target; -import java.lang.annotation.Retention; import java.lang.annotation.ElementType; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; -/** - * Annotation for marking classes that should serialize with the "hid" field included. - */ +/** Annotation for marking classes that should serialize with the "hid" field included. */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited -public @interface SerializeWithHid { -} +public @interface SerializeWithHid {} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerConstants.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerConstants.java index 49229709c4..ad6d7cf438 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerConstants.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerConstants.java @@ -64,20 +64,18 @@ package com.radixdlt.serialization; -/** - * Package-local constants. - */ +/** Package-local constants. */ public final class SerializerConstants { - private SerializerConstants() { - throw new IllegalStateException("Can't construct"); - } + private SerializerConstants() { + throw new IllegalStateException("Can't construct"); + } - // At least this will cause compilation fail when updated - public static final Class SERIALIZER_ID_ANNOTATION = SerializerId2.class; + // At least this will cause compilation fail when updated + public static final Class SERIALIZER_ID_ANNOTATION = SerializerId2.class; - // At least this will cause compilation fail when updated - public static final Class SERIALIZER_ROOT_ANNOTATION = SerializerRoot.class; + // At least this will cause compilation fail when updated + public static final Class SERIALIZER_ROOT_ANNOTATION = SerializerRoot.class; - // The serialized type field name - public static final String SERIALIZER_NAME = "sz"; + // The serialized type field name + public static final String SERIALIZER_NAME = "sz"; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerDummy.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerDummy.java index 48567f0d9d..39098939af 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerDummy.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerDummy.java @@ -64,13 +64,8 @@ package com.radixdlt.serialization; -/** - * Dummy class to include serializer field in output if required. - */ +/** Dummy class to include serializer field in output if required. */ public enum SerializerDummy { - /** - * One and only value assigned to instances of - * {@link SerializerDummy}. - */ - DUMMY; + /** One and only value assigned to instances of {@link SerializerDummy}. */ + DUMMY; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerId2.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerId2.java index 56efa14e38..3983d74635 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerId2.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerId2.java @@ -70,16 +70,15 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Annotation for marking classes as serializable. - */ +/** Annotation for marking classes as serializable. */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SerializerId2 { - /** - * The serializer ID for the annotated class. - * @return The serializer ID - */ - String value(); + /** + * The serializer ID for the annotated class. + * + * @return The serializer ID + */ + String value(); } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIds.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIds.java index 76e2f86744..e6285cf2df 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIds.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIds.java @@ -64,35 +64,32 @@ package com.radixdlt.serialization; -/** - * Interface for accessing serializer IDs given a class or vice-versa. - */ +/** Interface for accessing serializer IDs given a class or vice-versa. */ public interface SerializerIds { - /** - * Return the serializer ID, or {@code null} if no serializer known. - * - * @param cls The class to retrieve the serializer ID for. - * @return The serializer ID, or {@code null} if no serializer known. - */ - String getIdForClass(Class cls); - - /** - * Return an object's class, given the ID. If the serializer ID - * is unknown, {@code null} is returned. - * - * @param id The serializer ID to find the mapped class for. - * @return The class corresponding to the serializer ID, or {@code null} - * if serializer ID unknown. - */ - Class getClassForId(String id); + /** + * Return the serializer ID, or {@code null} if no serializer known. + * + * @param cls The class to retrieve the serializer ID for. + * @return The serializer ID, or {@code null} if no serializer known. + */ + String getIdForClass(Class cls); - /** - * Return true if class is serializable, or a supertype of - * a serializable class, excluding {@code Object}. - * @param cls The class to check - * @return {@code true} if class is serializable, or a supertype - */ - boolean isSerializableSuper(Class cls); + /** + * Return an object's class, given the ID. If the serializer ID is unknown, {@code null} is + * returned. + * + * @param id The serializer ID to find the mapped class for. + * @return The class corresponding to the serializer ID, or {@code null} if serializer ID unknown. + */ + Class getClassForId(String id); -} \ No newline at end of file + /** + * Return true if class is serializable, or a supertype of a serializable class, excluding {@code + * Object}. + * + * @param cls The class to check + * @return {@code true} if class is serializable, or a supertype + */ + boolean isSerializableSuper(Class cls); +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIdsException.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIdsException.java index 38a8dc7dd2..464e83bf2d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIdsException.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerIdsException.java @@ -64,40 +64,34 @@ package com.radixdlt.serialization; -/** - * Exception thrown on error while scanning for {@link SerializerId2} IDs. - */ +/** Exception thrown on error while scanning for {@link SerializerId2} IDs. */ public class SerializerIdsException extends RuntimeException { - private static final long serialVersionUID = -8065276555905937195L; + private static final long serialVersionUID = -8065276555905937195L; - /** - * Constructs a new exception with the specified detail message. The - * cause is not initialized, and may subsequently be initialized by - * a call to {@link #initCause}. - * - * @param message the detail message. The detail message is saved for - * later retrieval by the {@link #getMessage()} method. - */ - public SerializerIdsException(String message) { - super(message); - } + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and + * may subsequently be initialized by a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public SerializerIdsException(String message) { + super(message); + } - /** - * Constructs a new exception with the specified detail message and - * cause. - *

- * Note that the detail message associated with {@code cause} is - * not automatically incorporated in this exception's detail - * message. - * - * @param message the detail message (which is saved for later retrieval - * by the {@link #getMessage()} method). - * @param cause the cause (which is saved for later retrieval by the - * {@link #getCause()} method). (A {@code null} value is - * permitted, and indicates that the cause is nonexistent or - * unknown.) - */ - public SerializerIdsException(String message, Throwable cause) { - super(message, cause); - } + /** + * Constructs a new exception with the specified detail message and cause. + * + *

Note that the detail message associated with {@code cause} is not automatically + * incorporated in this exception's detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link + * #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). + * (A {@code null} value is permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public SerializerIdsException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerRoot.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerRoot.java index 5c756afe1d..01a00ceb0a 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerRoot.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/SerializerRoot.java @@ -70,11 +70,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Annotation for marking interfaces as a roots for serializable classes hierarchies. - */ +/** Annotation for marking interfaces as a roots for serializable classes hierarchies. */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface SerializerRoot { -} +public @interface SerializerRoot {} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializationPolicy.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializationPolicy.java index 85f0ff0604..8b7b1e76c4 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializationPolicy.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializationPolicy.java @@ -1,115 +1,114 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.serialization.core; - -import com.google.common.annotations.VisibleForTesting; -import com.radixdlt.serialization.ClassScanningSerializationPolicy; -import com.radixdlt.serialization.SerializationPolicy; -import com.radixdlt.serialization.SerializerConstants; -import java.util.Set; -import org.reflections.Reflections; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; -import org.reflections.util.FilterBuilder; - -/** - * Class that maintains a map of {@link DsonOutput.Output} types to - * a set of pairs of classes and field/method names to output for that - * serialization type. - *

- * This implementation works by scanning the classpath for classes - * annotated with {@code SerializerConstants.SERIALIZER_ID_ANNOTATION} and - * passing these classes to {@link ClassScanningSerializationPolicy}. - */ -public final class ClasspathScanningSerializationPolicy extends ClassScanningSerializationPolicy { - - /** - * Note that this class is expensive to create, potentially in the - * order of several seconds for a large classpath. - * - * @return A freshly created {@link ClasspathScanningSerializationPolicy} - */ - public static SerializationPolicy create() { - return new ClasspathScanningSerializationPolicy(); - } - - - @VisibleForTesting - ClasspathScanningSerializationPolicy() { - super(scanForSerializable()); - } - - private static Set> scanForSerializable() { - Reflections reflections = getReflections("org.radix", "com.radixdlt"); - return reflections.getTypesAnnotatedWith(SerializerConstants.SERIALIZER_ID_ANNOTATION); - } - - private static Reflections getReflections(String... packageNames) { - ConfigurationBuilder config = new ConfigurationBuilder() - .setUrls(ClasspathHelper.forJavaClassPath()) - .filterInputsBy(new FilterBuilder().includePackage(packageNames)); - return new Reflections(config); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.serialization.core; + +import com.google.common.annotations.VisibleForTesting; +import com.radixdlt.serialization.ClassScanningSerializationPolicy; +import com.radixdlt.serialization.SerializationPolicy; +import com.radixdlt.serialization.SerializerConstants; +import java.util.Set; +import org.reflections.Reflections; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; + +/** + * Class that maintains a map of {@link DsonOutput.Output} types to a set of pairs of classes and + * field/method names to output for that serialization type. + * + *

This implementation works by scanning the classpath for classes annotated with {@code + * SerializerConstants.SERIALIZER_ID_ANNOTATION} and passing these classes to {@link + * ClassScanningSerializationPolicy}. + */ +public final class ClasspathScanningSerializationPolicy extends ClassScanningSerializationPolicy { + + /** + * Note that this class is expensive to create, potentially in the order of several seconds for a + * large classpath. + * + * @return A freshly created {@link ClasspathScanningSerializationPolicy} + */ + public static SerializationPolicy create() { + return new ClasspathScanningSerializationPolicy(); + } + + @VisibleForTesting + ClasspathScanningSerializationPolicy() { + super(scanForSerializable()); + } + + private static Set> scanForSerializable() { + Reflections reflections = getReflections("org.radix", "com.radixdlt"); + return reflections.getTypesAnnotatedWith(SerializerConstants.SERIALIZER_ID_ANNOTATION); + } + + private static Reflections getReflections(String... packageNames) { + ConfigurationBuilder config = + new ConfigurationBuilder() + .setUrls(ClasspathHelper.forJavaClassPath()) + .filterInputsBy(new FilterBuilder().includePackage(packageNames)); + return new Reflections(config); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializerIds.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializerIds.java index ccd513e41c..c47814e4a1 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializerIds.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/core/ClasspathScanningSerializerIds.java @@ -1,112 +1,111 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.serialization.core; - -import com.google.common.annotations.VisibleForTesting; -import com.radixdlt.serialization.ClassScanningSerializerIds; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerIds; -import java.util.Set; -import org.reflections.Reflections; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; -import org.reflections.util.FilterBuilder; - -/** - * Class that maintains a map of serializer IDs to {@code Class} objects, - * and vice versa, for all serializable classes in the core system. - *

- * This {@link SerializerIds} operates by scanning the class path. - */ -public final class ClasspathScanningSerializerIds extends ClassScanningSerializerIds { - /** - * Create a freshly initialized instance of - * {@link ClasspathScanningSerializerIds}. - *

- * Note that is is quite expensive to create an instance of this - * class, perhaps in the order of seconds. Once created, the class - * is immutable, and therefore thread-safe. - * - * @return A freshly created and initialized instance - * @throws SerializerIdsException If two or more classes are - * found with the same {@code SerializerId} - */ - public static SerializerIds create() { - return new ClasspathScanningSerializerIds(); - } - - @VisibleForTesting - ClasspathScanningSerializerIds() { - super(scanForSerializable()); - } - - private static Set> scanForSerializable() { - ConfigurationBuilder config = new ConfigurationBuilder() - .setUrls(ClasspathHelper.forJavaClassPath()) - .filterInputsBy(new FilterBuilder().includePackage("org.radix", "com.radixdlt")); - Reflections reflections = new Reflections(config); - return reflections.getTypesAnnotatedWith(SerializerConstants.SERIALIZER_ID_ANNOTATION); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.serialization.core; + +import com.google.common.annotations.VisibleForTesting; +import com.radixdlt.serialization.ClassScanningSerializerIds; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerIds; +import java.util.Set; +import org.reflections.Reflections; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; + +/** + * Class that maintains a map of serializer IDs to {@code Class} objects, and vice versa, for all + * serializable classes in the core system. + * + *

This {@link SerializerIds} operates by scanning the class path. + */ +public final class ClasspathScanningSerializerIds extends ClassScanningSerializerIds { + /** + * Create a freshly initialized instance of {@link ClasspathScanningSerializerIds}. + * + *

Note that is is quite expensive to create an instance of this class, perhaps in the order of + * seconds. Once created, the class is immutable, and therefore thread-safe. + * + * @return A freshly created and initialized instance + * @throws SerializerIdsException If two or more classes are found with the same {@code + * SerializerId} + */ + public static SerializerIds create() { + return new ClasspathScanningSerializerIds(); + } + + @VisibleForTesting + ClasspathScanningSerializerIds() { + super(scanForSerializable()); + } + + private static Set> scanForSerializable() { + ConfigurationBuilder config = + new ConfigurationBuilder() + .setUrls(ClasspathHelper.forJavaClassPath()) + .filterInputsBy(new FilterBuilder().includePackage("org.radix", "com.radixdlt")); + Reflections reflections = new Reflections(config); + return reflections.getTypesAnnotatedWith(SerializerConstants.SERIALIZER_ID_ANNOTATION); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFieldFilter.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFieldFilter.java index 0137acbe2c..b5f96dccdd 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFieldFilter.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFieldFilter.java @@ -76,55 +76,57 @@ import java.util.Collection; import java.util.Map; -/** - * A field filter for DSON output modes. - */ +/** A field filter for DSON output modes. */ public class DsonFieldFilter extends SimpleBeanPropertyFilter { - private final ImmutableMap, ImmutableSet> includedItems; + private final ImmutableMap, ImmutableSet> includedItems; - /** - * Create a {@link FilterProvider} with a {@link DsonFieldFilter} - * @param includedFields Set of {@code Class} and field names to include for this filter. - * @return A freshly created {@link FilterProvider} with the specified DSON filter included. - */ - public static FilterProvider filterProviderFor(ImmutableMap, ImmutableSet> includedFields) { - return new SimpleFilterProvider().addFilter(MapperConstants.DSON_FILTER_NAME, new DsonFieldFilter(includedFields)); - } + /** + * Create a {@link FilterProvider} with a {@link DsonFieldFilter} + * + * @param includedFields Set of {@code Class} and field names to include for this filter. + * @return A freshly created {@link FilterProvider} with the specified DSON filter included. + */ + public static FilterProvider filterProviderFor( + ImmutableMap, ImmutableSet> includedFields) { + return new SimpleFilterProvider() + .addFilter(MapperConstants.DSON_FILTER_NAME, new DsonFieldFilter(includedFields)); + } - DsonFieldFilter(ImmutableMap, ImmutableSet> includedItems) { - // No need to make copy of immutable set. - this.includedItems = includedItems; - } + DsonFieldFilter(ImmutableMap, ImmutableSet> includedItems) { + // No need to make copy of immutable set. + this.includedItems = includedItems; + } - @Override - public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) - throws Exception { - Object parent = jgen.getOutputContext().getCurrentValue(); - if (shouldInclude(parent.getClass(), writer.getName())) { - writer.serializeAsField(pojo, jgen, provider); - } else if (!jgen.canOmitFields()) { // since 2.3 - writer.serializeAsOmittedField(pojo, jgen, provider); - } else { - // Field can just be omitted in this case, which means there is nothing to do here - } - } + @Override + public void serializeAsField( + Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) + throws Exception { + Object parent = jgen.getOutputContext().getCurrentValue(); + if (shouldInclude(parent.getClass(), writer.getName())) { + writer.serializeAsField(pojo, jgen, provider); + } else if (!jgen.canOmitFields()) { // since 2.3 + writer.serializeAsOmittedField(pojo, jgen, provider); + } else { + // Field can just be omitted in this case, which means there is nothing to do here + } + } - @Override - protected boolean include(BeanPropertyWriter writer) { - return true; - } + @Override + protected boolean include(BeanPropertyWriter writer) { + return true; + } - @Override - protected boolean include(PropertyWriter writer) { - return true; - } + @Override + protected boolean include(PropertyWriter writer) { + return true; + } - private boolean shouldInclude(Class cls, String property) { - // Maps and Collection sub-classes always have contents serialised - if (Map.class.isAssignableFrom(cls) || Collection.class.isAssignableFrom(cls)) { - return true; - } - ImmutableSet fieldsForClass = this.includedItems.get(cls); - return fieldsForClass != null && fieldsForClass.contains(property); - } + private boolean shouldInclude(Class cls, String property) { + // Maps and Collection sub-classes always have contents serialised + if (Map.class.isAssignableFrom(cls) || Collection.class.isAssignableFrom(cls)) { + return true; + } + ImmutableSet fieldsForClass = this.includedItems.get(cls); + return fieldsForClass != null && fieldsForClass.contains(property); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFilteringIntrospector.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFilteringIntrospector.java index 9304b1fd42..f06d42347c 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFilteringIntrospector.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonFilteringIntrospector.java @@ -68,18 +68,18 @@ import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; /** - * An annotation introspector that defaults to using the DSON filter - * if no other filter is specified. + * An annotation introspector that defaults to using the DSON filter if no other filter is + * specified. */ class DsonFilteringIntrospector extends JacksonAnnotationIntrospector { - private static final long serialVersionUID = 29L; + private static final long serialVersionUID = 29L; - @Override - public Object findFilterId(Annotated a) { - Object id = super.findFilterId(a); - if (id == null) { - return MapperConstants.DSON_FILTER_NAME; - } - return id; - } + @Override + public Object findFilterId(Annotated a) { + Object id = super.findFilterId(a); + if (id == null) { + return MapperConstants.DSON_FILTER_NAME; + } + return id; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeIdResolver.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeIdResolver.java index b32b087f1e..95e0551e8b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeIdResolver.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeIdResolver.java @@ -73,45 +73,45 @@ import java.io.IOException; /** - * {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver} implementation - * that converts between fully-qualified Java class names and (JSON) Strings. + * {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver} implementation that converts + * between fully-qualified Java class names and (JSON) Strings. */ public class DsonTypeIdResolver extends TypeIdResolverBase { - private final SerializerIds idLookup; + private final SerializerIds idLookup; - public DsonTypeIdResolver(JavaType baseType, TypeFactory typeFactory, SerializerIds idLookup) { - super(baseType, typeFactory); - this.idLookup = idLookup; - } + public DsonTypeIdResolver(JavaType baseType, TypeFactory typeFactory, SerializerIds idLookup) { + super(baseType, typeFactory); + this.idLookup = idLookup; + } - @Override - public JsonTypeInfo.Id getMechanism() { - return JsonTypeInfo.Id.CUSTOM; - } + @Override + public JsonTypeInfo.Id getMechanism() { + return JsonTypeInfo.Id.CUSTOM; + } - @Override - public String idFromValue(Object value) { - return this.idLookup.getIdForClass(value.getClass()); - } + @Override + public String idFromValue(Object value) { + return this.idLookup.getIdForClass(value.getClass()); + } - @Override - public String idFromValueAndType(Object value, Class type) { - return this.idLookup.getIdForClass(type); - } + @Override + public String idFromValueAndType(Object value, Class type) { + return this.idLookup.getIdForClass(type); + } - @Override - public JavaType typeFromId(DatabindContext context, String id) throws IOException { - var clazz = idLookup.getClassForId(id); + @Override + public JavaType typeFromId(DatabindContext context, String id) throws IOException { + var clazz = idLookup.getClassForId(id); - if (clazz == null) { - throw new IOException("Unknown type ID: " + id); - } + if (clazz == null) { + throw new IOException("Unknown type ID: " + id); + } - return _typeFactory.constructType(clazz); - } + return _typeFactory.constructType(clazz); + } - @Override - public String getDescForKnownTypeIds() { - return "DSON serializer id used as type id"; - } + @Override + public String getDescForKnownTypeIds() { + return "DSON serializer id used as type id"; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeResolverBuilder.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeResolverBuilder.java index 6e2c3f8b8f..c5f40e4015 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeResolverBuilder.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/DsonTypeResolverBuilder.java @@ -82,44 +82,49 @@ import java.util.Collection; /** - * TypeResolverBuilder that outputs type information for all classes that - * are part of the serializable class set. This set consists of all classes - * annotated with {@link com.radixdlt.serialization.SerializerId2} and their - * superclasses. + * TypeResolverBuilder that outputs type information for all classes that are part of the + * serializable class set. This set consists of all classes annotated with {@link + * com.radixdlt.serialization.SerializerId2} and their superclasses. */ class DsonTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder { - private static final long serialVersionUID = 29L; + private static final long serialVersionUID = 29L; - private final SerializerIds idLookup; + private final SerializerIds idLookup; - DsonTypeResolverBuilder(SerializerIds idLookup) { - super(ObjectMapper.DefaultTyping.NON_FINAL, LaissezFaireSubTypeValidator.instance); - init(Id.CUSTOM, null).inclusion(As.EXISTING_PROPERTY).typeProperty(SerializerConstants.SERIALIZER_NAME); - this.idLookup = idLookup; - } + DsonTypeResolverBuilder(SerializerIds idLookup) { + super(ObjectMapper.DefaultTyping.NON_FINAL, LaissezFaireSubTypeValidator.instance); + init(Id.CUSTOM, null) + .inclusion(As.EXISTING_PROPERTY) + .typeProperty(SerializerConstants.SERIALIZER_NAME); + this.idLookup = idLookup; + } - @Override - public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection subtypes) { - // Serialization handled already - return null; - } + @Override + public TypeSerializer buildTypeSerializer( + SerializationConfig config, JavaType baseType, Collection subtypes) { + // Serialization handled already + return null; + } - @Override - public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection subtypes) { - return super.buildTypeDeserializer(config, baseType, subtypes); - } + @Override + public TypeDeserializer buildTypeDeserializer( + DeserializationConfig config, JavaType baseType, Collection subtypes) { + return super.buildTypeDeserializer(config, baseType, subtypes); + } - @Override - public boolean useForType(JavaType t) { - return idLookup.isSerializableSuper(t.getRawClass()); - } + @Override + public boolean useForType(JavaType t) { + return idLookup.isSerializableSuper(t.getRawClass()); + } - @Override - protected TypeIdResolver idResolver( - MapperConfig config, JavaType baseType, - PolymorphicTypeValidator subtypeValidator, Collection subtypes, - boolean forSer, boolean forDeser - ) { - return new DsonTypeIdResolver(baseType, config.getTypeFactory(), idLookup); - } + @Override + protected TypeIdResolver idResolver( + MapperConfig config, + JavaType baseType, + PolymorphicTypeValidator subtypeValidator, + Collection subtypes, + boolean forSer, + boolean forDeser) { + return new DsonTypeIdResolver(baseType, config.getTypeFactory(), idLookup); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborMapper.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborMapper.java index a6b2ff5757..4bdcd6f4ec 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborMapper.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborMapper.java @@ -65,10 +65,10 @@ package com.radixdlt.serialization.mapper; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.KeyDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; @@ -84,162 +84,151 @@ import com.radixdlt.utils.Longs; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt384; - import java.io.IOException; import java.time.Instant; import java.util.Optional; import java.util.function.Function; /** - * A Jackson {@link RadixObjectMapperConfigurator} that will serialize and deserialize - * to the subset of CBOR that DSON uses. + * A Jackson {@link RadixObjectMapperConfigurator} that will serialize and deserialize to the subset + * of CBOR that DSON uses. */ public class JacksonCborMapper extends ObjectMapper { - private static final long serialVersionUID = 4917479892309630214L; + private static final long serialVersionUID = 4917479892309630214L; - public static JacksonCborMapper create(SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties) { - return new JacksonCborMapper(idLookup, filterProvider, sortProperties, Optional.empty()); - } + public static JacksonCborMapper create( + SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties) { + return new JacksonCborMapper(idLookup, filterProvider, sortProperties, Optional.empty()); + } - /** - * Create an {@link RadixObjectMapperConfigurator} that will serialize to/from - * CBOR encoded DSON. - * - * @param idLookup A {@link SerializerIds} used to perform serializer - * ID lookup - * @param filterProvider A {@link FilterProvider} to use for filtering - * serialized fields - * @param serializationModifier optional BeanSerializerModifier to mix in the serialization - * @return A freshly created {@link JacksonCborMapper} - */ - public static JacksonCborMapper create(SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties, - Optional serializationModifier) { - return new JacksonCborMapper(idLookup, filterProvider, sortProperties, serializationModifier); - } + /** + * Create an {@link RadixObjectMapperConfigurator} that will serialize to/from CBOR encoded DSON. + * + * @param idLookup A {@link SerializerIds} used to perform serializer ID lookup + * @param filterProvider A {@link FilterProvider} to use for filtering serialized fields + * @param serializationModifier optional BeanSerializerModifier to mix in the serialization + * @return A freshly created {@link JacksonCborMapper} + */ + public static JacksonCborMapper create( + SerializerIds idLookup, + FilterProvider filterProvider, + boolean sortProperties, + Optional serializationModifier) { + return new JacksonCborMapper(idLookup, filterProvider, sortProperties, serializationModifier); + } - private JacksonCborMapper(SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties, - Optional serializationModifier) { - super(new RadixCBORFactory()); - RadixObjectMapperConfigurator.configure(this, idLookup, filterProvider, sortProperties); - var cborModule = new SimpleModule(); + private JacksonCborMapper( + SerializerIds idLookup, + FilterProvider filterProvider, + boolean sortProperties, + Optional serializationModifier) { + super(new RadixCBORFactory()); + RadixObjectMapperConfigurator.configure(this, idLookup, filterProvider, sortProperties); + var cborModule = new SimpleModule(); - cborModule.addSerializer(SerializerDummy.class, new JacksonSerializerDummySerializer(idLookup)); - cborModule.addSerializer(EUID.class, new JacksonCborObjectBytesSerializer<>( - EUID.class, - JacksonCodecConstants.EUID_VALUE, - EUID::toByteArray - )); - cborModule.addSerializer(HashCode.class, new JacksonCborObjectBytesSerializer<>( - HashCode.class, - JacksonCodecConstants.HASH_VALUE, - HashCode::asBytes - )); - cborModule.addSerializer(byte[].class, new JacksonCborObjectBytesSerializer<>( - byte[].class, - JacksonCodecConstants.BYTES_VALUE, - Function.identity() - )); - cborModule.addSerializer(UInt256.class, new JacksonCborObjectBytesSerializer<>( - UInt256.class, - JacksonCodecConstants.U20_VALUE, - UInt256::toByteArray - )); - cborModule.addSerializer(UInt384.class, new JacksonCborObjectBytesSerializer<>( - UInt384.class, - JacksonCodecConstants.U30_VALUE, - UInt384::toByteArray - )); - cborModule.addSerializer(REAddr.class, new JacksonCborObjectBytesSerializer<>( - REAddr.class, - JacksonCodecConstants.RRI_VALUE, - REAddr::getBytes - )); - cborModule.addSerializer(AID.class, new JacksonCborObjectBytesSerializer<>( - AID.class, - JacksonCodecConstants.AID_VALUE, - AID::getBytes - )); - cborModule.addSerializer(long[].class, new JacksonCborObjectBytesSerializer<>( - long[].class, - JacksonCodecConstants.LONGS_VALUE, - Longs::toBytes - )); - cborModule.addSerializer(Instant.class, new JacksonCborObjectBytesSerializer<>( - Instant.class, - JacksonCodecConstants.INSTANT_VALUE, - Instants::toBytes - )); + cborModule.addSerializer(SerializerDummy.class, new JacksonSerializerDummySerializer(idLookup)); + cborModule.addSerializer( + EUID.class, + new JacksonCborObjectBytesSerializer<>( + EUID.class, JacksonCodecConstants.EUID_VALUE, EUID::toByteArray)); + cborModule.addSerializer( + HashCode.class, + new JacksonCborObjectBytesSerializer<>( + HashCode.class, JacksonCodecConstants.HASH_VALUE, HashCode::asBytes)); + cborModule.addSerializer( + byte[].class, + new JacksonCborObjectBytesSerializer<>( + byte[].class, JacksonCodecConstants.BYTES_VALUE, Function.identity())); + cborModule.addSerializer( + UInt256.class, + new JacksonCborObjectBytesSerializer<>( + UInt256.class, JacksonCodecConstants.U20_VALUE, UInt256::toByteArray)); + cborModule.addSerializer( + UInt384.class, + new JacksonCborObjectBytesSerializer<>( + UInt384.class, JacksonCodecConstants.U30_VALUE, UInt384::toByteArray)); + cborModule.addSerializer( + REAddr.class, + new JacksonCborObjectBytesSerializer<>( + REAddr.class, JacksonCodecConstants.RRI_VALUE, REAddr::getBytes)); + cborModule.addSerializer( + AID.class, + new JacksonCborObjectBytesSerializer<>( + AID.class, JacksonCodecConstants.AID_VALUE, AID::getBytes)); + cborModule.addSerializer( + long[].class, + new JacksonCborObjectBytesSerializer<>( + long[].class, JacksonCodecConstants.LONGS_VALUE, Longs::toBytes)); + cborModule.addSerializer( + Instant.class, + new JacksonCborObjectBytesSerializer<>( + Instant.class, JacksonCodecConstants.INSTANT_VALUE, Instants::toBytes)); - cborModule.addKeySerializer(AID.class, new StdSerializer(AID.class) { - @Override - public void serialize(AID value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeFieldName(JacksonCodecConstants.AID_STR_VALUE + value.toString()); - } - }); + cborModule.addKeySerializer( + AID.class, + new StdSerializer(AID.class) { + @Override + public void serialize(AID value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeFieldName(JacksonCodecConstants.AID_STR_VALUE + value.toString()); + } + }); - cborModule.addDeserializer(SerializerDummy.class, new JacksonSerializerDummyDeserializer()); - cborModule.addDeserializer(EUID.class, new JacksonCborObjectBytesDeserializer<>( - EUID.class, - JacksonCodecConstants.EUID_VALUE, - EUID::new - )); - cborModule.addDeserializer(HashCode.class, new JacksonCborObjectBytesDeserializer<>( - HashCode.class, - JacksonCodecConstants.HASH_VALUE, - HashCode::fromBytes - )); - cborModule.addDeserializer(byte[].class, new JacksonCborObjectBytesDeserializer<>( - byte[].class, - JacksonCodecConstants.BYTES_VALUE, - Function.identity() - )); - cborModule.addDeserializer(UInt256.class, new JacksonCborObjectBytesDeserializer<>( - UInt256.class, - JacksonCodecConstants.U20_VALUE, - UInt256::from - )); - cborModule.addDeserializer(UInt384.class, new JacksonCborObjectBytesDeserializer<>( - UInt384.class, - JacksonCodecConstants.U30_VALUE, - UInt384::from - )); - cborModule.addDeserializer(REAddr.class, new JacksonCborObjectBytesDeserializer<>( - REAddr.class, - JacksonCodecConstants.RRI_VALUE, - REAddr::of - )); - cborModule.addDeserializer(AID.class, new JacksonCborObjectBytesDeserializer<>( - AID.class, - JacksonCodecConstants.AID_VALUE, - AID::from - )); - cborModule.addDeserializer(long[].class, new JacksonCborObjectBytesDeserializer<>( - long[].class, - JacksonCodecConstants.LONGS_VALUE, - Longs::fromBytes - )); - cborModule.addDeserializer(Instant.class, new JacksonCborObjectBytesDeserializer<>( - Instant.class, - JacksonCodecConstants.INSTANT_VALUE, - Instants::fromBytes - )); - cborModule.addKeyDeserializer(AID.class, new KeyDeserializer() { - @Override - public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException { - if (!key.startsWith(JacksonCodecConstants.AID_STR_VALUE)) { - throw new InvalidFormatException( - ctxt.getParser(), - "Expecting prefix" + JacksonCodecConstants.AID_STR_VALUE, - key, - AID.class - ); - } - return AID.from(key.substring(JacksonCodecConstants.STR_VALUE_LEN)); - } - }); + cborModule.addDeserializer(SerializerDummy.class, new JacksonSerializerDummyDeserializer()); + cborModule.addDeserializer( + EUID.class, + new JacksonCborObjectBytesDeserializer<>( + EUID.class, JacksonCodecConstants.EUID_VALUE, EUID::new)); + cborModule.addDeserializer( + HashCode.class, + new JacksonCborObjectBytesDeserializer<>( + HashCode.class, JacksonCodecConstants.HASH_VALUE, HashCode::fromBytes)); + cborModule.addDeserializer( + byte[].class, + new JacksonCborObjectBytesDeserializer<>( + byte[].class, JacksonCodecConstants.BYTES_VALUE, Function.identity())); + cborModule.addDeserializer( + UInt256.class, + new JacksonCborObjectBytesDeserializer<>( + UInt256.class, JacksonCodecConstants.U20_VALUE, UInt256::from)); + cborModule.addDeserializer( + UInt384.class, + new JacksonCborObjectBytesDeserializer<>( + UInt384.class, JacksonCodecConstants.U30_VALUE, UInt384::from)); + cborModule.addDeserializer( + REAddr.class, + new JacksonCborObjectBytesDeserializer<>( + REAddr.class, JacksonCodecConstants.RRI_VALUE, REAddr::of)); + cborModule.addDeserializer( + AID.class, + new JacksonCborObjectBytesDeserializer<>( + AID.class, JacksonCodecConstants.AID_VALUE, AID::from)); + cborModule.addDeserializer( + long[].class, + new JacksonCborObjectBytesDeserializer<>( + long[].class, JacksonCodecConstants.LONGS_VALUE, Longs::fromBytes)); + cborModule.addDeserializer( + Instant.class, + new JacksonCborObjectBytesDeserializer<>( + Instant.class, JacksonCodecConstants.INSTANT_VALUE, Instants::fromBytes)); + cborModule.addKeyDeserializer( + AID.class, + new KeyDeserializer() { + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException { + if (!key.startsWith(JacksonCodecConstants.AID_STR_VALUE)) { + throw new InvalidFormatException( + ctxt.getParser(), + "Expecting prefix" + JacksonCodecConstants.AID_STR_VALUE, + key, + AID.class); + } + return AID.from(key.substring(JacksonCodecConstants.STR_VALUE_LEN)); + } + }); - serializationModifier.ifPresent(cborModule::setSerializerModifier); + serializationModifier.ifPresent(cborModule::setSerializerModifier); - registerModule(cborModule); - } + registerModule(cborModule); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesDeserializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesDeserializer.java index 13146e1c97..fe616238e6 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesDeserializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesDeserializer.java @@ -73,26 +73,25 @@ import java.util.function.Function; /** - * Deserializer for translation from CBOR binary data - * to DSON data for an object which can be represented by a string. + * Deserializer for translation from CBOR binary data to DSON data for an object which can be + * represented by a string. */ public class JacksonCborObjectBytesDeserializer extends StdDeserializer { - private final byte prefix; - private final Function bytesMapper; + private final byte prefix; + private final Function bytesMapper; - JacksonCborObjectBytesDeserializer(Class t, byte prefix, Function bytesMapper) { - super(t); - this.prefix = prefix; - this.bytesMapper = bytesMapper; - } - - @Override - public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - byte[] bytes = p.getBinaryValue(); - if (bytes == null || bytes.length == 0 || bytes[0] != prefix) { - throw new InvalidFormatException(p, "Expecting " + prefix, bytes, this.handledType()); - } - return bytesMapper.apply(Arrays.copyOfRange(bytes, 1, bytes.length)); - } + JacksonCborObjectBytesDeserializer(Class t, byte prefix, Function bytesMapper) { + super(t); + this.prefix = prefix; + this.bytesMapper = bytesMapper; + } + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + byte[] bytes = p.getBinaryValue(); + if (bytes == null || bytes.length == 0 || bytes[0] != prefix) { + throw new InvalidFormatException(p, "Expecting " + prefix, bytes, this.handledType()); + } + return bytesMapper.apply(Arrays.copyOfRange(bytes, 1, bytes.length)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesSerializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesSerializer.java index 47d0b00cda..19f126c3e9 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesSerializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCborObjectBytesSerializer.java @@ -72,22 +72,23 @@ public class JacksonCborObjectBytesSerializer extends StdSerializer { - private final byte prefix; - private final Function toByteArrayMapper; + private final byte prefix; + private final Function toByteArrayMapper; - JacksonCborObjectBytesSerializer(Class t, byte prefix, Function toByteArrayMapper) { - super(t); - this.prefix = prefix; - this.toByteArrayMapper = toByteArrayMapper; - } + JacksonCborObjectBytesSerializer(Class t, byte prefix, Function toByteArrayMapper) { + super(t); + this.prefix = prefix; + this.toByteArrayMapper = toByteArrayMapper; + } - @Override - public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - byte[] objectBytes = toByteArrayMapper.apply(value); - byte[] bytes = new byte[1 + objectBytes.length]; + @Override + public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + byte[] objectBytes = toByteArrayMapper.apply(value); + byte[] bytes = new byte[1 + objectBytes.length]; - bytes[0] = prefix; - System.arraycopy(objectBytes, 0, bytes, 1, objectBytes.length); - jgen.writeBinary(bytes); - } + bytes[0] = prefix; + System.arraycopy(objectBytes, 0, bytes, 1, objectBytes.length); + jgen.writeBinary(bytes); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCodecConstants.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCodecConstants.java index ceb6f69d7f..f4600a8c49 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCodecConstants.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonCodecConstants.java @@ -64,34 +64,32 @@ package com.radixdlt.serialization.mapper; -/** - * Constants for DSON protocol encoded in CBOR/JSON. - */ +/** Constants for DSON protocol encoded in CBOR/JSON. */ final class JacksonCodecConstants { - private JacksonCodecConstants() { - throw new IllegalStateException("Can't construct"); - } + private JacksonCodecConstants() { + throw new IllegalStateException("Can't construct"); + } - // Encodings for CBOR mappings - static final byte BYTES_VALUE = 0x01; - static final byte EUID_VALUE = 0x02; - static final byte HASH_VALUE = 0x03; - static final byte U20_VALUE = 0x05; // 0x20 byte = 256 bit unsigned int - static final byte RRI_VALUE = 0x06; - static final byte U30_VALUE = 0x07; // 0x30 byte = 384 bit unsigned int - static final byte AID_VALUE = 0x08; - static final byte LONGS_VALUE = 0x09; - static final byte INSTANT_VALUE = 0x0A; + // Encodings for CBOR mappings + static final byte BYTES_VALUE = 0x01; + static final byte EUID_VALUE = 0x02; + static final byte HASH_VALUE = 0x03; + static final byte U20_VALUE = 0x05; // 0x20 byte = 256 bit unsigned int + static final byte RRI_VALUE = 0x06; + static final byte U30_VALUE = 0x07; // 0x30 byte = 384 bit unsigned int + static final byte AID_VALUE = 0x08; + static final byte LONGS_VALUE = 0x09; + static final byte INSTANT_VALUE = 0x0A; - // Type tag prefixes used in strings for JSON mappings - static final int STR_VALUE_LEN = 5; - static final String BYTE_STR_VALUE = ":byt:"; - static final String EUID_STR_VALUE = ":uid:"; - static final String HASH_STR_VALUE = ":hsh:"; - static final String STR_STR_VALUE = ":str:"; - static final String U20_STR_VALUE = ":u20:"; // 0x20 byte = 256 bit unsigned int - static final String RRI_STR_VALUE = ":rri:"; - static final String U30_STR_VALUE = ":u30:"; // 0x30 byte = 384 bit unsigned int - static final String AID_STR_VALUE = ":aid:"; - static final String LONGS_STR_VALUE = ":lng:"; + // Type tag prefixes used in strings for JSON mappings + static final int STR_VALUE_LEN = 5; + static final String BYTE_STR_VALUE = ":byt:"; + static final String EUID_STR_VALUE = ":uid:"; + static final String HASH_STR_VALUE = ":hsh:"; + static final String STR_STR_VALUE = ":str:"; + static final String U20_STR_VALUE = ":u20:"; // 0x20 byte = 256 bit unsigned int + static final String RRI_STR_VALUE = ":rri:"; + static final String U30_STR_VALUE = ":u30:"; // 0x30 byte = 384 bit unsigned int + static final String AID_STR_VALUE = ":aid:"; + static final String LONGS_STR_VALUE = ":lng:"; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesDeserializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesDeserializer.java index 2cab2afe33..b043f789fb 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesDeserializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesDeserializer.java @@ -71,27 +71,24 @@ import com.radixdlt.utils.Bytes; import java.io.IOException; -/** - * Deserializer for translation from JSON encoded binary data - * to {@code byte[]} data. - */ +/** Deserializer for translation from JSON encoded binary data to {@code byte[]} data. */ class JacksonJsonBytesDeserializer extends StdDeserializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonJsonBytesDeserializer() { - this(null); - } + JacksonJsonBytesDeserializer() { + this(null); + } - JacksonJsonBytesDeserializer(Class t) { - super(t); - } + JacksonJsonBytesDeserializer(Class t) { + super(t); + } - @Override - public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); - if (!value.startsWith(JacksonCodecConstants.BYTE_STR_VALUE)) { - throw new InvalidFormatException(p, "Expecting bytes", value, byte[].class); - } - return Bytes.fromBase64String(value.substring(JacksonCodecConstants.STR_VALUE_LEN)); - } + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (!value.startsWith(JacksonCodecConstants.BYTE_STR_VALUE)) { + throw new InvalidFormatException(p, "Expecting bytes", value, byte[].class); + } + return Bytes.fromBase64String(value.substring(JacksonCodecConstants.STR_VALUE_LEN)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesSerializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesSerializer.java index 8b2fecaa31..d4820e7e40 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesSerializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonBytesSerializer.java @@ -70,23 +70,21 @@ import com.radixdlt.utils.Bytes; import java.io.IOException; -/** - * Serializer for conversion from {@code byte[]} data - * to the appropriate JSON encoding. - */ +/** Serializer for conversion from {@code byte[]} data to the appropriate JSON encoding. */ class JacksonJsonBytesSerializer extends StdSerializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonJsonBytesSerializer() { - this(null); - } + JacksonJsonBytesSerializer() { + this(null); + } - JacksonJsonBytesSerializer(Class t) { - super(t); - } + JacksonJsonBytesSerializer(Class t) { + super(t); + } - @Override - public void serialize(byte[] value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(JacksonCodecConstants.BYTE_STR_VALUE + Bytes.toBase64String(value)); - } + @Override + public void serialize(byte[] value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(JacksonCodecConstants.BYTE_STR_VALUE + Bytes.toBase64String(value)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeDeserializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeDeserializer.java index d94d81def4..10d369d477 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeDeserializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeDeserializer.java @@ -71,27 +71,24 @@ import com.google.common.hash.HashCode; import java.io.IOException; -/** - * Deserializer for translation from JSON encoded {@code Hash} data - * to a {@code Hash} object. - */ +/** Deserializer for translation from JSON encoded {@code Hash} data to a {@code Hash} object. */ class JacksonJsonHashCodeDeserializer extends StdDeserializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonJsonHashCodeDeserializer() { - this(null); - } + JacksonJsonHashCodeDeserializer() { + this(null); + } - JacksonJsonHashCodeDeserializer(Class t) { - super(t); - } + JacksonJsonHashCodeDeserializer(Class t) { + super(t); + } - @Override - public HashCode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); - if (!value.startsWith(JacksonCodecConstants.HASH_STR_VALUE)) { - throw new InvalidFormatException(p, "Expecting Hash", value, HashCode.class); - } - return HashCode.fromString(value.substring(JacksonCodecConstants.STR_VALUE_LEN)); - } + @Override + public HashCode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (!value.startsWith(JacksonCodecConstants.HASH_STR_VALUE)) { + throw new InvalidFormatException(p, "Expecting Hash", value, HashCode.class); + } + return HashCode.fromString(value.substring(JacksonCodecConstants.STR_VALUE_LEN)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeSerializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeSerializer.java index 0007490c4c..de7284948b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeSerializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonHashCodeSerializer.java @@ -70,23 +70,21 @@ import com.google.common.hash.HashCode; import java.io.IOException; -/** - * Serializer for conversion from {@code Hash} data - * to the appropriate JSON encoding. - */ +/** Serializer for conversion from {@code Hash} data to the appropriate JSON encoding. */ class JacksonJsonHashCodeSerializer extends StdSerializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonJsonHashCodeSerializer() { - this(null); - } + JacksonJsonHashCodeSerializer() { + this(null); + } - JacksonJsonHashCodeSerializer(Class t) { - super(t); - } + JacksonJsonHashCodeSerializer(Class t) { + super(t); + } - @Override - public void serialize(HashCode value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(JacksonCodecConstants.HASH_STR_VALUE + value.toString()); - } + @Override + public void serialize(HashCode value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(JacksonCodecConstants.HASH_STR_VALUE + value.toString()); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonMapper.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonMapper.java index 172b505607..ec3e101f40 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonMapper.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonMapper.java @@ -89,157 +89,158 @@ import com.radixdlt.serialization.SerializerIds; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.UInt384; -import org.bouncycastle.util.encoders.Hex; - import java.io.IOException; import java.util.Optional; +import org.bouncycastle.util.encoders.Hex; /** - * A Jackson {@link RadixObjectMapperConfigurator} that will serialize and deserialize - * to the JSON in the format that Radix requires. + * A Jackson {@link RadixObjectMapperConfigurator} that will serialize and deserialize to the JSON + * in the format that Radix requires. */ public class JacksonJsonMapper extends ObjectMapper { - private static final long serialVersionUID = 4917479892309630214L; + private static final long serialVersionUID = 4917479892309630214L; - public static JacksonJsonMapper create(SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties) { - return new JacksonJsonMapper(idLookup, filterProvider, sortProperties, Optional.empty()); - } + public static JacksonJsonMapper create( + SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties) { + return new JacksonJsonMapper(idLookup, filterProvider, sortProperties, Optional.empty()); + } - /** - * Create an {@link RadixObjectMapperConfigurator} that will serialize to/from the JSON - * format that radix requires. - * - * @param idLookup A {@link SerializerIds} used to perform serializer - * ID lookup - * @param filterProvider A {@link FilterProvider} to use for filtering - * serialized fields - * @param sortProperties {@code true} if JSON output properties should be - * sorted in lexicographical order - * @param serializationModifier optional BeanSerializerModifier to mix in the serialization - * @return A freshly created {@link JacksonJsonMapper} - */ - public static JacksonJsonMapper create(SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties, - Optional serializationModifier) { - return new JacksonJsonMapper(idLookup, filterProvider, sortProperties, serializationModifier); - } + /** + * Create an {@link RadixObjectMapperConfigurator} that will serialize to/from the JSON format + * that radix requires. + * + * @param idLookup A {@link SerializerIds} used to perform serializer ID lookup + * @param filterProvider A {@link FilterProvider} to use for filtering serialized fields + * @param sortProperties {@code true} if JSON output properties should be sorted in + * lexicographical order + * @param serializationModifier optional BeanSerializerModifier to mix in the serialization + * @return A freshly created {@link JacksonJsonMapper} + */ + public static JacksonJsonMapper create( + SerializerIds idLookup, + FilterProvider filterProvider, + boolean sortProperties, + Optional serializationModifier) { + return new JacksonJsonMapper(idLookup, filterProvider, sortProperties, serializationModifier); + } - private JacksonJsonMapper(SerializerIds idLookup, FilterProvider filterProvider, boolean sortProperties, - Optional serializationModifier) { - super(new JsonFactory()); - RadixObjectMapperConfigurator.configure(this, idLookup, filterProvider, sortProperties); - SimpleModule jsonModule = new SimpleModule(); - jsonModule.addSerializer(EUID.class, new JacksonJsonObjectStringSerializer<>( - EUID.class, - JacksonCodecConstants.EUID_STR_VALUE, - EUID::toString - )); - jsonModule.addSerializer(HashCode.class, new JacksonJsonHashCodeSerializer()); - jsonModule.addSerializer(byte[].class, new JacksonJsonBytesSerializer()); - jsonModule.addSerializer(String.class, new JacksonJsonStringSerializer()); - jsonModule.addSerializer(SerializerDummy.class, new JacksonSerializerDummySerializer(idLookup)); - jsonModule.addSerializer(UInt256.class, new JacksonJsonObjectStringSerializer<>( - UInt256.class, - JacksonCodecConstants.U20_STR_VALUE, - UInt256::toString - )); - jsonModule.addSerializer(UInt384.class, new JacksonJsonObjectStringSerializer<>( - UInt384.class, - JacksonCodecConstants.U30_STR_VALUE, - UInt384::toString - )); - jsonModule.addSerializer(REAddr.class, new JacksonJsonObjectStringSerializer<>( - REAddr.class, - JacksonCodecConstants.RRI_STR_VALUE, - rri -> Hex.toHexString(rri.getBytes()) - )); - jsonModule.addSerializer(AID.class, new JacksonJsonObjectStringSerializer<>( - AID.class, - JacksonCodecConstants.AID_STR_VALUE, - AID::toString - )); + private JacksonJsonMapper( + SerializerIds idLookup, + FilterProvider filterProvider, + boolean sortProperties, + Optional serializationModifier) { + super(new JsonFactory()); + RadixObjectMapperConfigurator.configure(this, idLookup, filterProvider, sortProperties); + SimpleModule jsonModule = new SimpleModule(); + jsonModule.addSerializer( + EUID.class, + new JacksonJsonObjectStringSerializer<>( + EUID.class, JacksonCodecConstants.EUID_STR_VALUE, EUID::toString)); + jsonModule.addSerializer(HashCode.class, new JacksonJsonHashCodeSerializer()); + jsonModule.addSerializer(byte[].class, new JacksonJsonBytesSerializer()); + jsonModule.addSerializer(String.class, new JacksonJsonStringSerializer()); + jsonModule.addSerializer(SerializerDummy.class, new JacksonSerializerDummySerializer(idLookup)); + jsonModule.addSerializer( + UInt256.class, + new JacksonJsonObjectStringSerializer<>( + UInt256.class, JacksonCodecConstants.U20_STR_VALUE, UInt256::toString)); + jsonModule.addSerializer( + UInt384.class, + new JacksonJsonObjectStringSerializer<>( + UInt384.class, JacksonCodecConstants.U30_STR_VALUE, UInt384::toString)); + jsonModule.addSerializer( + REAddr.class, + new JacksonJsonObjectStringSerializer<>( + REAddr.class, + JacksonCodecConstants.RRI_STR_VALUE, + rri -> Hex.toHexString(rri.getBytes()))); + jsonModule.addSerializer( + AID.class, + new JacksonJsonObjectStringSerializer<>( + AID.class, JacksonCodecConstants.AID_STR_VALUE, AID::toString)); - jsonModule.addKeySerializer(AID.class, new StdSerializer(AID.class) { - @Override - public void serialize(AID value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeFieldName(JacksonCodecConstants.AID_STR_VALUE + value.toString()); - } - }); + jsonModule.addKeySerializer( + AID.class, + new StdSerializer(AID.class) { + @Override + public void serialize(AID value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeFieldName(JacksonCodecConstants.AID_STR_VALUE + value.toString()); + } + }); - jsonModule.addDeserializer(EUID.class, new JacksonJsonObjectStringDeserializer<>( - EUID.class, - JacksonCodecConstants.EUID_STR_VALUE, - EUID::new - )); - jsonModule.addDeserializer(HashCode.class, new JacksonJsonHashCodeDeserializer()); - jsonModule.addDeserializer(byte[].class, new JacksonJsonBytesDeserializer()); - jsonModule.addDeserializer(String.class, new JacksonJsonStringDeserializer()); - jsonModule.addDeserializer(SerializerDummy.class, new JacksonSerializerDummyDeserializer()); - jsonModule.addDeserializer(UInt256.class, new JacksonJsonObjectStringDeserializer<>( - UInt256.class, - JacksonCodecConstants.U20_STR_VALUE, - UInt256::from - )); - jsonModule.addDeserializer(UInt384.class, new JacksonJsonObjectStringDeserializer<>( - UInt384.class, - JacksonCodecConstants.U30_STR_VALUE, - UInt384::from - )); - jsonModule.addDeserializer(REAddr.class, new JacksonJsonObjectStringDeserializer<>( - REAddr.class, - JacksonCodecConstants.RRI_STR_VALUE, - s -> REAddr.of(Hex.decode(s)) - )); - jsonModule.addDeserializer(AID.class, new JacksonJsonObjectStringDeserializer<>( - AID.class, - JacksonCodecConstants.AID_STR_VALUE, - AID::from - )); - jsonModule.addKeyDeserializer(AID.class, new KeyDeserializer() { - @Override - public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException { - if (!key.startsWith(JacksonCodecConstants.AID_STR_VALUE)) { - throw new InvalidFormatException( - ctxt.getParser(), - "Expecting prefix" + JacksonCodecConstants.AID_STR_VALUE, - key, - AID.class - ); - } - return AID.from(key.substring(JacksonCodecConstants.STR_VALUE_LEN)); - } - }); + jsonModule.addDeserializer( + EUID.class, + new JacksonJsonObjectStringDeserializer<>( + EUID.class, JacksonCodecConstants.EUID_STR_VALUE, EUID::new)); + jsonModule.addDeserializer(HashCode.class, new JacksonJsonHashCodeDeserializer()); + jsonModule.addDeserializer(byte[].class, new JacksonJsonBytesDeserializer()); + jsonModule.addDeserializer(String.class, new JacksonJsonStringDeserializer()); + jsonModule.addDeserializer(SerializerDummy.class, new JacksonSerializerDummyDeserializer()); + jsonModule.addDeserializer( + UInt256.class, + new JacksonJsonObjectStringDeserializer<>( + UInt256.class, JacksonCodecConstants.U20_STR_VALUE, UInt256::from)); + jsonModule.addDeserializer( + UInt384.class, + new JacksonJsonObjectStringDeserializer<>( + UInt384.class, JacksonCodecConstants.U30_STR_VALUE, UInt384::from)); + jsonModule.addDeserializer( + REAddr.class, + new JacksonJsonObjectStringDeserializer<>( + REAddr.class, JacksonCodecConstants.RRI_STR_VALUE, s -> REAddr.of(Hex.decode(s)))); + jsonModule.addDeserializer( + AID.class, + new JacksonJsonObjectStringDeserializer<>( + AID.class, JacksonCodecConstants.AID_STR_VALUE, AID::from)); + jsonModule.addKeyDeserializer( + AID.class, + new KeyDeserializer() { + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException { + if (!key.startsWith(JacksonCodecConstants.AID_STR_VALUE)) { + throw new InvalidFormatException( + ctxt.getParser(), + "Expecting prefix" + JacksonCodecConstants.AID_STR_VALUE, + key, + AID.class); + } + return AID.from(key.substring(JacksonCodecConstants.STR_VALUE_LEN)); + } + }); - // Special modifier for Enum values to remove :str: leadin from front - jsonModule.setDeserializerModifier(new BeanDeserializerModifier() { - @Override - @SuppressWarnings("rawtypes") - public JsonDeserializer modifyEnumDeserializer( - DeserializationConfig config, - final JavaType type, - BeanDescription beanDesc, - final JsonDeserializer deserializer - ) { - return new JsonDeserializer<>() { - @Override - @SuppressWarnings("unchecked") - public Enum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - String name = jp.getValueAsString(); - if (!name.startsWith(JacksonCodecConstants.STR_STR_VALUE)) { - throw new IllegalStateException(String.format( - "Expected value starting with %s, found: %s", - JacksonCodecConstants.STR_STR_VALUE, - name) - ); - } - Class rawClass = (Class>) type.getRawClass(); - return Enum.valueOf(rawClass, jp.getValueAsString().substring(JacksonCodecConstants.STR_VALUE_LEN)); - } - }; - } - }); + // Special modifier for Enum values to remove :str: leadin from front + jsonModule.setDeserializerModifier( + new BeanDeserializerModifier() { + @Override + @SuppressWarnings("rawtypes") + public JsonDeserializer modifyEnumDeserializer( + DeserializationConfig config, + final JavaType type, + BeanDescription beanDesc, + final JsonDeserializer deserializer) { + return new JsonDeserializer<>() { + @Override + @SuppressWarnings("unchecked") + public Enum deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException { + String name = jp.getValueAsString(); + if (!name.startsWith(JacksonCodecConstants.STR_STR_VALUE)) { + throw new IllegalStateException( + String.format( + "Expected value starting with %s, found: %s", + JacksonCodecConstants.STR_STR_VALUE, name)); + } + Class rawClass = (Class>) type.getRawClass(); + return Enum.valueOf( + rawClass, jp.getValueAsString().substring(JacksonCodecConstants.STR_VALUE_LEN)); + } + }; + } + }); - serializationModifier.ifPresent(jsonModule::setSerializerModifier); + serializationModifier.ifPresent(jsonModule::setSerializerModifier); - registerModule(jsonModule); - } + registerModule(jsonModule); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringDeserializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringDeserializer.java index 1a1288120c..5d1729f193 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringDeserializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringDeserializer.java @@ -73,26 +73,26 @@ import java.util.function.Function; /** - * Deserializer for translation from JSON encoded {@code String} data - * to an string representable object. + * Deserializer for translation from JSON encoded {@code String} data to an string representable + * object. */ public class JacksonJsonObjectStringDeserializer extends StdDeserializer { - private final Function stringMapper; - private final String prefix; + private final Function stringMapper; + private final String prefix; - JacksonJsonObjectStringDeserializer(Class t, String prefix, Function stringMapper) { - super(t); - this.prefix = Objects.requireNonNull(prefix); - this.stringMapper = Objects.requireNonNull(stringMapper); - } + JacksonJsonObjectStringDeserializer(Class t, String prefix, Function stringMapper) { + super(t); + this.prefix = Objects.requireNonNull(prefix); + this.stringMapper = Objects.requireNonNull(stringMapper); + } - @Override - public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); - if (!value.startsWith(prefix)) { - throw new InvalidFormatException(p, "Expecting string " + prefix, value, this.handledType()); - } + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (!value.startsWith(prefix)) { + throw new InvalidFormatException(p, "Expecting string " + prefix, value, this.handledType()); + } - return stringMapper.apply(value.substring(JacksonCodecConstants.STR_VALUE_LEN)); - } + return stringMapper.apply(value.substring(JacksonCodecConstants.STR_VALUE_LEN)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringSerializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringSerializer.java index 6ef1b7466a..0b4cc27296 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringSerializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonObjectStringSerializer.java @@ -72,22 +72,23 @@ import java.util.function.Function; /** - * Serializer for conversion from an object representable by a string - * to the appropriate JSON encoding. + * Serializer for conversion from an object representable by a string to the appropriate JSON + * encoding. */ public class JacksonJsonObjectStringSerializer extends StdSerializer { - private static final long serialVersionUID = -4231287848387995937L; - private final String prefix; - private final Function toStringMapper; + private static final long serialVersionUID = -4231287848387995937L; + private final String prefix; + private final Function toStringMapper; - JacksonJsonObjectStringSerializer(Class t, String prefix, Function toStringMapper) { - super(t); - this.prefix = Objects.requireNonNull(prefix); - this.toStringMapper = Objects.requireNonNull(toStringMapper); - } + JacksonJsonObjectStringSerializer(Class t, String prefix, Function toStringMapper) { + super(t); + this.prefix = Objects.requireNonNull(prefix); + this.toStringMapper = Objects.requireNonNull(toStringMapper); + } - @Override - public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(prefix + toStringMapper.apply(value)); - } + @Override + public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(prefix + toStringMapper.apply(value)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringDeserializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringDeserializer.java index 1b6ca3a183..ce0a4ff83c 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringDeserializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringDeserializer.java @@ -71,26 +71,25 @@ import java.io.IOException; /** - * Deserializer for translation from JSON encoded {@code String} data - * to a {@code String} object. + * Deserializer for translation from JSON encoded {@code String} data to a {@code String} object. */ class JacksonJsonStringDeserializer extends StdDeserializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonJsonStringDeserializer() { - this(null); - } + JacksonJsonStringDeserializer() { + this(null); + } - JacksonJsonStringDeserializer(Class t) { - super(t); - } + JacksonJsonStringDeserializer(Class t) { + super(t); + } - @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); - if (!value.startsWith(JacksonCodecConstants.STR_STR_VALUE)) { - throw new InvalidFormatException(p, "Expecting string", value, String.class); - } - return value.substring(JacksonCodecConstants.STR_VALUE_LEN); - } + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + if (!value.startsWith(JacksonCodecConstants.STR_STR_VALUE)) { + throw new InvalidFormatException(p, "Expecting string", value, String.class); + } + return value.substring(JacksonCodecConstants.STR_VALUE_LEN); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringSerializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringSerializer.java index 1bff8ef1b2..3e8ad52ed6 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringSerializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonJsonStringSerializer.java @@ -69,23 +69,21 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; -/** - * Serializer for conversion from {@code String} data - * to the appropriate JSON encoding. - */ +/** Serializer for conversion from {@code String} data to the appropriate JSON encoding. */ class JacksonJsonStringSerializer extends StdSerializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonJsonStringSerializer() { - this(null); - } + JacksonJsonStringSerializer() { + this(null); + } - JacksonJsonStringSerializer(Class t) { - super(t); - } + JacksonJsonStringSerializer(Class t) { + super(t); + } - @Override - public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(JacksonCodecConstants.STR_STR_VALUE + value); - } + @Override + public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(JacksonCodecConstants.STR_STR_VALUE + value); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummyDeserializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummyDeserializer.java index b8f289a5b1..a183e4da08 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummyDeserializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummyDeserializer.java @@ -70,23 +70,21 @@ import com.radixdlt.serialization.SerializerDummy; import java.io.IOException; -/** - * Deserializer for special {@link SerializerDummy} value. - */ +/** Deserializer for special {@link SerializerDummy} value. */ class JacksonSerializerDummyDeserializer extends StdDeserializer { - private static final long serialVersionUID = -2472482347700365657L; + private static final long serialVersionUID = -2472482347700365657L; - JacksonSerializerDummyDeserializer() { - this(null); - } + JacksonSerializerDummyDeserializer() { + this(null); + } - JacksonSerializerDummyDeserializer(Class t) { - super(t); - } + JacksonSerializerDummyDeserializer(Class t) { + super(t); + } - @Override - public SerializerDummy deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - p.getLongValue(); // Ignored - return SerializerDummy.DUMMY; - } -} \ No newline at end of file + @Override + public SerializerDummy deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + p.getLongValue(); // Ignored + return SerializerDummy.DUMMY; + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummySerializer.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummySerializer.java index baa5921f8b..141cb405fd 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummySerializer.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/JacksonSerializerDummySerializer.java @@ -72,35 +72,38 @@ import com.radixdlt.serialization.SerializerIds; import java.io.IOException; -/** - * Special converter for {@link SerializerDummy} class. - */ +/** Special converter for {@link SerializerDummy} class. */ class JacksonSerializerDummySerializer extends StdSerializer { - private static final long serialVersionUID = -2472482347700365657L; - private final SerializerIds idLookup; - - JacksonSerializerDummySerializer(SerializerIds idLookup) { - this(null, idLookup); - } + private static final long serialVersionUID = -2472482347700365657L; + private final SerializerIds idLookup; - JacksonSerializerDummySerializer(Class t, SerializerIds idLookup) { - super(t); - this.idLookup = idLookup; - } + JacksonSerializerDummySerializer(SerializerIds idLookup) { + this(null, idLookup); + } - @Override - public void serialize(SerializerDummy value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - Object parent = jgen.getOutputContext().getCurrentValue(); - String id = idLookup.getIdForClass(parent.getClass()); - if (id == null) { - throw new IllegalStateException("Can't find ID for class: " + parent.getClass().getName()); - } - jgen.writeString(id); - } + JacksonSerializerDummySerializer(Class t, SerializerIds idLookup) { + super(t); + this.idLookup = idLookup; + } - @Override - public void serializeWithType(SerializerDummy value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) - throws IOException { - serialize(value, jgen, provider); + @Override + public void serialize(SerializerDummy value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + Object parent = jgen.getOutputContext().getCurrentValue(); + String id = idLookup.getIdForClass(parent.getClass()); + if (id == null) { + throw new IllegalStateException("Can't find ID for class: " + parent.getClass().getName()); } -} \ No newline at end of file + jgen.writeString(id); + } + + @Override + public void serializeWithType( + SerializerDummy value, + JsonGenerator jgen, + SerializerProvider provider, + TypeSerializer typeSer) + throws IOException { + serialize(value, jgen, provider); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/MapperConstants.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/MapperConstants.java index d8b93ed967..c12d020b2f 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/MapperConstants.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/MapperConstants.java @@ -65,9 +65,9 @@ package com.radixdlt.serialization.mapper; final class MapperConstants { - private MapperConstants() { - throw new IllegalStateException("Can't construct"); - } + private MapperConstants() { + throw new IllegalStateException("Can't construct"); + } - static final String DSON_FILTER_NAME = "DSON"; + static final String DSON_FILTER_NAME = "DSON"; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/PackageVersion.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/PackageVersion.java index 06751d5a7e..e09da57fb3 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/PackageVersion.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/PackageVersion.java @@ -69,11 +69,12 @@ import com.fasterxml.jackson.core.util.VersionUtil; public final class PackageVersion implements Versioned { - public static final Version VERSION = - VersionUtil.parseVersion("2.9.9", "com.radixdlt.serialization.mapper", "jackson-dataformat-cbor-radix"); + public static final Version VERSION = + VersionUtil.parseVersion( + "2.9.9", "com.radixdlt.serialization.mapper", "jackson-dataformat-cbor-radix"); - @Override - public Version version() { - return VERSION; - } + @Override + public Version version() { + return VERSION; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORFactory.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORFactory.java index d5abf5b812..a8ef6ab914 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORFactory.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORFactory.java @@ -65,7 +65,7 @@ package com.radixdlt.serialization.mapper; // Checkstyle disabled here, as this file has been imported from Jackson -//CHECKSTYLE:OFF: +// CHECKSTYLE:OFF: import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; @@ -88,433 +88,406 @@ import java.net.URL; /** - * Factory used for constructing {@link CBORParser} and {@link CBORGenerator} - * instances; both of which handle - * CBOR - * encoded data. - *

- * Extends {@link JsonFactory} mostly so that users can actually use it in place - * of regular non-CBOR factory instances. - *

- * Note on using non-byte-based sources/targets (char based, like - * {@link Reader} and {@link Writer}): these can not be - * used for CBOR documents; attempt will throw exception. - *

- * NOTE: This code has been adapted from the Jackson CBOR class - * {@link com.fasterxml.jackson.dataformat.cbor.CBORFactory}. - * The only changes have been to change references to - * {@code CBORGenerator} to {@link RadixCBORGenerator}. - *

- * The - * Jackson-dataformats-binary package, and therefore this file, is - * published under the - * Apache License 2.0 + * Factory used for constructing {@link CBORParser} and {@link CBORGenerator} instances; both of + * which handle CBOR encoded data. + * + *

Extends {@link JsonFactory} mostly so that users can actually use it in place of regular + * non-CBOR factory instances. + * + *

Note on using non-byte-based sources/targets (char based, like {@link Reader} and {@link + * Writer}): these can not be used for CBOR documents; attempt will throw exception. + * + *

NOTE: This code has been adapted from the Jackson CBOR class {@link + * com.fasterxml.jackson.dataformat.cbor.CBORFactory}. The only changes have been to change + * references to {@code CBORGenerator} to {@link RadixCBORGenerator}. + * + *

The + * Jackson-dataformats-binary package, and therefore this file, is published under the Apache License 2.0 * * @author Tatu Saloranta (original Jackson file) */ -public class RadixCBORFactory extends JsonFactory -{ - private static final long serialVersionUID = 1; // 2.6 - - /* - /********************************************************** - /* Constants - /********************************************************** - */ - - /** - * Name used to identify CBOR format. - * (and returned by {@link #getFormatName()} - */ - public final static String FORMAT_NAME = "CBOR"; - - /** - * Bitfield (set of flags) of all parser features that are enabled - * by default. - */ - final static int DEFAULT_CBOR_PARSER_FEATURE_FLAGS = CBORParser.Feature.collectDefaults(); - - /** - * Bitfield (set of flags) of all generator features that are enabled - * by default. - */ - final static int DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS = RadixCBORGenerator.Feature.collectDefaults(); - - /* - /********************************************************** - /* Configuration - /********************************************************** - */ - - protected int _formatParserFeatures; - protected int _formatGeneratorFeatures; - - /* - /********************************************************** - /* Factory construction, configuration - /********************************************************** - */ - - /** - * Default constructor used to create factory instances. - * Creation of a factory instance is a light-weight operation, - * but it is still a good idea to reuse limited number of - * factory instances (and quite often just a single instance): - * factories are used as context for storing some reused - * processing objects (such as symbol tables parsers use) - * and this reuse only works within context of a single - * factory instance. - */ - public RadixCBORFactory() { this(null); } - - public RadixCBORFactory(ObjectCodec oc) { - super(oc); - _formatParserFeatures = DEFAULT_CBOR_PARSER_FEATURE_FLAGS; - _formatGeneratorFeatures = DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS; - } - - /** - * Note: REQUIRES at least 2.2.1 -- unfortunate intra-patch dep but seems - * preferable to just leaving bug be as is - * - * @since 2.2.1 - */ - public RadixCBORFactory(RadixCBORFactory src, ObjectCodec oc) - { - super(src, oc); - _formatParserFeatures = src._formatParserFeatures; - _formatGeneratorFeatures = src._formatGeneratorFeatures; - } - - @Override - public RadixCBORFactory copy() - { - _checkInvalidCopy(RadixCBORFactory.class); - // note: as with base class, must NOT copy mapper reference - return new RadixCBORFactory(this, null); - } - - /* - /********************************************************** - /* Serializable overrides - /********************************************************** - */ - - /** - * Method that we need to override to actually make restoration go - * through constructors etc. - * Also: must be overridden by sub-classes as well. - */ - @Override - protected Object readResolve() { - return new RadixCBORFactory(this, _objectCodec); - } - - /* - /********************************************************** - /* Versioned - /********************************************************** - */ - - @Override - public Version version() { - return PackageVersion.VERSION; - } - - /* - /********************************************************** - /* Format detection functionality - /********************************************************** - */ - - @Override - public String getFormatName() { - return FORMAT_NAME; - } - - // Defaults work fine for this: - // public boolean canUseSchema(FormatSchema schema) { } - - @Override - public boolean canUseCharArrays() { return false; } - - @Override - public MatchStrength hasFormat(InputAccessor acc) throws IOException { - return CBORParserBootstrapper.hasCBORFormat(acc); - } - - /* - /********************************************************** - /* Capability introspection - /********************************************************** - */ - - @Override - public boolean canHandleBinaryNatively() { - return true; - } - - @Override // since 2.6 - public Class getFormatReadFeatureType() { - return CBORParser.Feature.class; - } - - @Override // since 2.6 - public Class getFormatWriteFeatureType() { - return RadixCBORGenerator.Feature.class; - } - - /* - /********************************************************** - /* Configuration, parser settings - /********************************************************** - */ - - /** - * Method for enabling or disabling specified parser feature - * (check {@link CBORParser.Feature} for list of features) - */ - public final RadixCBORFactory configure(CBORParser.Feature f, boolean state) - { - if (state) { - enable(f); - } else { - disable(f); - } - return this; - } - - /** - * Method for enabling specified parser feature - * (check {@link CBORParser.Feature} for list of features) - */ - public RadixCBORFactory enable(CBORParser.Feature f) { - _formatParserFeatures |= f.getMask(); - return this; - } - - /** - * Method for disabling specified parser features - * (check {@link CBORParser.Feature} for list of features) - */ - public RadixCBORFactory disable(CBORParser.Feature f) { - _formatParserFeatures &= ~f.getMask(); - return this; - } - - /** - * Checked whether specified parser feature is enabled. - */ - public final boolean isEnabled(CBORParser.Feature f) { - return (_formatParserFeatures & f.getMask()) != 0; - } - - /* - /********************************************************** - /* Configuration, generator settings - /********************************************************** - */ - - /** - * Method for enabling or disabling specified generator feature - * (check {@link CBORGenerator.Feature} for list of features) - */ - public final RadixCBORFactory configure(RadixCBORGenerator.Feature f, boolean state) { - if (state) { - enable(f); - } else { - disable(f); - } - return this; - } - - - /** - * Method for enabling specified generator features - * (check {@link CBORGenerator.Feature} for list of features) - */ - public RadixCBORFactory enable(RadixCBORGenerator.Feature f) { - _formatGeneratorFeatures |= f.getMask(); - return this; - } - - /** - * Method for disabling specified generator feature - * (check {@link CBORGenerator.Feature} for list of features) - */ - public RadixCBORFactory disable(RadixCBORGenerator.Feature f) { - _formatGeneratorFeatures &= ~f.getMask(); - return this; - } - - /** - * Check whether specified generator feature is enabled. - */ - public final boolean isEnabled(RadixCBORGenerator.Feature f) { - return (_formatGeneratorFeatures & f.getMask()) != 0; - } - - /* - /********************************************************** - /* Overridden parser factory methods, new (2.1) - /********************************************************** - */ - - @SuppressWarnings("resource") - @Override - public CBORParser createParser(File f) throws IOException { - IOContext ctxt = _createContext(f, true); - return _createParser(_decorate(new FileInputStream(f), ctxt), ctxt); - } - - @Override - public CBORParser createParser(URL url) throws IOException { - IOContext ctxt = _createContext(url, true); - return _createParser(_decorate(_optimizedStreamFromURL(url), ctxt), ctxt); - } - - @Override - public CBORParser createParser(InputStream in) throws IOException { - IOContext ctxt = _createContext(in, false); - return _createParser(_decorate(in, ctxt), ctxt); - } - - @Override - public CBORParser createParser(byte[] data) throws IOException { - return createParser(data, 0, data.length); - } - - @SuppressWarnings("resource") - @Override - public CBORParser createParser(byte[] data, int offset, int len) throws IOException { - IOContext ctxt = _createContext(data, true); - if (_inputDecorator != null) { - InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); - if (in != null) { - return _createParser(in, ctxt); - } - } - return _createParser(data, offset, len, ctxt); - } - - /* - /********************************************************** - /* Overridden generator factory methods - /********************************************************** - */ - - /** - * Method for constructing {@link JsonGenerator} for generating - * CBOR-encoded output. - *

- * Since CBOR format always uses UTF-8 internally, enc - * argument is ignored. - */ - @Override - public RadixCBORGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { - final IOContext ctxt = _createContext(out, false); - return _createCBORGenerator(ctxt, - _generatorFeatures, _formatGeneratorFeatures, _objectCodec, - _decorate(out, ctxt)); - } - - /** - * Method for constructing {@link JsonGenerator} for generating - * CBOR-encoded output. - *

- * Since CBOR format always uses UTF-8 internally, no encoding need - * to be passed to this method. - */ - @Override - public RadixCBORGenerator createGenerator(OutputStream out) throws IOException { - final IOContext ctxt = _createContext(out, false); - return _createCBORGenerator(ctxt, - _generatorFeatures, _formatGeneratorFeatures, _objectCodec, - _decorate(out, ctxt)); - } - - /* - /****************************************************** - /* Overridden internal factory methods - /****************************************************** - */ - - @Override - protected IOContext _createContext(Object srcRef, boolean resourceManaged) { - return super._createContext(srcRef, resourceManaged); - } - - /** - * Overridable factory method that actually instantiates desired - * parser. - */ - @Override - protected CBORParser _createParser(InputStream in, IOContext ctxt) throws IOException - { - return new CBORParserBootstrapper(ctxt, in).constructParser(_factoryFeatures, - _parserFeatures, _formatParserFeatures, - _objectCodec, _byteSymbolCanonicalizer); - } - - /** - * Overridable factory method that actually instantiates desired - * parser. - */ - @Override - protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { - return _nonByteSource(); - } - - @Override - protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, - boolean recyclable) throws IOException { - return _nonByteSource(); - } - - /** - * Overridable factory method that actually instantiates desired - * parser. - */ - @Override - protected CBORParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException - { - return new CBORParserBootstrapper(ctxt, data, offset, len).constructParser( - _factoryFeatures, _parserFeatures, _formatParserFeatures, - _objectCodec, _byteSymbolCanonicalizer); - } - - @Override - protected RadixCBORGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { - return _nonByteTarget(); - } - - @Override - protected RadixCBORGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { - return _createCBORGenerator(ctxt, - _generatorFeatures, _formatGeneratorFeatures, _objectCodec, out); - } - - @Override - protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException { - return _nonByteTarget(); - } - - private RadixCBORGenerator _createCBORGenerator(IOContext ctxt, - int stdFeat, int formatFeat, ObjectCodec codec, OutputStream out) throws IOException - { - // false -> we won't manage the stream unless explicitly directed to - RadixCBORGenerator gen = new RadixCBORGenerator(ctxt, stdFeat, formatFeat, _objectCodec, out); - if (RadixCBORGenerator.Feature.WRITE_TYPE_HEADER.enabledIn(formatFeat)) { - gen.writeTag(CBORConstants.TAG_ID_SELF_DESCRIBE); - } - return gen; - } - - protected T _nonByteSource() { - throw new UnsupportedOperationException("Can not create parser for non-byte-based source"); - } - - protected T _nonByteTarget() { - throw new UnsupportedOperationException("Can not create generator for non-byte-based target"); - } +public class RadixCBORFactory extends JsonFactory { + private static final long serialVersionUID = 1; // 2.6 + + /* + /********************************************************** + /* Constants + /********************************************************** + */ + + /** Name used to identify CBOR format. (and returned by {@link #getFormatName()} */ + public static final String FORMAT_NAME = "CBOR"; + + /** Bitfield (set of flags) of all parser features that are enabled by default. */ + static final int DEFAULT_CBOR_PARSER_FEATURE_FLAGS = CBORParser.Feature.collectDefaults(); + + /** Bitfield (set of flags) of all generator features that are enabled by default. */ + static final int DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS = + RadixCBORGenerator.Feature.collectDefaults(); + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected int _formatParserFeatures; + protected int _formatGeneratorFeatures; + + /* + /********************************************************** + /* Factory construction, configuration + /********************************************************** + */ + + /** + * Default constructor used to create factory instances. Creation of a factory instance is a + * light-weight operation, but it is still a good idea to reuse limited number of factory + * instances (and quite often just a single instance): factories are used as context for storing + * some reused processing objects (such as symbol tables parsers use) and this reuse only works + * within context of a single factory instance. + */ + public RadixCBORFactory() { + this(null); + } + + public RadixCBORFactory(ObjectCodec oc) { + super(oc); + _formatParserFeatures = DEFAULT_CBOR_PARSER_FEATURE_FLAGS; + _formatGeneratorFeatures = DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS; + } + + /** + * Note: REQUIRES at least 2.2.1 -- unfortunate intra-patch dep but seems preferable to just + * leaving bug be as is + * + * @since 2.2.1 + */ + public RadixCBORFactory(RadixCBORFactory src, ObjectCodec oc) { + super(src, oc); + _formatParserFeatures = src._formatParserFeatures; + _formatGeneratorFeatures = src._formatGeneratorFeatures; + } + + @Override + public RadixCBORFactory copy() { + _checkInvalidCopy(RadixCBORFactory.class); + // note: as with base class, must NOT copy mapper reference + return new RadixCBORFactory(this, null); + } + + /* + /********************************************************** + /* Serializable overrides + /********************************************************** + */ + + /** + * Method that we need to override to actually make restoration go through constructors etc. Also: + * must be overridden by sub-classes as well. + */ + @Override + protected Object readResolve() { + return new RadixCBORFactory(this, _objectCodec); + } + + /* + /********************************************************** + /* Versioned + /********************************************************** + */ + + @Override + public Version version() { + return PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Format detection functionality + /********************************************************** + */ + + @Override + public String getFormatName() { + return FORMAT_NAME; + } + + // Defaults work fine for this: + // public boolean canUseSchema(FormatSchema schema) { } + + @Override + public boolean canUseCharArrays() { + return false; + } + + @Override + public MatchStrength hasFormat(InputAccessor acc) throws IOException { + return CBORParserBootstrapper.hasCBORFormat(acc); + } + + /* + /********************************************************** + /* Capability introspection + /********************************************************** + */ + + @Override + public boolean canHandleBinaryNatively() { + return true; + } + + @Override // since 2.6 + public Class getFormatReadFeatureType() { + return CBORParser.Feature.class; + } + + @Override // since 2.6 + public Class getFormatWriteFeatureType() { + return RadixCBORGenerator.Feature.class; + } + + /* + /********************************************************** + /* Configuration, parser settings + /********************************************************** + */ + + /** + * Method for enabling or disabling specified parser feature (check {@link CBORParser.Feature} for + * list of features) + */ + public final RadixCBORFactory configure(CBORParser.Feature f, boolean state) { + if (state) { + enable(f); + } else { + disable(f); + } + return this; + } + + /** + * Method for enabling specified parser feature (check {@link CBORParser.Feature} for list of + * features) + */ + public RadixCBORFactory enable(CBORParser.Feature f) { + _formatParserFeatures |= f.getMask(); + return this; + } + + /** + * Method for disabling specified parser features (check {@link CBORParser.Feature} for list of + * features) + */ + public RadixCBORFactory disable(CBORParser.Feature f) { + _formatParserFeatures &= ~f.getMask(); + return this; + } + + /** Checked whether specified parser feature is enabled. */ + public final boolean isEnabled(CBORParser.Feature f) { + return (_formatParserFeatures & f.getMask()) != 0; + } + + /* + /********************************************************** + /* Configuration, generator settings + /********************************************************** + */ + + /** + * Method for enabling or disabling specified generator feature (check {@link + * CBORGenerator.Feature} for list of features) + */ + public final RadixCBORFactory configure(RadixCBORGenerator.Feature f, boolean state) { + if (state) { + enable(f); + } else { + disable(f); + } + return this; + } + + /** + * Method for enabling specified generator features (check {@link CBORGenerator.Feature} for list + * of features) + */ + public RadixCBORFactory enable(RadixCBORGenerator.Feature f) { + _formatGeneratorFeatures |= f.getMask(); + return this; + } + + /** + * Method for disabling specified generator feature (check {@link CBORGenerator.Feature} for list + * of features) + */ + public RadixCBORFactory disable(RadixCBORGenerator.Feature f) { + _formatGeneratorFeatures &= ~f.getMask(); + return this; + } + + /** Check whether specified generator feature is enabled. */ + public final boolean isEnabled(RadixCBORGenerator.Feature f) { + return (_formatGeneratorFeatures & f.getMask()) != 0; + } + + /* + /********************************************************** + /* Overridden parser factory methods, new (2.1) + /********************************************************** + */ + + @SuppressWarnings("resource") + @Override + public CBORParser createParser(File f) throws IOException { + IOContext ctxt = _createContext(f, true); + return _createParser(_decorate(new FileInputStream(f), ctxt), ctxt); + } + + @Override + public CBORParser createParser(URL url) throws IOException { + IOContext ctxt = _createContext(url, true); + return _createParser(_decorate(_optimizedStreamFromURL(url), ctxt), ctxt); + } + + @Override + public CBORParser createParser(InputStream in) throws IOException { + IOContext ctxt = _createContext(in, false); + return _createParser(_decorate(in, ctxt), ctxt); + } + + @Override + public CBORParser createParser(byte[] data) throws IOException { + return createParser(data, 0, data.length); + } + + @SuppressWarnings("resource") + @Override + public CBORParser createParser(byte[] data, int offset, int len) throws IOException { + IOContext ctxt = _createContext(data, true); + if (_inputDecorator != null) { + InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); + if (in != null) { + return _createParser(in, ctxt); + } + } + return _createParser(data, offset, len, ctxt); + } + + /* + /********************************************************** + /* Overridden generator factory methods + /********************************************************** + */ + + /** + * Method for constructing {@link JsonGenerator} for generating CBOR-encoded output. + * + *

Since CBOR format always uses UTF-8 internally, enc argument is ignored. + */ + @Override + public RadixCBORGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { + final IOContext ctxt = _createContext(out, false); + return _createCBORGenerator( + ctxt, _generatorFeatures, _formatGeneratorFeatures, _objectCodec, _decorate(out, ctxt)); + } + + /** + * Method for constructing {@link JsonGenerator} for generating CBOR-encoded output. + * + *

Since CBOR format always uses UTF-8 internally, no encoding need to be passed to this + * method. + */ + @Override + public RadixCBORGenerator createGenerator(OutputStream out) throws IOException { + final IOContext ctxt = _createContext(out, false); + return _createCBORGenerator( + ctxt, _generatorFeatures, _formatGeneratorFeatures, _objectCodec, _decorate(out, ctxt)); + } + + /* + /****************************************************** + /* Overridden internal factory methods + /****************************************************** + */ + + @Override + protected IOContext _createContext(Object srcRef, boolean resourceManaged) { + return super._createContext(srcRef, resourceManaged); + } + + /** Overridable factory method that actually instantiates desired parser. */ + @Override + protected CBORParser _createParser(InputStream in, IOContext ctxt) throws IOException { + return new CBORParserBootstrapper(ctxt, in) + .constructParser( + _factoryFeatures, + _parserFeatures, + _formatParserFeatures, + _objectCodec, + _byteSymbolCanonicalizer); + } + + /** Overridable factory method that actually instantiates desired parser. */ + @Override + protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { + return _nonByteSource(); + } + + @Override + protected JsonParser _createParser( + char[] data, int offset, int len, IOContext ctxt, boolean recyclable) throws IOException { + return _nonByteSource(); + } + + /** Overridable factory method that actually instantiates desired parser. */ + @Override + protected CBORParser _createParser(byte[] data, int offset, int len, IOContext ctxt) + throws IOException { + return new CBORParserBootstrapper(ctxt, data, offset, len) + .constructParser( + _factoryFeatures, + _parserFeatures, + _formatParserFeatures, + _objectCodec, + _byteSymbolCanonicalizer); + } + + @Override + protected RadixCBORGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { + return _nonByteTarget(); + } + + @Override + protected RadixCBORGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) + throws IOException { + return _createCBORGenerator( + ctxt, _generatorFeatures, _formatGeneratorFeatures, _objectCodec, out); + } + + @Override + protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) + throws IOException { + return _nonByteTarget(); + } + + private RadixCBORGenerator _createCBORGenerator( + IOContext ctxt, int stdFeat, int formatFeat, ObjectCodec codec, OutputStream out) + throws IOException { + // false -> we won't manage the stream unless explicitly directed to + RadixCBORGenerator gen = new RadixCBORGenerator(ctxt, stdFeat, formatFeat, _objectCodec, out); + if (RadixCBORGenerator.Feature.WRITE_TYPE_HEADER.enabledIn(formatFeat)) { + gen.writeTag(CBORConstants.TAG_ID_SELF_DESCRIBE); + } + return gen; + } + + protected T _nonByteSource() { + throw new UnsupportedOperationException("Can not create parser for non-byte-based source"); + } + + protected T _nonByteTarget() { + throw new UnsupportedOperationException("Can not create generator for non-byte-based target"); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORGenerator.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORGenerator.java index 6273a398de..32a51a1967 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORGenerator.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixCBORGenerator.java @@ -65,32 +65,7 @@ package com.radixdlt.serialization.mapper; // Checkstyle disabled here, as this file has been imported from Jackson -//CHECKSTYLE:OFF: - -import com.fasterxml.jackson.core.Base64Variant; -import com.fasterxml.jackson.core.FormatFeature; -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonStreamContext; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.core.PrettyPrinter; -import com.fasterxml.jackson.core.SerializableString; -import com.fasterxml.jackson.core.Version; -import com.fasterxml.jackson.core.base.GeneratorBase; -import com.fasterxml.jackson.core.io.IOContext; -import com.fasterxml.jackson.core.json.JsonWriteContext; -import com.fasterxml.jackson.dataformat.cbor.CBORParser; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; +// CHECKSTYLE:OFF: import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.BYTE_ARRAY_2_ELEMENTS; import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.BYTE_ARRAY_INDEFINITE; @@ -119,1666 +94,1641 @@ import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.SUFFIX_UINT64_ELEMENTS; import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.SUFFIX_UINT8_ELEMENTS; +import com.fasterxml.jackson.core.Base64Variant; +import com.fasterxml.jackson.core.FormatFeature; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonStreamContext; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.core.base.GeneratorBase; +import com.fasterxml.jackson.core.io.IOContext; +import com.fasterxml.jackson.core.json.JsonWriteContext; +import com.fasterxml.jackson.dataformat.cbor.CBORParser; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + /** * {@link JsonGenerator} implementation that writes CBOR encoded content. - *

- * NOTE: This code has been adapted from the Jackson CBOR class - * {@link com.fasterxml.jackson.dataformat.cbor.CBORGenerator}. - * The only changes have been to change {@code _writeChunkedString(...)} - * to the slower but more canonical {@code writeLongString(...)}. - *

- * The - * Jackson-dataformats-binary package, and therefore this file, is - * published under the - * Apache License 2.0 + * + *

NOTE: This code has been adapted from the Jackson CBOR class {@link + * com.fasterxml.jackson.dataformat.cbor.CBORGenerator}. The only changes have been to change {@code + * _writeChunkedString(...)} to the slower but more canonical {@code writeLongString(...)}. + * + *

The + * Jackson-dataformats-binary package, and therefore this file, is published under the Apache License 2.0 * * @author Tatu Saloranta (original Jackson file) */ -public class RadixCBORGenerator extends GeneratorBase -{ - private final static int[] NO_INTS = new int[0]; - - /** - * Let's ensure that we have big enough output buffer because of safety - * margins we need for UTF-8 encoding. - */ - final static int BYTE_BUFFER_FOR_OUTPUT = 16000; - - /** - * Longest char chunk we will output is chosen so that it is guaranteed to - * fit in an empty buffer even if everything encoded in 3-byte sequences; - * but also fit two full chunks in case of single-byte (ascii) output. - */ - private final static int MAX_LONG_STRING_CHARS = (BYTE_BUFFER_FOR_OUTPUT / 4) - 4; - - /** - * This is the worst case length (in bytes) of maximum chunk we ever write. - */ - private final static int MAX_LONG_STRING_BYTES = (MAX_LONG_STRING_CHARS * 3) + 3; - - /** - * Enumeration that defines all togglable features for CBOR generator. - */ - public enum Feature implements FormatFeature { - /** - * Feature that determines whether generator should try to use smallest - * (size-wise) integer representation: if true, will use smallest - * representation that is enough to retain value; if false, will use - * length indicated by argument type (4-byte for int, - * 8-byte for long and so on). - */ - WRITE_MINIMAL_INTS(true), - - /** - * Feature that determines whether CBOR "Self-Describe Tag" (value - * 55799, encoded as 3-byte sequence of 0xD9, 0xD9, 0xF7) - * should be written at the beginning of document or not. - *

- * Default value is false meaning that type tag will not be - * written at the beginning of a new document. - * - * @since 2.5 - */ - WRITE_TYPE_HEADER(false), ; - - protected final boolean _defaultState; - protected final int _mask; - - /** - * Method that calculates bit set (flags) of all features that are - * enabled by default. - */ - public static int collectDefaults() { - int flags = 0; - for (Feature f : values()) { - if (f.enabledByDefault()) { - flags |= f.getMask(); - } - } - return flags; - } - - Feature(boolean defaultState) { - _defaultState = defaultState; - _mask = (1 << ordinal()); - } - - @Override - public boolean enabledByDefault() { - return _defaultState; - } - - @Override - public boolean enabledIn(int flags) { - return (flags & getMask()) != 0; - } - - @Override - public int getMask() { - return _mask; - } - } - - /** - * To simplify certain operations, we require output buffer length to allow - * outputting of contiguous 256 character UTF-8 encoded String value. Length - * of the longest UTF-8 code point (from Java char) is 3 bytes, and we need - * both initial token byte and single-byte end marker so we get following - * value. - *

- * Note: actually we could live with shorter one; absolute minimum would be - * for encoding 64-character Strings. - */ - private final static int MIN_BUFFER_LENGTH = (3 * 256) + 2; - - private final static long MIN_INT_AS_LONG = Integer.MIN_VALUE; - private final static long MAX_INT_AS_LONG = Integer.MAX_VALUE; - - /** - * Special value that is use to keep tracks of arrays and maps opened with infinite length - */ - private final static int INDEFINITE_LENGTH = -2; // just to allow -1 as marker for "one too many" - - /* - /********************************************************** - /* Configuration - /********************************************************** - */ - - final protected IOContext _ioContext; - - final protected OutputStream _out; - - /** - * Bit flag composed of bits that indicate which - * {@code CBORGenerator.Feature}s are enabled. - */ - protected int _formatFeatures; - - protected boolean _cfgMinimalInts; - - /* - /********************************************************** - /* Output buffering - /********************************************************** - */ - - /** - * Intermediate buffer in which contents are buffered before being written - * using {@link #_out}. - */ - protected byte[] _outputBuffer; - - /** - * Pointer to the next available byte in {@link #_outputBuffer} - */ - protected int _outputTail = 0; - - /** - * Offset to index after the last valid index in {@link #_outputBuffer}. - * Typically same as length of the buffer. - */ - protected final int _outputEnd; - - /** - * Intermediate buffer in which characters of a String are copied before - * being encoded. - */ - protected char[] _charBuffer; - - protected final int _charBufferLength; - - /** - * Let's keep track of how many bytes have been output, may prove useful - * when debugging. This does not include bytes buffered in the output - * buffer, just bytes that have been written using underlying stream writer. - */ - protected int _bytesWritten; - - /* - /********************************************************** - /* Tracking of remaining elements to write - /********************************************************** - */ - - protected int[] _elementCounts = NO_INTS; - - protected int _elementCountsPtr; - - /** - * Number of elements remaining in the current complex structure (if any), - * when writing defined-length Arrays, Objects; marker {@link #INDEFINITE_LENGTH} - * otherwise. - */ - protected int _currentRemainingElements = INDEFINITE_LENGTH; - - /* - /********************************************************** - /* Shared String detection - /********************************************************** - */ - +public class RadixCBORGenerator extends GeneratorBase { + private static final int[] NO_INTS = new int[0]; + + /** + * Let's ensure that we have big enough output buffer because of safety margins we need for UTF-8 + * encoding. + */ + static final int BYTE_BUFFER_FOR_OUTPUT = 16000; + + /** + * Longest char chunk we will output is chosen so that it is guaranteed to fit in an empty buffer + * even if everything encoded in 3-byte sequences; but also fit two full chunks in case of + * single-byte (ascii) output. + */ + private static final int MAX_LONG_STRING_CHARS = (BYTE_BUFFER_FOR_OUTPUT / 4) - 4; + + /** This is the worst case length (in bytes) of maximum chunk we ever write. */ + private static final int MAX_LONG_STRING_BYTES = (MAX_LONG_STRING_CHARS * 3) + 3; + + /** Enumeration that defines all togglable features for CBOR generator. */ + public enum Feature implements FormatFeature { /** - * Flag that indicates whether the output buffer is recycable (and needs to - * be returned to recycler once we are done) or not. - */ - protected boolean _bufferRecyclable; - - /* - /********************************************************** - /* Life-cycle - /********************************************************** + * Feature that determines whether generator should try to use smallest (size-wise) integer + * representation: if true, will use smallest representation that is enough to retain value; if + * false, will use length indicated by argument type (4-byte for int, 8-byte for + * long and so on). */ - - public RadixCBORGenerator(IOContext ctxt, int stdFeatures, int formatFeatures, - ObjectCodec codec, OutputStream out) { - super(stdFeatures, codec); - _formatFeatures = formatFeatures; - _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(formatFeatures); - _ioContext = ctxt; - _out = out; - _bufferRecyclable = true; - _outputBuffer = ctxt.allocWriteEncodingBuffer(BYTE_BUFFER_FOR_OUTPUT); - _outputEnd = _outputBuffer.length; - _charBuffer = ctxt.allocConcatBuffer(); - _charBufferLength = _charBuffer.length; - // let's just sanity check to prevent nasty odd errors - if (_outputEnd < MIN_BUFFER_LENGTH) { - throw new IllegalStateException("Internal encoding buffer length (" - + _outputEnd + ") too short, must be at least " - + MIN_BUFFER_LENGTH); - } - } + WRITE_MINIMAL_INTS(true), /** - * Alternative constructor that may be used to feed partially initialized content. + * Feature that determines whether CBOR "Self-Describe Tag" (value 55799, encoded as 3-byte + * sequence of 0xD9, 0xD9, 0xF7) should be written at the beginning of document or + * not. * - * @param outputBuffer - * Buffer to use for output before flushing to the underlying stream - * @param offset - * Offset pointing past already buffered content; that is, number - * of bytes of valid content to output, within buffer. - */ - public RadixCBORGenerator(IOContext ctxt, int stdFeatures, int formatFeatures, - ObjectCodec codec, OutputStream out, byte[] outputBuffer, - int offset, boolean bufferRecyclable) { - super(stdFeatures, codec); - _formatFeatures = formatFeatures; - _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(formatFeatures); - _ioContext = ctxt; - _out = out; - _bufferRecyclable = bufferRecyclable; - _outputTail = offset; - _outputBuffer = outputBuffer; - _outputEnd = _outputBuffer.length; - _charBuffer = ctxt.allocConcatBuffer(); - _charBufferLength = _charBuffer.length; - // let's just sanity check to prevent nasty odd errors - if (_outputEnd < MIN_BUFFER_LENGTH) { - throw new IllegalStateException("Internal encoding buffer length (" - + _outputEnd + ") too short, must be at least " - + MIN_BUFFER_LENGTH); - } - } - - /* - /********************************************************** - /* Versioned - /********************************************************** - */ - - @Override - public Version version() { - return PackageVersion.VERSION; - } - - /* - /********************************************************** - /* Capability introspection - /********************************************************** - */ - - @Override - public boolean canWriteBinaryNatively() { - return true; - } - - /* - /********************************************************** - /* Overridden methods, configuration - /********************************************************** - */ - - /** - * No way (or need) to indent anything, so let's block any attempts. (should - * we throw an exception instead?) + *

Default value is false meaning that type tag will not be written at the + * beginning of a new document. + * + * @since 2.5 */ - @Override - public JsonGenerator useDefaultPrettyPrinter() { - return this; - } + WRITE_TYPE_HEADER(false), + ; - /** - * No way (or need) to indent anything, so let's block any attempts. (should - * we throw an exception instead?) - */ - @Override - public JsonGenerator setPrettyPrinter(PrettyPrinter pp) { - return this; - } + protected final boolean _defaultState; + protected final int _mask; - @Override - public Object getOutputTarget() { - return _out; + /** Method that calculates bit set (flags) of all features that are enabled by default. */ + public static int collectDefaults() { + int flags = 0; + for (Feature f : values()) { + if (f.enabledByDefault()) { + flags |= f.getMask(); + } + } + return flags; } - @Override - public int getOutputBuffered() { - return _outputTail; + Feature(boolean defaultState) { + _defaultState = defaultState; + _mask = (1 << ordinal()); } - // public JsonParser overrideStdFeatures(int values, int mask) - @Override - public int getFormatFeatures() { - return _formatFeatures; + public boolean enabledByDefault() { + return _defaultState; } @Override - public JsonGenerator overrideStdFeatures(int values, int mask) { - int oldState = _features; - int newState = (oldState & ~mask) | (values & mask); - if (oldState != newState) { - _features = newState; - } - return this; + public boolean enabledIn(int flags) { + return (flags & getMask()) != 0; } @Override - public JsonGenerator overrideFormatFeatures(int values, int mask) { - int oldState = _formatFeatures; - int newState = (_formatFeatures & ~mask) | (values & mask); - if (oldState != newState) { - _formatFeatures = newState; - _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(newState); - } - return this; - } - + public int getMask() { + return _mask; + } + } + + /** + * To simplify certain operations, we require output buffer length to allow outputting of + * contiguous 256 character UTF-8 encoded String value. Length of the longest UTF-8 code point + * (from Java char) is 3 bytes, and we need both initial token byte and single-byte end marker so + * we get following value. + * + *

Note: actually we could live with shorter one; absolute minimum would be for encoding + * 64-character Strings. + */ + private static final int MIN_BUFFER_LENGTH = (3 * 256) + 2; + + private static final long MIN_INT_AS_LONG = Integer.MIN_VALUE; + private static final long MAX_INT_AS_LONG = Integer.MAX_VALUE; + + /** Special value that is use to keep tracks of arrays and maps opened with infinite length */ + private static final int INDEFINITE_LENGTH = -2; // just to allow -1 as marker for "one too many" + + /* + /********************************************************** + /* Configuration + /********************************************************** + */ + + protected final IOContext _ioContext; + + protected final OutputStream _out; + + /** Bit flag composed of bits that indicate which {@code CBORGenerator.Feature}s are enabled. */ + protected int _formatFeatures; + + protected boolean _cfgMinimalInts; + + /* + /********************************************************** + /* Output buffering + /********************************************************** + */ + + /** + * Intermediate buffer in which contents are buffered before being written using {@link #_out}. + */ + protected byte[] _outputBuffer; + + /** Pointer to the next available byte in {@link #_outputBuffer} */ + protected int _outputTail = 0; + + /** + * Offset to index after the last valid index in {@link #_outputBuffer}. Typically same as length + * of the buffer. + */ + protected final int _outputEnd; + + /** Intermediate buffer in which characters of a String are copied before being encoded. */ + protected char[] _charBuffer; + + protected final int _charBufferLength; + + /** + * Let's keep track of how many bytes have been output, may prove useful when debugging. This does + * not include bytes buffered in the output buffer, just bytes that have been written using + * underlying stream writer. + */ + protected int _bytesWritten; + + /* + /********************************************************** + /* Tracking of remaining elements to write + /********************************************************** + */ + + protected int[] _elementCounts = NO_INTS; + + protected int _elementCountsPtr; + + /** + * Number of elements remaining in the current complex structure (if any), when writing + * defined-length Arrays, Objects; marker {@link #INDEFINITE_LENGTH} otherwise. + */ + protected int _currentRemainingElements = INDEFINITE_LENGTH; + + /* + /********************************************************** + /* Shared String detection + /********************************************************** + */ + + /** + * Flag that indicates whether the output buffer is recycable (and needs to be returned to + * recycler once we are done) or not. + */ + protected boolean _bufferRecyclable; + + /* + /********************************************************** + /* Life-cycle + /********************************************************** + */ + + public RadixCBORGenerator( + IOContext ctxt, int stdFeatures, int formatFeatures, ObjectCodec codec, OutputStream out) { + super(stdFeatures, codec); + _formatFeatures = formatFeatures; + _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(formatFeatures); + _ioContext = ctxt; + _out = out; + _bufferRecyclable = true; + _outputBuffer = ctxt.allocWriteEncodingBuffer(BYTE_BUFFER_FOR_OUTPUT); + _outputEnd = _outputBuffer.length; + _charBuffer = ctxt.allocConcatBuffer(); + _charBufferLength = _charBuffer.length; + // let's just sanity check to prevent nasty odd errors + if (_outputEnd < MIN_BUFFER_LENGTH) { + throw new IllegalStateException( + "Internal encoding buffer length (" + + _outputEnd + + ") too short, must be at least " + + MIN_BUFFER_LENGTH); + } + } + + /** + * Alternative constructor that may be used to feed partially initialized content. + * + * @param outputBuffer Buffer to use for output before flushing to the underlying stream + * @param offset Offset pointing past already buffered content; that is, number of bytes of valid + * content to output, within buffer. + */ + public RadixCBORGenerator( + IOContext ctxt, + int stdFeatures, + int formatFeatures, + ObjectCodec codec, + OutputStream out, + byte[] outputBuffer, + int offset, + boolean bufferRecyclable) { + super(stdFeatures, codec); + _formatFeatures = formatFeatures; + _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(formatFeatures); + _ioContext = ctxt; + _out = out; + _bufferRecyclable = bufferRecyclable; + _outputTail = offset; + _outputBuffer = outputBuffer; + _outputEnd = _outputBuffer.length; + _charBuffer = ctxt.allocConcatBuffer(); + _charBufferLength = _charBuffer.length; + // let's just sanity check to prevent nasty odd errors + if (_outputEnd < MIN_BUFFER_LENGTH) { + throw new IllegalStateException( + "Internal encoding buffer length (" + + _outputEnd + + ") too short, must be at least " + + MIN_BUFFER_LENGTH); + } + } + + /* + /********************************************************** + /* Versioned + /********************************************************** + */ + + @Override + public Version version() { + return PackageVersion.VERSION; + } + + /* + /********************************************************** + /* Capability introspection + /********************************************************** + */ + + @Override + public boolean canWriteBinaryNatively() { + return true; + } + + /* + /********************************************************** + /* Overridden methods, configuration + /********************************************************** + */ + + /** + * No way (or need) to indent anything, so let's block any attempts. (should we throw an exception + * instead?) + */ + @Override + public JsonGenerator useDefaultPrettyPrinter() { + return this; + } + + /** + * No way (or need) to indent anything, so let's block any attempts. (should we throw an exception + * instead?) + */ + @Override + public JsonGenerator setPrettyPrinter(PrettyPrinter pp) { + return this; + } + + @Override + public Object getOutputTarget() { + return _out; + } + + @Override + public int getOutputBuffered() { + return _outputTail; + } + + // public JsonParser overrideStdFeatures(int values, int mask) + + @Override + public int getFormatFeatures() { + return _formatFeatures; + } + + @Override + public JsonGenerator overrideStdFeatures(int values, int mask) { + int oldState = _features; + int newState = (oldState & ~mask) | (values & mask); + if (oldState != newState) { + _features = newState; + } + return this; + } + + @Override + public JsonGenerator overrideFormatFeatures(int values, int mask) { + int oldState = _formatFeatures; + int newState = (_formatFeatures & ~mask) | (values & mask); + if (oldState != newState) { + _formatFeatures = newState; + _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(newState); + } + return this; + } + + /* + /********************************************************** + /* Extended API, configuration + /********************************************************** + */ + + public RadixCBORGenerator enable(Feature f) { + _formatFeatures |= f.getMask(); + if (f == Feature.WRITE_MINIMAL_INTS) { + _cfgMinimalInts = true; + } + return this; + } + + public RadixCBORGenerator disable(Feature f) { + _formatFeatures &= ~f.getMask(); + if (f == Feature.WRITE_MINIMAL_INTS) { + _cfgMinimalInts = false; + } + return this; + } + + public final boolean isEnabled(Feature f) { + return (_formatFeatures & f.getMask()) != 0; + } + + public RadixCBORGenerator configure(Feature f, boolean state) { + if (state) { + enable(f); + } else { + disable(f); + } + return this; + } + + /* + /********************************************************** + /* Overridden methods, write methods + /********************************************************** + */ + + /* + * And then methods overridden to make final, streamline some aspects... + */ + + @Override + public final void writeFieldName(String name) throws IOException { + if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) { + _reportError("Can not write a field name, expecting a value"); + } + _writeString(name); + } + + @Override + public final void writeFieldName(SerializableString name) throws IOException { + // Object is a value, need to verify it's allowed + if (_writeContext.writeFieldName(name.getValue()) == JsonWriteContext.STATUS_EXPECT_VALUE) { + _reportError("Can not write a field name, expecting a value"); + } + byte[] raw = name.asUnquotedUTF8(); + final int len = raw.length; + if (len == 0) { + _writeByte(BYTE_EMPTY_STRING); + return; + } + _writeLengthMarker(PREFIX_TYPE_TEXT, len); + _writeBytes(raw, 0, len); + } + + @Override // since 2.8 + public final void writeFieldId(long size) throws IOException { + if (_writeContext.writeFieldName(String.valueOf(size)) + == JsonWriteContext.STATUS_EXPECT_VALUE) { + _reportError("Can not write a field name, expecting a value"); + } + _writeNumberNoCheck(size); + } + + @Override + public final void writeStringField(String fieldName, String value) throws IOException { + if (_writeContext.writeFieldName(fieldName) == JsonWriteContext.STATUS_EXPECT_VALUE) { + _reportError("Can not write a field name, expecting a value"); + } + _writeString(fieldName); + // inlined from 'writeString()' + if (value == null) { + writeNull(); + return; + } + _verifyValueWrite("write String value"); + _writeString(value); + } + + /* + /********************************************************** + /* Overridden methods, copying with tag-awareness + /********************************************************** + */ + + /** Specialize {@link JsonGenerator#copyCurrentEvent} to handle tags. */ + @Override + public void copyCurrentEvent(JsonParser p) throws IOException { + maybeCopyTag(p); + super.copyCurrentEvent(p); + } + + /** Specialize {@link JsonGenerator#copyCurrentStructure} to handle tags. */ + @Override + public void copyCurrentStructure(JsonParser p) throws IOException { + maybeCopyTag(p); + super.copyCurrentStructure(p); + } + + protected void maybeCopyTag(JsonParser p) throws IOException { + if (p instanceof CBORParser) { + if (p.hasCurrentToken()) { + final int currentTag = ((CBORParser) p).getCurrentTag(); + + if (currentTag != -1) { + writeTag(currentTag); + } + } + } + } + + /* + /********************************************************** + /* Output method implementations, structural + /********************************************************** + */ + + @Override + public final void writeStartArray() throws IOException { + _verifyValueWrite("start an array"); + _writeContext = _writeContext.createChildArrayContext(); + if (_elementCountsPtr > 0) { + _pushRemainingElements(); + } + _currentRemainingElements = INDEFINITE_LENGTH; + _writeByte(BYTE_ARRAY_INDEFINITE); + } + + /* + * Unlike with JSON, this method is using slightly optimized version since + * CBOR has a variant that allows embedding length in array start marker. + */ + + @Override + @SuppressWarnings("deprecation") + public void writeStartArray(int elementsToWrite) throws IOException { + _verifyValueWrite("start an array"); + _writeContext = _writeContext.createChildArrayContext(); + _pushRemainingElements(); + _currentRemainingElements = elementsToWrite; + _writeLengthMarker(PREFIX_TYPE_ARRAY, elementsToWrite); + } + + @Override + public final void writeEndArray() throws IOException { + if (!_writeContext.inArray()) { + _reportError("Current context not Array but " + _writeContext.typeDesc()); + } + closeComplexElement(); + _writeContext = _writeContext.getParent(); + } + + @Override + public final void writeStartObject() throws IOException { + _verifyValueWrite("start an object"); + _writeContext = _writeContext.createChildObjectContext(); + if (_elementCountsPtr > 0) { + _pushRemainingElements(); + } + _currentRemainingElements = INDEFINITE_LENGTH; + _writeByte(BYTE_OBJECT_INDEFINITE); + } + + @Override + // since 2.8 + public final void writeStartObject(Object forValue) throws IOException { + _verifyValueWrite("start an object"); + JsonWriteContext ctxt = _writeContext.createChildObjectContext(); + _writeContext = ctxt; + if (forValue != null) { + ctxt.setCurrentValue(forValue); + } + if (_elementCountsPtr > 0) { + _pushRemainingElements(); + } + _currentRemainingElements = INDEFINITE_LENGTH; + _writeByte(BYTE_OBJECT_INDEFINITE); + } + + public final void writeStartObject(int elementsToWrite) throws IOException { + _verifyValueWrite("start an object"); + _writeContext = _writeContext.createChildObjectContext(); + _pushRemainingElements(); + _currentRemainingElements = elementsToWrite; + _writeLengthMarker(PREFIX_TYPE_OBJECT, elementsToWrite); + } + + @Override + public final void writeEndObject() throws IOException { + if (!_writeContext.inObject()) { + _reportError("Current context not Object but " + _writeContext.typeDesc()); + } + closeComplexElement(); + _writeContext = _writeContext.getParent(); + } + + @Override // since 2.8 + public void writeArray(int[] array, int offset, int length) throws IOException { + _verifyOffsets(array.length, offset, length); + // short-cut, do not create child array context etc + _verifyValueWrite("write int array"); + _writeLengthMarker(PREFIX_TYPE_ARRAY, length); + for (int i = offset, end = offset + length; i < end; ++i) { + _writeNumberNoCheck(array[i]); + } + } + + @Override // since 2.8 + public void writeArray(long[] array, int offset, int length) throws IOException { + _verifyOffsets(array.length, offset, length); + // short-cut, do not create child array context etc + _verifyValueWrite("write int array"); + _writeLengthMarker(PREFIX_TYPE_ARRAY, length); + for (int i = offset, end = offset + length; i < end; ++i) { + _writeNumberNoCheck(array[i]); + } + } + + @Override // since 2.8 + public void writeArray(double[] array, int offset, int length) throws IOException { + _verifyOffsets(array.length, offset, length); + // short-cut, do not create child array context etc + _verifyValueWrite("write int array"); + _writeLengthMarker(PREFIX_TYPE_ARRAY, length); + for (int i = offset, end = offset + length; i < end; ++i) { + _writeNumberNoCheck(array[i]); + } + } + + // @since 2.8.8 + private final void _pushRemainingElements() { + if (_elementCounts.length == _elementCountsPtr) { // initially, as well as if full + _elementCounts = Arrays.copyOf(_elementCounts, _elementCounts.length + 10); + } + _elementCounts[_elementCountsPtr++] = _currentRemainingElements; + } + + private final void _writeNumberNoCheck(int i) throws IOException { + int marker; + if (i < 0) { + i = -i - 1; + marker = PREFIX_TYPE_INT_NEG; + } else { + marker = PREFIX_TYPE_INT_POS; + } + + // if ((_outputTail + needed) >= _outputEnd) { _flushBuffer(); } + _ensureRoomForOutput(5); + + byte b0; + if (_cfgMinimalInts) { + if (i < 24) { + _outputBuffer[_outputTail++] = (byte) (marker + i); + return; + } + if (i <= 0xFF) { + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT8_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) i; + return; + } + b0 = (byte) i; + i >>= 8; + if (i <= 0xFF) { + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT16_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) i; + _outputBuffer[_outputTail++] = b0; + return; + } + } else { + b0 = (byte) i; + i >>= 8; + } + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT32_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + _outputBuffer[_outputTail++] = b0; + } + + private final void _writeNumberNoCheck(long l) throws IOException { + if (_cfgMinimalInts) { + if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) { + _writeNumberNoCheck((int) l); + return; + } + } + _ensureRoomForOutput(9); + if (l < 0L) { + l += 1; + l = -l; + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); + } else { + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); + } + int i = (int) (l >> 32); + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + i = (int) l; + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + } + + private final void _writeNumberNoCheck(double d) throws IOException { + _ensureRoomForOutput(11); + // 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems + // more accurate to use exact representation; and possibly faster. + // However, if there are cases where collapsing of NaN was needed (for + // non-Java clients), this can be changed + long l = Double.doubleToRawLongBits(d); + _outputBuffer[_outputTail++] = BYTE_FLOAT64; + + int i = (int) (l >> 32); + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + i = (int) l; + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + } + + /* + /*********************************************************** + /* Output method implementations, textual + /*********************************************************** + */ + + @Override + public void writeString(String text) throws IOException { + if (text == null) { + writeNull(); + return; + } + _verifyValueWrite("write String value"); + _writeString(text); + } + + @Override + public final void writeString(SerializableString sstr) throws IOException { + _verifyValueWrite("write String value"); + byte[] raw = sstr.asUnquotedUTF8(); + final int len = raw.length; + if (len == 0) { + _writeByte(BYTE_EMPTY_STRING); + return; + } + _writeLengthMarker(PREFIX_TYPE_TEXT, len); + _writeBytes(raw, 0, len); + } + + @Override + public void writeString(char[] text, int offset, int len) throws IOException { + _verifyValueWrite("write String value"); + if (len == 0) { + _writeByte(BYTE_EMPTY_STRING); + return; + } + _writeString(text, offset, len); + } + + @Override + public void writeRawUTF8String(byte[] raw, int offset, int len) throws IOException { + _verifyValueWrite("write String value"); + if (len == 0) { + _writeByte(BYTE_EMPTY_STRING); + return; + } + _writeLengthMarker(PREFIX_TYPE_TEXT, len); + _writeBytes(raw, 0, len); + } + + @Override + public final void writeUTF8String(byte[] text, int offset, int len) throws IOException { + // Since no escaping is needed, same as 'writeRawUTF8String' + writeRawUTF8String(text, offset, len); + } + + /* + /********************************************************** + /* Output method implementations, unprocessed ("raw") + /********************************************************** + */ + + @Override + public void writeRaw(String text) throws IOException { + throw _notSupported(); + } + + @Override + public void writeRaw(String text, int offset, int len) throws IOException { + throw _notSupported(); + } + + @Override + public void writeRaw(char[] text, int offset, int len) throws IOException { + throw _notSupported(); + } + + @Override + public void writeRaw(char c) throws IOException { + throw _notSupported(); + } + + @Override + public void writeRawValue(String text) throws IOException { + throw _notSupported(); + } + + @Override + public void writeRawValue(String text, int offset, int len) throws IOException { + throw _notSupported(); + } + + @Override + public void writeRawValue(char[] text, int offset, int len) throws IOException { + throw _notSupported(); + } + + /* + * /********************************************************** /* Output + * method implementations, base64-encoded binary + * /********************************************************** + */ + + @Override + public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) + throws IOException { + if (data == null) { + writeNull(); + return; + } + _verifyValueWrite("write Binary value"); + _writeLengthMarker(PREFIX_TYPE_BYTES, len); + _writeBytes(data, offset, len); + } + + @Override + public int writeBinary(InputStream data, int dataLength) throws IOException { /* - /********************************************************** - /* Extended API, configuration - /********************************************************** + * 28-Mar-2014, tatu: Theoretically we could implement encoder that uses + * chunking to output binary content of unknown (a priori) length. But + * for no let's require knowledge of length, for simplicity: may be + * revisited in future. */ - - public RadixCBORGenerator enable(Feature f) { - _formatFeatures |= f.getMask(); - if (f == Feature.WRITE_MINIMAL_INTS) { - _cfgMinimalInts = true; - } - return this; - } - - public RadixCBORGenerator disable(Feature f) { - _formatFeatures &= ~f.getMask(); - if (f == Feature.WRITE_MINIMAL_INTS) { - _cfgMinimalInts = false; - } - return this; - } - - public final boolean isEnabled(Feature f) { - return (_formatFeatures & f.getMask()) != 0; - } - - public RadixCBORGenerator configure(Feature f, boolean state) { - if (state) { - enable(f); - } else { - disable(f); - } - return this; - } - + if (dataLength < 0) { + throw new UnsupportedOperationException("Must pass actual length for CBOR encoded data"); + } + _verifyValueWrite("write Binary value"); + int missing; + + _writeLengthMarker(PREFIX_TYPE_BYTES, dataLength); + missing = _writeBytes(data, dataLength); + if (missing > 0) { + _reportError( + "Too few bytes available: missing " + missing + " bytes (out of " + dataLength + ")"); + } + return dataLength; + } + + @Override + public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) + throws IOException { + return writeBinary(data, dataLength); + } + + /* + /********************************************************** + /* Output method implementations, primitive + /********************************************************** + */ + + @Override + public void writeBoolean(boolean state) throws IOException { + _verifyValueWrite("write boolean value"); + if (state) { + _writeByte(BYTE_TRUE); + } else { + _writeByte(BYTE_FALSE); + } + } + + @Override + public void writeNull() throws IOException { + _verifyValueWrite("write null value"); + _writeByte(BYTE_NULL); + } + + @Override + public void writeNumber(int i) throws IOException { + _verifyValueWrite("write number"); + int marker; + if (i < 0) { + i = -i - 1; + marker = PREFIX_TYPE_INT_NEG; + } else { + marker = PREFIX_TYPE_INT_POS; + } + _ensureRoomForOutput(5); + byte b0; + if (_cfgMinimalInts) { + if (i < 24) { + _outputBuffer[_outputTail++] = (byte) (marker + i); + return; + } + if (i <= 0xFF) { + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT8_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) i; + return; + } + b0 = (byte) i; + i >>= 8; + if (i <= 0xFF) { + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT16_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) i; + _outputBuffer[_outputTail++] = b0; + return; + } + } else { + b0 = (byte) i; + i >>= 8; + } + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT32_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + _outputBuffer[_outputTail++] = b0; + } + + @Override + public void writeNumber(long l) throws IOException { + if (_cfgMinimalInts) { + // First: maybe 32 bits is enough? + if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) { + writeNumber((int) l); + return; + } + } + _verifyValueWrite("write number"); + _ensureRoomForOutput(9); + if (l < 0L) { + l += 1; + l = -l; + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); + } else { + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); + } + int i = (int) (l >> 32); + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + i = (int) l; + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + } + + @Override + public void writeNumber(BigInteger v) throws IOException { + if (v == null) { + writeNull(); + return; + } + _verifyValueWrite("write number"); + _write(v); + } + + // Main write method isolated so that it can be called directly + // in cases where that is needed (to encode BigDecimal) + protected void _write(BigInteger v) throws IOException { /* - /********************************************************** - /* Overridden methods, write methods - /********************************************************** + * Supported by using type tags, as per spec: major type for tag '6'; 5 + * LSB either 2 for positive bignum or 3 for negative bignum. And then + * byte sequence that encode variable length integer. */ - + if (v.signum() < 0) { + _writeByte(BYTE_TAG_BIGNUM_NEG); + v = v.negate(); + } else { + _writeByte(BYTE_TAG_BIGNUM_POS); + } + byte[] data = v.toByteArray(); + final int len = data.length; + _writeLengthMarker(PREFIX_TYPE_BYTES, len); + _writeBytes(data, 0, len); + } + + @Override + public void writeNumber(double d) throws IOException { + _verifyValueWrite("write number"); + _ensureRoomForOutput(11); /* - * And then methods overridden to make final, streamline some aspects... + * 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems + * more accurate to use exact representation; and possibly faster. + * However, if there are cases where collapsing of NaN was needed (for + * non-Java clients), this can be changed */ - - @Override - public final void writeFieldName(String name) throws IOException { - if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) { - _reportError("Can not write a field name, expecting a value"); - } - _writeString(name); - } - - @Override - public final void writeFieldName(SerializableString name) - throws IOException { - // Object is a value, need to verify it's allowed - if (_writeContext.writeFieldName(name.getValue()) == JsonWriteContext.STATUS_EXPECT_VALUE) { - _reportError("Can not write a field name, expecting a value"); - } - byte[] raw = name.asUnquotedUTF8(); - final int len = raw.length; - if (len == 0) { - _writeByte(BYTE_EMPTY_STRING); - return; - } - _writeLengthMarker(PREFIX_TYPE_TEXT, len); - _writeBytes(raw, 0, len); - } - - @Override // since 2.8 - public final void writeFieldId(long size) throws IOException { - if (_writeContext.writeFieldName(String.valueOf(size)) == JsonWriteContext.STATUS_EXPECT_VALUE) { - _reportError("Can not write a field name, expecting a value"); - } - _writeNumberNoCheck(size); - } - - @Override - public final void writeStringField(String fieldName, String value) - throws IOException - { - if (_writeContext.writeFieldName(fieldName) == JsonWriteContext.STATUS_EXPECT_VALUE) { - _reportError("Can not write a field name, expecting a value"); - } - _writeString(fieldName); - // inlined from 'writeString()' - if (value == null) { - writeNull(); - return; - } - _verifyValueWrite("write String value"); - _writeString(value); - } + long l = Double.doubleToRawLongBits(d); + _outputBuffer[_outputTail++] = BYTE_FLOAT64; + + int i = (int) (l >> 32); + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + i = (int) l; + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + } + + @Override + public void writeNumber(float f) throws IOException { + // Ok, now, we needed token type byte plus 5 data bytes (7 bits each) + _ensureRoomForOutput(6); + _verifyValueWrite("write number"); /* - /********************************************************** - /* Overridden methods, copying with tag-awareness - /********************************************************** - */ - - /** - * Specialize {@link JsonGenerator#copyCurrentEvent} to handle tags. - */ - @Override - public void copyCurrentEvent(JsonParser p) throws IOException { - maybeCopyTag(p); - super.copyCurrentEvent(p); - } - - /** - * Specialize {@link JsonGenerator#copyCurrentStructure} to handle tags. + * 17-Apr-2010, tatu: could also use 'floatToIntBits', but it seems more + * accurate to use exact representation; and possibly faster. However, + * if there are cases where collapsing of NaN was needed (for non-Java + * clients), this can be changed */ - @Override - public void copyCurrentStructure(JsonParser p) throws IOException { - maybeCopyTag(p); - super.copyCurrentStructure(p); - } - - protected void maybeCopyTag(JsonParser p) throws IOException { - if (p instanceof CBORParser) { - if (p.hasCurrentToken()) { - final int currentTag = ((CBORParser) p).getCurrentTag(); - - if (currentTag != -1) { - writeTag(currentTag); - } - } - } - } - - /* - /********************************************************** - /* Output method implementations, structural - /********************************************************** + int i = Float.floatToRawIntBits(f); + _outputBuffer[_outputTail++] = BYTE_FLOAT32; + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + } + + @Override + public void writeNumber(BigDecimal dec) throws IOException { + if (dec == null) { + writeNull(); + return; + } + _verifyValueWrite("write number"); + /* Supported by using type tags, as per spec: major type for tag '6'; 5 + * LSB 4. And then a two-element array; integer exponent, and int/bigint + * mantissa */ - - @Override - public final void writeStartArray() throws IOException { - _verifyValueWrite("start an array"); - _writeContext = _writeContext.createChildArrayContext(); - if (_elementCountsPtr > 0) { - _pushRemainingElements(); - } - _currentRemainingElements = INDEFINITE_LENGTH; - _writeByte(BYTE_ARRAY_INDEFINITE); - } - - /* - * Unlike with JSON, this method is using slightly optimized version since - * CBOR has a variant that allows embedding length in array start marker. + // 12-May-2016, tatu: Before 2.8, used "bigfloat", but that was + // incorrect... + _writeByte(BYTE_TAG_DECIMAL_FRACTION); + _writeByte(BYTE_ARRAY_2_ELEMENTS); + + int scale = dec.scale(); + _writeIntValue(scale); + /* Hmmmh. Specification suggest use of regular integer for mantissa. But + * if it doesn't fit, use "bignum" */ - - @Override - @SuppressWarnings("deprecation") - public void writeStartArray(int elementsToWrite) throws IOException { - _verifyValueWrite("start an array"); - _writeContext = _writeContext.createChildArrayContext(); - _pushRemainingElements(); - _currentRemainingElements = elementsToWrite; - _writeLengthMarker(PREFIX_TYPE_ARRAY, elementsToWrite); - } - - @Override - public final void writeEndArray() throws IOException { - if (!_writeContext.inArray()) { - _reportError("Current context not Array but "+_writeContext.typeDesc()); - } - closeComplexElement(); - _writeContext = _writeContext.getParent(); - } - - @Override - public final void writeStartObject() throws IOException { - _verifyValueWrite("start an object"); - _writeContext = _writeContext.createChildObjectContext(); - if (_elementCountsPtr > 0) { - _pushRemainingElements(); - } - _currentRemainingElements = INDEFINITE_LENGTH; - _writeByte(BYTE_OBJECT_INDEFINITE); - } - - @Override - // since 2.8 - public final void writeStartObject(Object forValue) throws IOException { - _verifyValueWrite("start an object"); - JsonWriteContext ctxt = _writeContext.createChildObjectContext(); - _writeContext = ctxt; - if (forValue != null) { - ctxt.setCurrentValue(forValue); - } - if (_elementCountsPtr > 0) { - _pushRemainingElements(); - } - _currentRemainingElements = INDEFINITE_LENGTH; - _writeByte(BYTE_OBJECT_INDEFINITE); - } - - public final void writeStartObject(int elementsToWrite) throws IOException { - _verifyValueWrite("start an object"); - _writeContext = _writeContext.createChildObjectContext(); - _pushRemainingElements(); - _currentRemainingElements = elementsToWrite; - _writeLengthMarker(PREFIX_TYPE_OBJECT, elementsToWrite); - } - - @Override - public final void writeEndObject() throws IOException { - if (!_writeContext.inObject()) { - _reportError("Current context not Object but "+ _writeContext.typeDesc()); - } - closeComplexElement(); - _writeContext = _writeContext.getParent(); - } - - @Override // since 2.8 - public void writeArray(int[] array, int offset, int length) throws IOException - { - _verifyOffsets(array.length, offset, length); - // short-cut, do not create child array context etc - _verifyValueWrite("write int array"); - _writeLengthMarker(PREFIX_TYPE_ARRAY, length); - for (int i = offset, end = offset+length; i < end; ++i) { - _writeNumberNoCheck(array[i]); - } - } - - @Override // since 2.8 - public void writeArray(long[] array, int offset, int length) throws IOException - { - _verifyOffsets(array.length, offset, length); - // short-cut, do not create child array context etc - _verifyValueWrite("write int array"); - _writeLengthMarker(PREFIX_TYPE_ARRAY, length); - for (int i = offset, end = offset+length; i < end; ++i) { - _writeNumberNoCheck(array[i]); - } - } - - @Override // since 2.8 - public void writeArray(double[] array, int offset, int length) throws IOException - { - _verifyOffsets(array.length, offset, length); - // short-cut, do not create child array context etc - _verifyValueWrite("write int array"); - _writeLengthMarker(PREFIX_TYPE_ARRAY, length); - for (int i = offset, end = offset+length; i < end; ++i) { - _writeNumberNoCheck(array[i]); - } - } - - // @since 2.8.8 - private final void _pushRemainingElements() { - if (_elementCounts.length == _elementCountsPtr) { // initially, as well as if full - _elementCounts = Arrays.copyOf(_elementCounts, _elementCounts.length+10); - } - _elementCounts[_elementCountsPtr++] = _currentRemainingElements; - } - - private final void _writeNumberNoCheck(int i) throws IOException { - int marker; - if (i < 0) { - i = -i - 1; - marker = PREFIX_TYPE_INT_NEG; - } else { - marker = PREFIX_TYPE_INT_POS; - } - - // if ((_outputTail + needed) >= _outputEnd) { _flushBuffer(); } - _ensureRoomForOutput(5); - - byte b0; - if (_cfgMinimalInts) { - if (i < 24) { - _outputBuffer[_outputTail++] = (byte) (marker + i); - return; - } - if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT8_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) i; - return; - } - b0 = (byte) i; - i >>= 8; - if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT16_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) i; - _outputBuffer[_outputTail++] = b0; - return; - } + BigInteger unscaled = dec.unscaledValue(); + int bitLength = unscaled.bitLength(); + if (bitLength <= 31) { + _writeIntValue(unscaled.intValue()); + } else if (bitLength <= 63) { + _writeLongValue(unscaled.longValue()); + } else { + _write(unscaled); + } + } + + @Override + public void writeNumber(String encodedValue) + throws IOException, JsonGenerationException, UnsupportedOperationException { + // just write as a String -- CBOR does not require schema, so + // databinding + // on receiving end should be able to coerce it appropriately + writeString(encodedValue); + } + + /* + /********************************************************** + /* Implementations for other methods + /********************************************************** + */ + + @Override + protected final void _verifyValueWrite(String typeMsg) throws IOException { + int status = _writeContext.writeValue(); + if (status == JsonWriteContext.STATUS_EXPECT_NAME) { + _reportError("Can not " + typeMsg + ", expecting field name"); + } + // decrementElementsRemainingCount() + int count = _currentRemainingElements; + if (count != INDEFINITE_LENGTH) { + --count; + + // 28-Jun-2016, tatu: _Should_ check overrun immediately (instead of waiting + // for end of Object/Array), but has 10% performance penalty for some reason, + // should figure out why and how to avoid + if (count < 0) { + _failSizedArrayOrObject(); + return; // never gets here + } + _currentRemainingElements = count; + } + } + + private void _failSizedArrayOrObject() throws IOException { + _reportError( + String.format( + "%s size mismatch: number of element encoded is not equal to reported array/map size.", + _writeContext.typeDesc())); + } + + /* + /********************************************************** + /* Low-level output handling + /********************************************************** + */ + + @Override + public final void flush() throws IOException { + _flushBuffer(); + if (isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)) { + _out.flush(); + } + } + + @Override + public void close() throws IOException { + // First: let's see that we still have buffers... + if ((_outputBuffer != null) && isEnabled(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)) { + while (true) { + JsonStreamContext ctxt = getOutputContext(); + if (ctxt.inArray()) { + writeEndArray(); + } else if (ctxt.inObject()) { + writeEndObject(); } else { - b0 = (byte) i; - i >>= 8; - } - _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT32_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - _outputBuffer[_outputTail++] = b0; - } - - private final void _writeNumberNoCheck(long l) throws IOException { - if (_cfgMinimalInts) { - if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) { - _writeNumberNoCheck((int) l); - return; - } - } - _ensureRoomForOutput(9); - if (l < 0L) { - l += 1; - l = -l; - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); - } else { - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); - } - int i = (int) (l >> 32); - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - i = (int) l; - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - } - - private final void _writeNumberNoCheck(double d) throws IOException { - _ensureRoomForOutput(11); - // 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems - // more accurate to use exact representation; and possibly faster. - // However, if there are cases where collapsing of NaN was needed (for - // non-Java clients), this can be changed - long l = Double.doubleToRawLongBits(d); - _outputBuffer[_outputTail++] = BYTE_FLOAT64; - - int i = (int) (l >> 32); - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - i = (int) l; - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - } - - /* - /*********************************************************** - /* Output method implementations, textual - /*********************************************************** - */ - - @Override - public void writeString(String text) throws IOException { - if (text == null) { - writeNull(); - return; - } - _verifyValueWrite("write String value"); - _writeString(text); - } - - @Override - public final void writeString(SerializableString sstr) throws IOException { - _verifyValueWrite("write String value"); - byte[] raw = sstr.asUnquotedUTF8(); - final int len = raw.length; - if (len == 0) { - _writeByte(BYTE_EMPTY_STRING); - return; - } - _writeLengthMarker(PREFIX_TYPE_TEXT, len); - _writeBytes(raw, 0, len); - } - - @Override - public void writeString(char[] text, int offset, int len) - throws IOException { - _verifyValueWrite("write String value"); - if (len == 0) { - _writeByte(BYTE_EMPTY_STRING); - return; - } - _writeString(text, offset, len); - } - - @Override - public void writeRawUTF8String(byte[] raw, int offset, int len) - throws IOException - { - _verifyValueWrite("write String value"); - if (len == 0) { - _writeByte(BYTE_EMPTY_STRING); - return; - } - _writeLengthMarker(PREFIX_TYPE_TEXT, len); - _writeBytes(raw, 0, len); - } - - @Override - public final void writeUTF8String(byte[] text, int offset, int len) - throws IOException { - // Since no escaping is needed, same as 'writeRawUTF8String' - writeRawUTF8String(text, offset, len); - } - - /* - /********************************************************** - /* Output method implementations, unprocessed ("raw") - /********************************************************** - */ - - @Override - public void writeRaw(String text) throws IOException { - throw _notSupported(); - } - - @Override - public void writeRaw(String text, int offset, int len) throws IOException { - throw _notSupported(); - } - - @Override - public void writeRaw(char[] text, int offset, int len) throws IOException { - throw _notSupported(); - } - - @Override - public void writeRaw(char c) throws IOException { - throw _notSupported(); - } - - @Override - public void writeRawValue(String text) throws IOException { - throw _notSupported(); - } - - @Override - public void writeRawValue(String text, int offset, int len) - throws IOException { - throw _notSupported(); - } - - @Override - public void writeRawValue(char[] text, int offset, int len) - throws IOException { - throw _notSupported(); - } - - /* - * /********************************************************** /* Output - * method implementations, base64-encoded binary - * /********************************************************** - */ - - @Override - public void writeBinary(Base64Variant b64variant, byte[] data, int offset, - int len) throws IOException { - if (data == null) { - writeNull(); - return; - } - _verifyValueWrite("write Binary value"); - _writeLengthMarker(PREFIX_TYPE_BYTES, len); - _writeBytes(data, offset, len); - } - - @Override - public int writeBinary(InputStream data, int dataLength) throws IOException { - /* - * 28-Mar-2014, tatu: Theoretically we could implement encoder that uses - * chunking to output binary content of unknown (a priori) length. But - * for no let's require knowledge of length, for simplicity: may be - * revisited in future. - */ - if (dataLength < 0) { - throw new UnsupportedOperationException( - "Must pass actual length for CBOR encoded data"); - } - _verifyValueWrite("write Binary value"); - int missing; - - _writeLengthMarker(PREFIX_TYPE_BYTES, dataLength); - missing = _writeBytes(data, dataLength); - if (missing > 0) { - _reportError("Too few bytes available: missing " + missing - + " bytes (out of " + dataLength + ")"); - } - return dataLength; - } - - @Override - public int writeBinary(Base64Variant b64variant, InputStream data, - int dataLength) throws IOException { - return writeBinary(data, dataLength); - } - - /* - /********************************************************** - /* Output method implementations, primitive - /********************************************************** - */ - - @Override - public void writeBoolean(boolean state) throws IOException { - _verifyValueWrite("write boolean value"); - if (state) { - _writeByte(BYTE_TRUE); - } else { - _writeByte(BYTE_FALSE); - } - } - - @Override - public void writeNull() throws IOException { - _verifyValueWrite("write null value"); - _writeByte(BYTE_NULL); - } - - @Override - public void writeNumber(int i) throws IOException { - _verifyValueWrite("write number"); - int marker; - if (i < 0) { - i = -i - 1; - marker = PREFIX_TYPE_INT_NEG; - } else { - marker = PREFIX_TYPE_INT_POS; - } - _ensureRoomForOutput(5); - byte b0; - if (_cfgMinimalInts) { - if (i < 24) { - _outputBuffer[_outputTail++] = (byte) (marker + i); - return; - } - if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT8_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) i; - return; - } - b0 = (byte) i; - i >>= 8; - if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT16_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) i; - _outputBuffer[_outputTail++] = b0; - return; - } - } else { - b0 = (byte) i; - i >>= 8; - } - _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT32_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - _outputBuffer[_outputTail++] = b0; - } - - @Override - public void writeNumber(long l) throws IOException { - if (_cfgMinimalInts) { - // First: maybe 32 bits is enough? - if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) { - writeNumber((int) l); - return; - } - } - _verifyValueWrite("write number"); - _ensureRoomForOutput(9); - if (l < 0L) { - l += 1; - l = -l; - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); - } else { - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); - } - int i = (int) (l >> 32); - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - i = (int) l; - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - } - - @Override - public void writeNumber(BigInteger v) throws IOException { - if (v == null) { - writeNull(); - return; - } - _verifyValueWrite("write number"); - _write(v); - } - - // Main write method isolated so that it can be called directly - // in cases where that is needed (to encode BigDecimal) - protected void _write(BigInteger v) throws IOException { - /* - * Supported by using type tags, as per spec: major type for tag '6'; 5 - * LSB either 2 for positive bignum or 3 for negative bignum. And then - * byte sequence that encode variable length integer. - */ - if (v.signum() < 0) { - _writeByte(BYTE_TAG_BIGNUM_NEG); - v = v.negate(); - } else { - _writeByte(BYTE_TAG_BIGNUM_POS); - } - byte[] data = v.toByteArray(); - final int len = data.length; - _writeLengthMarker(PREFIX_TYPE_BYTES, len); - _writeBytes(data, 0, len); - } - - @Override - public void writeNumber(double d) throws IOException { - _verifyValueWrite("write number"); - _ensureRoomForOutput(11); - /* - * 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems - * more accurate to use exact representation; and possibly faster. - * However, if there are cases where collapsing of NaN was needed (for - * non-Java clients), this can be changed - */ - long l = Double.doubleToRawLongBits(d); - _outputBuffer[_outputTail++] = BYTE_FLOAT64; - - int i = (int) (l >> 32); - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - i = (int) l; - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - } - - @Override - public void writeNumber(float f) throws IOException { - // Ok, now, we needed token type byte plus 5 data bytes (7 bits each) - _ensureRoomForOutput(6); - _verifyValueWrite("write number"); - - /* - * 17-Apr-2010, tatu: could also use 'floatToIntBits', but it seems more - * accurate to use exact representation; and possibly faster. However, - * if there are cases where collapsing of NaN was needed (for non-Java - * clients), this can be changed - */ - int i = Float.floatToRawIntBits(f); - _outputBuffer[_outputTail++] = BYTE_FLOAT32; - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - } - - @Override - public void writeNumber(BigDecimal dec) throws IOException { - if (dec == null) { - writeNull(); - return; - } - _verifyValueWrite("write number"); - /* Supported by using type tags, as per spec: major type for tag '6'; 5 - * LSB 4. And then a two-element array; integer exponent, and int/bigint - * mantissa - */ - // 12-May-2016, tatu: Before 2.8, used "bigfloat", but that was - // incorrect... - _writeByte(BYTE_TAG_DECIMAL_FRACTION); - _writeByte(BYTE_ARRAY_2_ELEMENTS); - - int scale = dec.scale(); - _writeIntValue(scale); - /* Hmmmh. Specification suggest use of regular integer for mantissa. But - * if it doesn't fit, use "bignum" - */ - BigInteger unscaled = dec.unscaledValue(); - int bitLength = unscaled.bitLength(); - if (bitLength <= 31) { - _writeIntValue(unscaled.intValue()); - } else if (bitLength <= 63) { - _writeLongValue(unscaled.longValue()); - } else { - _write(unscaled); - } - } - - @Override - public void writeNumber(String encodedValue) throws IOException, - JsonGenerationException, UnsupportedOperationException { - // just write as a String -- CBOR does not require schema, so - // databinding - // on receiving end should be able to coerce it appropriately - writeString(encodedValue); - } - - /* - /********************************************************** - /* Implementations for other methods - /********************************************************** - */ - - @Override - protected final void _verifyValueWrite(String typeMsg) throws IOException { - int status = _writeContext.writeValue(); - if (status == JsonWriteContext.STATUS_EXPECT_NAME) { - _reportError("Can not " + typeMsg + ", expecting field name"); - } - // decrementElementsRemainingCount() - int count = _currentRemainingElements; - if (count != INDEFINITE_LENGTH) { - --count; - - // 28-Jun-2016, tatu: _Should_ check overrun immediately (instead of waiting - // for end of Object/Array), but has 10% performance penalty for some reason, - // should figure out why and how to avoid - if (count < 0) { - _failSizedArrayOrObject(); - return; // never gets here - } - _currentRemainingElements = count; - } - } - - private void _failSizedArrayOrObject() throws IOException - { - _reportError(String.format("%s size mismatch: number of element encoded is not equal to reported array/map size.", - _writeContext.typeDesc())); - } - - /* - /********************************************************** - /* Low-level output handling - /********************************************************** - */ - - @Override - public final void flush() throws IOException { - _flushBuffer(); - if (isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)) { - _out.flush(); - } - } - - @Override - public void close() throws IOException { - // First: let's see that we still have buffers... - if ((_outputBuffer != null) - && isEnabled(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)) { - while (true) { - JsonStreamContext ctxt = getOutputContext(); - if (ctxt.inArray()) { - writeEndArray(); - } else if (ctxt.inObject()) { - writeEndObject(); - } else { - break; - } - } - } - // boolean wasClosed = _closed; - super.close(); + break; + } + } + } + // boolean wasClosed = _closed; + super.close(); + _flushBuffer(); + + if (_ioContext.isResourceManaged() || isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)) { + _out.close(); + } else { + // If we can't close it, we should at least flush + _out.flush(); + } + // Internal buffer(s) generator has can now be released as well + _releaseBuffers(); + } + + /* + /********************************************************** + * Extended API, CBOR-specific encoded output + /********************************************************** + */ + + /** + * Method for writing out an explicit CBOR Tag. + * + * @param tagId Positive integer (0 or higher) + * @since 2.5 + */ + public void writeTag(int tagId) throws IOException { + if (tagId < 0) { + throw new IllegalArgumentException("Can not write negative tag ids (" + tagId + ")"); + } + _writeLengthMarker(PREFIX_TYPE_TAG, tagId); + } + + /* + /********************************************************** + /* Extended API, raw bytes (by-passing encoder) + /********************************************************** + */ + + /** + * Method for directly inserting specified byte in output at current position. + * + *

NOTE: only use this method if you really know what you are doing. + */ + public void writeRaw(byte b) throws IOException { + _writeByte(b); + } + + /** + * Method for directly inserting specified bytes in output at current position. + * + *

NOTE: only use this method if you really know what you are doing. + */ + public void writeBytes(byte[] data, int offset, int len) throws IOException { + _writeBytes(data, offset, len); + } + + /* + /********************************************************** + /* Internal methods: low-level text output + /********************************************************** + */ + + private static final int MAX_SHORT_STRING_CHARS = 23; + // in case it's > 23 bytes + private static final int MAX_SHORT_STRING_BYTES = 23 * 3 + 2; + + private static final int MAX_MEDIUM_STRING_CHARS = 255; + // in case it's > 255 bytes + private static final int MAX_MEDIUM_STRING_BYTES = 255 * 3 + 3; + + protected final void _writeString(String name) throws IOException { + int len = name.length(); + if (len == 0) { + _writeByte(BYTE_EMPTY_STRING); + return; + } + // Actually, let's not bother with copy for shortest strings + if (len <= MAX_SHORT_STRING_CHARS) { + _ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate + // length + int actual = _encode(_outputTail + 1, name, len); + final byte[] buf = _outputBuffer; + int ix = _outputTail; + if (actual <= MAX_SHORT_STRING_CHARS) { // fits in prefix byte + buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual); + _outputTail = ix + actual; + return; + } + // no, have to move. Blah. + System.arraycopy(buf, ix + 1, buf, ix + 2, actual); + buf[ix++] = BYTE_STRING_1BYTE_LEN; + buf[ix++] = (byte) actual; + _outputTail = ix + actual; + return; + } + + char[] cbuf = _charBuffer; + if (len > cbuf.length) { + _charBuffer = cbuf = new char[Math.max(_charBuffer.length + 32, len)]; + } + name.getChars(0, len, cbuf, 0); + _writeString(cbuf, 0, len); + } + + protected final void _ensureSpace(int needed) throws IOException { + if ((_outputTail + needed + 3) > _outputEnd) { + _flushBuffer(); + } + } + + protected final void _writeString(char[] text, int offset, int len) throws IOException { + if (len <= MAX_SHORT_STRING_CHARS) { // possibly short string (not necessarily) + _ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate length + int actual = _encode(_outputTail + 1, text, offset, offset + len); + final byte[] buf = _outputBuffer; + int ix = _outputTail; + if (actual <= MAX_SHORT_STRING_CHARS) { // fits in prefix byte + buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual); + _outputTail = ix + actual; + return; + } + // no, have to move. Blah. + System.arraycopy(buf, ix + 1, buf, ix + 2, actual); + buf[ix++] = BYTE_STRING_1BYTE_LEN; + buf[ix++] = (byte) actual; + _outputTail = ix + actual; + return; + } + if (len <= MAX_MEDIUM_STRING_CHARS) { + _ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can approximate + int actual = _encode(_outputTail + 2, text, offset, offset + len); + final byte[] buf = _outputBuffer; + int ix = _outputTail; + if (actual <= MAX_MEDIUM_STRING_CHARS) { // fits as expected + buf[ix++] = BYTE_STRING_1BYTE_LEN; + buf[ix++] = (byte) actual; + _outputTail = ix + actual; + return; + } + // no, have to move. Blah. + System.arraycopy(buf, ix + 2, buf, ix + 3, actual); + buf[ix++] = BYTE_STRING_2BYTE_LEN; + buf[ix++] = (byte) (actual >> 8); + buf[ix++] = (byte) actual; + _outputTail = ix + actual; + return; + } + if (len <= MAX_LONG_STRING_CHARS) { // no need to chunk yet + // otherwise, long but single chunk + _ensureSpace(MAX_LONG_STRING_BYTES); // calculate accurate length to + // avoid extra flushing + int ix = _outputTail; + int actual = _encode(ix + 3, text, offset, offset + len); + final byte[] buf = _outputBuffer; + buf[ix++] = BYTE_STRING_2BYTE_LEN; + buf[ix++] = (byte) (actual >> 8); + buf[ix++] = (byte) actual; + _outputTail = ix + actual; + return; + } + writeLongString(text, offset, len); + } + + protected final void writeLongString(char[] text, int offset, int len) throws IOException { + CharBuffer charBuffer = CharBuffer.wrap(text, offset, len); + ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); + int byteLen = byteBuffer.remaining(); + _writeLengthMarker(PREFIX_TYPE_TEXT, byteLen); + _writeBytes(byteBuffer.array(), byteBuffer.position(), byteLen); + } + + /* + /********************************************************** + /* Internal methods, UTF-8 encoding + /********************************************************** + */ + + /** + * Helper method called when the whole character sequence is known to fit in the output buffer + * regardless of UTF-8 expansion. + */ + private final int _encode(int outputPtr, char[] str, int i, int end) { + // First: let's see if it's all ASCII: that's rather fast + final byte[] outBuf = _outputBuffer; + final int outputStart = outputPtr; + do { + int c = str[i]; + if (c > 0x7F) { + return _shortUTF8Encode2(str, i, end, outputPtr, outputStart); + } + outBuf[outputPtr++] = (byte) c; + } while (++i < end); + return outputPtr - outputStart; + } + + /** + * Helper method called when the whole character sequence is known to fit in the output buffer, + * but not all characters are single-byte (ASCII) characters. + */ + private final int _shortUTF8Encode2(char[] str, int i, int end, int outputPtr, int outputStart) { + final byte[] outBuf = _outputBuffer; + while (i < end) { + int c = str[i++]; + if (c <= 0x7F) { + outBuf[outputPtr++] = (byte) c; + continue; + } + // Nope, multi-byte: + if (c < 0x800) { // 2-byte + outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6)); + outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + continue; + } + // 3 or 4 bytes (surrogate) + // Surrogates? + if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character + outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12)); + outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + continue; + } + // Yup, a surrogate pair + if (c > SURR1_LAST) { // must be from first range; second won't do + _throwIllegalSurrogate(c); + } + // ... meaning it must have a pair + if (i >= end) { + _throwIllegalSurrogate(c); + } + c = _convertSurrogate(c, str[i++]); + if (c > 0x10FFFF) { // illegal in JSON as well as in XML + _throwIllegalSurrogate(c); + } + outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18)); + outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); + outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + } + return (outputPtr - outputStart); + } + + private final int _encode(int outputPtr, String str, int len) { + final byte[] outBuf = _outputBuffer; + final int outputStart = outputPtr; + + for (int i = 0; i < len; ++i) { + int c = str.charAt(i); + if (c > 0x7F) { + return _encode2(i, outputPtr, str, len, outputStart); + } + outBuf[outputPtr++] = (byte) c; + } + return (outputPtr - outputStart); + } + + private final int _encode2(int i, int outputPtr, String str, int len, int outputStart) { + final byte[] outBuf = _outputBuffer; + // no; non-ASCII stuff, slower loop + while (i < len) { + int c = str.charAt(i++); + if (c <= 0x7F) { + outBuf[outputPtr++] = (byte) c; + continue; + } + // Nope, multi-byte: + if (c < 0x800) { // 2-byte + outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6)); + outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + continue; + } + // 3 or 4 bytes (surrogate) + // Surrogates? + if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte + // character + outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12)); + outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + continue; + } + // Yup, a surrogate pair + if (c > SURR1_LAST) { // must be from first range; second won't do + _throwIllegalSurrogate(c); + } + // ... meaning it must have a pair + if (i >= len) { + _throwIllegalSurrogate(c); + } + c = _convertSurrogate(c, str.charAt(i++)); + if (c > 0x10FFFF) { // illegal in JSON as well as in XML + _throwIllegalSurrogate(c); + } + outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18)); + outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); + outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); + } + return (outputPtr - outputStart); + } + + /** Method called to calculate UTF codepoint, from a surrogate pair. */ + private int _convertSurrogate(int firstPart, int secondPart) { + // Ok, then, is the second part valid? + if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { + throw new IllegalArgumentException( + "Broken surrogate pair: first char 0x" + + Integer.toHexString(firstPart) + + ", second 0x" + + Integer.toHexString(secondPart) + + "; illegal combination"); + } + return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST); + } + + private void _throwIllegalSurrogate(int code) { + if (code > 0x10FFFF) { // over max? + throw new IllegalArgumentException( + "Illegal character point (0x" + + Integer.toHexString(code) + + ") to output; max is 0x10FFFF as per RFC 4627"); + } + if (code >= SURR1_FIRST) { + if (code <= SURR1_LAST) { // Unmatched first part (closing without + // second part?) + throw new IllegalArgumentException( + "Unmatched first part of surrogate pair (0x" + Integer.toHexString(code) + ")"); + } + throw new IllegalArgumentException( + "Unmatched second part of surrogate pair (0x" + Integer.toHexString(code) + ")"); + } + // should we ever get this? + throw new IllegalArgumentException( + "Illegal character point (0x" + Integer.toHexString(code) + ") to output"); + } + + /* + /********************************************************** + /* Internal methods, writing bytes + /********************************************************** + */ + + private final void _ensureRoomForOutput(int needed) throws IOException { + if ((_outputTail + needed) >= _outputEnd) { + _flushBuffer(); + } + } + + private final void _writeIntValue(int i) throws IOException { + int marker; + if (i < 0) { + i = -i - 1; + marker = PREFIX_TYPE_INT_NEG; + } else { + marker = PREFIX_TYPE_INT_POS; + } + _writeLengthMarker(marker, i); + } + + private final void _writeLongValue(long l) throws IOException { + _ensureRoomForOutput(9); + if (l < 0) { + l += 1; + l = -l; + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); + } else { + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); + } + int i = (int) (l >> 32); + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + i = (int) l; + _outputBuffer[_outputTail++] = (byte) (i >> 24); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + } + + private final void _writeLengthMarker(int majorType, int i) throws IOException { + _ensureRoomForOutput(5); + if (i < 24) { + _outputBuffer[_outputTail++] = (byte) (majorType + i); + return; + } + if (i <= 0xFF) { + _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT8_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) i; + return; + } + final byte b0 = (byte) i; + i >>= 8; + if (i <= 0xFF) { + _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT16_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) i; + _outputBuffer[_outputTail++] = b0; + return; + } + _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT32_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) (i >> 16); + _outputBuffer[_outputTail++] = (byte) (i >> 8); + _outputBuffer[_outputTail++] = (byte) i; + _outputBuffer[_outputTail++] = b0; + } + + private final void _writeByte(byte b) throws IOException { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + _outputBuffer[_outputTail++] = b; + } + + /* + * private final void _writeBytes(byte b1, byte b2) throws IOException { if + * ((_outputTail + 1) >= _outputEnd) { _flushBuffer(); } + * _outputBuffer[_outputTail++] = b1; _outputBuffer[_outputTail++] = b2; } + */ + + private final void _writeBytes(byte[] data, int offset, int len) throws IOException { + if (len == 0) { + return; + } + if ((_outputTail + len) >= _outputEnd) { + _writeBytesLong(data, offset, len); + return; + } + // common case, non-empty, fits in just fine: + System.arraycopy(data, offset, _outputBuffer, _outputTail, len); + _outputTail += len; + } + + private final int _writeBytes(InputStream in, int bytesLeft) throws IOException { + while (bytesLeft > 0) { + int room = _outputEnd - _outputTail; + if (room <= 0) { _flushBuffer(); - - if (_ioContext.isResourceManaged() - || isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)) { - _out.close(); - } else { - // If we can't close it, we should at least flush - _out.flush(); - } - // Internal buffer(s) generator has can now be released as well - _releaseBuffers(); - } - - /* - /********************************************************** - * Extended API, CBOR-specific encoded output - /********************************************************** - */ - - /** - * Method for writing out an explicit CBOR Tag. - * - * @param tagId - * Positive integer (0 or higher) - * - * @since 2.5 - */ - public void writeTag(int tagId) throws IOException { - if (tagId < 0) { - throw new IllegalArgumentException( - "Can not write negative tag ids (" + tagId + ")"); - } - _writeLengthMarker(PREFIX_TYPE_TAG, tagId); - } - - /* - /********************************************************** - /* Extended API, raw bytes (by-passing encoder) - /********************************************************** - */ - - /** - * Method for directly inserting specified byte in output at current - * position. - *

- * NOTE: only use this method if you really know what you are doing. - */ - public void writeRaw(byte b) throws IOException { - _writeByte(b); - } - - /** - * Method for directly inserting specified bytes in output at current - * position. - *

- * NOTE: only use this method if you really know what you are doing. - */ - public void writeBytes(byte[] data, int offset, int len) throws IOException { - _writeBytes(data, offset, len); - } - - /* - /********************************************************** - /* Internal methods: low-level text output - /********************************************************** - */ - - private final static int MAX_SHORT_STRING_CHARS = 23; - // in case it's > 23 bytes - private final static int MAX_SHORT_STRING_BYTES = 23 * 3 + 2; - - private final static int MAX_MEDIUM_STRING_CHARS = 255; - // in case it's > 255 bytes - private final static int MAX_MEDIUM_STRING_BYTES = 255 * 3 + 3; - - protected final void _writeString(String name) throws IOException { - int len = name.length(); - if (len == 0) { - _writeByte(BYTE_EMPTY_STRING); - return; - } - // Actually, let's not bother with copy for shortest strings - if (len <= MAX_SHORT_STRING_CHARS) { - _ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate - // length - int actual = _encode(_outputTail + 1, name, len); - final byte[] buf = _outputBuffer; - int ix = _outputTail; - if (actual <= MAX_SHORT_STRING_CHARS) { // fits in prefix byte - buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual); - _outputTail = ix + actual; - return; - } - // no, have to move. Blah. - System.arraycopy(buf, ix + 1, buf, ix + 2, actual); - buf[ix++] = BYTE_STRING_1BYTE_LEN; - buf[ix++] = (byte) actual; - _outputTail = ix + actual; - return; - } - - char[] cbuf = _charBuffer; - if (len > cbuf.length) { - _charBuffer = cbuf = new char[Math - .max(_charBuffer.length + 32, len)]; - } - name.getChars(0, len, cbuf, 0); - _writeString(cbuf, 0, len); - } - - protected final void _ensureSpace(int needed) throws IOException { - if ((_outputTail + needed + 3) > _outputEnd) { - _flushBuffer(); - } - } - - protected final void _writeString(char[] text, int offset, int len) - throws IOException - { - if (len <= MAX_SHORT_STRING_CHARS) { // possibly short string (not necessarily) - _ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate length - int actual = _encode(_outputTail + 1, text, offset, offset + len); - final byte[] buf = _outputBuffer; - int ix = _outputTail; - if (actual <= MAX_SHORT_STRING_CHARS) { // fits in prefix byte - buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual); - _outputTail = ix + actual; - return; - } - // no, have to move. Blah. - System.arraycopy(buf, ix + 1, buf, ix + 2, actual); - buf[ix++] = BYTE_STRING_1BYTE_LEN; - buf[ix++] = (byte) actual; - _outputTail = ix + actual; - return; - } - if (len <= MAX_MEDIUM_STRING_CHARS) { - _ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can approximate - int actual = _encode(_outputTail + 2, text, offset, offset + len); - final byte[] buf = _outputBuffer; - int ix = _outputTail; - if (actual <= MAX_MEDIUM_STRING_CHARS) { // fits as expected - buf[ix++] = BYTE_STRING_1BYTE_LEN; - buf[ix++] = (byte) actual; - _outputTail = ix + actual; - return; - } - // no, have to move. Blah. - System.arraycopy(buf, ix + 2, buf, ix + 3, actual); - buf[ix++] = BYTE_STRING_2BYTE_LEN; - buf[ix++] = (byte) (actual >> 8); - buf[ix++] = (byte) actual; - _outputTail = ix + actual; - return; - } - if (len <= MAX_LONG_STRING_CHARS) { // no need to chunk yet - // otherwise, long but single chunk - _ensureSpace(MAX_LONG_STRING_BYTES); // calculate accurate length to - // avoid extra flushing - int ix = _outputTail; - int actual = _encode(ix + 3, text, offset, offset+len); - final byte[] buf = _outputBuffer; - buf[ix++] = BYTE_STRING_2BYTE_LEN; - buf[ix++] = (byte) (actual >> 8); - buf[ix++] = (byte) actual; - _outputTail = ix + actual; - return; - } - writeLongString(text, offset, len); - } - - protected final void writeLongString(char[] text, int offset, int len) throws IOException { - CharBuffer charBuffer = CharBuffer.wrap(text, offset, len); - ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); - int byteLen = byteBuffer.remaining(); - _writeLengthMarker(PREFIX_TYPE_TEXT, byteLen); - _writeBytes(byteBuffer.array(), byteBuffer.position(), byteLen); - } - - /* - /********************************************************** - /* Internal methods, UTF-8 encoding - /********************************************************** - */ - - /** - * Helper method called when the whole character sequence is known to fit in - * the output buffer regardless of UTF-8 expansion. - */ - private final int _encode(int outputPtr, char[] str, int i, int end) { - // First: let's see if it's all ASCII: that's rather fast - final byte[] outBuf = _outputBuffer; - final int outputStart = outputPtr; - do { - int c = str[i]; - if (c > 0x7F) { - return _shortUTF8Encode2(str, i, end, outputPtr, outputStart); - } - outBuf[outputPtr++] = (byte) c; - } while (++i < end); - return outputPtr - outputStart; - } - - /** - * Helper method called when the whole character sequence is known to fit in - * the output buffer, but not all characters are single-byte (ASCII) - * characters. - */ - private final int _shortUTF8Encode2(char[] str, int i, int end, - int outputPtr, int outputStart) { - final byte[] outBuf = _outputBuffer; - while (i < end) { - int c = str[i++]; - if (c <= 0x7F) { - outBuf[outputPtr++] = (byte) c; - continue; - } - // Nope, multi-byte: - if (c < 0x800) { // 2-byte - outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6)); - outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); - continue; - } - // 3 or 4 bytes (surrogate) - // Surrogates? - if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character - outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12)); - outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); - outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); - continue; - } - // Yup, a surrogate pair - if (c > SURR1_LAST) { // must be from first range; second won't do - _throwIllegalSurrogate(c); - } - // ... meaning it must have a pair - if (i >= end) { - _throwIllegalSurrogate(c); - } - c = _convertSurrogate(c, str[i++]); - if (c > 0x10FFFF) { // illegal in JSON as well as in XML - _throwIllegalSurrogate(c); - } - outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18)); - outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); - outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); - outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); - } - return (outputPtr - outputStart); - } - - private final int _encode(int outputPtr, String str, int len) { - final byte[] outBuf = _outputBuffer; - final int outputStart = outputPtr; - - for (int i = 0; i < len; ++i) { - int c = str.charAt(i); - if (c > 0x7F) { - return _encode2(i, outputPtr, str, len, outputStart); - } - outBuf[outputPtr++] = (byte) c; - } - return (outputPtr - outputStart); - } - - private final int _encode2(int i, int outputPtr, String str, int len, - int outputStart) { - final byte[] outBuf = _outputBuffer; - // no; non-ASCII stuff, slower loop - while (i < len) { - int c = str.charAt(i++); - if (c <= 0x7F) { - outBuf[outputPtr++] = (byte) c; - continue; - } - // Nope, multi-byte: - if (c < 0x800) { // 2-byte - outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6)); - outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); - continue; - } - // 3 or 4 bytes (surrogate) - // Surrogates? - if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte - // character - outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12)); - outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); - outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); - continue; - } - // Yup, a surrogate pair - if (c > SURR1_LAST) { // must be from first range; second won't do - _throwIllegalSurrogate(c); - } - // ... meaning it must have a pair - if (i >= len) { - _throwIllegalSurrogate(c); - } - c = _convertSurrogate(c, str.charAt(i++)); - if (c > 0x10FFFF) { // illegal in JSON as well as in XML - _throwIllegalSurrogate(c); - } - outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18)); - outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); - outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); - outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); - } - return (outputPtr - outputStart); - } - - /** - * Method called to calculate UTF codepoint, from a surrogate pair. - */ - private int _convertSurrogate(int firstPart, int secondPart) { - // Ok, then, is the second part valid? - if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { - throw new IllegalArgumentException( - "Broken surrogate pair: first char 0x" - + Integer.toHexString(firstPart) + ", second 0x" - + Integer.toHexString(secondPart) - + "; illegal combination"); - } - return 0x10000 + ((firstPart - SURR1_FIRST) << 10) - + (secondPart - SURR2_FIRST); - } - - private void _throwIllegalSurrogate(int code) { - if (code > 0x10FFFF) { // over max? - throw new IllegalArgumentException("Illegal character point (0x" - + Integer.toHexString(code) - + ") to output; max is 0x10FFFF as per RFC 4627"); - } - if (code >= SURR1_FIRST) { - if (code <= SURR1_LAST) { // Unmatched first part (closing without - // second part?) - throw new IllegalArgumentException( - "Unmatched first part of surrogate pair (0x" - + Integer.toHexString(code) + ")"); - } - throw new IllegalArgumentException( - "Unmatched second part of surrogate pair (0x" - + Integer.toHexString(code) + ")"); - } - // should we ever get this? - throw new IllegalArgumentException("Illegal character point (0x" - + Integer.toHexString(code) + ") to output"); - } - - /* - /********************************************************** - /* Internal methods, writing bytes - /********************************************************** - */ - - private final void _ensureRoomForOutput(int needed) throws IOException { - if ((_outputTail + needed) >= _outputEnd) { - _flushBuffer(); - } - } - - private final void _writeIntValue(int i) throws IOException { - int marker; - if (i < 0) { - i = -i - 1; - marker = PREFIX_TYPE_INT_NEG; - } else { - marker = PREFIX_TYPE_INT_POS; - } - _writeLengthMarker(marker, i); - } - - private final void _writeLongValue(long l) throws IOException { - _ensureRoomForOutput(9); - if (l < 0) { - l += 1; - l = -l; - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); - } else { - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); - } - int i = (int) (l >> 32); - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - i = (int) l; - _outputBuffer[_outputTail++] = (byte) (i >> 24); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - } - - private final void _writeLengthMarker(int majorType, int i) - throws IOException { - _ensureRoomForOutput(5); - if (i < 24) { - _outputBuffer[_outputTail++] = (byte) (majorType + i); - return; - } - if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT8_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) i; - return; - } - final byte b0 = (byte) i; - i >>= 8; - if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT16_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) i; - _outputBuffer[_outputTail++] = b0; - return; - } - _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT32_ELEMENTS); - _outputBuffer[_outputTail++] = (byte) (i >> 16); - _outputBuffer[_outputTail++] = (byte) (i >> 8); - _outputBuffer[_outputTail++] = (byte) i; - _outputBuffer[_outputTail++] = b0; - } - - private final void _writeByte(byte b) throws IOException { - if (_outputTail >= _outputEnd) { - _flushBuffer(); - } - _outputBuffer[_outputTail++] = b; - } - - /* - * private final void _writeBytes(byte b1, byte b2) throws IOException { if - * ((_outputTail + 1) >= _outputEnd) { _flushBuffer(); } - * _outputBuffer[_outputTail++] = b1; _outputBuffer[_outputTail++] = b2; } - */ - - private final void _writeBytes(byte[] data, int offset, int len) - throws IOException { - if (len == 0) { - return; - } - if ((_outputTail + len) >= _outputEnd) { - _writeBytesLong(data, offset, len); - return; - } - // common case, non-empty, fits in just fine: - System.arraycopy(data, offset, _outputBuffer, _outputTail, len); - _outputTail += len; - } - - private final int _writeBytes(InputStream in, int bytesLeft) - throws IOException { - while (bytesLeft > 0) { - int room = _outputEnd - _outputTail; - if (room <= 0) { - _flushBuffer(); - room = _outputEnd - _outputTail; - } - int count = in.read(_outputBuffer, _outputTail, room); - if (count < 0) { - break; - } - _outputTail += count; - bytesLeft -= count; - } - return bytesLeft; - } - - private final void _writeBytesLong(byte[] data, int offset, int len) - throws IOException { - if (_outputTail >= _outputEnd) { - _flushBuffer(); - } - while (true) { - int currLen = Math.min(len, (_outputEnd - _outputTail)); - System.arraycopy(data, offset, _outputBuffer, _outputTail, currLen); - _outputTail += currLen; - if ((len -= currLen) == 0) { - break; - } - offset += currLen; - _flushBuffer(); - } - } - - /* - /********************************************************** - /* Internal methods, buffer handling - /********************************************************** - */ - - @Override - protected void _releaseBuffers() { - byte[] buf = _outputBuffer; - if (buf != null && _bufferRecyclable) { - _outputBuffer = null; - _ioContext.releaseWriteEncodingBuffer(buf); - } - char[] cbuf = _charBuffer; - if (cbuf != null) { - _charBuffer = null; - _ioContext.releaseConcatBuffer(cbuf); - } - } - - protected final void _flushBuffer() throws IOException { - if (_outputTail > 0) { - _bytesWritten += _outputTail; - _out.write(_outputBuffer, 0, _outputTail); - _outputTail = 0; - } - } - - /* - /********************************************************** - /* Internal methods, size control for array and objects - /********************************************************** - */ - - private final void closeComplexElement() throws IOException { - switch (_currentRemainingElements) { - case INDEFINITE_LENGTH: - _writeByte(BYTE_BREAK); - break; - case 0: // expected for sized ones - break; - default: - _reportError(String.format("%s size mismatch: expected %d more elements", - _writeContext.typeDesc(), _currentRemainingElements)); - } - _currentRemainingElements = (_elementCountsPtr == 0) - ? INDEFINITE_LENGTH - : _elementCounts[--_elementCountsPtr]; - } - - /* - /********************************************************** - /* Internal methods, error reporting - /********************************************************** - */ - - protected UnsupportedOperationException _notSupported() { - return new UnsupportedOperationException(); - } + room = _outputEnd - _outputTail; + } + int count = in.read(_outputBuffer, _outputTail, room); + if (count < 0) { + break; + } + _outputTail += count; + bytesLeft -= count; + } + return bytesLeft; + } + + private final void _writeBytesLong(byte[] data, int offset, int len) throws IOException { + if (_outputTail >= _outputEnd) { + _flushBuffer(); + } + while (true) { + int currLen = Math.min(len, (_outputEnd - _outputTail)); + System.arraycopy(data, offset, _outputBuffer, _outputTail, currLen); + _outputTail += currLen; + if ((len -= currLen) == 0) { + break; + } + offset += currLen; + _flushBuffer(); + } + } + + /* + /********************************************************** + /* Internal methods, buffer handling + /********************************************************** + */ + + @Override + protected void _releaseBuffers() { + byte[] buf = _outputBuffer; + if (buf != null && _bufferRecyclable) { + _outputBuffer = null; + _ioContext.releaseWriteEncodingBuffer(buf); + } + char[] cbuf = _charBuffer; + if (cbuf != null) { + _charBuffer = null; + _ioContext.releaseConcatBuffer(cbuf); + } + } + + protected final void _flushBuffer() throws IOException { + if (_outputTail > 0) { + _bytesWritten += _outputTail; + _out.write(_outputBuffer, 0, _outputTail); + _outputTail = 0; + } + } + + /* + /********************************************************** + /* Internal methods, size control for array and objects + /********************************************************** + */ + + private final void closeComplexElement() throws IOException { + switch (_currentRemainingElements) { + case INDEFINITE_LENGTH: + _writeByte(BYTE_BREAK); + break; + case 0: // expected for sized ones + break; + default: + _reportError( + String.format( + "%s size mismatch: expected %d more elements", + _writeContext.typeDesc(), _currentRemainingElements)); + } + _currentRemainingElements = + (_elementCountsPtr == 0) ? INDEFINITE_LENGTH : _elementCounts[--_elementCountsPtr]; + } + + /* + /********************************************************** + /* Internal methods, error reporting + /********************************************************** + */ + + protected UnsupportedOperationException _notSupported() { + return new UnsupportedOperationException(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfigurator.java b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfigurator.java index 9fbda26dd4..9804f2be48 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfigurator.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfigurator.java @@ -77,31 +77,33 @@ public class RadixObjectMapperConfigurator { - private RadixObjectMapperConfigurator() { - throw new IllegalStateException("Class instance creation is not allowed"); - } + private RadixObjectMapperConfigurator() { + throw new IllegalStateException("Class instance creation is not allowed"); + } - public static void configure( - ObjectMapper objectMapper, - SerializerIds idLookup, - FilterProvider filterProvider, - boolean sortProperties - ) { - objectMapper.registerModule(new JsonOrgModule()); - objectMapper.registerModule(new GuavaModule()); + public static void configure( + ObjectMapper objectMapper, + SerializerIds idLookup, + FilterProvider filterProvider, + boolean sortProperties) { + objectMapper.registerModule(new JsonOrgModule()); + objectMapper.registerModule(new GuavaModule()); - objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, sortProperties); - objectMapper.configure(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST, !sortProperties); - objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, sortProperties); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker() - .withFieldVisibility(JsonAutoDetect.Visibility.NONE) - .withGetterVisibility(JsonAutoDetect.Visibility.NONE) - .withSetterVisibility(JsonAutoDetect.Visibility.NONE) - .withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY)); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - objectMapper.setFilterProvider(filterProvider); - objectMapper.setAnnotationIntrospector(new DsonFilteringIntrospector()); - objectMapper.setDefaultTyping(new DsonTypeResolverBuilder(idLookup)); - } + objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, sortProperties); + objectMapper.configure(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST, !sortProperties); + objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, sortProperties); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.setVisibility( + objectMapper + .getSerializationConfig() + .getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.NONE) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY)); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.setFilterProvider(filterProvider); + objectMapper.setAnnotationIntrospector(new DsonFilteringIntrospector()); + objectMapper.setDefaultTyping(new DsonTypeResolverBuilder(idLookup)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretManager.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretManager.java index 2a50a70ee1..f99301537c 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretManager.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretManager.java @@ -64,22 +64,8 @@ package com.radixdlt.utils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; -import software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest; -import software.amazon.awssdk.services.secretsmanager.model.CreateSecretResponse; -import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; -import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; -import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException; -import software.amazon.awssdk.services.secretsmanager.model.Tag; -import software.amazon.awssdk.services.secretsmanager.model.UpdateSecretRequest; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; @@ -91,333 +77,320 @@ import java.util.Optional; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; - +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest; +import software.amazon.awssdk.services.secretsmanager.model.CreateSecretResponse; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; +import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException; +import software.amazon.awssdk.services.secretsmanager.model.Tag; +import software.amazon.awssdk.services.secretsmanager.model.UpdateSecretRequest; public class AWSSecretManager { - private AWSSecretManager() { - - } - - public static void createSecret(String secretName, Object secretValue, String network, Region region, boolean binarySecret) { - removeBouncyCastleSecurityProvider(); - SecretsManagerClient secretsClient = SecretsManagerClient.builder() - .region(region) - .build(); - - String secretARN = createNewSecret(secretsClient, secretName, secretValue, network, binarySecret); - System.out.println("Secret created with ARN " + secretARN); - secretsClient.close(); - } - - public static void createSecret(String secretName, String secretValue, String network, String region) { - boolean binarySecret = false; - createSecret(secretName, secretValue, network, Region.of(region), binarySecret); - } - public static void createSecret(String secretName, String secretValue, String network) { - boolean binarySecret = false; - createSecret(secretName, secretValue, network, getRegion(), binarySecret); - } - - public static void createBinarySecret(String secretName, SdkBytes secretValue, String network, String region) { - boolean binarySecret = true; - createSecret(secretName, secretValue, network, Region.of(region), binarySecret); - } - public static void createBinarySecret(String secretName, SdkBytes secretValue, String network) { - boolean binarySecret = true; - createSecret(secretName, secretValue, network, getRegion(), binarySecret); - } - - public static String getSecret(String secretName, Region region) { - removeBouncyCastleSecurityProvider(); - - SecretsManagerClient secretsClient = SecretsManagerClient.builder() - .region(region) - .build(); - String secret = getValue(secretsClient, secretName); - secretsClient.close(); - return secret; - } - - public static void downloadPrivateKey(String secretName, String destFile, Region region) throws IOException { - System.out.format("About to download private key for %s %n", secretName); - removeBouncyCastleSecurityProvider(); - - SecretsManagerClient secretsClient = SecretsManagerClient.builder() - .region(region) - .build(); - SdkBytes secret = getBinaryValue(secretsClient, secretName); - secretsClient.close(); - - uncompressData(secret, destFile); - } - public static void downloadPrivateKey(String secretName, String destFile) throws IOException { - downloadPrivateKey(secretName, destFile, getRegion()); + private AWSSecretManager() {} + + public static void createSecret( + String secretName, Object secretValue, String network, Region region, boolean binarySecret) { + removeBouncyCastleSecurityProvider(); + SecretsManagerClient secretsClient = SecretsManagerClient.builder().region(region).build(); + + String secretARN = + createNewSecret(secretsClient, secretName, secretValue, network, binarySecret); + System.out.println("Secret created with ARN " + secretARN); + secretsClient.close(); + } + + public static void createSecret( + String secretName, String secretValue, String network, String region) { + boolean binarySecret = false; + createSecret(secretName, secretValue, network, Region.of(region), binarySecret); + } + + public static void createSecret(String secretName, String secretValue, String network) { + boolean binarySecret = false; + createSecret(secretName, secretValue, network, getRegion(), binarySecret); + } + + public static void createBinarySecret( + String secretName, SdkBytes secretValue, String network, String region) { + boolean binarySecret = true; + createSecret(secretName, secretValue, network, Region.of(region), binarySecret); + } + + public static void createBinarySecret(String secretName, SdkBytes secretValue, String network) { + boolean binarySecret = true; + createSecret(secretName, secretValue, network, getRegion(), binarySecret); + } + + public static String getSecret(String secretName, Region region) { + removeBouncyCastleSecurityProvider(); + + SecretsManagerClient secretsClient = SecretsManagerClient.builder().region(region).build(); + String secret = getValue(secretsClient, secretName); + secretsClient.close(); + return secret; + } + + public static void downloadPrivateKey(String secretName, String destFile, Region region) + throws IOException { + System.out.format("About to download private key for %s %n", secretName); + removeBouncyCastleSecurityProvider(); + + SecretsManagerClient secretsClient = SecretsManagerClient.builder().region(region).build(); + SdkBytes secret = getBinaryValue(secretsClient, secretName); + secretsClient.close(); + + uncompressData(secret, destFile); + } + + public static void downloadPrivateKey(String secretName, String destFile) throws IOException { + downloadPrivateKey(secretName, destFile, getRegion()); + } + + public static String getSecret(String secretName) { + return getSecret(secretName, getRegion()); + } + + public static boolean awsSecretExists(String secretName) { + try { + getSecret(secretName); + } catch (SecretsManagerException e) { + if (e.awsErrorDetails().errorCode().equals("ResourceNotFoundException")) { + return false; + } else { + throw e; + } } - - public static String getSecret(String secretName) { - return getSecret(secretName, getRegion()); - } - - public static boolean awsSecretExists(String secretName) { - try { - getSecret(secretName); - } catch (SecretsManagerException e) { - if (e.awsErrorDetails().errorCode().equals("ResourceNotFoundException")) { - return false; - } else { - throw e; - } - } - return true; - } - - public static void updateBinarySecret(String secretName, SdkBytes secretValue, Region region) { - removeBouncyCastleSecurityProvider(); - SecretsManagerClient secretsClient = SecretsManagerClient.builder() - .region(region) - .build(); - - updateMyBinarySecret(secretsClient, secretName, secretValue); - secretsClient.close(); - } - - public static void updateSecret(String secretName, String secretValue, Region region) { - removeBouncyCastleSecurityProvider(); - SecretsManagerClient secretsClient = SecretsManagerClient.builder() - .region(region) - .build(); - - updateMySecret(secretsClient, secretName, secretValue); - secretsClient.close(); - } - - public static void updateBinarySecret(String secretName, SdkBytes secretValue) { - updateBinarySecret(secretName, secretValue, getRegion()); - } - - private static Region getRegion() { - String regionEnv = Optional.ofNullable((System.getenv("AWS_DEFAULT_REGION"))).orElse("eu-west-2"); - Region region = Region.of(regionEnv); - return region; - } - - public static void updateSecret(String secretName, String secretValue) { - updateSecret(secretName, secretValue, getRegion()); - } - - private static void updateMySecret(SecretsManagerClient secretsClient, String secretName, String secretValue) { - UpdateSecretRequest secretRequest = UpdateSecretRequest.builder() - .secretId(secretName) - .secretString(secretValue) - .build(); - - secretsClient.updateSecret(secretRequest); - } - - private static void updateMyBinarySecret(SecretsManagerClient secretsClient, String secretName, SdkBytes secretValue) { - UpdateSecretRequest secretRequest = UpdateSecretRequest.builder() - .secretId(secretName) - .secretBinary(secretValue) - .build(); - - secretsClient.updateSecret(secretRequest); + return true; + } + + public static void updateBinarySecret(String secretName, SdkBytes secretValue, Region region) { + removeBouncyCastleSecurityProvider(); + SecretsManagerClient secretsClient = SecretsManagerClient.builder().region(region).build(); + + updateMyBinarySecret(secretsClient, secretName, secretValue); + secretsClient.close(); + } + + public static void updateSecret(String secretName, String secretValue, Region region) { + removeBouncyCastleSecurityProvider(); + SecretsManagerClient secretsClient = SecretsManagerClient.builder().region(region).build(); + + updateMySecret(secretsClient, secretName, secretValue); + secretsClient.close(); + } + + public static void updateBinarySecret(String secretName, SdkBytes secretValue) { + updateBinarySecret(secretName, secretValue, getRegion()); + } + + private static Region getRegion() { + String regionEnv = + Optional.ofNullable((System.getenv("AWS_DEFAULT_REGION"))).orElse("eu-west-2"); + Region region = Region.of(regionEnv); + return region; + } + + public static void updateSecret(String secretName, String secretValue) { + updateSecret(secretName, secretValue, getRegion()); + } + + private static void updateMySecret( + SecretsManagerClient secretsClient, String secretName, String secretValue) { + UpdateSecretRequest secretRequest = + UpdateSecretRequest.builder().secretId(secretName).secretString(secretValue).build(); + + secretsClient.updateSecret(secretRequest); + } + + private static void updateMyBinarySecret( + SecretsManagerClient secretsClient, String secretName, SdkBytes secretValue) { + UpdateSecretRequest secretRequest = + UpdateSecretRequest.builder().secretId(secretName).secretBinary(secretValue).build(); + + secretsClient.updateSecret(secretRequest); + } + + private static String getValue(SecretsManagerClient secretsClient, String secretName) { + GetSecretValueRequest valueRequest = + GetSecretValueRequest.builder().secretId(secretName).build(); + + GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest); + return valueResponse.secretString(); + } + + private static SdkBytes getBinaryValue(SecretsManagerClient secretsClient, String secretName) { + GetSecretValueRequest valueRequest = + GetSecretValueRequest.builder().secretId(secretName).build(); + + GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest); + return valueResponse.secretBinary(); + } + + private static String createNewSecret( + SecretsManagerClient secretsClient, + String secretName, + Object secretValue, + String network, + boolean binarySecret) { + List tagList = buildTags(network, secretName); + CreateSecretRequest secretRequest; + if (binarySecret) { + secretRequest = + CreateSecretRequest.builder() + .name(secretName) + .description("Validator keys") + .secretBinary((SdkBytes) secretValue) + .tags(tagList) + .build(); + } else { + secretRequest = + CreateSecretRequest.builder() + .name(secretName) + .description("Validator keys") + .secretString((String) secretValue) + .tags(tagList) + .build(); } - private static String getValue(SecretsManagerClient secretsClient, String secretName) { - GetSecretValueRequest valueRequest = GetSecretValueRequest.builder() - .secretId(secretName) - .build(); - - GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest); - return valueResponse.secretString(); - - } - private static SdkBytes getBinaryValue(SecretsManagerClient secretsClient, String secretName) { - GetSecretValueRequest valueRequest = GetSecretValueRequest.builder() - .secretId(secretName) - .build(); - - GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest); - return valueResponse.secretBinary(); - } - - private static String createNewSecret( - SecretsManagerClient secretsClient, - String secretName, - Object secretValue, - String network, - boolean binarySecret - ) { - List tagList = buildTags(network, secretName); - CreateSecretRequest secretRequest; + CreateSecretResponse secretResponse = secretsClient.createSecret(secretRequest); + return secretResponse.arn(); + } + + public static void createAWSSecret( + final Map awsSecret, + final String secretName, + final AWSSecretsOutputOptions awsSecretsOutputOptions, + boolean compress, + boolean binarySecret) { + ObjectMapper objectMapper = new ObjectMapper(); + + try { + String jsonSecret = objectMapper.writeValueAsString(awsSecret); + if (compress) { + byte[] compressedBytes = compressData(jsonSecret); + createBinarySecret( + secretName, + SdkBytes.fromByteArray(compressedBytes), + awsSecretsOutputOptions.getNetworkName()); + } else { if (binarySecret) { - secretRequest = CreateSecretRequest.builder() - .name(secretName) - .description("Validator keys") - .secretBinary((SdkBytes) secretValue) - .tags(tagList) - .build(); + createBinarySecret( + secretName, + SdkBytes.fromByteArray((byte[]) awsSecret.get("key")), + awsSecretsOutputOptions.getNetworkName()); } else { - secretRequest = CreateSecretRequest.builder() - .name(secretName) - .description("Validator keys") - .secretString((String) secretValue) - .tags(tagList) - .build(); - } - - - CreateSecretResponse secretResponse = secretsClient.createSecret(secretRequest); - return secretResponse.arn(); - } - - public static void createAWSSecret( - final Map awsSecret, - final String secretName, - final AWSSecretsOutputOptions awsSecretsOutputOptions, - boolean compress, - boolean binarySecret - ) { - ObjectMapper objectMapper = new ObjectMapper(); - - try { - String jsonSecret = objectMapper.writeValueAsString(awsSecret); - if (compress) { - byte[] compressedBytes = compressData(jsonSecret); - createBinarySecret(secretName, SdkBytes.fromByteArray(compressedBytes), awsSecretsOutputOptions.getNetworkName()); - } else { - if (binarySecret) { - createBinarySecret( - secretName, - SdkBytes.fromByteArray((byte[]) awsSecret.get("key")), - awsSecretsOutputOptions.getNetworkName() - ); - } else { - createSecret(secretName, jsonSecret, awsSecretsOutputOptions.getNetworkName()); - } - } - } catch (JsonProcessingException e) { - System.out.println(e); - } catch (SecretsManagerException e) { - System.err.println(e.awsErrorDetails().errorMessage()); - System.exit(1); - } catch (IOException e) { - System.out.println(e); - System.exit(1); + createSecret(secretName, jsonSecret, awsSecretsOutputOptions.getNetworkName()); } + } + } catch (JsonProcessingException e) { + System.out.println(e); + } catch (SecretsManagerException e) { + System.err.println(e.awsErrorDetails().errorMessage()); + System.exit(1); + } catch (IOException e) { + System.out.println(e); + System.exit(1); } - - private static byte[] compressData(String data) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length()); - GZIPOutputStream gzip = new GZIPOutputStream(bos); - gzip.write(data.getBytes()); - gzip.close(); - byte[] compressed = bos.toByteArray(); - bos.close(); - return compressed; + } + + private static byte[] compressData(String data) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length()); + GZIPOutputStream gzip = new GZIPOutputStream(bos); + gzip.write(data.getBytes()); + gzip.close(); + byte[] compressed = bos.toByteArray(); + bos.close(); + return compressed; + } + + private static void uncompressData(SdkBytes data, String destFile) throws IOException { + try { + + ByteArrayInputStream bis = new ByteArrayInputStream(data.asByteArray()); + GZIPInputStream gZIPInputStream = new GZIPInputStream(bis); + FileOutputStream fileOutputStream = new FileOutputStream(destFile); + int bytesRead; + byte[] buffer = new byte[1024]; + while ((bytesRead = gZIPInputStream.read(buffer)) > 0) { + fileOutputStream.write(buffer, 0, bytesRead); + } + gZIPInputStream.close(); + fileOutputStream.close(); + System.out.format("Data decompressed successfully %n"); + + } catch (IOException ex) { + ex.printStackTrace(); } - - private static void uncompressData(SdkBytes data, String destFile) throws IOException { - try { - - ByteArrayInputStream bis = new ByteArrayInputStream(data.asByteArray()); - GZIPInputStream gZIPInputStream = new GZIPInputStream(bis); - FileOutputStream fileOutputStream = new FileOutputStream(destFile); - int bytesRead; - byte[] buffer = new byte[1024]; - while ((bytesRead = gZIPInputStream.read(buffer)) > 0) { - fileOutputStream.write(buffer, 0, bytesRead); - } - gZIPInputStream.close(); - fileOutputStream.close(); - System.out.format("Data decompressed successfully %n"); - - } catch (IOException ex) { - ex.printStackTrace(); - } -} - - public static void updateAWSSecret( - Map awsSecret, - String secretName, - AWSSecretsOutputOptions awsSecretsOutputOptions, - boolean compress, - boolean binarySecret - ) { - ObjectMapper objectMapper = new ObjectMapper(); - if (canBeUpdated(awsSecretsOutputOptions)) { - System.out.format("Secret %s exists. And it's going to be replaced %n", secretName); - try { - String jsonSecret = objectMapper.writeValueAsString(awsSecret); - if (compress) { - byte[] compressedBytes = compressData(jsonSecret); - updateBinarySecret(secretName, SdkBytes.fromByteArray(compressedBytes)); - } else { - if (binarySecret) { - updateBinarySecret(secretName, SdkBytes.fromByteArray((byte[]) awsSecret.get("key"))); - } else { - updateSecret(secretName, jsonSecret); - } - - } - } catch (JsonProcessingException e) { - System.out.println(e); - } catch (SecretsManagerException e) { - System.err.println(e.awsErrorDetails().errorMessage()); - System.exit(1); - } catch (IOException e) { - System.out.println(e); - System.exit(1); - } + } + + public static void updateAWSSecret( + Map awsSecret, + String secretName, + AWSSecretsOutputOptions awsSecretsOutputOptions, + boolean compress, + boolean binarySecret) { + ObjectMapper objectMapper = new ObjectMapper(); + if (canBeUpdated(awsSecretsOutputOptions)) { + System.out.format("Secret %s exists. And it's going to be replaced %n", secretName); + try { + String jsonSecret = objectMapper.writeValueAsString(awsSecret); + if (compress) { + byte[] compressedBytes = compressData(jsonSecret); + updateBinarySecret(secretName, SdkBytes.fromByteArray(compressedBytes)); } else { - System.out.format("Secret %s exists. It will not be created again %n", secretName); + if (binarySecret) { + updateBinarySecret(secretName, SdkBytes.fromByteArray((byte[]) awsSecret.get("key"))); + } else { + updateSecret(secretName, jsonSecret); + } } + } catch (JsonProcessingException e) { + System.out.println(e); + } catch (SecretsManagerException e) { + System.err.println(e.awsErrorDetails().errorMessage()); + System.exit(1); + } catch (IOException e) { + System.out.println(e); + System.exit(1); + } + } else { + System.out.format("Secret %s exists. It will not be created again %n", secretName); } - - private static boolean canBeUpdated(final AWSSecretsOutputOptions awsSecretsOutputOptions) { - return awsSecretsOutputOptions.getRecreateAwsSecrets() - && (!awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("betanet") - || !awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("mainnet")); - } - - //This is needed or the connection to AWS fails with - /* - Unable to execute HTTP request: No X509TrustManager implementation available - software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: No X509TrustManager implementation available - */ - private static void removeBouncyCastleSecurityProvider() { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } - - private static List buildTags(String network, String name) { - List tagList = new ArrayList<>(); - - Tag envTypeTag = Tag.builder() - .key("radixdlt:environment-type") - .value("development") - .build(); - Tag teamTag = Tag.builder() - .key("radixdlt:team") - .value("devops") - .build(); - Tag appTag = Tag.builder() - .key("radixdlt:application") - .value("validator") - .build(); - Tag nameTag = Tag.builder() - .key("radixdlt:name") - .value(name) - .build(); - Tag networkTag = Tag.builder() - .key("radixdlt:network") - .value(network) - .build(); - - tagList.add(envTypeTag); - tagList.add(appTag); - tagList.add(teamTag); - tagList.add(nameTag); - tagList.add(networkTag); - return tagList; - } + } + + private static boolean canBeUpdated(final AWSSecretsOutputOptions awsSecretsOutputOptions) { + return awsSecretsOutputOptions.getRecreateAwsSecrets() + && (!awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("betanet") + || !awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("mainnet")); + } + + // This is needed or the connection to AWS fails with + /* + Unable to execute HTTP request: No X509TrustManager implementation available + software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: No X509TrustManager implementation available + */ + private static void removeBouncyCastleSecurityProvider() { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + private static List buildTags(String network, String name) { + List tagList = new ArrayList<>(); + + Tag envTypeTag = Tag.builder().key("radixdlt:environment-type").value("development").build(); + Tag teamTag = Tag.builder().key("radixdlt:team").value("devops").build(); + Tag appTag = Tag.builder().key("radixdlt:application").value("validator").build(); + Tag nameTag = Tag.builder().key("radixdlt:name").value(name).build(); + Tag networkTag = Tag.builder().key("radixdlt:network").value(network).build(); + + tagList.add(envTypeTag); + tagList.add(appTag); + tagList.add(teamTag); + tagList.add(nameTag); + tagList.add(networkTag); + return tagList; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretsOutputOptions.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretsOutputOptions.java index d9614a1a49..a867456068 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretsOutputOptions.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/AWSSecretsOutputOptions.java @@ -65,26 +65,26 @@ package com.radixdlt.utils; public class AWSSecretsOutputOptions { - private Boolean enableAwsSecrets; - private Boolean recreateAwsSecrets; - private String networkName; + private Boolean enableAwsSecrets; + private Boolean recreateAwsSecrets; + private String networkName; - public AWSSecretsOutputOptions(Boolean enableAwsSecrets, Boolean recreateAwsSecrets, String networkName) { - this.enableAwsSecrets = enableAwsSecrets; - this.recreateAwsSecrets = recreateAwsSecrets; - this.networkName = networkName; - } + public AWSSecretsOutputOptions( + Boolean enableAwsSecrets, Boolean recreateAwsSecrets, String networkName) { + this.enableAwsSecrets = enableAwsSecrets; + this.recreateAwsSecrets = recreateAwsSecrets; + this.networkName = networkName; + } - public Boolean getEnableAwsSecrets() { - return enableAwsSecrets; - } + public Boolean getEnableAwsSecrets() { + return enableAwsSecrets; + } - public Boolean getRecreateAwsSecrets() { - return recreateAwsSecrets; - } - - public String getNetworkName() { - return networkName; - } + public Boolean getRecreateAwsSecrets() { + return recreateAwsSecrets; + } + public String getNetworkName() { + return networkName; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Base58.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Base58.java index f380d61c40..3ae7d22906 100755 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Base58.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Base58.java @@ -1,168 +1,221 @@ -/* - * Copyright 2011 Google Inc. - * Copyright 2018 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.radixdlt.utils; - -import java.util.Arrays; - -/** - * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings. - *

- * Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet. - *

- * You may want to consider working with {@link PrefixedChecksummedBytes} instead, which - * adds support for testing the prefix and suffix bytes commonly found in addresses. - *

- * Satoshi explains: why base-58 instead of standard base-64 encoding? - *

    - *
  • Don't want 0OIl characters that look the same in some fonts and - * could be used to create visually identical looking account numbers.
  • - *
  • A string with non-alphanumeric characters is not as easily accepted as an account number.
  • - *
  • E-mail usually won't line-break if there's no punctuation to break at.
  • - *
  • Doubleclicking selects the whole number as one word if it's all alphanumeric.
  • - *
- *

- * However, note that the encoding/decoding runs in O(n²) time, so it is not useful for large data. - *

- * The basic idea of the encoding is to treat the data bytes as a large number represented using - * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact - * number of leading zeros (which are otherwise lost during the mathematical operations on the - * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters. - *

- * Note: This code is subject to the license noted in the header comment and was taken from the bitcoinj - * library available on - * - * Github. - */ -public final class Base58 { - private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); - private static final char ENCODED_ZERO = ALPHABET[0]; - private static final int[] INDEXES = new int[128]; - static { - Arrays.fill(INDEXES, -1); - for (int i = 0; i < ALPHABET.length; i++) { - INDEXES[ALPHABET[i]] = i; - } - } - - private Base58() { - throw new IllegalArgumentException("Can't construct"); - } - - /** - * Encodes the given bytes as a base58 string (no checksum is appended). - * - * @param input the bytes to encode - * @return the base58-encoded string - */ - public static String toBase58(byte[] input) { - if (input.length == 0) { - return ""; - } - // Count leading zeros. - int zeros = 0; - while (zeros < input.length && input[zeros] == 0) { - ++zeros; - } - // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) - input = Arrays.copyOf(input, input.length); // since we modify it in-place - char[] encoded = new char[input.length * 2]; // upper bound - int outputStart = encoded.length; - for (int inputStart = zeros; inputStart < input.length;) { - encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; - if (input[inputStart] == 0) { - ++inputStart; // optimization - skip leading zeros - } - } - // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input. - while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { - ++outputStart; - } - while (--zeros >= 0) { - encoded[--outputStart] = ENCODED_ZERO; - } - // Return encoded string (including encoded leading zeros). - return new String(encoded, outputStart, encoded.length - outputStart); - } - - /** - * Decodes the given base58 string into the original data bytes. - * - * @param input the base58-encoded string to decode - * @return the decoded data bytes - * @throws IllegalArgumentException if the given string is not a valid base58 string - */ - public static byte[] fromBase58(String input) { - if (input.length() == 0) { - return new byte[0]; - } - // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). - byte[] input58 = new byte[input.length()]; - for (int i = 0; i < input.length(); ++i) { - char c = input.charAt(i); - int digit = c < 128 ? INDEXES[c] : -1; - if (digit < 0) { - throw new IllegalArgumentException("Invalid base58 character: " + c); - } - input58[i] = (byte) digit; - } - // Count leading zeros. - int zeros = 0; - while (zeros < input58.length && input58[zeros] == 0) { - ++zeros; - } - // Convert base-58 digits to base-256 digits. - byte[] decoded = new byte[input.length()]; - int outputStart = decoded.length; - for (int inputStart = zeros; inputStart < input58.length;) { - decoded[--outputStart] = divmod(input58, inputStart, 58, 256); - if (input58[inputStart] == 0) { - ++inputStart; // optimization - skip leading zeros - } - } - // Ignore extra leading zeroes that were added during the calculation. - while (outputStart < decoded.length && decoded[outputStart] == 0) { - ++outputStart; - } - // Return decoded data (including original number of leading zeros). - return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); - } - - /** - * Divides a number, represented as an array of bytes each containing a single digit - * in the specified base, by the given divisor. The given number is modified in-place - * to contain the quotient, and the return value is the remainder. - * - * @param number the number to divide - * @param firstDigit the index within the array of the first non-zero digit - * (this is used for optimization by skipping the leading zeros) - * @param base the base in which the number's digits are represented (up to 256) - * @param divisor the number to divide by (up to 256) - * @return the remainder of the division operation - */ - private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { - // this is just long division which accounts for the base of the input digits - int remainder = 0; - for (int i = firstDigit; i < number.length; i++) { - int digit = number[i] & 0xFF; - int temp = remainder * base + digit; - number[i] = (byte) (temp / divisor); - remainder = temp % divisor; - } - return (byte) remainder; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +import java.util.Arrays; + +/** + * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings. + * + *

Note that this is not the same base58 as used by Flickr, which you may find referenced around + * the Internet. + * + *

You may want to consider working with {@link PrefixedChecksummedBytes} instead, which adds + * support for testing the prefix and suffix bytes commonly found in addresses. + * + *

Satoshi explains: why base-58 instead of standard base-64 encoding? + * + *

    + *
  • Don't want 0OIl characters that look the same in some fonts and could be used to create + * visually identical looking account numbers. + *
  • A string with non-alphanumeric characters is not as easily accepted as an account number. + *
  • E-mail usually won't line-break if there's no punctuation to break at. + *
  • Doubleclicking selects the whole number as one word if it's all alphanumeric. + *
+ * + *

However, note that the encoding/decoding runs in O(n²) time, so it is not useful for + * large data. + * + *

The basic idea of the encoding is to treat the data bytes as a large number represented using + * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact + * number of leading zeros (which are otherwise lost during the mathematical operations on the + * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters. + * + *

Note: This code is subject to the license noted in the header comment and was taken from the + * bitcoinj library available on + * Github. + */ +public final class Base58 { + private static final char[] ALPHABET = + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); + private static final char ENCODED_ZERO = ALPHABET[0]; + private static final int[] INDEXES = new int[128]; + + static { + Arrays.fill(INDEXES, -1); + for (int i = 0; i < ALPHABET.length; i++) { + INDEXES[ALPHABET[i]] = i; + } + } + + private Base58() { + throw new IllegalArgumentException("Can't construct"); + } + + /** + * Encodes the given bytes as a base58 string (no checksum is appended). + * + * @param input the bytes to encode + * @return the base58-encoded string + */ + public static String toBase58(byte[] input) { + if (input.length == 0) { + return ""; + } + // Count leading zeros. + int zeros = 0; + while (zeros < input.length && input[zeros] == 0) { + ++zeros; + } + // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) + input = Arrays.copyOf(input, input.length); // since we modify it in-place + char[] encoded = new char[input.length * 2]; // upper bound + int outputStart = encoded.length; + for (int inputStart = zeros; inputStart < input.length; ) { + encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; + if (input[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Preserve exactly as many leading encoded zeros in output as there were leading zeros in + // input. + while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { + ++outputStart; + } + while (--zeros >= 0) { + encoded[--outputStart] = ENCODED_ZERO; + } + // Return encoded string (including encoded leading zeros). + return new String(encoded, outputStart, encoded.length - outputStart); + } + + /** + * Decodes the given base58 string into the original data bytes. + * + * @param input the base58-encoded string to decode + * @return the decoded data bytes + * @throws IllegalArgumentException if the given string is not a valid base58 string + */ + public static byte[] fromBase58(String input) { + if (input.length() == 0) { + return new byte[0]; + } + // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). + byte[] input58 = new byte[input.length()]; + for (int i = 0; i < input.length(); ++i) { + char c = input.charAt(i); + int digit = c < 128 ? INDEXES[c] : -1; + if (digit < 0) { + throw new IllegalArgumentException("Invalid base58 character: " + c); + } + input58[i] = (byte) digit; + } + // Count leading zeros. + int zeros = 0; + while (zeros < input58.length && input58[zeros] == 0) { + ++zeros; + } + // Convert base-58 digits to base-256 digits. + byte[] decoded = new byte[input.length()]; + int outputStart = decoded.length; + for (int inputStart = zeros; inputStart < input58.length; ) { + decoded[--outputStart] = divmod(input58, inputStart, 58, 256); + if (input58[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Ignore extra leading zeroes that were added during the calculation. + while (outputStart < decoded.length && decoded[outputStart] == 0) { + ++outputStart; + } + // Return decoded data (including original number of leading zeros). + return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); + } + + /** + * Divides a number, represented as an array of bytes each containing a single digit in the + * specified base, by the given divisor. The given number is modified in-place to contain the + * quotient, and the return value is the remainder. + * + * @param number the number to divide + * @param firstDigit the index within the array of the first non-zero digit (this is used for + * optimization by skipping the leading zeros) + * @param base the base in which the number's digits are represented (up to 256) + * @param divisor the number to divide by (up to 256) + * @return the remainder of the division operation + */ + private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { + // this is just long division which accounts for the base of the input digits + int remainder = 0; + for (int i = firstDigit; i < number.length; i++) { + int digit = number[i] & 0xFF; + int temp = remainder * base + digit; + number[i] = (byte) (temp / divisor); + remainder = temp % divisor; + } + return (byte) remainder; + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bits.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bits.java index e32b3b360f..5c328df6b2 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bits.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bits.java @@ -64,42 +64,47 @@ package com.radixdlt.utils; -import org.bitcoinj.core.AddressFormatException; - import java.io.ByteArrayOutputStream; +import org.bitcoinj.core.AddressFormatException; public final class Bits { - private Bits() { - throw new IllegalStateException("Cannot instantiate"); - } + private Bits() { + throw new IllegalStateException("Cannot instantiate"); + } - public static byte[] convertBits(final byte[] in, final int inStart, final int inLen, final int fromBits, - final int toBits, final boolean pad) throws AddressFormatException { - int acc = 0; - int bits = 0; - ByteArrayOutputStream out = new ByteArrayOutputStream(64); - final int maxv = (1 << toBits) - 1; - final int maxAcc = (1 << (fromBits + toBits - 1)) - 1; - for (int i = 0; i < inLen; i++) { - int value = in[i + inStart] & 0xff; - if ((value >>> fromBits) != 0) { - throw new AddressFormatException( - String.format("Input value '%X' exceeds '%d' bit size", value, fromBits)); - } - acc = ((acc << fromBits) | value) & maxAcc; - bits += fromBits; - while (bits >= toBits) { - bits -= toBits; - out.write((acc >>> bits) & maxv); - } - } - if (pad) { - if (bits > 0) { - out.write((acc << (toBits - bits)) & maxv); - } - } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) { - throw new AddressFormatException("Could not convert bits, invalid padding"); - } - return out.toByteArray(); - } + public static byte[] convertBits( + final byte[] in, + final int inStart, + final int inLen, + final int fromBits, + final int toBits, + final boolean pad) + throws AddressFormatException { + int acc = 0; + int bits = 0; + ByteArrayOutputStream out = new ByteArrayOutputStream(64); + final int maxv = (1 << toBits) - 1; + final int maxAcc = (1 << (fromBits + toBits - 1)) - 1; + for (int i = 0; i < inLen; i++) { + int value = in[i + inStart] & 0xff; + if ((value >>> fromBits) != 0) { + throw new AddressFormatException( + String.format("Input value '%X' exceeds '%d' bit size", value, fromBits)); + } + acc = ((acc << fromBits) | value) & maxAcc; + bits += fromBits; + while (bits >= toBits) { + bits -= toBits; + out.write((acc >>> bits) & maxv); + } + } + if (pad) { + if (bits > 0) { + out.write((acc << (toBits - bits)) & maxv); + } + } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) { + throw new AddressFormatException("Could not convert bits, invalid padding"); + } + return out.toByteArray(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bytes.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bytes.java index 79ea8cb6ba..d11f599311 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bytes.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Bytes.java @@ -68,222 +68,217 @@ import java.util.Arrays; import java.util.Base64; -/** - * Utility class for manipulating primitive bytes. - */ +/** Utility class for manipulating primitive bytes. */ public class Bytes { - private static final char[] hexChars = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; + private static final char[] hexChars = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; - /** - * An empty array of bytes. - */ - public static final byte[] EMPTY_BYTES = new byte[0]; + /** An empty array of bytes. */ + public static final byte[] EMPTY_BYTES = new byte[0]; - private Bytes() { - throw new IllegalStateException("Can't construct"); - } + private Bytes() { + throw new IllegalStateException("Can't construct"); + } - /** - * Compare two byte array segments for equality. - * - * @param a1 The first array to compare - * @param offset1 The offset within {@code a1} to begin the comparison - * @param length1 The quantity of {@code a1} to compare - * @param a2 The second array to compare - * @param offset2 The offset within {@code a2} to begin the comparison - * @param length2 The quantity of {@code a2} to compare - * @return {@code true} iff {@code length1 == length2} and {@code a1[offset1 + i] == a2[offset2 + i]} - * for {@code i} ∈ {@code [0, length1)}. - */ - public static boolean arrayEquals(byte[] a1, int offset1, int length1, byte[] a2, int offset2, int length2) { - if (length1 != length2) { - return false; - } - for (int i = 0; i < length1; ++i) { - if (a1[offset1 + i] != a2[offset2 + i]) { - return false; - } - } - return true; - } + /** + * Compare two byte array segments for equality. + * + * @param a1 The first array to compare + * @param offset1 The offset within {@code a1} to begin the comparison + * @param length1 The quantity of {@code a1} to compare + * @param a2 The second array to compare + * @param offset2 The offset within {@code a2} to begin the comparison + * @param length2 The quantity of {@code a2} to compare + * @return {@code true} iff {@code length1 == length2} and {@code a1[offset1 + i] == a2[offset2 + + * i]} for {@code i} ∈ {@code [0, length1)}. + */ + public static boolean arrayEquals( + byte[] a1, int offset1, int length1, byte[] a2, int offset2, int length2) { + if (length1 != length2) { + return false; + } + for (int i = 0; i < length1; ++i) { + if (a1[offset1 + i] != a2[offset2 + i]) { + return false; + } + } + return true; + } - /** - * Calculate hash code of a byte array segment. - * - * @param a The array for which to calculate the hash code. - * @param offset The offset within the array to start the calculation. - * @param length The number of bytes for which to calculate the hash code. - * @return The hash code. - */ - public static int hashCode(byte[] a, int offset, int length) { - int i = length; - int hc = i + 1; - while (--i >= 0) { - hc *= 257; - hc ^= a[offset + i]; - } - return hc; - } + /** + * Calculate hash code of a byte array segment. + * + * @param a The array for which to calculate the hash code. + * @param offset The offset within the array to start the calculation. + * @param length The number of bytes for which to calculate the hash code. + * @return The hash code. + */ + public static int hashCode(byte[] a, int offset, int length) { + int i = length; + int hc = i + 1; + while (--i >= 0) { + hc *= 257; + hc ^= a[offset + i]; + } + return hc; + } - /** - * Convert a byte array into a {@link String} using the - * {@link RadixConstants#STANDARD_CHARSET} character set. - * - * @param bytes The bytes to convert. - * @return The string - */ - public static String toString(byte[] bytes) { - return new String(bytes, RadixConstants.STANDARD_CHARSET); - } + /** + * Convert a byte array into a {@link String} using the {@link RadixConstants#STANDARD_CHARSET} + * character set. + * + * @param bytes The bytes to convert. + * @return The string + */ + public static String toString(byte[] bytes) { + return new String(bytes, RadixConstants.STANDARD_CHARSET); + } - /** - * Convert a byte into a two-digit hex string. - *

- * Note that digits a-f are output as lower case. - * - * @param b The byte to convert - * @return The converted string - */ - public static String toHexString(byte b) { - char[] value = { - toHexChar(b >> 4), toHexChar(b) - }; - return new String(value); - } + /** + * Convert a byte into a two-digit hex string. + * + *

Note that digits a-f are output as lower case. + * + * @param b The byte to convert + * @return The converted string + */ + public static String toHexString(byte b) { + char[] value = {toHexChar(b >> 4), toHexChar(b)}; + return new String(value); + } - /** - * Convert an array into a string of hex digits. - *

- * The output string will have length {@code 2*bytes.length}. - * Hex digits a-f are encoded as lower case. - * - * @param bytes The bytes to convert - * @return The converted string - */ - public static String toHexString(byte[] bytes) { - return toHexString(bytes, 0, bytes.length); - } + /** + * Convert an array into a string of hex digits. + * + *

The output string will have length {@code 2*bytes.length}. Hex digits a-f are encoded as + * lower case. + * + * @param bytes The bytes to convert + * @return The converted string + */ + public static String toHexString(byte[] bytes) { + return toHexString(bytes, 0, bytes.length); + } - /** - * Convert a portion of an array into a string of hex digits. - *

- * The output string will have length {@code 2*length}. - * Hex digits a-f are encoded as lower case. - * - * @param bytes The bytes to convert - * @param offset The offset at which to start converting - * @param length The number of bytes to convert - * @return The converted string - */ - public static String toHexString(byte[] bytes, int offset, int length) { - char[] chars = new char[length * 2]; - for (int i = 0; i < length; ++i) { - byte b = bytes[offset + i]; - chars[i * 2] = hexChars[(b >> 4) & 0xF]; - chars[i * 2 + 1] = hexChars[b & 0xF]; - } - return new String(chars); - } + /** + * Convert a portion of an array into a string of hex digits. + * + *

The output string will have length {@code 2*length}. Hex digits a-f are encoded as lower + * case. + * + * @param bytes The bytes to convert + * @param offset The offset at which to start converting + * @param length The number of bytes to convert + * @return The converted string + */ + public static String toHexString(byte[] bytes, int offset, int length) { + char[] chars = new char[length * 2]; + for (int i = 0; i < length; ++i) { + byte b = bytes[offset + i]; + chars[i * 2] = hexChars[(b >> 4) & 0xF]; + chars[i * 2 + 1] = hexChars[b & 0xF]; + } + return new String(chars); + } - /** - * Convert a string of hexadecimal digits to an array of bytes. - *

- * If the string length is odd, a leading '0' is assumed. - * - * @param s The string to convert to a byte array. - * @return The byte array corresponding to the converted string - * @throws IllegalArgumentException if any character in s is not a hex digit - */ - public static byte[] fromHexString(String s) { - int byteCount = (s.length() + 1) / 2; - byte[] bytes = new byte[byteCount]; - int index = 0; - int offset = 0; - // If an odd number of chars, assume leading zero - if ((s.length() & 1) != 0) { - bytes[offset++] = fromHexNybble(s.charAt(index++)); - } - while (index < s.length()) { - byte msn = fromHexNybble(s.charAt(index++)); - byte lsn = fromHexNybble(s.charAt(index++)); - bytes[offset++] = (byte) (((msn & 0xFF) << 4) | (lsn & 0xFF)); - } - return bytes; - } + /** + * Convert a string of hexadecimal digits to an array of bytes. + * + *

If the string length is odd, a leading '0' is assumed. + * + * @param s The string to convert to a byte array. + * @return The byte array corresponding to the converted string + * @throws IllegalArgumentException if any character in s is not a hex digit + */ + public static byte[] fromHexString(String s) { + int byteCount = (s.length() + 1) / 2; + byte[] bytes = new byte[byteCount]; + int index = 0; + int offset = 0; + // If an odd number of chars, assume leading zero + if ((s.length() & 1) != 0) { + bytes[offset++] = fromHexNybble(s.charAt(index++)); + } + while (index < s.length()) { + byte msn = fromHexNybble(s.charAt(index++)); + byte lsn = fromHexNybble(s.charAt(index++)); + bytes[offset++] = (byte) (((msn & 0xFF) << 4) | (lsn & 0xFF)); + } + return bytes; + } - /** - * Convert an array of bytes into a Base-64 encoded using RFC 4648 rules. - * - * @param bytes The bytes to encode - * @return The base-64 encoded string - */ - public static String toBase64String(byte[] bytes) { - byte[] result = Base64.getEncoder().encode(bytes); - return Strings.fromAsciiBytes(result, 0, result.length); - } + /** + * Convert an array of bytes into a Base-64 encoded using RFC 4648 rules. + * + * @param bytes The bytes to encode + * @return The base-64 encoded string + */ + public static String toBase64String(byte[] bytes) { + byte[] result = Base64.getEncoder().encode(bytes); + return Strings.fromAsciiBytes(result, 0, result.length); + } - /** - * Convert a base-64 encoded string into an array of bytes using RFC 4648 rules. - * - * @param s The string to convert - * @return The decoded bytes - */ - public static byte[] fromBase64String(String s) { - return Base64.getDecoder().decode(s); - } + /** + * Convert a base-64 encoded string into an array of bytes using RFC 4648 rules. + * + * @param s The string to convert + * @return The decoded bytes + */ + public static byte[] fromBase64String(String s) { + return Base64.getDecoder().decode(s); + } - private static char toHexChar(int value) { - return hexChars[value & 0xF]; - } + private static char toHexChar(int value) { + return hexChars[value & 0xF]; + } - private static byte fromHexNybble(char value) { - char c = Character.toLowerCase(value); - if (c >= '0' && c <= '9') { - return (byte) (c - '0'); - } - if (c >= 'a' && c <= 'f') { - return (byte) (10 + c - 'a'); - } - throw new IllegalArgumentException("Unknown hex digit: " + value); - } + private static byte fromHexNybble(char value) { + char c = Character.toLowerCase(value); + if (c >= '0' && c <= '9') { + return (byte) (c - '0'); + } + if (c >= 'a' && c <= 'f') { + return (byte) (10 + c - 'a'); + } + throw new IllegalArgumentException("Unknown hex digit: " + value); + } - /** - * Trims any leading zero bytes from {@code bytes} until either no - * leading zero exists, or only a single zero byte exists. - * - * @param bytes the byte a - * @return @code bytes} with leading zeros removed, if any - */ - public static byte[] trimLeadingZeros(byte[] bytes) { - if (bytes == null || bytes.length <= 1 || bytes[0] != 0) { - return bytes; - } - int trimLeadingZeros = 1; - int maxTrim = bytes.length - 1; - while (trimLeadingZeros < maxTrim && bytes[trimLeadingZeros] == 0) { - trimLeadingZeros += 1; - } - return Arrays.copyOfRange(bytes, trimLeadingZeros, bytes.length); - } + /** + * Trims any leading zero bytes from {@code bytes} until either no leading zero exists, or only a + * single zero byte exists. + * + * @param bytes the byte a + * @return @code bytes} with leading zeros removed, if any + */ + public static byte[] trimLeadingZeros(byte[] bytes) { + if (bytes == null || bytes.length <= 1 || bytes[0] != 0) { + return bytes; + } + int trimLeadingZeros = 1; + int maxTrim = bytes.length - 1; + while (trimLeadingZeros < maxTrim && bytes[trimLeadingZeros] == 0) { + trimLeadingZeros += 1; + } + return Arrays.copyOfRange(bytes, trimLeadingZeros, bytes.length); + } - public static byte[] bigIntegerToBytes(BigInteger b, int numBytes) { - final var bytes = new byte[numBytes]; - final var biBytes = b.toByteArray(); - final var start = biBytes.length == numBytes + 1 ? 1 : 0; - final var length = Math.min(biBytes.length, numBytes); - System.arraycopy(biBytes, start, bytes, numBytes - length, length); - return bytes; - } + public static byte[] bigIntegerToBytes(BigInteger b, int numBytes) { + final var bytes = new byte[numBytes]; + final var biBytes = b.toByteArray(); + final var start = biBytes.length == numBytes + 1 ? 1 : 0; + final var length = Math.min(biBytes.length, numBytes); + System.arraycopy(biBytes, start, bytes, numBytes - length, length); + return bytes; + } - public static byte[] xor(byte[] a, byte[] b) { - final var ret = new byte[a.length]; - var i = 0; - while (i < a.length) { - ret[i] = (byte) (a[i] ^ b[i]); - i += 1; - } - return ret; - } + public static byte[] xor(byte[] a, byte[] b) { + final var ret = new byte[a.length]; + var i = 0; + while (i < a.length) { + ret[i] = (byte) (a[i] ^ b[i]); + i += 1; + } + return ret; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Instants.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Instants.java index 563866fc87..0354e77105 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Instants.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Instants.java @@ -67,24 +67,25 @@ import java.time.Instant; public final class Instants { - private static final int BYTES = Long.BYTES + Integer.BYTES; + private static final int BYTES = Long.BYTES + Integer.BYTES; - private Instants() { } + private Instants() {} - public static byte[] toBytes(Instant instant) { - var result = new byte[BYTES]; + public static byte[] toBytes(Instant instant) { + var result = new byte[BYTES]; - Longs.copyTo(instant.getEpochSecond(), result, 0); - Ints.copyTo(instant.getNano(), result, Long.BYTES); + Longs.copyTo(instant.getEpochSecond(), result, 0); + Ints.copyTo(instant.getNano(), result, Long.BYTES); - return result; - } + return result; + } - public static Instant fromBytes(byte[] bytes) { - if (bytes.length < BYTES) { - throw new IllegalArgumentException("Instant decoding error: not enough data to decode"); - } + public static Instant fromBytes(byte[] bytes) { + if (bytes.length < BYTES) { + throw new IllegalArgumentException("Instant decoding error: not enough data to decode"); + } - return Instant.ofEpochSecond(Longs.fromByteArray(bytes, 0), Ints.fromByteArray(bytes, Long.BYTES)); - } + return Instant.ofEpochSecond( + Longs.fromByteArray(bytes, 0), Ints.fromByteArray(bytes, Long.BYTES)); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Ints.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Ints.java index 64a617926b..ceec590e43 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Ints.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Ints.java @@ -1,173 +1,168 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.utils; - -import java.util.Objects; - -/** - * Utilities for manipulating primitive {@code int} values. - */ -public final class Ints { - private Ints() { - throw new IllegalStateException("Can't construct"); - } - - /** - * Create a byte array of length {@link Integer#BYTES}, and - * populate it with {@code value} in big-endian order. - * - * @param value The value to convert - * @return The resultant byte array. - */ - public static byte[] toByteArray(int value) { - return copyTo(value, new byte[Integer.BYTES], 0); - } - - /** - * Copy the byte value of {@code value} into {@code bytes} - * starting at {@code offset}. A total of {@link Integer#BYTES} - * will be written to {@code bytes}. - * - * @param value The value to convert - * @param bytes The array to write the value into - * @param offset The offset at which to write the value - * @return The value of {@code bytes} - */ - public static byte[] copyTo(int value, byte[] bytes, int offset) { - Objects.requireNonNull(bytes, "bytes is null for 'int' conversion"); - for (int i = offset + Integer.BYTES - 1; i >= offset; i--) { - bytes[i] = (byte) (value & 0xFF); - value >>>= 8; - } - return bytes; - } - - /** - * Exactly equivalent to {@code fromByteArray(bytes, 0)}. - * - * @param bytes The byte array to decode to an integer - * @return The decoded integer value - * @see #fromByteArray(byte[], int) - */ - public static int fromByteArray(byte[] bytes) { - return fromByteArray(bytes, 0); - } - - /** - * Decode an integer from array {@code bytes} at {@code offset}. - * Bytes from array {@code bytes[offset]} up to and including - * {@code bytes[offset + Integer.BYTES - 1]} will be read from - * array {@code bytes}. - * - * @param bytes The byte array to decode to an integer - * @param offset The offset within the array to start decoding - * @return The decoded integer value - */ - public static int fromByteArray(byte[] bytes, int offset) { - Objects.requireNonNull(bytes, "bytes is null for 'int' conversion"); - int value = 0; - for (int b = 0; b < Integer.BYTES; b++) { - value <<= 8; - value |= bytes[offset + b] & 0xFF; - } - - return value; - } - - /** - * Decode an integer from array {@code bytes} at {@code offset} - * of length {@code len}. Bytes from array {@code bytes[offset]} - * up to and including {@code bytes[offset + len - 1]} will be read - * from array {@code bytes}. - * - * @param bytes The byte array to decode to an integer - * @param offset The offset within the array to start decoding - * @param len The number of bytes to read - * @return The decoded integer value - */ - public static int fromByteArray(byte[] bytes, int offset, int len) { - Objects.requireNonNull(bytes, "bytes is null for 'int' conversion"); - int value = 0; - for (int b = 0; b < len; b++) { - value <<= 8; - value |= bytes[offset + b] & 0xFF; - } - - return value; - } - - /** - * Assemble an {@code int} value from it's component bytes. - * - * @param b0 Most significant byte - * @param b1 Next most significant byte - * @param b2 Next least significant byte - * @param b3 Least significant byte - * @return The {@code int} value represented by the arguments. - */ - public static int fromBytes(byte b0, byte b1, byte b2, byte b3) { - return b0 << 24 | (b1 & 0xFF) << 16 | (b2 & 0xFF) << 8 | (b3 & 0xFF); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +import java.util.Objects; + +/** Utilities for manipulating primitive {@code int} values. */ +public final class Ints { + private Ints() { + throw new IllegalStateException("Can't construct"); + } + + /** + * Create a byte array of length {@link Integer#BYTES}, and populate it with {@code value} in + * big-endian order. + * + * @param value The value to convert + * @return The resultant byte array. + */ + public static byte[] toByteArray(int value) { + return copyTo(value, new byte[Integer.BYTES], 0); + } + + /** + * Copy the byte value of {@code value} into {@code bytes} starting at {@code offset}. A total of + * {@link Integer#BYTES} will be written to {@code bytes}. + * + * @param value The value to convert + * @param bytes The array to write the value into + * @param offset The offset at which to write the value + * @return The value of {@code bytes} + */ + public static byte[] copyTo(int value, byte[] bytes, int offset) { + Objects.requireNonNull(bytes, "bytes is null for 'int' conversion"); + for (int i = offset + Integer.BYTES - 1; i >= offset; i--) { + bytes[i] = (byte) (value & 0xFF); + value >>>= 8; + } + return bytes; + } + + /** + * Exactly equivalent to {@code fromByteArray(bytes, 0)}. + * + * @param bytes The byte array to decode to an integer + * @return The decoded integer value + * @see #fromByteArray(byte[], int) + */ + public static int fromByteArray(byte[] bytes) { + return fromByteArray(bytes, 0); + } + + /** + * Decode an integer from array {@code bytes} at {@code offset}. Bytes from array {@code + * bytes[offset]} up to and including {@code bytes[offset + Integer.BYTES - 1]} will be read from + * array {@code bytes}. + * + * @param bytes The byte array to decode to an integer + * @param offset The offset within the array to start decoding + * @return The decoded integer value + */ + public static int fromByteArray(byte[] bytes, int offset) { + Objects.requireNonNull(bytes, "bytes is null for 'int' conversion"); + int value = 0; + for (int b = 0; b < Integer.BYTES; b++) { + value <<= 8; + value |= bytes[offset + b] & 0xFF; + } + + return value; + } + + /** + * Decode an integer from array {@code bytes} at {@code offset} of length {@code len}. Bytes from + * array {@code bytes[offset]} up to and including {@code bytes[offset + len - 1]} will be read + * from array {@code bytes}. + * + * @param bytes The byte array to decode to an integer + * @param offset The offset within the array to start decoding + * @param len The number of bytes to read + * @return The decoded integer value + */ + public static int fromByteArray(byte[] bytes, int offset, int len) { + Objects.requireNonNull(bytes, "bytes is null for 'int' conversion"); + int value = 0; + for (int b = 0; b < len; b++) { + value <<= 8; + value |= bytes[offset + b] & 0xFF; + } + + return value; + } + + /** + * Assemble an {@code int} value from it's component bytes. + * + * @param b0 Most significant byte + * @param b1 Next most significant byte + * @param b2 Next least significant byte + * @param b3 Least significant byte + * @return The {@code int} value represented by the arguments. + */ + public static int fromBytes(byte b0, byte b1, byte b2, byte b3) { + return b0 << 24 | (b1 & 0xFF) << 16 | (b2 & 0xFF) << 8 | (b3 & 0xFF); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/KeyComparator.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/KeyComparator.java index a04c22cb2d..4625073055 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/KeyComparator.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/KeyComparator.java @@ -66,15 +66,15 @@ import com.google.common.primitives.UnsignedBytes; import com.radixdlt.crypto.ECPublicKey; - import java.util.Comparator; public final class KeyComparator { - private KeyComparator() { - throw new IllegalStateException("Cannot instantiate."); - } + private KeyComparator() { + throw new IllegalStateException("Cannot instantiate."); + } - public static Comparator instance() { - return Comparator.comparing(ECPublicKey::getCompressedBytes, UnsignedBytes.lexicographicalComparator()); - } + public static Comparator instance() { + return Comparator.comparing( + ECPublicKey::getCompressedBytes, UnsignedBytes.lexicographicalComparator()); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Longs.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Longs.java index c543b145ea..7392b465cb 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Longs.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Longs.java @@ -1,186 +1,191 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.utils; - -import java.util.Objects; - -/** - * Utilities for manipulating primitive {@code long} values. - */ -public final class Longs { - - private Longs() { - throw new IllegalStateException("Can't construct"); - } - - /** - * Create a byte array of length {@link Long#BYTES}, and - * populate it with {@code value} in big-endian order. - * - * @param value The value to convert - * @return The resultant byte array. - */ - public static byte[] toByteArray(long value) { - return copyTo(value, new byte[Long.BYTES], 0); - } - - /** - * Copy the byte value of {@code value} into {@code bytes} - * starting at {@code offset}. A total of {@link Long#BYTES} - * will be written to {@code bytes}. - * - * @param value The value to convert - * @param bytes The array to write the value into - * @param offset The offset at which to write the value - * @return The value of {@code bytes} - */ - public static byte[] copyTo(long value, byte[] bytes, int offset) { - Objects.requireNonNull(bytes, "bytes is null for 'long' conversion"); - for (int i = offset + Long.BYTES - 1; i >= offset; i--) { - bytes[i] = (byte) (value & 0xFFL); - value >>>= 8; - } - return bytes; - } - - /** - * Exactly equivalent to {@code fromByteArray(bytes, 0)}. - * - * @param bytes The byte array to decode to a long - * @return The decoded long value - * @see #fromByteArray(byte[], int) - */ - public static long fromByteArray(byte[] bytes) { - return fromByteArray(bytes, 0); - } - - /** - * Decode a long from array {@code bytes} at {@code offset}. - * Bytes from array {@code bytes[offset]} up to and including - * {@code bytes[offset + Long.BYTES - 1]} will be read from - * array {@code bytes}. - * - * @param bytes The byte array to decode to a long - * @param offset The offset within the array to start decoding - * @return The decoded long value - */ - public static long fromByteArray(byte[] bytes, int offset) { - Objects.requireNonNull(bytes, "bytes is null for 'long' conversion"); - long value = 0; - for (int b = 0; b < Long.BYTES; b++) { - value <<= 8; - value |= bytes[offset + b] & 0xFFL; - } - return value; - } - - /** - * Assemble a {@code long} value from it's component bytes. - * - * @param b0 Most significant byte - * @param b1 Next most significant byte - * @param b2 … - * @param b3 … - * @param b4 … - * @param b5 … - * @param b6 Next least significant byte - * @param b7 Least significant byte - * @return The {@code long} value represented by the arguments. - */ - public static long fromBytes(byte b0, byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7) { - return (b0 & 0xFFL) << 56 | (b1 & 0xFFL) << 48 | (b2 & 0xFFL) << 40 | (b3 & 0xFFL) << 32 - | (b4 & 0xFFL) << 24 | (b5 & 0xFFL) << 16 | (b6 & 0xFFL) << 8 | (b7 & 0xFFL); - } - - // TODO simple hack for efficiently converting long array to byte array, revisit - - /** - * Convert the given long array to an equivalent array of bytes - * @param longArray The long array - * @return An equivalent array of bytes of length longArray.length * Long.BYTES - */ - public static byte[] toBytes(long[] longArray) { - // TODO optimise - byte[] byteArray = new byte[longArray.length * Long.BYTES]; - for (int i = 0; i < longArray.length; i++) { - copyTo(longArray[i], byteArray, i * Long.BYTES); - } - return byteArray; - } - - /** - * Convert the given byte array to an equivalent array of longs - * @param byteArray The byte array - * @return An equivalent array of longs of length byteArray.length / Long.BYTES - */ - public static long[] fromBytes(byte[] byteArray) { - // TODO optimise - long[] longArray = new long[byteArray.length / Long.BYTES]; - for (int i = 0; i < longArray.length; i++) { - longArray[i] = fromByteArray(byteArray, i * Long.BYTES); - } - return longArray; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +import java.util.Objects; + +/** Utilities for manipulating primitive {@code long} values. */ +public final class Longs { + + private Longs() { + throw new IllegalStateException("Can't construct"); + } + + /** + * Create a byte array of length {@link Long#BYTES}, and populate it with {@code value} in + * big-endian order. + * + * @param value The value to convert + * @return The resultant byte array. + */ + public static byte[] toByteArray(long value) { + return copyTo(value, new byte[Long.BYTES], 0); + } + + /** + * Copy the byte value of {@code value} into {@code bytes} starting at {@code offset}. A total of + * {@link Long#BYTES} will be written to {@code bytes}. + * + * @param value The value to convert + * @param bytes The array to write the value into + * @param offset The offset at which to write the value + * @return The value of {@code bytes} + */ + public static byte[] copyTo(long value, byte[] bytes, int offset) { + Objects.requireNonNull(bytes, "bytes is null for 'long' conversion"); + for (int i = offset + Long.BYTES - 1; i >= offset; i--) { + bytes[i] = (byte) (value & 0xFFL); + value >>>= 8; + } + return bytes; + } + + /** + * Exactly equivalent to {@code fromByteArray(bytes, 0)}. + * + * @param bytes The byte array to decode to a long + * @return The decoded long value + * @see #fromByteArray(byte[], int) + */ + public static long fromByteArray(byte[] bytes) { + return fromByteArray(bytes, 0); + } + + /** + * Decode a long from array {@code bytes} at {@code offset}. Bytes from array {@code + * bytes[offset]} up to and including {@code bytes[offset + Long.BYTES - 1]} will be read from + * array {@code bytes}. + * + * @param bytes The byte array to decode to a long + * @param offset The offset within the array to start decoding + * @return The decoded long value + */ + public static long fromByteArray(byte[] bytes, int offset) { + Objects.requireNonNull(bytes, "bytes is null for 'long' conversion"); + long value = 0; + for (int b = 0; b < Long.BYTES; b++) { + value <<= 8; + value |= bytes[offset + b] & 0xFFL; + } + return value; + } + + /** + * Assemble a {@code long} value from it's component bytes. + * + * @param b0 Most significant byte + * @param b1 Next most significant byte + * @param b2 … + * @param b3 … + * @param b4 … + * @param b5 … + * @param b6 Next least significant byte + * @param b7 Least significant byte + * @return The {@code long} value represented by the arguments. + */ + public static long fromBytes( + byte b0, byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7) { + return (b0 & 0xFFL) << 56 + | (b1 & 0xFFL) << 48 + | (b2 & 0xFFL) << 40 + | (b3 & 0xFFL) << 32 + | (b4 & 0xFFL) << 24 + | (b5 & 0xFFL) << 16 + | (b6 & 0xFFL) << 8 + | (b7 & 0xFFL); + } + + // TODO simple hack for efficiently converting long array to byte array, revisit + + /** + * Convert the given long array to an equivalent array of bytes + * + * @param longArray The long array + * @return An equivalent array of bytes of length longArray.length * Long.BYTES + */ + public static byte[] toBytes(long[] longArray) { + // TODO optimise + byte[] byteArray = new byte[longArray.length * Long.BYTES]; + for (int i = 0; i < longArray.length; i++) { + copyTo(longArray[i], byteArray, i * Long.BYTES); + } + return byteArray; + } + + /** + * Convert the given byte array to an equivalent array of longs + * + * @param byteArray The byte array + * @return An equivalent array of longs of length byteArray.length / Long.BYTES + */ + public static long[] fromBytes(byte[] byteArray) { + // TODO optimise + long[] longArray = new long[byteArray.length / Long.BYTES]; + for (int i = 0; i < longArray.length; i++) { + longArray[i] = fromByteArray(byteArray, i * Long.BYTES); + } + return longArray; + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Offset.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Offset.java index 39cbb238f6..c169d066c8 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Offset.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Offset.java @@ -65,15 +65,17 @@ package com.radixdlt.utils; public enum Offset { - PREVIOUS(-1), NONE(0), NEXT(1); + PREVIOUS(-1), + NONE(0), + NEXT(1); - private final int offset; + private final int offset; - Offset(int offset) { - this.offset = offset; - } + Offset(int offset) { + this.offset = offset; + } - public int getOffset() { - return this.offset; - } + public int getOffset() { + return this.offset; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/POW.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/POW.java index fd8b9e80cb..83c533ddeb 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/POW.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/POW.java @@ -1,112 +1,111 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.utils; - -import com.google.common.hash.HashCode; -import com.radixdlt.crypto.HashUtils; - -import java.nio.ByteBuffer; -import java.util.Objects; - -public class POW { - private final int magic; - private final HashCode seed; - private final long nonce; - private final ByteBuffer buffer = ByteBuffer.allocate(32 + 4 + Long.BYTES); - - public POW(int magic, HashCode seed) { - this(magic, seed, Long.MIN_VALUE); - } - - public POW(int magic, HashCode seed, long nonce) { - Objects.requireNonNull(seed); - - this.magic = magic; - this.seed = seed; - this.nonce = nonce; - } - - public int getMagic() { - return magic; - } - - public HashCode getSeed() { - return seed; - } - - public long getNonce() { - return nonce; - } - - public synchronized HashCode getHash() { - buffer.clear(); - buffer.putInt(magic); - buffer.put(seed.asBytes()); - buffer.putLong(nonce); - buffer.flip(); - - return HashUtils.sha256(buffer.array()); - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +import com.google.common.hash.HashCode; +import com.radixdlt.crypto.HashUtils; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class POW { + private final int magic; + private final HashCode seed; + private final long nonce; + private final ByteBuffer buffer = ByteBuffer.allocate(32 + 4 + Long.BYTES); + + public POW(int magic, HashCode seed) { + this(magic, seed, Long.MIN_VALUE); + } + + public POW(int magic, HashCode seed, long nonce) { + Objects.requireNonNull(seed); + + this.magic = magic; + this.seed = seed; + this.nonce = nonce; + } + + public int getMagic() { + return magic; + } + + public HashCode getSeed() { + return seed; + } + + public long getNonce() { + return nonce; + } + + public synchronized HashCode getHash() { + buffer.clear(); + buffer.putInt(magic); + buffer.put(seed.asBytes()); + buffer.putLong(nonce); + buffer.flip(); + + return HashUtils.sha256(buffer.array()); + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Pair.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Pair.java index 8b00a8ce80..37833c578a 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Pair.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Pair.java @@ -70,158 +70,149 @@ /** * An immutable pair of elements. - *

- * Note that in reality, as for all containers, - * instances of this class are only immutable - * if their contained objects are also immutable. + * + *

Note that in reality, as for all containers, instances of this class are only immutable if + * their contained objects are also immutable. * * @param Type of the first element * @param Type of the second element */ public final class Pair { - private final F first; - private final S second; - - /** - * Create a pair from the specified arguments. - * - * @param first The first element of the pair. - * @param second The second element of the pair. - * - * @return A {@link Pair} containing {@code first} and {@code second}. - */ - public static Pair of(final A first, final B second) { - return new Pair<>(first, second); - } - - /** - * Constructor for a pair of items. - *

- * Please consider using the factory method {@link #of(Object, Object)} - * instead of this constructor. - * - * @param first The first element of the pair. - * @param second The second element of the pair. - */ - public Pair(final F first, final S second) { - this.first = first; - this.second = second; - } - - /** - * Retrieve the first element from this pair. - * - * @return The first element. - */ - public F getFirst() { - return this.first; - } - - /** - * Retrieve the second element from this pair. - * - * @return The second element. - */ - public S getSecond() { - return this.second; - } - - /** - * Maps the first element, returning a new pair with the mapped first - * element, and the original second element. - * - * @param mapper the mapper to apply to the first element - * - * @return the new pair - */ - public Pair mapFirst(Function mapper) { - return Pair.of(mapper.apply(this.first), this.second); - } - - /** - * Maps the second element, returning a new pair with the original first - * element, and the mapped second element. - * - * @param mapper the mapper to apply to the second element - * - * @return the new pair - */ - public Pair mapSecond(Function mapper) { - return Pair.of(this.first, mapper.apply(this.second)); - } - - /** - * Maps both elements into single result. - * - * @param mapper the mapper to apply to both elements - * - * @return result of the mapping - */ - public R map(BiFunction mapper) { - return mapper.apply(first, second); - } - - /** - * Returns {@code true} if the first element is non-null, {@code false} - * otherwise. - * - * @return {@code true} if the first element is non-null, else {@code false} - */ - public boolean firstNonNull() { - return this.first != null; - } - - /** - * Returns {@code true} if the first element is null, {@code false} - * otherwise. - * - * @return {@code true} if the first element is null, else {@code false} - */ - public boolean firstIsNull() { - return this.first == null; - } - - /** - * Returns {@code true} if the second element is non-null, {@code false} - * otherwise. - * - * @return {@code true} if the second element is non-null, else {@code false} - */ - public boolean secondNonNull() { - return this.second != null; - } - - /** - * Returns {@code true} if the second element is null, {@code false} - * otherwise. - * - * @return {@code true} if the second element is null, else {@code false} - */ - public boolean secondIsNull() { - return this.second == null; - } - - @Override - public int hashCode() { - return Objects.hash(this.first, this.second); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!(obj instanceof Pair)) { - return false; - } - - var p = (Pair) obj; - return Objects.equals(this.first, p.first) && Objects.equals(this.second, p.second); - } - - @Override - public String toString() { - return String.format("%s[first=%s, second=%s]", getClass().getSimpleName(), first, second); - } + private final F first; + private final S second; + + /** + * Create a pair from the specified arguments. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + * @return A {@link Pair} containing {@code first} and {@code second}. + */ + public static Pair of(final A first, final B second) { + return new Pair<>(first, second); + } + + /** + * Constructor for a pair of items. + * + *

Please consider using the factory method {@link #of(Object, Object)} instead of this + * constructor. + * + * @param first The first element of the pair. + * @param second The second element of the pair. + */ + public Pair(final F first, final S second) { + this.first = first; + this.second = second; + } + + /** + * Retrieve the first element from this pair. + * + * @return The first element. + */ + public F getFirst() { + return this.first; + } + + /** + * Retrieve the second element from this pair. + * + * @return The second element. + */ + public S getSecond() { + return this.second; + } + + /** + * Maps the first element, returning a new pair with the mapped first element, and the original + * second element. + * + * @param mapper the mapper to apply to the first element + * @return the new pair + */ + public Pair mapFirst(Function mapper) { + return Pair.of(mapper.apply(this.first), this.second); + } + + /** + * Maps the second element, returning a new pair with the original first element, and the mapped + * second element. + * + * @param mapper the mapper to apply to the second element + * @return the new pair + */ + public Pair mapSecond(Function mapper) { + return Pair.of(this.first, mapper.apply(this.second)); + } + + /** + * Maps both elements into single result. + * + * @param mapper the mapper to apply to both elements + * @return result of the mapping + */ + public R map(BiFunction mapper) { + return mapper.apply(first, second); + } + + /** + * Returns {@code true} if the first element is non-null, {@code false} otherwise. + * + * @return {@code true} if the first element is non-null, else {@code false} + */ + public boolean firstNonNull() { + return this.first != null; + } + + /** + * Returns {@code true} if the first element is null, {@code false} otherwise. + * + * @return {@code true} if the first element is null, else {@code false} + */ + public boolean firstIsNull() { + return this.first == null; + } + + /** + * Returns {@code true} if the second element is non-null, {@code false} otherwise. + * + * @return {@code true} if the second element is non-null, else {@code false} + */ + public boolean secondNonNull() { + return this.second != null; + } + + /** + * Returns {@code true} if the second element is null, {@code false} otherwise. + * + * @return {@code true} if the second element is null, else {@code false} + */ + public boolean secondIsNull() { + return this.second == null; + } + + @Override + public int hashCode() { + return Objects.hash(this.first, this.second); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof Pair)) { + return false; + } + + var p = (Pair) obj; + return Objects.equals(this.first, p.first) && Objects.equals(this.second, p.second); + } + + @Override + public String toString() { + return String.format("%s[first=%s, second=%s]", getClass().getSimpleName(), first, second); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/PrivateKeys.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/PrivateKeys.java index c077753d1d..5cf486719c 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/PrivateKeys.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/PrivateKeys.java @@ -67,33 +67,29 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; - import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; import java.util.stream.Stream; -/** - * For testing - */ +/** For testing */ public final class PrivateKeys { - private PrivateKeys() { - throw new IllegalStateException("Cannot instantiate."); - } + private PrivateKeys() { + throw new IllegalStateException("Cannot instantiate."); + } - public static ECKeyPair ofNumeric(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + public static ECKeyPair ofNumeric(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } - public static Stream numeric(int start) { - var nextGeneratedKey = new AtomicInteger(start); - return IntStream.generate(() -> nextGeneratedKey.getAndAdd(1)) - .mapToObj(PrivateKeys::ofNumeric); - } + public static Stream numeric(int start) { + var nextGeneratedKey = new AtomicInteger(start); + return IntStream.generate(() -> nextGeneratedKey.getAndAdd(1)).mapToObj(PrivateKeys::ofNumeric); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/RadixConstants.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/RadixConstants.java index 18019a68fe..5e006b5218 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/RadixConstants.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/RadixConstants.java @@ -74,10 +74,10 @@ */ public final class RadixConstants { - private RadixConstants() { - throw new IllegalStateException("Can't construct."); - } + private RadixConstants() { + throw new IllegalStateException("Can't construct."); + } - /** Default {@link Charset} to use when converting to/from bytes. */ - public static final Charset STANDARD_CHARSET = StandardCharsets.UTF_8; + /** Default {@link Charset} to use when converting to/from bytes. */ + public static final Charset STANDARD_CHARSET = StandardCharsets.UTF_8; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/RuntimeUtils.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/RuntimeUtils.java index ea0c86d45f..18ebcba68b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/RuntimeUtils.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/RuntimeUtils.java @@ -66,19 +66,19 @@ public final class RuntimeUtils { - // Taken from BitcoinJ implementation - // https://github.com/bitcoinj/bitcoinj/blob/3cb1f6c6c589f84fe6e1fb56bf26d94cccc85429/core/src/main/java/org/bitcoinj/core/Utils.java#L573 - private static int isAndroid = -1; + // Taken from BitcoinJ implementation + // https://github.com/bitcoinj/bitcoinj/blob/3cb1f6c6c589f84fe6e1fb56bf26d94cccc85429/core/src/main/java/org/bitcoinj/core/Utils.java#L573 + private static int isAndroid = -1; - public static boolean isAndroidRuntime() { - if (isAndroid == -1) { - final String runtime = System.getProperty("java.runtime.name"); - isAndroid = (runtime != null && runtime.equals("Android Runtime")) ? 1 : 0; - } - return isAndroid == 1; - } + public static boolean isAndroidRuntime() { + if (isAndroid == -1) { + final String runtime = System.getProperty("java.runtime.name"); + isAndroid = (runtime != null && runtime.equals("Android Runtime")) ? 1 : 0; + } + return isAndroid == 1; + } - private RuntimeUtils() { - // cannot construct - } + private RuntimeUtils() { + // cannot construct + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Shorts.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Shorts.java index 97d0017b8f..5131e2b57b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Shorts.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Shorts.java @@ -1,97 +1,94 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.utils; - -public final class Shorts { - private Shorts() { - throw new IllegalStateException("Can't construct"); - } - - public static byte[] toByteArray(short value) { - return new byte[] { - (byte) (value >> 8), - (byte) value - }; - } - - public static short fromByteArray(byte[] bytes) { - if (bytes == null || bytes.length == 0) { - throw new IllegalArgumentException("Array is null or has zero length for 'int' conversion"); - } - - int length = Math.min(bytes.length, Short.BYTES); - short value = 0; - - for (int b = bytes.length - length; b < bytes.length; b++) { - value |= (bytes[b] & 0xFFL); - - if (b < bytes.length - 1) { - value = (short) (value << 8); - } - } - - return value; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.utils; + +public final class Shorts { + private Shorts() { + throw new IllegalStateException("Can't construct"); + } + + public static byte[] toByteArray(short value) { + return new byte[] {(byte) (value >> 8), (byte) value}; + } + + public static short fromByteArray(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + throw new IllegalArgumentException("Array is null or has zero length for 'int' conversion"); + } + + int length = Math.min(bytes.length, Short.BYTES); + short value = 0; + + for (int b = bytes.length - length; b < bytes.length; b++) { + value |= (bytes[b] & 0xFFL); + + if (b < bytes.length - 1) { + value = (short) (value << 8); + } + } + + return value; + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Strings.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Strings.java index d9906dbe8a..7dde69549d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/Strings.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/Strings.java @@ -64,55 +64,48 @@ package com.radixdlt.utils; -/** - * Some useful string handling methods, currently mostly here - * for performance reasons. - */ +/** Some useful string handling methods, currently mostly here for performance reasons. */ public final class Strings { - private Strings() { - throw new IllegalStateException("Can't construct"); - } + private Strings() { + throw new IllegalStateException("Can't construct"); + } - /** - * Brutally convert a string to a sequence of ASCII bytes by - * discarding all but the lower 7 bits of each {@code char} in - * {@code s}. - *

- * The primary purpose of this method is to implement a speedy - * converter between strings and bytes where characters are - * known to be limited to the ASCII character set. - *

- * Note that the output will consume exactly {@code s.length()} - * bytes. - * - * @param s The string to convert. - * @param bytes The buffer to place the converted bytes into. - * @param ofs The offset within the buffer to place the converted bytes. - * @return The offset within the buffer immediately past the converted string. - */ - public static int toAsciiBytes(String s, byte[] bytes, int ofs) { - for (int i = 0; i < s.length(); ++i) { - bytes[ofs++] = (byte) (s.charAt(i) & 0x7F); - } - return ofs; - } + /** + * Brutally convert a string to a sequence of ASCII bytes by discarding all but the lower 7 bits + * of each {@code char} in {@code s}. + * + *

The primary purpose of this method is to implement a speedy converter between strings and + * bytes where characters are known to be limited to the ASCII character set. + * + *

Note that the output will consume exactly {@code s.length()} bytes. + * + * @param s The string to convert. + * @param bytes The buffer to place the converted bytes into. + * @param ofs The offset within the buffer to place the converted bytes. + * @return The offset within the buffer immediately past the converted string. + */ + public static int toAsciiBytes(String s, byte[] bytes, int ofs) { + for (int i = 0; i < s.length(); ++i) { + bytes[ofs++] = (byte) (s.charAt(i) & 0x7F); + } + return ofs; + } - /** - * Convert a sequence of ASCII bytes into a string. Note that - * no bounds checking is performed on the incoming bytes — - * the upper bit is silently discarded. - * - * @param bytes The buffer to convert to a string. - * @param ofs The offset within the buffer to start conversion. - * @param len The number of bytes to convert. - * @return A {@link String} of length {@code len}. - */ - public static String fromAsciiBytes(byte[] bytes, int ofs, int len) { - char[] chars = new char[len]; - for (int i = 0; i < len; ++i) { - chars[i] = (char) (bytes[ofs + i] & 0x7F); - } - return new String(chars); - } + /** + * Convert a sequence of ASCII bytes into a string. Note that no bounds checking is performed on + * the incoming bytes — the upper bit is silently discarded. + * + * @param bytes The buffer to convert to a string. + * @param ofs The offset within the buffer to start conversion. + * @param len The number of bytes to convert. + * @return A {@link String} of length {@code len}. + */ + public static String fromAsciiBytes(byte[] bytes, int ofs, int len) { + char[] chars = new char[len]; + for (int i = 0; i < len; ++i) { + chars[i] = (char) (bytes[ofs + i] & 0x7F); + } + return new String(chars); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt128.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt128.java index c3d8fa464c..e998f0c9ba 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt128.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt128.java @@ -64,818 +64,810 @@ package com.radixdlt.utils; -import java.util.Arrays; -import java.util.Objects; - import com.radixdlt.SecurityCritical; import com.radixdlt.SecurityCritical.SecurityKind; +import java.util.Arrays; +import java.util.Objects; -/** - * A 128-bit unsigned integer, with comparison and some basic arithmetic - * operations. - */ +/** A 128-bit unsigned integer, with comparison and some basic arithmetic operations. */ @SecurityCritical(SecurityKind.NUMERIC) public final class UInt128 implements Comparable { - // Some sizing constants in line with Integer, Long etc - /** - * Size of this numeric type in bits. - */ - public static final int SIZE = Integer.SIZE * 4; - /** - * Size of this numeric type in bytes. - */ - public static final int BYTES = Integer.BYTES * 4; - - /** - * A constant holding the minimum value an {@code UInt128} can - * have, 0. - */ - public static final UInt128 MIN_VALUE = new UInt128(0x0000_0000_0000_0000L, 0x0000_0000_0000_0000L); - - /** - * A constant holding the maximum value an {@code UInt128} can - * have, 2128-1. - */ - public static final UInt128 MAX_VALUE = new UInt128(0xFFFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFFFL); - - // Some commonly used values - public static final UInt128 ZERO = new UInt128(0L, 0L); - public static final UInt128 ONE = new UInt128(0L, 1L); - public static final UInt128 TWO = new UInt128(0L, 2L); - public static final UInt128 THREE = new UInt128(0L, 3L); - public static final UInt128 FOUR = new UInt128(0L, 4L); - public static final UInt128 FIVE = new UInt128(0L, 5L); - public static final UInt128 SIX = new UInt128(0L, 6L); - public static final UInt128 SEVEN = new UInt128(0L, 7L); - public static final UInt128 EIGHT = new UInt128(0L, 8L); - public static final UInt128 NINE = new UInt128(0L, 9L); - public static final UInt128 TEN = new UInt128(0L, 10L); - - /** - * Number with the highest bit set. Value is equal to 2127. - */ - public static final UInt128 HIGH_BIT = new UInt128(0x8000_0000_0000_0000L, 0); - - // Numbers in order. This is used by factory methods. - private static final UInt128[] numbers = { - ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN - }; - - // Mask of int bits as a long - private static final long INT_MASK = (1L << Integer.SIZE) - 1L; - - // The actual value. - // We use integers here so that we can use double-wide long multiplication. - // This significantly improves performance of the multiply operation. - final int high; - final int midHigh; - final int midLow; - final int low; - - /** - * Factory method for materialising an {@link UInt128} from a {@code short} - * value. - * - * @param value The value to be represented as an {@link UInt128}. - * @return {@code value} as an {@link UInt128} type. - */ - public static UInt128 from(short value) { - return from(value & 0xFFFF); - } - - /** - * Factory method for materialising an {@link UInt128} from an {@code int} value. - * - * @param value The value to be represented as an {@link UInt128}. - * @return {@code value} as an {@link UInt128} type. - */ - public static UInt128 from(int value) { - return from(value & 0xFFFF_FFFFL); - } - - /** - * Factory method for materialising an {@link UInt128} from a {@code long} value. - * Note that values are zero extended into the 128 bit value. - * - * @param value The value to be represented as an {@link UInt128}. - * @return {@code value} as an {@link UInt128} type. - */ - public static UInt128 from(long value) { - // Assume unsigned long - return from(0L, value); - } - - /** - * Factory method for materialising an {@link UInt128} from two {@code long} - * values. {@code high} is the most significant word, and {@code low} the least - * significant. - * - * @param high The most significant word of the 128 bit value. - * @param low The least significant word of the 128 bit value. - * @return {@code (high << 64) | low} as an {@link UInt128} type. - */ - public static UInt128 from(long high, long low) { - return from((int) (high >>> Integer.SIZE), (int) high, (int) (low >>> Integer.SIZE), (int) low); - } - - /** - * Factory method for materialising an {@link UInt128} from four {@code int} - * values. {@code high} is the most significant word, and {@code low} the least - * significant. - * - * @param high The most significant word of the 128 bit value. - * @param low The least significant word of the 128 bit value. - * @return {@code (high << 64) | low} as an {@link UInt128} type. - */ - public static UInt128 from(int high, int midHigh, int midLow, int low) { - if (high == 0 && midHigh == 0 && midLow == 0) { - if (low >= 0L && low < numbers.length) { - return numbers[low]; - } - } - return new UInt128(high, midHigh, midLow, low); - } - - /** - * Factory method for materialising an {@link UInt128} from an array - * of bytes. The array is most-significant byte first, and must not be - * zero length. - *

If the array is smaller than {@link #BYTES}, then it is effectively - * padded with leading zero bytes. - *

If the array is longer than {@link #BYTES}, then values at index - * {@link #BYTES} and beyond are ignored. - * - * @param bytes The array of bytes to be used. - * @return {@code bytes} as an {@link UInt128} type. - * @throws IllegalArgumentException if {@code bytes} is 0 length. - * @see #toByteArray() - */ - public static UInt128 from(byte[] bytes) { - Objects.requireNonNull(bytes); - if (bytes.length == 0) { - throw new IllegalArgumentException("bytes is 0 bytes long"); - } - byte[] newBytes = extend(bytes); - return from(newBytes, 0); - } - - /** - * Factory method for materialising an {@link UInt128} from an array - * of bytes. The array is most-significant byte first. - * - * @param bytes The array of bytes to be used. - * @param offset The offset within the array to be used. - * @return {@code bytes} from {@code offset} as an {@link UInt128} type. - * @see #toByteArray() - */ - public static UInt128 from(byte[] bytes, int offset) { - long high = Longs.fromByteArray(bytes, offset); - long low = Longs.fromByteArray(bytes, offset + Long.BYTES); - return from(high, low); - } - - /** - * Factory method for materialising an {@link UInt128} from a string. - * Conversion is performed base 10 and leading '+' sign character is - * permitted. - * - * @param s The array of bytes to be used. - * @return {@code s} as an {@link UInt128} type. - * @throws NumberFormatException if {@code s} is not a valid - * integer number. - */ - public static UInt128 from(String s) { - Objects.requireNonNull(s); - - int len = s.length(); - if (len > 0) { - int i = 0; - char ch = s.charAt(0); - if (ch == '+') { - i += 1; // skip first char - } - if (i >= len) { - throw new NumberFormatException(s); - } - // No real effort to catch overflow here - UInt128 result = UInt128.ZERO; - while (i < len) { - int digit = Character.digit(s.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException(s); - } - result = result.multiply(UInt128.TEN).add(numbers[digit]); - } - return result; - } else { - throw new NumberFormatException(s); - } - } - - // Pad short (< BYTES length) array with appropriate lead bytes. - private static byte[] extend(byte[] bytes) { - if (bytes.length >= BYTES) { - return bytes; - } - byte[] newBytes = new byte[BYTES]; - int newPos = BYTES - bytes.length; - // Zero extension - Arrays.fill(newBytes, 0, newPos, (byte) 0); - System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); - return newBytes; - } - - private UInt128(long high, long low) { - this((int) (high >>> Integer.SIZE), (int) high, (int) (low >>> Integer.SIZE), (int) low); - } - - private UInt128(int high, int midHigh, int midLow, int low) { - this.high = high; - this.midHigh = midHigh; - this.midLow = midLow; - this.low = low; - } - - /** - * Convert value to an array of bytes. - * The most significant byte will be returned in index zero. - * The array will always be {@link #BYTES} bytes long, and - * will be zero filled to suit the actual value. - * - * @return An array of {@link #BYTES} bytes representing the - * value of this {@link UInt128}. - */ - public byte[] toByteArray() { - return toByteArray(new byte[BYTES], 0); - } - - /** - * Convert value to an array of bytes. - * The most significant byte will be returned in index {@code offset}. - * The array must be at least {@code offset + BYTES} long. - * - * @param bytes The array to place the bytes in. - * @param offset The offset within the array to place the bytes. - * @return The passed-in value of {@code bytes}. - */ - public byte[] toByteArray(byte[] bytes, int offset) { - Ints.copyTo(this.high, bytes, offset); - Ints.copyTo(this.midHigh, bytes, offset + Integer.BYTES); - Ints.copyTo(this.midLow, bytes, offset + 2 * Integer.BYTES); - Ints.copyTo(this.low, bytes, offset + 3 * Integer.BYTES); - return bytes; - } - - /** - * Add {@code other} to {@code this}, returning the result. - * - * @param other The addend. - * @return An {@link UInt128} with the value {@code this + other}. - */ - public UInt128 add(UInt128 other) { - long partial0 = (this.low & INT_MASK) + (other.low & INT_MASK); - long partial1 = (this.midLow & INT_MASK) + (other.midLow & INT_MASK) + (partial0 >>> Integer.SIZE); - long partial2 = (this.midHigh & INT_MASK) + (other.midHigh & INT_MASK) + (partial1 >>> Integer.SIZE); - long partial3 = (this.high & INT_MASK) + (other.high & INT_MASK) + (partial2 >>> Integer.SIZE); - return UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0); - } - - /** - * Subtract {@code other} from {@code this}, returning the result. - * - * @param other The subtrahend. - * @return An {@link UInt128} with the value {@code this - other}. - */ - public UInt128 subtract(UInt128 other) { - long partial0 = (this.low & INT_MASK) - (other.low & INT_MASK); - long partial1 = (this.midLow & INT_MASK) - (other.midLow & INT_MASK) + (partial0 >> Integer.SIZE); - long partial2 = (this.midHigh & INT_MASK) - (other.midHigh & INT_MASK) + (partial1 >> Integer.SIZE); - long partial3 = (this.high & INT_MASK) - (other.high & INT_MASK) + (partial2 >> Integer.SIZE); - return UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0); - } - - /** - * Increment a number. Equivalent to {@code this.add(UInt128.ONE)}, but - * faster. - * - * @return This number incremented by one. - */ - public UInt128 increment() { - int l = this.low + 1; - int ml = this.midLow; - int mh = this.midHigh; - int h = this.high; - if (l == 0) { - ml += 1; - if (ml == 0) { - mh += 1; - if (mh == 0) { - h += 1; - } - } - } - return UInt128.from(h, mh, ml, l); - } - - /** - * Decrement a number. Equivalent to {@code this.subtract(UInt128.ONE)}, but - * faster. - * - * @return This number decremented by one. - */ - public UInt128 decrement() { - int l = this.low - 1; - int ml = this.midLow; - int mh = this.midHigh; - int h = this.high; - if (l == -1) { - ml -= 1; - if (ml == -1) { - mh -= 1; - if (mh == -1) { - h -= 1; - } - } - } - return UInt128.from(h, mh, ml, l); - } - - /** - * Multiply {@code this} by the specified multiplicand. - * - * @param multiplicand The multiplicand to multiply {@code this} by. - * @return The result {@code this * multiplicand}. - */ - public UInt128 multiply(UInt128 multiplicand) { - // I appreciate that this looks like a wall of code, and it is, - // but the underlying algorithm is long multiplication base 2^32. - - // Avoid field access ops - long tlow = (this.low & INT_MASK); - long tmidlow = (this.midLow & INT_MASK); - long tmidhigh = (this.midHigh & INT_MASK); - - long llow = (multiplicand.low & INT_MASK); - long partial00 = tlow * llow; - long partial01 = tmidlow * llow + (partial00 >>> Integer.SIZE); - long partial02 = tmidhigh * llow + (partial01 >>> Integer.SIZE); - long partial03 = (this.high & INT_MASK) * llow + (partial02 >>> Integer.SIZE); - - long lmidlow = (multiplicand.midLow & INT_MASK); - long partial10 = tlow * lmidlow; - long partial11 = tmidlow * lmidlow + (partial10 >>> Integer.SIZE); - long partial12 = tmidhigh * lmidlow + (partial11 >>> Integer.SIZE); - - long lmidhigh = (multiplicand.midHigh & INT_MASK); - long partial20 = tlow * lmidhigh; - long partial21 = tmidlow * lmidhigh + (partial20 >>> Integer.SIZE); - - long partial30 = tlow * (multiplicand.high & INT_MASK); - - long ll = (partial00 & INT_MASK); - long ml = (partial10 & INT_MASK) + (partial01 & INT_MASK); - long mh = (partial20 & INT_MASK) + (partial11 & INT_MASK) + (partial02 & INT_MASK) + (ml >>> Integer.SIZE); - long hh = (partial30 & INT_MASK) + (partial21 & INT_MASK) + (partial12 & INT_MASK) + (partial03 & INT_MASK) + (mh >>> Integer.SIZE); - - return UInt128.from((int) hh, (int) mh, (int) ml, (int) ll); - } - - /** - * Divide {@code this} by the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * @return The result {@code floor(this / divisor)}. - */ - public UInt128 divide(UInt128 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - // Some special cases - if (this.isZero()) { - return ZERO; - } - int cmp = this.compareTo(divisor); - if (cmp < 0) { - return ZERO; + // Some sizing constants in line with Integer, Long etc + /** Size of this numeric type in bits. */ + public static final int SIZE = Integer.SIZE * 4; + /** Size of this numeric type in bytes. */ + public static final int BYTES = Integer.BYTES * 4; + + /** A constant holding the minimum value an {@code UInt128} can have, 0. */ + public static final UInt128 MIN_VALUE = + new UInt128(0x0000_0000_0000_0000L, 0x0000_0000_0000_0000L); + + /** A constant holding the maximum value an {@code UInt128} can have, 2128-1. */ + public static final UInt128 MAX_VALUE = + new UInt128(0xFFFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFFFL); + + // Some commonly used values + public static final UInt128 ZERO = new UInt128(0L, 0L); + public static final UInt128 ONE = new UInt128(0L, 1L); + public static final UInt128 TWO = new UInt128(0L, 2L); + public static final UInt128 THREE = new UInt128(0L, 3L); + public static final UInt128 FOUR = new UInt128(0L, 4L); + public static final UInt128 FIVE = new UInt128(0L, 5L); + public static final UInt128 SIX = new UInt128(0L, 6L); + public static final UInt128 SEVEN = new UInt128(0L, 7L); + public static final UInt128 EIGHT = new UInt128(0L, 8L); + public static final UInt128 NINE = new UInt128(0L, 9L); + public static final UInt128 TEN = new UInt128(0L, 10L); + + /** Number with the highest bit set. Value is equal to 2127. */ + public static final UInt128 HIGH_BIT = new UInt128(0x8000_0000_0000_0000L, 0); + + // Numbers in order. This is used by factory methods. + private static final UInt128[] numbers = { + ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN + }; + + // Mask of int bits as a long + private static final long INT_MASK = (1L << Integer.SIZE) - 1L; + + // The actual value. + // We use integers here so that we can use double-wide long multiplication. + // This significantly improves performance of the multiply operation. + final int high; + final int midHigh; + final int midLow; + final int low; + + /** + * Factory method for materialising an {@link UInt128} from a {@code short} value. + * + * @param value The value to be represented as an {@link UInt128}. + * @return {@code value} as an {@link UInt128} type. + */ + public static UInt128 from(short value) { + return from(value & 0xFFFF); + } + + /** + * Factory method for materialising an {@link UInt128} from an {@code int} value. + * + * @param value The value to be represented as an {@link UInt128}. + * @return {@code value} as an {@link UInt128} type. + */ + public static UInt128 from(int value) { + return from(value & 0xFFFF_FFFFL); + } + + /** + * Factory method for materialising an {@link UInt128} from a {@code long} value. Note that values + * are zero extended into the 128 bit value. + * + * @param value The value to be represented as an {@link UInt128}. + * @return {@code value} as an {@link UInt128} type. + */ + public static UInt128 from(long value) { + // Assume unsigned long + return from(0L, value); + } + + /** + * Factory method for materialising an {@link UInt128} from two {@code long} values. {@code high} + * is the most significant word, and {@code low} the least significant. + * + * @param high The most significant word of the 128 bit value. + * @param low The least significant word of the 128 bit value. + * @return {@code (high << 64) | low} as an {@link UInt128} type. + */ + public static UInt128 from(long high, long low) { + return from((int) (high >>> Integer.SIZE), (int) high, (int) (low >>> Integer.SIZE), (int) low); + } + + /** + * Factory method for materialising an {@link UInt128} from four {@code int} values. {@code high} + * is the most significant word, and {@code low} the least significant. + * + * @param high The most significant word of the 128 bit value. + * @param low The least significant word of the 128 bit value. + * @return {@code (high << 64) | low} as an {@link UInt128} type. + */ + public static UInt128 from(int high, int midHigh, int midLow, int low) { + if (high == 0 && midHigh == 0 && midLow == 0) { + if (low >= 0L && low < numbers.length) { + return numbers[low]; + } + } + return new UInt128(high, midHigh, midLow, low); + } + + /** + * Factory method for materialising an {@link UInt128} from an array of bytes. The array is + * most-significant byte first, and must not be zero length. + * + *

If the array is smaller than {@link #BYTES}, then it is effectively padded with leading zero + * bytes. + * + *

If the array is longer than {@link #BYTES}, then values at index {@link #BYTES} and beyond + * are ignored. + * + * @param bytes The array of bytes to be used. + * @return {@code bytes} as an {@link UInt128} type. + * @throws IllegalArgumentException if {@code bytes} is 0 length. + * @see #toByteArray() + */ + public static UInt128 from(byte[] bytes) { + Objects.requireNonNull(bytes); + if (bytes.length == 0) { + throw new IllegalArgumentException("bytes is 0 bytes long"); + } + byte[] newBytes = extend(bytes); + return from(newBytes, 0); + } + + /** + * Factory method for materialising an {@link UInt128} from an array of bytes. The array is + * most-significant byte first. + * + * @param bytes The array of bytes to be used. + * @param offset The offset within the array to be used. + * @return {@code bytes} from {@code offset} as an {@link UInt128} type. + * @see #toByteArray() + */ + public static UInt128 from(byte[] bytes, int offset) { + long high = Longs.fromByteArray(bytes, offset); + long low = Longs.fromByteArray(bytes, offset + Long.BYTES); + return from(high, low); + } + + /** + * Factory method for materialising an {@link UInt128} from a string. Conversion is performed base + * 10 and leading '+' sign character is permitted. + * + * @param s The array of bytes to be used. + * @return {@code s} as an {@link UInt128} type. + * @throws NumberFormatException if {@code s} is not a valid integer number. + */ + public static UInt128 from(String s) { + Objects.requireNonNull(s); + + int len = s.length(); + if (len > 0) { + int i = 0; + char ch = s.charAt(0); + if (ch == '+') { + i += 1; // skip first char + } + if (i >= len) { + throw new NumberFormatException(s); + } + // No real effort to catch overflow here + UInt128 result = UInt128.ZERO; + while (i < len) { + int digit = Character.digit(s.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException(s); } - if (cmp == 0) { - return ONE; - } - - UInt128 q = UInt128.ZERO; - UInt128 r = UInt128.ZERO; - UInt128 n = this; - for (int i = 0; i < SIZE; ++i) { - r = r.shiftLeft(); - q = q.shiftLeft(); - if (n.high < 0) { - r = r.or(UInt128.ONE); - } - n = n.shiftLeft(); - if (r.compareTo(divisor) >= 0) { - r = r.subtract(divisor); - q = q.or(UInt128.ONE); - } - } - return q; - } - - /** - * Return the remainder of the division of {@code this} by - * the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * @return The remainder of the division {@code this / divisor}. - */ - public UInt128 remainder(UInt128 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - // Some special cases - if (this.isZero()) { - return ZERO; - } - int cmp = this.compareTo(divisor); - if (cmp < 0) { - return this; + result = result.multiply(UInt128.TEN).add(numbers[digit]); + } + return result; + } else { + throw new NumberFormatException(s); + } + } + + // Pad short (< BYTES length) array with appropriate lead bytes. + private static byte[] extend(byte[] bytes) { + if (bytes.length >= BYTES) { + return bytes; + } + byte[] newBytes = new byte[BYTES]; + int newPos = BYTES - bytes.length; + // Zero extension + Arrays.fill(newBytes, 0, newPos, (byte) 0); + System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); + return newBytes; + } + + private UInt128(long high, long low) { + this((int) (high >>> Integer.SIZE), (int) high, (int) (low >>> Integer.SIZE), (int) low); + } + + private UInt128(int high, int midHigh, int midLow, int low) { + this.high = high; + this.midHigh = midHigh; + this.midLow = midLow; + this.low = low; + } + + /** + * Convert value to an array of bytes. The most significant byte will be returned in index zero. + * The array will always be {@link #BYTES} bytes long, and will be zero filled to suit the actual + * value. + * + * @return An array of {@link #BYTES} bytes representing the value of this {@link UInt128}. + */ + public byte[] toByteArray() { + return toByteArray(new byte[BYTES], 0); + } + + /** + * Convert value to an array of bytes. The most significant byte will be returned in index {@code + * offset}. The array must be at least {@code offset + BYTES} long. + * + * @param bytes The array to place the bytes in. + * @param offset The offset within the array to place the bytes. + * @return The passed-in value of {@code bytes}. + */ + public byte[] toByteArray(byte[] bytes, int offset) { + Ints.copyTo(this.high, bytes, offset); + Ints.copyTo(this.midHigh, bytes, offset + Integer.BYTES); + Ints.copyTo(this.midLow, bytes, offset + 2 * Integer.BYTES); + Ints.copyTo(this.low, bytes, offset + 3 * Integer.BYTES); + return bytes; + } + + /** + * Add {@code other} to {@code this}, returning the result. + * + * @param other The addend. + * @return An {@link UInt128} with the value {@code this + other}. + */ + public UInt128 add(UInt128 other) { + long partial0 = (this.low & INT_MASK) + (other.low & INT_MASK); + long partial1 = + (this.midLow & INT_MASK) + (other.midLow & INT_MASK) + (partial0 >>> Integer.SIZE); + long partial2 = + (this.midHigh & INT_MASK) + (other.midHigh & INT_MASK) + (partial1 >>> Integer.SIZE); + long partial3 = (this.high & INT_MASK) + (other.high & INT_MASK) + (partial2 >>> Integer.SIZE); + return UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0); + } + + /** + * Subtract {@code other} from {@code this}, returning the result. + * + * @param other The subtrahend. + * @return An {@link UInt128} with the value {@code this - other}. + */ + public UInt128 subtract(UInt128 other) { + long partial0 = (this.low & INT_MASK) - (other.low & INT_MASK); + long partial1 = + (this.midLow & INT_MASK) - (other.midLow & INT_MASK) + (partial0 >> Integer.SIZE); + long partial2 = + (this.midHigh & INT_MASK) - (other.midHigh & INT_MASK) + (partial1 >> Integer.SIZE); + long partial3 = (this.high & INT_MASK) - (other.high & INT_MASK) + (partial2 >> Integer.SIZE); + return UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0); + } + + /** + * Increment a number. Equivalent to {@code this.add(UInt128.ONE)}, but faster. + * + * @return This number incremented by one. + */ + public UInt128 increment() { + int l = this.low + 1; + int ml = this.midLow; + int mh = this.midHigh; + int h = this.high; + if (l == 0) { + ml += 1; + if (ml == 0) { + mh += 1; + if (mh == 0) { + h += 1; } - if (cmp == 0) { - return ZERO; + } + } + return UInt128.from(h, mh, ml, l); + } + + /** + * Decrement a number. Equivalent to {@code this.subtract(UInt128.ONE)}, but faster. + * + * @return This number decremented by one. + */ + public UInt128 decrement() { + int l = this.low - 1; + int ml = this.midLow; + int mh = this.midHigh; + int h = this.high; + if (l == -1) { + ml -= 1; + if (ml == -1) { + mh -= 1; + if (mh == -1) { + h -= 1; } - UInt128 r = UInt128.ZERO; - UInt128 n = this; - for (int i = 0; i < SIZE; ++i) { - r = r.shiftLeft(); - if (n.high < 0) { - r = r.or(UInt128.ONE); - } - n = n.shiftLeft(); - if (r.compareTo(divisor) >= 0) { - r = r.subtract(divisor); - } - } - return r; - } - - /** - * Calculates {@code this}{@code exp}. - * - * @param exp the exponent to raise {@code this} to - * @return {@code this}{@code exp} - */ - public UInt128 pow(int exp) { - if (exp < 0) { - throw new IllegalArgumentException("exp must be >= 0"); - } - - // Mirrors algorithm in multiply(...) - UInt128 result = UInt128.ONE; - UInt128 base = this; - - while (exp != 0) { - if ((exp & 1) != 0) { - result = result.multiply(base); - } - - base = base.multiply(base); - exp >>>= 1; - } - return result; - } - - /** - * Calculates the integer square root, that is the largest number {@code n} - * such that {@code n * n <= this}. - * - * @return The integer square root. - */ - public UInt128 isqrt() { - // This is a decently performing isqrt given that the division operator is quite - // slow (circa 200x slower than multiply). If a faster division operator is - // implemented, then the Newton-Raphson method with double approximation might - // prove to be faster. - UInt128 bit = UInt128.ONE.shiftLeft(SIZE - 2); - - // "bit" starts at the highest power of four <= this - UInt128 num = this; - while (bit.compareTo(num) > 0) { - bit = bit.shiftRight(2); - } - - UInt128 res = UInt128.ZERO; - while (!bit.isZero()) { - UInt128 rab = res.add(bit); - if (num.compareTo(rab) >= 0) { - num = num.subtract(rab); - res = res.shiftRight().add(bit); - } else { - res = res.shiftRight(); - } - bit = bit.shiftRight(2); - } - return res; - } - - /** - * Shift {@code this} left 1 bit. A zero bit is moved into the - * rightmost bit. - * - * @return The result of shifting {@code this} left one bit. - */ - public UInt128 shiftLeft() { - int hh = (this.high << 1) | (this.midHigh >>> (Integer.SIZE - 1)); - int mh = (this.midHigh << 1) | (this.midLow >>> (Integer.SIZE - 1)); - int ml = (this.midLow << 1) | (this.low >>> (Integer.SIZE - 1)); - int ll = this.low << 1; - return UInt128.from(hh, mh, ml, ll); - } - - /** - * Shift {@code this} left n bits. A zero bit is moved into the - * rightmost bits. - * - * @return The result of shifting {@code this} left n bits. - */ - public UInt128 shiftLeft(int n) { - if (n == 0) { - return this; - } else if (n >= SIZE || n <= -SIZE) { - return ZERO; // All bits are gone - } else if (n < 0) { - return shiftRight(-n); // -ve left shift is right shift - } - long h = (n >= Long.SIZE) ? this.getLow() : this.getHigh(); - long l = (n >= Long.SIZE) ? 0 : this.getLow(); - int r = n % Long.SIZE; - if (r > 0) { - long c = l >>> (Long.SIZE - r); - h = (h << r) | c; - l <<= r; - } - return UInt128.from(h, l); - } - - /** - * Logical shift {@code this} right 1 bit. Zeros are shifted into - * the leftmost bit. - * - * @return The result of logical shifting {@code this} right one bit. - */ - public UInt128 shiftRight() { - int hh = this.high >>> 1; - int mh = (this.midHigh >>> 1) | (this.high << (Integer.SIZE - 1)); - int ml = (this.midLow >>> 1) | (this.midHigh << (Integer.SIZE - 1)); - int ll = (this.low >>> 1) | (this.midLow << (Integer.SIZE - 1)); - return UInt128.from(hh, mh, ml, ll); - } - - /** - * Logical shift {@code this} right n bits. Zeros are shifted into - * the leftmost bits. - * - * @return The result of logical shifting {@code this} right n bits. - */ - public UInt128 shiftRight(int n) { - if (n == 0) { - return this; - } else if (n >= SIZE || n <= -SIZE) { - return ZERO; // All bits are gone - } else if (n < 0) { - return shiftLeft(-n); // -ve right shift is left shift - } - long h = (n >= Long.SIZE) ? 0L : this.getHigh(); - long l = (n >= Long.SIZE) ? this.getHigh() : this.getLow(); - int r = n % Long.SIZE; - if (r > 0) { - long c = h << (Long.SIZE - r); - h >>>= r; - l = (l >>> r) | c; - } - return UInt128.from(h, l); - } - - /** - * Return the value of {@code ~this}. - * - * @return The logical inverse of {@code this}. - */ - public UInt128 invert() { - return UInt128.from(~this.high, ~this.midHigh, ~this.midLow, ~this.low); - } - - @Override - public int compareTo(UInt128 n) { - int cmp = Long.compareUnsigned(this.getHigh(), n.getHigh()); - if (cmp == 0) { - cmp = Long.compareUnsigned(this.getLow(), n.getLow()); - } - return cmp; - } - - /** - * Return the most significant word. - * - * @return the most significant word. - */ - public long getHigh() { - return ((this.high & INT_MASK) << Integer.SIZE) | (this.midHigh & INT_MASK); - } - - /** - * Return the least significant word. - * - * @return the least significant word. - */ - public long getLow() { - return ((this.midLow & INT_MASK) << Integer.SIZE) | (this.low & INT_MASK); - } - - /** - * Calculates the bitwise inclusive-or of {@code this} with {@code other} - * ({@code this | other}). - * - * @param other The value to inclusive-or with {@code this}. - * @return {@code this | other} - */ - public UInt128 or(UInt128 other) { - return UInt128.from(this.high | other.high, this.midHigh | other.midHigh, this.midLow | other.midLow, this.low | other.low); - } - - /** - * Calculates the bitwise and of {@code this} with {@code other} - * ({@code this & other}). - * - * @param other The value to and with {@code this}. - * @return {@code this & other} - */ - public UInt128 and(UInt128 other) { - return UInt128.from(this.high & other.high, this.midHigh & other.midHigh, this.midLow & other.midLow, this.low & other.low); - } - - /** - * Calculates the exclusive-or of {@code this} with {@code other} - * ({@code this ^ other}). - * - * @param other The value to exclusive-or with {@code this}. - * @return {@code this ^ other} - */ - public UInt128 xor(UInt128 other) { - return UInt128.from(this.high ^ other.high, this.midHigh ^ other.midHigh, this.midLow ^ other.midLow, this.low ^ other.low); - } - - /** - * Returns the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code long} value. Returns 128 if the - * specified value has no one-bits in its two's complement representation, - * in other words if it is equal to zero. - * - *

Note that this method is closely related to the logarithm base 2. - * For all positive {@code long} values x: - *

    - *
  • floor(log2(x)) = {@code 127 - numberOfLeadingZeros(x)} - *
  • ceil(log2(x)) = {@code 128 - numberOfLeadingZeros(x - 1)} - *
- * - * @return the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code long} value, or 128 if the value - * is equal to zero. - */ - public int numberOfLeadingZeros() { - long highValue = this.getHigh(); - return (highValue == 0) - ? Long.SIZE + Long.numberOfLeadingZeros(this.getLow()) - : Long.numberOfLeadingZeros(highValue); - } - - /** - * Returns the index of the rightmost (lowest-order) one bit in this - * UInt128 (the number of zero bits to the right of the rightmost - * one bit). Returns -1 if this UInt128 contains no one bits. - * (Computes {@code (this == 0? -1 : log2(this & -this))}.) - * - * @return index of the rightmost one bit in this BigInteger. - */ - public int getLowestSetBit() { - // Mirrors java.math.BigInteger#getLowestSetBit() - if (isZero()) { - return -1; - } - int trailingZeros = Long.numberOfTrailingZeros(this.getLow()); - if (trailingZeros == Long.SIZE) { - // Low part is zero - trailingZeros += Long.numberOfTrailingZeros(this.getHigh()); - } - return trailingZeros; - } - - /** - * Return {@code true} if the {@link UInt128} has its high bit set. - * - * @return {@code true} if the {@link UInt128} has its high bit set. - */ - public boolean isHighBitSet() { - return this.high < 0; - } - - /** - * Return {@code true} if {@code this} is zero. - * - * @return {@code true} if {@code this} is zero. - */ - public boolean isZero() { - return this.high == 0 && this.midHigh == 0 && this.midLow == 0 && this.low == 0; - } - - /** - * Return {@code true} if {@code this} is an even number. - * - * @return {@code true} if {@code this} is an even number. - */ - public boolean isEven() { - return (this.low & 1) == 0; - } - - /** - * Return {@code true} if {@code this} is an odd number. - * - * @return {@code true} if {@code this} is an odd number. - */ - public boolean isOdd() { - return (this.low & 1) != 0; - } - - @Override - public int hashCode() { - return this.high * 31 + this.midHigh * 23 + this.midLow * 13 + this.low; - } - - @Override - public boolean equals(Object obj) { - // Note that this needs to be consistent with compareTo - if (this == obj) { - return true; - } - if (obj instanceof UInt128) { - UInt128 other = (UInt128) obj; - return this.high == other.high - && this.midHigh == other.midHigh - && this.midLow == other.midLow - && this.low == other.low; - } - return false; - } - - @Override - public String toString() { - return toString(10); - } - - /** - * Returns a string representation of this object in the specified radix. - *

- * If the radix is smaller than {@code Character.MIN_RADIX} or larger than - * {@code Character.MAX_RADIX}, an {@link IllegalArgumentException} is - * thrown. - *

- * The characters of the result represent the magnitude of {@code this}. - * If the magnitude is zero, it is represented by a single zero character - * {@code '0'}; otherwise no leading zeros are output. - *

- * The following ASCII characters are used as digits: - * - *

- * {@code 0123456789abcdefghijklmnopqrstuvwxyz} - *
- * - * If {@code radix} is N, then the first N of these - * characters are used as radix-N digits in the order shown, - * i.e. the digits for hexadecimal (radix 16) are {@code 0123456789abcdef}. - * - * @param radix the radix to use in the string representation. - * @return a string representation of the argument in the specified radix. - * @see Character#MAX_RADIX - * @throws IllegalArgumentException if {@code radix} is less than - * {@code Character.MIN_RADIX} or greater than - * {@code Character.MAX_RADIX}. - * @see Character#MIN_RADIX - */ - public String toString(int radix) { - if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new IllegalArgumentException("Illegal radix: " + radix); - } - if (isZero()) { - return "0"; - } - StringBuilder sb = new StringBuilder(); - UInt128 n = this; - UInt128 r = UInt128.from(radix); - while (!n.isZero()) { - UInt128 digit = n.remainder(r); - sb.append(Character.forDigit(digit.low, radix)); - n = n.divide(r); - } - return sb.reverse().toString(); - } + } + } + return UInt128.from(h, mh, ml, l); + } + + /** + * Multiply {@code this} by the specified multiplicand. + * + * @param multiplicand The multiplicand to multiply {@code this} by. + * @return The result {@code this * multiplicand}. + */ + public UInt128 multiply(UInt128 multiplicand) { + // I appreciate that this looks like a wall of code, and it is, + // but the underlying algorithm is long multiplication base 2^32. + + // Avoid field access ops + long tlow = (this.low & INT_MASK); + long tmidlow = (this.midLow & INT_MASK); + long tmidhigh = (this.midHigh & INT_MASK); + + long llow = (multiplicand.low & INT_MASK); + long partial00 = tlow * llow; + long partial01 = tmidlow * llow + (partial00 >>> Integer.SIZE); + long partial02 = tmidhigh * llow + (partial01 >>> Integer.SIZE); + long partial03 = (this.high & INT_MASK) * llow + (partial02 >>> Integer.SIZE); + + long lmidlow = (multiplicand.midLow & INT_MASK); + long partial10 = tlow * lmidlow; + long partial11 = tmidlow * lmidlow + (partial10 >>> Integer.SIZE); + long partial12 = tmidhigh * lmidlow + (partial11 >>> Integer.SIZE); + + long lmidhigh = (multiplicand.midHigh & INT_MASK); + long partial20 = tlow * lmidhigh; + long partial21 = tmidlow * lmidhigh + (partial20 >>> Integer.SIZE); + + long partial30 = tlow * (multiplicand.high & INT_MASK); + + long ll = (partial00 & INT_MASK); + long ml = (partial10 & INT_MASK) + (partial01 & INT_MASK); + long mh = + (partial20 & INT_MASK) + + (partial11 & INT_MASK) + + (partial02 & INT_MASK) + + (ml >>> Integer.SIZE); + long hh = + (partial30 & INT_MASK) + + (partial21 & INT_MASK) + + (partial12 & INT_MASK) + + (partial03 & INT_MASK) + + (mh >>> Integer.SIZE); + + return UInt128.from((int) hh, (int) mh, (int) ml, (int) ll); + } + + /** + * Divide {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The result {@code floor(this / divisor)}. + */ + public UInt128 divide(UInt128 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + // Some special cases + if (this.isZero()) { + return ZERO; + } + int cmp = this.compareTo(divisor); + if (cmp < 0) { + return ZERO; + } + if (cmp == 0) { + return ONE; + } + + UInt128 q = UInt128.ZERO; + UInt128 r = UInt128.ZERO; + UInt128 n = this; + for (int i = 0; i < SIZE; ++i) { + r = r.shiftLeft(); + q = q.shiftLeft(); + if (n.high < 0) { + r = r.or(UInt128.ONE); + } + n = n.shiftLeft(); + if (r.compareTo(divisor) >= 0) { + r = r.subtract(divisor); + q = q.or(UInt128.ONE); + } + } + return q; + } + + /** + * Return the remainder of the division of {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The remainder of the division {@code this / divisor}. + */ + public UInt128 remainder(UInt128 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + // Some special cases + if (this.isZero()) { + return ZERO; + } + int cmp = this.compareTo(divisor); + if (cmp < 0) { + return this; + } + if (cmp == 0) { + return ZERO; + } + UInt128 r = UInt128.ZERO; + UInt128 n = this; + for (int i = 0; i < SIZE; ++i) { + r = r.shiftLeft(); + if (n.high < 0) { + r = r.or(UInt128.ONE); + } + n = n.shiftLeft(); + if (r.compareTo(divisor) >= 0) { + r = r.subtract(divisor); + } + } + return r; + } + + /** + * Calculates {@code this}{@code exp}. + * + * @param exp the exponent to raise {@code this} to + * @return {@code this}{@code exp} + */ + public UInt128 pow(int exp) { + if (exp < 0) { + throw new IllegalArgumentException("exp must be >= 0"); + } + + // Mirrors algorithm in multiply(...) + UInt128 result = UInt128.ONE; + UInt128 base = this; + + while (exp != 0) { + if ((exp & 1) != 0) { + result = result.multiply(base); + } + + base = base.multiply(base); + exp >>>= 1; + } + return result; + } + + /** + * Calculates the integer square root, that is the largest number {@code n} such that {@code n * n + * <= this}. + * + * @return The integer square root. + */ + public UInt128 isqrt() { + // This is a decently performing isqrt given that the division operator is quite + // slow (circa 200x slower than multiply). If a faster division operator is + // implemented, then the Newton-Raphson method with double approximation might + // prove to be faster. + UInt128 bit = UInt128.ONE.shiftLeft(SIZE - 2); + + // "bit" starts at the highest power of four <= this + UInt128 num = this; + while (bit.compareTo(num) > 0) { + bit = bit.shiftRight(2); + } + + UInt128 res = UInt128.ZERO; + while (!bit.isZero()) { + UInt128 rab = res.add(bit); + if (num.compareTo(rab) >= 0) { + num = num.subtract(rab); + res = res.shiftRight().add(bit); + } else { + res = res.shiftRight(); + } + bit = bit.shiftRight(2); + } + return res; + } + + /** + * Shift {@code this} left 1 bit. A zero bit is moved into the rightmost bit. + * + * @return The result of shifting {@code this} left one bit. + */ + public UInt128 shiftLeft() { + int hh = (this.high << 1) | (this.midHigh >>> (Integer.SIZE - 1)); + int mh = (this.midHigh << 1) | (this.midLow >>> (Integer.SIZE - 1)); + int ml = (this.midLow << 1) | (this.low >>> (Integer.SIZE - 1)); + int ll = this.low << 1; + return UInt128.from(hh, mh, ml, ll); + } + + /** + * Shift {@code this} left n bits. A zero bit is moved into the rightmost bits. + * + * @return The result of shifting {@code this} left n bits. + */ + public UInt128 shiftLeft(int n) { + if (n == 0) { + return this; + } else if (n >= SIZE || n <= -SIZE) { + return ZERO; // All bits are gone + } else if (n < 0) { + return shiftRight(-n); // -ve left shift is right shift + } + long h = (n >= Long.SIZE) ? this.getLow() : this.getHigh(); + long l = (n >= Long.SIZE) ? 0 : this.getLow(); + int r = n % Long.SIZE; + if (r > 0) { + long c = l >>> (Long.SIZE - r); + h = (h << r) | c; + l <<= r; + } + return UInt128.from(h, l); + } + + /** + * Logical shift {@code this} right 1 bit. Zeros are shifted into the leftmost bit. + * + * @return The result of logical shifting {@code this} right one bit. + */ + public UInt128 shiftRight() { + int hh = this.high >>> 1; + int mh = (this.midHigh >>> 1) | (this.high << (Integer.SIZE - 1)); + int ml = (this.midLow >>> 1) | (this.midHigh << (Integer.SIZE - 1)); + int ll = (this.low >>> 1) | (this.midLow << (Integer.SIZE - 1)); + return UInt128.from(hh, mh, ml, ll); + } + + /** + * Logical shift {@code this} right n bits. Zeros are shifted into the leftmost bits. + * + * @return The result of logical shifting {@code this} right n bits. + */ + public UInt128 shiftRight(int n) { + if (n == 0) { + return this; + } else if (n >= SIZE || n <= -SIZE) { + return ZERO; // All bits are gone + } else if (n < 0) { + return shiftLeft(-n); // -ve right shift is left shift + } + long h = (n >= Long.SIZE) ? 0L : this.getHigh(); + long l = (n >= Long.SIZE) ? this.getHigh() : this.getLow(); + int r = n % Long.SIZE; + if (r > 0) { + long c = h << (Long.SIZE - r); + h >>>= r; + l = (l >>> r) | c; + } + return UInt128.from(h, l); + } + + /** + * Return the value of {@code ~this}. + * + * @return The logical inverse of {@code this}. + */ + public UInt128 invert() { + return UInt128.from(~this.high, ~this.midHigh, ~this.midLow, ~this.low); + } + + @Override + public int compareTo(UInt128 n) { + int cmp = Long.compareUnsigned(this.getHigh(), n.getHigh()); + if (cmp == 0) { + cmp = Long.compareUnsigned(this.getLow(), n.getLow()); + } + return cmp; + } + + /** + * Return the most significant word. + * + * @return the most significant word. + */ + public long getHigh() { + return ((this.high & INT_MASK) << Integer.SIZE) | (this.midHigh & INT_MASK); + } + + /** + * Return the least significant word. + * + * @return the least significant word. + */ + public long getLow() { + return ((this.midLow & INT_MASK) << Integer.SIZE) | (this.low & INT_MASK); + } + + /** + * Calculates the bitwise inclusive-or of {@code this} with {@code other} ({@code this | other}). + * + * @param other The value to inclusive-or with {@code this}. + * @return {@code this | other} + */ + public UInt128 or(UInt128 other) { + return UInt128.from( + this.high | other.high, + this.midHigh | other.midHigh, + this.midLow | other.midLow, + this.low | other.low); + } + + /** + * Calculates the bitwise and of {@code this} with {@code other} ({@code this & other}). + * + * @param other The value to and with {@code this}. + * @return {@code this & other} + */ + public UInt128 and(UInt128 other) { + return UInt128.from( + this.high & other.high, + this.midHigh & other.midHigh, + this.midLow & other.midLow, + this.low & other.low); + } + + /** + * Calculates the exclusive-or of {@code this} with {@code other} ({@code this ^ other}). + * + * @param other The value to exclusive-or with {@code this}. + * @return {@code this ^ other} + */ + public UInt128 xor(UInt128 other) { + return UInt128.from( + this.high ^ other.high, + this.midHigh ^ other.midHigh, + this.midLow ^ other.midLow, + this.low ^ other.low); + } + + /** + * Returns the number of zero bits preceding the highest-order ("leftmost") one-bit in the two's + * complement binary representation of the specified {@code long} value. Returns 128 if the + * specified value has no one-bits in its two's complement representation, in other words if it is + * equal to zero. + * + *

Note that this method is closely related to the logarithm base 2. For all positive {@code + * long} values x: + * + *

    + *
  • floor(log2(x)) = {@code 127 - numberOfLeadingZeros(x)} + *
  • ceil(log2(x)) = {@code 128 - numberOfLeadingZeros(x - 1)} + *
+ * + * @return the number of zero bits preceding the highest-order ("leftmost") one-bit in the two's + * complement binary representation of the specified {@code long} value, or 128 if the value + * is equal to zero. + */ + public int numberOfLeadingZeros() { + long highValue = this.getHigh(); + return (highValue == 0) + ? Long.SIZE + Long.numberOfLeadingZeros(this.getLow()) + : Long.numberOfLeadingZeros(highValue); + } + + /** + * Returns the index of the rightmost (lowest-order) one bit in this UInt128 (the number of zero + * bits to the right of the rightmost one bit). Returns -1 if this UInt128 contains no one bits. + * (Computes {@code (this == 0? -1 : log2(this & -this))}.) + * + * @return index of the rightmost one bit in this BigInteger. + */ + public int getLowestSetBit() { + // Mirrors java.math.BigInteger#getLowestSetBit() + if (isZero()) { + return -1; + } + int trailingZeros = Long.numberOfTrailingZeros(this.getLow()); + if (trailingZeros == Long.SIZE) { + // Low part is zero + trailingZeros += Long.numberOfTrailingZeros(this.getHigh()); + } + return trailingZeros; + } + + /** + * Return {@code true} if the {@link UInt128} has its high bit set. + * + * @return {@code true} if the {@link UInt128} has its high bit set. + */ + public boolean isHighBitSet() { + return this.high < 0; + } + + /** + * Return {@code true} if {@code this} is zero. + * + * @return {@code true} if {@code this} is zero. + */ + public boolean isZero() { + return this.high == 0 && this.midHigh == 0 && this.midLow == 0 && this.low == 0; + } + + /** + * Return {@code true} if {@code this} is an even number. + * + * @return {@code true} if {@code this} is an even number. + */ + public boolean isEven() { + return (this.low & 1) == 0; + } + + /** + * Return {@code true} if {@code this} is an odd number. + * + * @return {@code true} if {@code this} is an odd number. + */ + public boolean isOdd() { + return (this.low & 1) != 0; + } + + @Override + public int hashCode() { + return this.high * 31 + this.midHigh * 23 + this.midLow * 13 + this.low; + } + + @Override + public boolean equals(Object obj) { + // Note that this needs to be consistent with compareTo + if (this == obj) { + return true; + } + if (obj instanceof UInt128) { + UInt128 other = (UInt128) obj; + return this.high == other.high + && this.midHigh == other.midHigh + && this.midLow == other.midLow + && this.low == other.low; + } + return false; + } + + @Override + public String toString() { + return toString(10); + } + + /** + * Returns a string representation of this object in the specified radix. + * + *

If the radix is smaller than {@code Character.MIN_RADIX} or larger than {@code + * Character.MAX_RADIX}, an {@link IllegalArgumentException} is thrown. + * + *

The characters of the result represent the magnitude of {@code this}. If the magnitude is + * zero, it is represented by a single zero character {@code '0'}; otherwise no leading zeros are + * output. + * + *

The following ASCII characters are used as digits: + * + *

+ * + * {@code 0123456789abcdefghijklmnopqrstuvwxyz} + * + *
+ * + * If {@code radix} is N, then the first N of these characters are used as + * radix-N digits in the order shown, i.e. the digits for hexadecimal (radix 16) are + * {@code 0123456789abcdef}. + * + * @param radix the radix to use in the string representation. + * @return a string representation of the argument in the specified radix. + * @see Character#MAX_RADIX + * @throws IllegalArgumentException if {@code radix} is less than {@code Character.MIN_RADIX} or + * greater than {@code Character.MAX_RADIX}. + * @see Character#MIN_RADIX + */ + public String toString(int radix) { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new IllegalArgumentException("Illegal radix: " + radix); + } + if (isZero()) { + return "0"; + } + StringBuilder sb = new StringBuilder(); + UInt128 n = this; + UInt128 r = UInt128.from(radix); + while (!n.isZero()) { + UInt128 digit = n.remainder(r); + sb.append(Character.forDigit(digit.low, radix)); + n = n.divide(r); + } + return sb.reverse().toString(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256.java index 06168f0f9e..f313983eeb 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256.java @@ -64,866 +64,865 @@ package com.radixdlt.utils; +import static com.radixdlt.errors.ApiErrors.UNABLE_TO_PARSE_UINT; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import com.google.common.annotations.VisibleForTesting; import com.radixdlt.SecurityCritical; import com.radixdlt.SecurityCritical.SecurityKind; import com.radixdlt.utils.functional.Result; - import java.math.BigInteger; import java.util.Arrays; import java.util.Objects; -import static com.radixdlt.errors.ApiErrors.UNABLE_TO_PARSE_UINT; - -/** - * A 256-bit unsigned integer, with comparison and some basic arithmetic - * operations. - */ +/** A 256-bit unsigned integer, with comparison and some basic arithmetic operations. */ @SecurityCritical(SecurityKind.NUMERIC) public final class UInt256 implements Comparable { - // Some sizing constants in line with Integer, Long etc - /** - * Size of this numeric type in bits. - */ - public static final int SIZE = UInt128.SIZE * 2; - - /** - * Size of this numeric type in bytes. - */ - public static final int BYTES = UInt128.BYTES * 2; - - /** - * A constant holding the minimum value an {@code Int256} can - * have, 0. - */ - public static final UInt256 MIN_VALUE = new UInt256(UInt128.ZERO, UInt128.ZERO); - - /** - * Highest bit. - */ - public static final UInt256 HIGH_BIT = new UInt256(UInt128.HIGH_BIT, UInt128.ZERO); - - /** - * A constant holding the maximum value an {@code Int256} can - * have, 2256-1. - */ - public static final UInt256 MAX_VALUE = new UInt256(UInt128.MAX_VALUE, UInt128.MAX_VALUE); - - // Some commonly used values - public static final UInt256 ZERO = new UInt256(UInt128.ZERO, UInt128.ZERO); - public static final UInt256 ONE = new UInt256(UInt128.ZERO, UInt128.ONE); - public static final UInt256 TWO = new UInt256(UInt128.ZERO, UInt128.TWO); - public static final UInt256 THREE = new UInt256(UInt128.ZERO, UInt128.THREE); - public static final UInt256 FOUR = new UInt256(UInt128.ZERO, UInt128.FOUR); - public static final UInt256 FIVE = new UInt256(UInt128.ZERO, UInt128.FIVE); - public static final UInt256 SIX = new UInt256(UInt128.ZERO, UInt128.SIX); - public static final UInt256 SEVEN = new UInt256(UInt128.ZERO, UInt128.SEVEN); - public static final UInt256 EIGHT = new UInt256(UInt128.ZERO, UInt128.EIGHT); - public static final UInt256 NINE = new UInt256(UInt128.ZERO, UInt128.NINE); - public static final UInt256 TEN = new UInt256(UInt128.ZERO, UInt128.TEN); - - // Numbers in order. This is used by factory methods. - private static final UInt256[] numbers = { - ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN - }; - - // Mask of int bits as a long - private static final long INT_MASK = (1L << Integer.SIZE) - 1L; - - // The actual value. - // @PackageLocalForTest - final UInt128 high; - // @PackageLocalForTest - final UInt128 low; - - /** - * Factory method for materialising an {@link UInt256} from a {@code short} - * value. - * - * @param value The value to be represented as an {@link UInt256}. - * - * @return {@code value} as an {@link UInt256} type. - */ - public static UInt256 from(short value) { - return from(value & 0xFFFF); - } - - /** - * Factory method for materialising an {@link UInt256} from an {@code int} value. - * - * @param value The value to be represented as an {@link UInt256}. - * - * @return {@code value} as an {@link UInt256} type. - */ - public static UInt256 from(int value) { - return from(value & 0xFFFF_FFFFL); - } - - /** - * Factory method for materialising an {@link UInt256} from a {@code long} value. - * Note that values are zero extended into the 256 bit value. - * - * @param value The value to be represented as an {@link UInt256}. - * - * @return {@code value} as an {@link UInt256} type. - */ - public static UInt256 from(long value) { - return from(UInt128.from(value)); - } - - /** - * Factory method for materialising an {@link UInt256} from an {@link UInt128} - * value. Note that values are zero extended into the 256 bit value. - * - * @param value The least significant word of the value. - * - * @return the specified value as an {@link UInt256} type. - */ - public static UInt256 from(UInt128 value) { - return from(UInt128.ZERO, value); - } - - /** - * Factory method for materialising an {@link UInt256} from an {@link UInt128} - * value. - * - * @param high The most significant word of the value. - * @param low The least significant word of the value. - * - * @return the specified values as an {@link UInt256} type. - */ - public static UInt256 from(UInt128 high, UInt128 low) { - return new UInt256(high, low); - } - - /** - * Factory method for materialising an {@link UInt256} from an array - * of bytes. The array is most-significant byte first, and must not be - * zero length. - *

If the array is smaller than {@link #BYTES}, then it is effectively - * padded with leading zero bytes. - *

If the array is longer than {@link #BYTES}, then values at index - * {@link #BYTES} and beyond are ignored. - * - * @param bytes The array of bytes to be used. - * - * @return {@code bytes} as an {@link UInt256} type. - * - * @throws IllegalArgumentException if {@code bytes} is 0 length. - * @see #toByteArray() - */ - public static UInt256 from(byte[] bytes) { - Objects.requireNonNull(bytes); - if (bytes.length == 0) { - throw new IllegalArgumentException("bytes is 0 bytes long"); - } - byte[] newBytes = extend(bytes); - return from(newBytes, 0); - } - - /** - * Factory method for materialising an {@link UInt256} from an array - * of bytes. The array is most-significant byte first. - * - * @param bytes The array of bytes to be used. - * @param offset The offset within the array to be used. - * - * @return {@code bytes} from {@code offset} as an {@link UInt256} type. - * - * @see #toByteArray() - */ - public static UInt256 from(byte[] bytes, int offset) { - UInt128 high = UInt128.from(bytes, offset); - UInt128 low = UInt128.from(bytes, offset + UInt128.BYTES); - return from(high, low); - } - - /** - * Factory method for materialising an {@link UInt256} from a string. - * Conversion is performed base 10 and leading '+' sign character is - * permitted. - * - * @param s The array of bytes to be used. - * - * @return {@code s} as an {@link UInt256} type. - * - * @throws NumberFormatException if {@code s} is not a valid integer number. - */ - @JsonCreator - public static UInt256 from(String s) { - Objects.requireNonNull(s); - - int len = s.length(); - if (len > 0) { - int i = 0; - char ch = s.charAt(0); - if (ch == '+') { - i += 1; // skip first char - } - if (i >= len) { - throw new NumberFormatException(s); - } - // No real effort to catch overflow here - UInt256 result = UInt256.ZERO; - while (i < len) { - int digit = Character.digit(s.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException(s); - } - result = result.multiply(UInt256.TEN).add(numbers[digit]); - } - return result; - } else { - throw new NumberFormatException(s); - } - } - - /** - * Functional style friendly version of {@link #from(String)}. Instead of throwing exceptions - * this method returns {@link Result}. - * - * @param input The string to parse - * - * @return Success {@link Result} if value can be parsed and failure {@link Result} otherwise. - */ - public static Result fromString(String input) { - return Result.wrap(() -> UNABLE_TO_PARSE_UINT.with(input), () -> from(input)); - } - - // Pad short (< BYTES length) array with appropriate lead bytes. - private static byte[] extend(byte[] bytes) { - if (bytes.length >= BYTES) { - return bytes; - } - byte[] newBytes = new byte[BYTES]; - int newPos = BYTES - bytes.length; - Arrays.fill(newBytes, 0, newPos, (byte) 0); - System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); - return newBytes; - } - - private UInt256(UInt128 high, UInt128 low) { - this.high = Objects.requireNonNull(high); - this.low = Objects.requireNonNull(low); - } - - @VisibleForTesting - UInt256(byte[] bytes) { - int[] ints = new int[BYTES / Integer.BYTES]; - int intIndex = ints.length; - int intLen = Math.min(bytes.length, Integer.BYTES); - int byteIndex = bytes.length - intLen; - while (intIndex > 0 && intLen > 0) { - ints[--intIndex] = Ints.fromByteArray(bytes, byteIndex, intLen); - intLen = Math.min(byteIndex, Integer.BYTES); - byteIndex -= intLen; - } - this.high = UInt128.from(ints[0], ints[1], ints[2], ints[3]); - this.low = UInt128.from(ints[4], ints[5], ints[6], ints[7]); - } - - /** - * Converts {@code this} to an array of bytes. - * The most significant byte will be returned in index zero. - * The array will always be {@link #BYTES} bytes long, and - * will be zero filled to suit the actual value. - * - * @return An array of {@link #BYTES} bytes representing the - * value of this {@link UInt256}. - */ - public byte[] toByteArray() { - return toByteArray(new byte[BYTES], 0); - } - - /** - * Converts {@code this} to an array of bytes. - * The most significant byte will be returned in index {@code offset}. - * The array must be at least {@code offset + BYTES} long. - * - * @param bytes The array to place the bytes in. - * @param offset The offset within the array to place the bytes. - * - * @return The passed-in value of {@code bytes}. - */ - public byte[] toByteArray(byte[] bytes, int offset) { - this.high.toByteArray(bytes, offset); - this.low.toByteArray(bytes, offset + UInt128.BYTES); - return bytes; - } - - /** - * Adds {@code other} to {@code this}, returning the result. - * - * @param other The addend. - * - * @return An {@link UInt256} with the value {@code this + other}. - */ - public UInt256 add(UInt256 other) { - long partial0 = (this.low.low & INT_MASK) + (other.low.low & INT_MASK); - long partial1 = (this.low.midLow & INT_MASK) + (other.low.midLow & INT_MASK) + (partial0 >>> Integer.SIZE); - long partial2 = (this.low.midHigh & INT_MASK) + (other.low.midHigh & INT_MASK) + (partial1 >>> Integer.SIZE); - long partial3 = (this.low.high & INT_MASK) + (other.low.high & INT_MASK) + (partial2 >>> Integer.SIZE); - long partial4 = (this.high.low & INT_MASK) + (other.high.low & INT_MASK) + (partial3 >>> Integer.SIZE); - long partial5 = (this.high.midLow & INT_MASK) + (other.high.midLow & INT_MASK) + (partial4 >>> Integer.SIZE); - long partial6 = (this.high.midHigh & INT_MASK) + (other.high.midHigh & INT_MASK) + (partial5 >>> Integer.SIZE); - long partial7 = (this.high.high & INT_MASK) + (other.high.high & INT_MASK) + (partial6 >>> Integer.SIZE); - return UInt256.from( - UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), - UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0) - ); - } - - /** - * Adds {@code other} to {@code this}, returning the result. - * - * @param other The addend. - * - * @return An {@link UInt256} with the value {@code this + other}. - */ - public UInt256 add(UInt128 other) { - long partial0 = (this.low.low & INT_MASK) + (other.low & INT_MASK); - long partial1 = (this.low.midLow & INT_MASK) + (other.midLow & INT_MASK) + (partial0 >>> Integer.SIZE); - long partial2 = (this.low.midHigh & INT_MASK) + (other.midHigh & INT_MASK) + (partial1 >>> Integer.SIZE); - long partial3 = (this.low.high & INT_MASK) + (other.high & INT_MASK) + (partial2 >>> Integer.SIZE); - long partial4 = (this.high.low & INT_MASK) + (partial3 >>> Integer.SIZE); - long partial5 = (this.high.midLow & INT_MASK) + (partial4 >>> Integer.SIZE); - long partial6 = (this.high.midHigh & INT_MASK) + (partial5 >>> Integer.SIZE); - long partial7 = (this.high.high & INT_MASK) + (partial6 >>> Integer.SIZE); - return UInt256.from( - UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), - UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0) - ); - } - - /** - * Subtracts {@code other} from {@code this}, returning the result. - * - * @param other The subtrahend. - * - * @return An {@link UInt256} with the value {@code this - other}. - */ - public UInt256 subtract(UInt256 other) { - long partial0 = (this.low.low & INT_MASK) - (other.low.low & INT_MASK); - long partial1 = (this.low.midLow & INT_MASK) - (other.low.midLow & INT_MASK) + (partial0 >> Integer.SIZE); - long partial2 = (this.low.midHigh & INT_MASK) - (other.low.midHigh & INT_MASK) + (partial1 >> Integer.SIZE); - long partial3 = (this.low.high & INT_MASK) - (other.low.high & INT_MASK) + (partial2 >> Integer.SIZE); - long partial4 = (this.high.low & INT_MASK) - (other.high.low & INT_MASK) + (partial3 >> Integer.SIZE); - long partial5 = (this.high.midLow & INT_MASK) - (other.high.midLow & INT_MASK) + (partial4 >> Integer.SIZE); - long partial6 = (this.high.midHigh & INT_MASK) - (other.high.midHigh & INT_MASK) + (partial5 >> Integer.SIZE); - long partial7 = (this.high.high & INT_MASK) - (other.high.high & INT_MASK) + (partial6 >> Integer.SIZE); - return UInt256.from( - UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), - UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0) - ); - } - - /** - * Subtracts {@code other} from {@code this}, returning the result. - * - * @param other The subtrahend. - * - * @return An {@link UInt256} with the value {@code this - other}. - */ - public UInt256 subtract(UInt128 other) { - long partial0 = (this.low.low & INT_MASK) - (other.low & INT_MASK); - long partial1 = (this.low.midLow & INT_MASK) - (other.midLow & INT_MASK) + (partial0 >> Integer.SIZE); - long partial2 = (this.low.midHigh & INT_MASK) - (other.midHigh & INT_MASK) + (partial1 >> Integer.SIZE); - long partial3 = (this.low.high & INT_MASK) - (other.high & INT_MASK) + (partial2 >> Integer.SIZE); - long partial4 = (this.high.low & INT_MASK) + (partial3 >> Integer.SIZE); - long partial5 = (this.high.midLow & INT_MASK) + (partial4 >> Integer.SIZE); - long partial6 = (this.high.midHigh & INT_MASK) + (partial5 >> Integer.SIZE); - long partial7 = (this.high.high & INT_MASK) + (partial6 >> Integer.SIZE); - return UInt256.from( - UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), - UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0) - ); - } - - /** - * Increments {@code this}. Equivalent to {@code this.add(Int256.ONE)}, but - * faster. - * - * @return This number incremented by one. - */ - public UInt256 increment() { - UInt128 l = this.low.increment(); - UInt128 h = l.isZero() ? this.high.increment() : this.high; - return UInt256.from(h, l); - } - - /** - * Decrements {@code this}. Equivalent to {@code this.subtract(Int256.ONE)}, but - * faster. - * - * @return This number decremented by one. - */ - public UInt256 decrement() { - UInt128 l = this.low.decrement(); - UInt128 h = this.low.isZero() ? this.high.decrement() : this.high; - return UInt256.from(h, l); - } - - /** - * Multiplies {@code this} by the specified multiplicand. - * - * @param multiplicand The multiplicand to multiply {@code this} by. - * - * @return The result {@code this * multiplicand}. - */ - public UInt256 multiply(UInt256 multiplicand) { - // I appreciate that this looks like a wall of code, and it is, - // but the underlying algorithm is long multiplication base 2^32. - - long llowlow = (multiplicand.low.low & INT_MASK); - long partial00 = (this.low.low & INT_MASK) * llowlow; - long partial01 = (this.low.midLow & INT_MASK) * llowlow + (partial00 >>> Integer.SIZE); - long partial02 = (this.low.midHigh & INT_MASK) * llowlow + (partial01 >>> Integer.SIZE); - long partial03 = (this.low.high & INT_MASK) * llowlow + (partial02 >>> Integer.SIZE); - long partial04 = (this.high.low & INT_MASK) * llowlow + (partial03 >>> Integer.SIZE); - long partial05 = (this.high.midLow & INT_MASK) * llowlow + (partial04 >>> Integer.SIZE); - long partial06 = (this.high.midHigh & INT_MASK) * llowlow + (partial05 >>> Integer.SIZE); - long partial07 = (this.high.high & INT_MASK) * llowlow + (partial06 >>> Integer.SIZE); - - long llowmidlow = (multiplicand.low.midLow & INT_MASK); - long partial10 = (this.low.low & INT_MASK) * llowmidlow; - long partial11 = (this.low.midLow & INT_MASK) * llowmidlow + (partial10 >>> Integer.SIZE); - long partial12 = (this.low.midHigh & INT_MASK) * llowmidlow + (partial11 >>> Integer.SIZE); - long partial13 = (this.low.high & INT_MASK) * llowmidlow + (partial12 >>> Integer.SIZE); - long partial14 = (this.high.low & INT_MASK) * llowmidlow + (partial13 >>> Integer.SIZE); - long partial15 = (this.high.midLow & INT_MASK) * llowmidlow + (partial14 >>> Integer.SIZE); - long partial16 = (this.high.midHigh & INT_MASK) * llowmidlow + (partial15 >>> Integer.SIZE); - - long llowmidhigh = (multiplicand.low.midHigh & INT_MASK); - long partial20 = (this.low.low & INT_MASK) * llowmidhigh; - long partial21 = (this.low.midLow & INT_MASK) * llowmidhigh + (partial20 >>> Integer.SIZE); - long partial22 = (this.low.midHigh & INT_MASK) * llowmidhigh + (partial21 >>> Integer.SIZE); - long partial23 = (this.low.high & INT_MASK) * llowmidhigh + (partial22 >>> Integer.SIZE); - long partial24 = (this.high.low & INT_MASK) * llowmidhigh + (partial23 >>> Integer.SIZE); - long partial25 = (this.high.midLow & INT_MASK) * llowmidhigh + (partial24 >>> Integer.SIZE); - - long llowhigh = (multiplicand.low.high & INT_MASK); - long partial30 = (this.low.low & INT_MASK) * llowhigh; - long partial31 = (this.low.midLow & INT_MASK) * llowhigh + (partial30 >>> Integer.SIZE); - long partial32 = (this.low.midHigh & INT_MASK) * llowhigh + (partial31 >>> Integer.SIZE); - long partial33 = (this.low.high & INT_MASK) * llowhigh + (partial32 >>> Integer.SIZE); - long partial34 = (this.high.low & INT_MASK) * llowhigh + (partial33 >>> Integer.SIZE); - - long lhighlow = (multiplicand.high.low & INT_MASK); - long partial40 = (this.low.low & INT_MASK) * lhighlow; - long partial41 = (this.low.midLow & INT_MASK) * lhighlow + (partial40 >>> Integer.SIZE); - long partial42 = (this.low.midHigh & INT_MASK) * lhighlow + (partial41 >>> Integer.SIZE); - long partial43 = (this.low.high & INT_MASK) * lhighlow + (partial42 >>> Integer.SIZE); - - long lhighmidlow = (multiplicand.high.midLow & INT_MASK); - long partial50 = (this.low.low & INT_MASK) * lhighmidlow; - long partial51 = (this.low.midLow & INT_MASK) * lhighmidlow + (partial50 >>> Integer.SIZE); - long partial52 = (this.low.midHigh & INT_MASK) * lhighmidlow + (partial51 >>> Integer.SIZE); - - long lhighmidhigh = (multiplicand.high.midHigh & INT_MASK); - long partial60 = (this.low.low & INT_MASK) * lhighmidhigh; - long partial61 = (this.low.midLow & INT_MASK) * lhighmidhigh + (partial60 >>> Integer.SIZE); - - long partial70 = (this.low.low & INT_MASK) * (multiplicand.high.high & INT_MASK); - - long i0 = (partial00 & INT_MASK); - long i1 = (partial10 & INT_MASK) + (partial01 & INT_MASK); - long i2 = (partial20 & INT_MASK) + (partial11 & INT_MASK) + (partial02 & INT_MASK) + (i1 >>> Integer.SIZE); - long i3 = (partial30 & INT_MASK) + (partial21 & INT_MASK) + (partial12 & INT_MASK) + (partial03 & INT_MASK) + (i2 >>> Integer.SIZE); - long i4 = (partial40 & INT_MASK) + (partial31 & INT_MASK) + (partial22 & INT_MASK) + (partial13 & INT_MASK) + (partial04 & INT_MASK) - + (i3 >>> Integer.SIZE); - long i5 = (partial50 & INT_MASK) + (partial41 & INT_MASK) + (partial32 & INT_MASK) + (partial23 & INT_MASK) + (partial14 & INT_MASK) - + (partial05 & INT_MASK) + (i4 >>> Integer.SIZE); - long i6 = (partial60 & INT_MASK) + (partial51 & INT_MASK) + (partial42 & INT_MASK) + (partial33 & INT_MASK) + (partial24 & INT_MASK) - + (partial15 & INT_MASK) + (partial06 & INT_MASK) + (i5 >>> Integer.SIZE); - long i7 = (partial70 & INT_MASK) + (partial61 & INT_MASK) + (partial52 & INT_MASK) + (partial43 & INT_MASK) + (partial34 & INT_MASK) - + (partial25 & INT_MASK) + (partial16 & INT_MASK) + (partial07 & INT_MASK) + (i6 >>> Integer.SIZE); - - return UInt256.from( - UInt128.from((int) i7, (int) i6, (int) i5, (int) i4), - UInt128.from((int) i3, (int) i2, (int) i1, (int) i0) - ); - } - - /** - * Divides {@code this} by the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * - * @return The result {@code floor(this / divisor)}. - * - * @throws IllegalArgumentException if {@code divisor} is zero - */ - public UInt256 divide(UInt256 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - BigInteger q = new BigInteger(1, this.toByteArray()); - BigInteger d = new BigInteger(1, divisor.toByteArray()); - return new UInt256(q.divide(d).toByteArray()); - } - - /** - * Returns the remainder of the division of {@code this} by - * the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * - * @return The remainder of the division {@code this / divisor}. - */ - public UInt256 remainder(UInt256 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - BigInteger q = new BigInteger(1, this.toByteArray()); - BigInteger d = new BigInteger(1, divisor.toByteArray()); - return new UInt256(q.remainder(d).toByteArray()); - } - - /** - * Calculates {@code this}{@code exp}. - * - * @param exp the exponent to raise {@code this} to - * - * @return {@code this}{@code exp} - */ - public UInt256 pow(int exp) { - if (exp < 0) { - throw new IllegalArgumentException("exp must be >= 0"); - } - - // Mirrors algorithm in multiply(...) - UInt256 result = UInt256.ONE; - UInt256 base = this; - - while (exp != 0) { - if ((exp & 1) != 0) { - result = result.multiply(base); - } - - base = base.multiply(base); - exp >>>= 1; - } - return result; - } - - /** - * Calculates the integer square root, that is the largest number {@code n} - * such that {@code n * n <= this}. - * - * @return The integer square root. - */ - public UInt256 isqrt() { - UInt256 bit = UInt256.ONE.shiftLeft(SIZE - 2); - - // "bit" starts at the highest power of four <= this - UInt256 num = this; - while (bit.compareTo(num) > 0) { - bit = bit.shiftRight(2); - } - - UInt256 res = UInt256.ZERO; - while (!bit.isZero()) { - UInt256 rab = res.add(bit); - if (num.compareTo(rab) >= 0) { - num = num.subtract(rab); - res = res.shiftRight().add(bit); - } else { - res = res.shiftRight(); - } - bit = bit.shiftRight(2); - } - return res; - } - - /** - * Shifts {@code this} left 1 bit. A zero bit is moved into the - * rightmost bit. - * - * @return The result of shifting {@code this} left one bit. - */ - public UInt256 shiftLeft() { - UInt128 h = this.high.shiftLeft(); - if (this.low.isHighBitSet()) { - h = h.or(UInt128.ONE); - } - UInt128 l = this.low.shiftLeft(); - return UInt256.from(h, l); - } - - /** - * Shift {@code this} left n bits. A zero bit is moved into the - * rightmost bits. - * - * @return The result of shifting {@code this} left n bits. - */ - public UInt256 shiftLeft(int n) { - if (n == 0) { - return this; - } else if (n >= SIZE || n <= -SIZE) { - return ZERO; // All bits are gone - } else if (n < 0) { - return shiftRight(-n); // -ve left shift is right shift - } - UInt128 h = (n >= UInt128.SIZE) ? this.low : this.high; - UInt128 l = (n >= UInt128.SIZE) ? UInt128.ZERO : this.low; - int r = n % UInt128.SIZE; - if (r > 0) { - UInt128 c = l.shiftRight(UInt128.SIZE - r); - h = h.shiftLeft(r).or(c); - l = l.shiftLeft(r); - } - return UInt256.from(h, l); - } - - /** - * Shifts {@code this} right 1 bit. A zero bit is moved into the - * into the leftmost bit. - * - * @return The result of arithmetic shifting {@code this} right one bit. - */ - public UInt256 shiftRight() { - UInt128 h = this.high.shiftRight(); - UInt128 l = this.low.shiftRight(); - if (this.high.isOdd()) { - l = l.or(UInt128.HIGH_BIT); - } - return UInt256.from(h, l); - } - - /** - * Logical shift {@code this} right n bits. Zeros are shifted into - * the leftmost bits. - * - * @return The result of logical shifting {@code this} right n bits. - */ - public UInt256 shiftRight(int n) { - if (n == 0) { - return this; - } else if (n >= SIZE || n <= -SIZE) { - return ZERO; // All bits are gone - } else if (n < 0) { - return shiftLeft(-n); // -ve right shift is left shift - } - UInt128 h = (n >= UInt128.SIZE) ? UInt128.ZERO : this.high; - UInt128 l = (n >= UInt128.SIZE) ? this.high : this.low; - int r = n % UInt128.SIZE; - if (r > 0) { - UInt128 c = h.shiftLeft(UInt128.SIZE - r); - h = h.shiftRight(r); - l = l.shiftRight(r).or(c); - } - return UInt256.from(h, l); - } - - /** - * Returns the value of {@code ~this}. - * - * @return The logical inverse of {@code this}. - */ - public UInt256 invert() { - return UInt256.from(this.high.invert(), this.low.invert()); - } - - @Override - public int compareTo(UInt256 n) { - int cmp = this.high.compareTo(n.high); - if (cmp == 0) { - cmp = this.low.compareTo(n.low); - } - return cmp; - } - - /** - * Returns the most significant word. - * - * @return the most significant word. - */ - public UInt128 getHigh() { - return this.high; - } - - /** - * Returns the least significant word. - * - * @return the least significant word. - */ - public UInt128 getLow() { - return this.low; - } - - /** - * Calculates the bitwise inclusive-or of {@code this} with {@code other} - * ({@code this | other}). - * - * @param other The value to inclusive-or with {@code this}. - * - * @return {@code this | other} - */ - public UInt256 or(UInt256 other) { - return UInt256.from(this.high.or(other.high), this.low.or(other.low)); - } - - /** - * Calculates the bitwise and of {@code this} with {@code other} - * ({@code this & other}). - * - * @param other The value to and with {@code this}. - * - * @return {@code this & other} - */ - public UInt256 and(UInt256 other) { - return UInt256.from(this.high.and(other.high), this.low.and(other.low)); - } - - /** - * Calculates the exclusive-or of {@code this} with {@code other} - * ({@code this ^ other}). - * - * @param other The value to exclusive-or with {@code this}. - * - * @return {@code this ^ other} - */ - public UInt256 xor(UInt256 other) { - return UInt256.from(this.high.xor(other.high), this.low.xor(other.low)); - } - - /** - * Returns the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code long} value. Returns 128 if the - * specified value has no one-bits in its two's complement representation, - * in other words if it is equal to zero. - * - *

Note that this method is closely related to the logarithm base 2. - * For all positive {@code long} values x: - *

    - *
  • floor(log2(x)) = {@code 255 - numberOfLeadingZeros(x)} - *
  • ceil(log2(x)) = {@code 256 - numberOfLeadingZeros(x - 1)} - *
- * - * @return the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code long} value, or 256 if the value - * is equal to zero. - */ - public int numberOfLeadingZeros() { - return this.high.isZero() - ? UInt128.SIZE + this.low.numberOfLeadingZeros() - : this.high.numberOfLeadingZeros(); - } - - /** - * Return {@code true} if the {@link UInt128} has its high bit set. - * - * @return {@code true} if the {@link UInt128} has its high bit set. - */ - public boolean isHighBitSet() { - return this.high.isHighBitSet(); - } - - /** - * Returns {@code true} if {@code this} is zero. - * - * @return {@code true} if {@code this} is zero. - */ - public boolean isZero() { - return this.high.isZero() && this.low.isZero(); - } - - /** - * Returns {@code true} if {@code this} is an even number. - * - * @return {@code true} if {@code this} is an even number. - */ - public boolean isEven() { - return (this.low.low & 1) == 0; - } - - /** - * Returns {@code true} if {@code this} is an odd number. - * - * @return {@code true} if {@code this} is an odd number. - */ - public boolean isOdd() { - return (this.low.low & 1) != 0; - } - - @Override - public int hashCode() { - return this.high.hashCode() * 31 + this.low.hashCode(); - } - - @Override - public boolean equals(Object obj) { - // Note that this needs to be consistent with compareTo - if (this == obj) { - return true; - } - if (obj instanceof UInt256) { - UInt256 other = (UInt256) obj; - return Objects.equals(this.high, other.high) && Objects.equals(this.low, other.low); - } - return false; - } - - @JsonValue - public String toJson() { - return toString(10); - } - - @Override - public String toString() { - return toString(10); - } - - /** - * Returns a string representation of this object in the specified radix. - *

- * If the radix is smaller than {@code Character.MIN_RADIX} or larger than - * {@code Character.MAX_RADIX}, an {@link IllegalArgumentException} is - * thrown. - *

- * The characters of the result represent the magnitude of {@code this}. - * If the magnitude is zero, it is represented by a single zero character - * {@code '0'}; otherwise no leading zeros are output. - *

- * The following ASCII characters are used as digits: - * - *

- * {@code 0123456789abcdefghijklmnopqrstuvwxyz} - *
- *

- * If {@code radix} is N, then the first N of these - * characters are used as radix-N digits in the order shown, - * i.e. the digits for hexadecimal (radix 16) are {@code 0123456789abcdef}. - * - * @param radix the radix to use in the string representation. - * - * @return a string representation of the argument in the specified radix. - * - * @throws IllegalArgumentException if {@code radix} is less than - * {@code Character.MIN_RADIX} or greater than - * {@code Character.MAX_RADIX}. - * @see Character#MAX_RADIX - * @see Character#MIN_RADIX - */ - public String toString(int radix) { - if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new IllegalArgumentException("Illegal radix: " + radix); - } - if (isZero()) { - return "0"; - } - StringBuilder sb = new StringBuilder(); - UInt256 n = this; - UInt256 r = UInt256.from(radix); - while (!n.isZero()) { - UInt256 digit = n.remainder(r); - sb.append(Character.forDigit(digit.low.low, radix)); - n = n.divide(r); - } - return sb.reverse().toString(); - } + // Some sizing constants in line with Integer, Long etc + /** Size of this numeric type in bits. */ + public static final int SIZE = UInt128.SIZE * 2; + + /** Size of this numeric type in bytes. */ + public static final int BYTES = UInt128.BYTES * 2; + + /** A constant holding the minimum value an {@code Int256} can have, 0. */ + public static final UInt256 MIN_VALUE = new UInt256(UInt128.ZERO, UInt128.ZERO); + + /** Highest bit. */ + public static final UInt256 HIGH_BIT = new UInt256(UInt128.HIGH_BIT, UInt128.ZERO); + + /** A constant holding the maximum value an {@code Int256} can have, 2256-1. */ + public static final UInt256 MAX_VALUE = new UInt256(UInt128.MAX_VALUE, UInt128.MAX_VALUE); + + // Some commonly used values + public static final UInt256 ZERO = new UInt256(UInt128.ZERO, UInt128.ZERO); + public static final UInt256 ONE = new UInt256(UInt128.ZERO, UInt128.ONE); + public static final UInt256 TWO = new UInt256(UInt128.ZERO, UInt128.TWO); + public static final UInt256 THREE = new UInt256(UInt128.ZERO, UInt128.THREE); + public static final UInt256 FOUR = new UInt256(UInt128.ZERO, UInt128.FOUR); + public static final UInt256 FIVE = new UInt256(UInt128.ZERO, UInt128.FIVE); + public static final UInt256 SIX = new UInt256(UInt128.ZERO, UInt128.SIX); + public static final UInt256 SEVEN = new UInt256(UInt128.ZERO, UInt128.SEVEN); + public static final UInt256 EIGHT = new UInt256(UInt128.ZERO, UInt128.EIGHT); + public static final UInt256 NINE = new UInt256(UInt128.ZERO, UInt128.NINE); + public static final UInt256 TEN = new UInt256(UInt128.ZERO, UInt128.TEN); + + // Numbers in order. This is used by factory methods. + private static final UInt256[] numbers = { + ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN + }; + + // Mask of int bits as a long + private static final long INT_MASK = (1L << Integer.SIZE) - 1L; + + // The actual value. + // @PackageLocalForTest + final UInt128 high; + // @PackageLocalForTest + final UInt128 low; + + /** + * Factory method for materialising an {@link UInt256} from a {@code short} value. + * + * @param value The value to be represented as an {@link UInt256}. + * @return {@code value} as an {@link UInt256} type. + */ + public static UInt256 from(short value) { + return from(value & 0xFFFF); + } + + /** + * Factory method for materialising an {@link UInt256} from an {@code int} value. + * + * @param value The value to be represented as an {@link UInt256}. + * @return {@code value} as an {@link UInt256} type. + */ + public static UInt256 from(int value) { + return from(value & 0xFFFF_FFFFL); + } + + /** + * Factory method for materialising an {@link UInt256} from a {@code long} value. Note that values + * are zero extended into the 256 bit value. + * + * @param value The value to be represented as an {@link UInt256}. + * @return {@code value} as an {@link UInt256} type. + */ + public static UInt256 from(long value) { + return from(UInt128.from(value)); + } + + /** + * Factory method for materialising an {@link UInt256} from an {@link UInt128} value. Note that + * values are zero extended into the 256 bit value. + * + * @param value The least significant word of the value. + * @return the specified value as an {@link UInt256} type. + */ + public static UInt256 from(UInt128 value) { + return from(UInt128.ZERO, value); + } + + /** + * Factory method for materialising an {@link UInt256} from an {@link UInt128} value. + * + * @param high The most significant word of the value. + * @param low The least significant word of the value. + * @return the specified values as an {@link UInt256} type. + */ + public static UInt256 from(UInt128 high, UInt128 low) { + return new UInt256(high, low); + } + + /** + * Factory method for materialising an {@link UInt256} from an array of bytes. The array is + * most-significant byte first, and must not be zero length. + * + *

If the array is smaller than {@link #BYTES}, then it is effectively padded with leading zero + * bytes. + * + *

If the array is longer than {@link #BYTES}, then values at index {@link #BYTES} and beyond + * are ignored. + * + * @param bytes The array of bytes to be used. + * @return {@code bytes} as an {@link UInt256} type. + * @throws IllegalArgumentException if {@code bytes} is 0 length. + * @see #toByteArray() + */ + public static UInt256 from(byte[] bytes) { + Objects.requireNonNull(bytes); + if (bytes.length == 0) { + throw new IllegalArgumentException("bytes is 0 bytes long"); + } + byte[] newBytes = extend(bytes); + return from(newBytes, 0); + } + + /** + * Factory method for materialising an {@link UInt256} from an array of bytes. The array is + * most-significant byte first. + * + * @param bytes The array of bytes to be used. + * @param offset The offset within the array to be used. + * @return {@code bytes} from {@code offset} as an {@link UInt256} type. + * @see #toByteArray() + */ + public static UInt256 from(byte[] bytes, int offset) { + UInt128 high = UInt128.from(bytes, offset); + UInt128 low = UInt128.from(bytes, offset + UInt128.BYTES); + return from(high, low); + } + + /** + * Factory method for materialising an {@link UInt256} from a string. Conversion is performed base + * 10 and leading '+' sign character is permitted. + * + * @param s The array of bytes to be used. + * @return {@code s} as an {@link UInt256} type. + * @throws NumberFormatException if {@code s} is not a valid integer number. + */ + @JsonCreator + public static UInt256 from(String s) { + Objects.requireNonNull(s); + + int len = s.length(); + if (len > 0) { + int i = 0; + char ch = s.charAt(0); + if (ch == '+') { + i += 1; // skip first char + } + if (i >= len) { + throw new NumberFormatException(s); + } + // No real effort to catch overflow here + UInt256 result = UInt256.ZERO; + while (i < len) { + int digit = Character.digit(s.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException(s); + } + result = result.multiply(UInt256.TEN).add(numbers[digit]); + } + return result; + } else { + throw new NumberFormatException(s); + } + } + + /** + * Functional style friendly version of {@link #from(String)}. Instead of throwing exceptions this + * method returns {@link Result}. + * + * @param input The string to parse + * @return Success {@link Result} if value can be parsed and failure {@link Result} otherwise. + */ + public static Result fromString(String input) { + return Result.wrap(() -> UNABLE_TO_PARSE_UINT.with(input), () -> from(input)); + } + + // Pad short (< BYTES length) array with appropriate lead bytes. + private static byte[] extend(byte[] bytes) { + if (bytes.length >= BYTES) { + return bytes; + } + byte[] newBytes = new byte[BYTES]; + int newPos = BYTES - bytes.length; + Arrays.fill(newBytes, 0, newPos, (byte) 0); + System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); + return newBytes; + } + + private UInt256(UInt128 high, UInt128 low) { + this.high = Objects.requireNonNull(high); + this.low = Objects.requireNonNull(low); + } + + @VisibleForTesting + UInt256(byte[] bytes) { + int[] ints = new int[BYTES / Integer.BYTES]; + int intIndex = ints.length; + int intLen = Math.min(bytes.length, Integer.BYTES); + int byteIndex = bytes.length - intLen; + while (intIndex > 0 && intLen > 0) { + ints[--intIndex] = Ints.fromByteArray(bytes, byteIndex, intLen); + intLen = Math.min(byteIndex, Integer.BYTES); + byteIndex -= intLen; + } + this.high = UInt128.from(ints[0], ints[1], ints[2], ints[3]); + this.low = UInt128.from(ints[4], ints[5], ints[6], ints[7]); + } + + /** + * Converts {@code this} to an array of bytes. The most significant byte will be returned in index + * zero. The array will always be {@link #BYTES} bytes long, and will be zero filled to suit the + * actual value. + * + * @return An array of {@link #BYTES} bytes representing the value of this {@link UInt256}. + */ + public byte[] toByteArray() { + return toByteArray(new byte[BYTES], 0); + } + + /** + * Converts {@code this} to an array of bytes. The most significant byte will be returned in index + * {@code offset}. The array must be at least {@code offset + BYTES} long. + * + * @param bytes The array to place the bytes in. + * @param offset The offset within the array to place the bytes. + * @return The passed-in value of {@code bytes}. + */ + public byte[] toByteArray(byte[] bytes, int offset) { + this.high.toByteArray(bytes, offset); + this.low.toByteArray(bytes, offset + UInt128.BYTES); + return bytes; + } + + /** + * Adds {@code other} to {@code this}, returning the result. + * + * @param other The addend. + * @return An {@link UInt256} with the value {@code this + other}. + */ + public UInt256 add(UInt256 other) { + long partial0 = (this.low.low & INT_MASK) + (other.low.low & INT_MASK); + long partial1 = + (this.low.midLow & INT_MASK) + (other.low.midLow & INT_MASK) + (partial0 >>> Integer.SIZE); + long partial2 = + (this.low.midHigh & INT_MASK) + + (other.low.midHigh & INT_MASK) + + (partial1 >>> Integer.SIZE); + long partial3 = + (this.low.high & INT_MASK) + (other.low.high & INT_MASK) + (partial2 >>> Integer.SIZE); + long partial4 = + (this.high.low & INT_MASK) + (other.high.low & INT_MASK) + (partial3 >>> Integer.SIZE); + long partial5 = + (this.high.midLow & INT_MASK) + + (other.high.midLow & INT_MASK) + + (partial4 >>> Integer.SIZE); + long partial6 = + (this.high.midHigh & INT_MASK) + + (other.high.midHigh & INT_MASK) + + (partial5 >>> Integer.SIZE); + long partial7 = + (this.high.high & INT_MASK) + (other.high.high & INT_MASK) + (partial6 >>> Integer.SIZE); + return UInt256.from( + UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), + UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0)); + } + + /** + * Adds {@code other} to {@code this}, returning the result. + * + * @param other The addend. + * @return An {@link UInt256} with the value {@code this + other}. + */ + public UInt256 add(UInt128 other) { + long partial0 = (this.low.low & INT_MASK) + (other.low & INT_MASK); + long partial1 = + (this.low.midLow & INT_MASK) + (other.midLow & INT_MASK) + (partial0 >>> Integer.SIZE); + long partial2 = + (this.low.midHigh & INT_MASK) + (other.midHigh & INT_MASK) + (partial1 >>> Integer.SIZE); + long partial3 = + (this.low.high & INT_MASK) + (other.high & INT_MASK) + (partial2 >>> Integer.SIZE); + long partial4 = (this.high.low & INT_MASK) + (partial3 >>> Integer.SIZE); + long partial5 = (this.high.midLow & INT_MASK) + (partial4 >>> Integer.SIZE); + long partial6 = (this.high.midHigh & INT_MASK) + (partial5 >>> Integer.SIZE); + long partial7 = (this.high.high & INT_MASK) + (partial6 >>> Integer.SIZE); + return UInt256.from( + UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), + UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0)); + } + + /** + * Subtracts {@code other} from {@code this}, returning the result. + * + * @param other The subtrahend. + * @return An {@link UInt256} with the value {@code this - other}. + */ + public UInt256 subtract(UInt256 other) { + long partial0 = (this.low.low & INT_MASK) - (other.low.low & INT_MASK); + long partial1 = + (this.low.midLow & INT_MASK) - (other.low.midLow & INT_MASK) + (partial0 >> Integer.SIZE); + long partial2 = + (this.low.midHigh & INT_MASK) - (other.low.midHigh & INT_MASK) + (partial1 >> Integer.SIZE); + long partial3 = + (this.low.high & INT_MASK) - (other.low.high & INT_MASK) + (partial2 >> Integer.SIZE); + long partial4 = + (this.high.low & INT_MASK) - (other.high.low & INT_MASK) + (partial3 >> Integer.SIZE); + long partial5 = + (this.high.midLow & INT_MASK) - (other.high.midLow & INT_MASK) + (partial4 >> Integer.SIZE); + long partial6 = + (this.high.midHigh & INT_MASK) + - (other.high.midHigh & INT_MASK) + + (partial5 >> Integer.SIZE); + long partial7 = + (this.high.high & INT_MASK) - (other.high.high & INT_MASK) + (partial6 >> Integer.SIZE); + return UInt256.from( + UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), + UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0)); + } + + /** + * Subtracts {@code other} from {@code this}, returning the result. + * + * @param other The subtrahend. + * @return An {@link UInt256} with the value {@code this - other}. + */ + public UInt256 subtract(UInt128 other) { + long partial0 = (this.low.low & INT_MASK) - (other.low & INT_MASK); + long partial1 = + (this.low.midLow & INT_MASK) - (other.midLow & INT_MASK) + (partial0 >> Integer.SIZE); + long partial2 = + (this.low.midHigh & INT_MASK) - (other.midHigh & INT_MASK) + (partial1 >> Integer.SIZE); + long partial3 = + (this.low.high & INT_MASK) - (other.high & INT_MASK) + (partial2 >> Integer.SIZE); + long partial4 = (this.high.low & INT_MASK) + (partial3 >> Integer.SIZE); + long partial5 = (this.high.midLow & INT_MASK) + (partial4 >> Integer.SIZE); + long partial6 = (this.high.midHigh & INT_MASK) + (partial5 >> Integer.SIZE); + long partial7 = (this.high.high & INT_MASK) + (partial6 >> Integer.SIZE); + return UInt256.from( + UInt128.from((int) partial7, (int) partial6, (int) partial5, (int) partial4), + UInt128.from((int) partial3, (int) partial2, (int) partial1, (int) partial0)); + } + + /** + * Increments {@code this}. Equivalent to {@code this.add(Int256.ONE)}, but faster. + * + * @return This number incremented by one. + */ + public UInt256 increment() { + UInt128 l = this.low.increment(); + UInt128 h = l.isZero() ? this.high.increment() : this.high; + return UInt256.from(h, l); + } + + /** + * Decrements {@code this}. Equivalent to {@code this.subtract(Int256.ONE)}, but faster. + * + * @return This number decremented by one. + */ + public UInt256 decrement() { + UInt128 l = this.low.decrement(); + UInt128 h = this.low.isZero() ? this.high.decrement() : this.high; + return UInt256.from(h, l); + } + + /** + * Multiplies {@code this} by the specified multiplicand. + * + * @param multiplicand The multiplicand to multiply {@code this} by. + * @return The result {@code this * multiplicand}. + */ + public UInt256 multiply(UInt256 multiplicand) { + // I appreciate that this looks like a wall of code, and it is, + // but the underlying algorithm is long multiplication base 2^32. + + long llowlow = (multiplicand.low.low & INT_MASK); + long partial00 = (this.low.low & INT_MASK) * llowlow; + long partial01 = (this.low.midLow & INT_MASK) * llowlow + (partial00 >>> Integer.SIZE); + long partial02 = (this.low.midHigh & INT_MASK) * llowlow + (partial01 >>> Integer.SIZE); + long partial03 = (this.low.high & INT_MASK) * llowlow + (partial02 >>> Integer.SIZE); + long partial04 = (this.high.low & INT_MASK) * llowlow + (partial03 >>> Integer.SIZE); + long partial05 = (this.high.midLow & INT_MASK) * llowlow + (partial04 >>> Integer.SIZE); + long partial06 = (this.high.midHigh & INT_MASK) * llowlow + (partial05 >>> Integer.SIZE); + long partial07 = (this.high.high & INT_MASK) * llowlow + (partial06 >>> Integer.SIZE); + + long llowmidlow = (multiplicand.low.midLow & INT_MASK); + long partial10 = (this.low.low & INT_MASK) * llowmidlow; + long partial11 = (this.low.midLow & INT_MASK) * llowmidlow + (partial10 >>> Integer.SIZE); + long partial12 = (this.low.midHigh & INT_MASK) * llowmidlow + (partial11 >>> Integer.SIZE); + long partial13 = (this.low.high & INT_MASK) * llowmidlow + (partial12 >>> Integer.SIZE); + long partial14 = (this.high.low & INT_MASK) * llowmidlow + (partial13 >>> Integer.SIZE); + long partial15 = (this.high.midLow & INT_MASK) * llowmidlow + (partial14 >>> Integer.SIZE); + long partial16 = (this.high.midHigh & INT_MASK) * llowmidlow + (partial15 >>> Integer.SIZE); + + long llowmidhigh = (multiplicand.low.midHigh & INT_MASK); + long partial20 = (this.low.low & INT_MASK) * llowmidhigh; + long partial21 = (this.low.midLow & INT_MASK) * llowmidhigh + (partial20 >>> Integer.SIZE); + long partial22 = (this.low.midHigh & INT_MASK) * llowmidhigh + (partial21 >>> Integer.SIZE); + long partial23 = (this.low.high & INT_MASK) * llowmidhigh + (partial22 >>> Integer.SIZE); + long partial24 = (this.high.low & INT_MASK) * llowmidhigh + (partial23 >>> Integer.SIZE); + long partial25 = (this.high.midLow & INT_MASK) * llowmidhigh + (partial24 >>> Integer.SIZE); + + long llowhigh = (multiplicand.low.high & INT_MASK); + long partial30 = (this.low.low & INT_MASK) * llowhigh; + long partial31 = (this.low.midLow & INT_MASK) * llowhigh + (partial30 >>> Integer.SIZE); + long partial32 = (this.low.midHigh & INT_MASK) * llowhigh + (partial31 >>> Integer.SIZE); + long partial33 = (this.low.high & INT_MASK) * llowhigh + (partial32 >>> Integer.SIZE); + long partial34 = (this.high.low & INT_MASK) * llowhigh + (partial33 >>> Integer.SIZE); + + long lhighlow = (multiplicand.high.low & INT_MASK); + long partial40 = (this.low.low & INT_MASK) * lhighlow; + long partial41 = (this.low.midLow & INT_MASK) * lhighlow + (partial40 >>> Integer.SIZE); + long partial42 = (this.low.midHigh & INT_MASK) * lhighlow + (partial41 >>> Integer.SIZE); + long partial43 = (this.low.high & INT_MASK) * lhighlow + (partial42 >>> Integer.SIZE); + + long lhighmidlow = (multiplicand.high.midLow & INT_MASK); + long partial50 = (this.low.low & INT_MASK) * lhighmidlow; + long partial51 = (this.low.midLow & INT_MASK) * lhighmidlow + (partial50 >>> Integer.SIZE); + long partial52 = (this.low.midHigh & INT_MASK) * lhighmidlow + (partial51 >>> Integer.SIZE); + + long lhighmidhigh = (multiplicand.high.midHigh & INT_MASK); + long partial60 = (this.low.low & INT_MASK) * lhighmidhigh; + long partial61 = (this.low.midLow & INT_MASK) * lhighmidhigh + (partial60 >>> Integer.SIZE); + + long partial70 = (this.low.low & INT_MASK) * (multiplicand.high.high & INT_MASK); + + long i0 = (partial00 & INT_MASK); + long i1 = (partial10 & INT_MASK) + (partial01 & INT_MASK); + long i2 = + (partial20 & INT_MASK) + + (partial11 & INT_MASK) + + (partial02 & INT_MASK) + + (i1 >>> Integer.SIZE); + long i3 = + (partial30 & INT_MASK) + + (partial21 & INT_MASK) + + (partial12 & INT_MASK) + + (partial03 & INT_MASK) + + (i2 >>> Integer.SIZE); + long i4 = + (partial40 & INT_MASK) + + (partial31 & INT_MASK) + + (partial22 & INT_MASK) + + (partial13 & INT_MASK) + + (partial04 & INT_MASK) + + (i3 >>> Integer.SIZE); + long i5 = + (partial50 & INT_MASK) + + (partial41 & INT_MASK) + + (partial32 & INT_MASK) + + (partial23 & INT_MASK) + + (partial14 & INT_MASK) + + (partial05 & INT_MASK) + + (i4 >>> Integer.SIZE); + long i6 = + (partial60 & INT_MASK) + + (partial51 & INT_MASK) + + (partial42 & INT_MASK) + + (partial33 & INT_MASK) + + (partial24 & INT_MASK) + + (partial15 & INT_MASK) + + (partial06 & INT_MASK) + + (i5 >>> Integer.SIZE); + long i7 = + (partial70 & INT_MASK) + + (partial61 & INT_MASK) + + (partial52 & INT_MASK) + + (partial43 & INT_MASK) + + (partial34 & INT_MASK) + + (partial25 & INT_MASK) + + (partial16 & INT_MASK) + + (partial07 & INT_MASK) + + (i6 >>> Integer.SIZE); + + return UInt256.from( + UInt128.from((int) i7, (int) i6, (int) i5, (int) i4), + UInt128.from((int) i3, (int) i2, (int) i1, (int) i0)); + } + + /** + * Divides {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The result {@code floor(this / divisor)}. + * @throws IllegalArgumentException if {@code divisor} is zero + */ + public UInt256 divide(UInt256 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + BigInteger q = new BigInteger(1, this.toByteArray()); + BigInteger d = new BigInteger(1, divisor.toByteArray()); + return new UInt256(q.divide(d).toByteArray()); + } + + /** + * Returns the remainder of the division of {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The remainder of the division {@code this / divisor}. + */ + public UInt256 remainder(UInt256 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + BigInteger q = new BigInteger(1, this.toByteArray()); + BigInteger d = new BigInteger(1, divisor.toByteArray()); + return new UInt256(q.remainder(d).toByteArray()); + } + + /** + * Calculates {@code this}{@code exp}. + * + * @param exp the exponent to raise {@code this} to + * @return {@code this}{@code exp} + */ + public UInt256 pow(int exp) { + if (exp < 0) { + throw new IllegalArgumentException("exp must be >= 0"); + } + + // Mirrors algorithm in multiply(...) + UInt256 result = UInt256.ONE; + UInt256 base = this; + + while (exp != 0) { + if ((exp & 1) != 0) { + result = result.multiply(base); + } + + base = base.multiply(base); + exp >>>= 1; + } + return result; + } + + /** + * Calculates the integer square root, that is the largest number {@code n} such that {@code n * n + * <= this}. + * + * @return The integer square root. + */ + public UInt256 isqrt() { + UInt256 bit = UInt256.ONE.shiftLeft(SIZE - 2); + + // "bit" starts at the highest power of four <= this + UInt256 num = this; + while (bit.compareTo(num) > 0) { + bit = bit.shiftRight(2); + } + + UInt256 res = UInt256.ZERO; + while (!bit.isZero()) { + UInt256 rab = res.add(bit); + if (num.compareTo(rab) >= 0) { + num = num.subtract(rab); + res = res.shiftRight().add(bit); + } else { + res = res.shiftRight(); + } + bit = bit.shiftRight(2); + } + return res; + } + + /** + * Shifts {@code this} left 1 bit. A zero bit is moved into the rightmost bit. + * + * @return The result of shifting {@code this} left one bit. + */ + public UInt256 shiftLeft() { + UInt128 h = this.high.shiftLeft(); + if (this.low.isHighBitSet()) { + h = h.or(UInt128.ONE); + } + UInt128 l = this.low.shiftLeft(); + return UInt256.from(h, l); + } + + /** + * Shift {@code this} left n bits. A zero bit is moved into the rightmost bits. + * + * @return The result of shifting {@code this} left n bits. + */ + public UInt256 shiftLeft(int n) { + if (n == 0) { + return this; + } else if (n >= SIZE || n <= -SIZE) { + return ZERO; // All bits are gone + } else if (n < 0) { + return shiftRight(-n); // -ve left shift is right shift + } + UInt128 h = (n >= UInt128.SIZE) ? this.low : this.high; + UInt128 l = (n >= UInt128.SIZE) ? UInt128.ZERO : this.low; + int r = n % UInt128.SIZE; + if (r > 0) { + UInt128 c = l.shiftRight(UInt128.SIZE - r); + h = h.shiftLeft(r).or(c); + l = l.shiftLeft(r); + } + return UInt256.from(h, l); + } + + /** + * Shifts {@code this} right 1 bit. A zero bit is moved into the into the leftmost bit. + * + * @return The result of arithmetic shifting {@code this} right one bit. + */ + public UInt256 shiftRight() { + UInt128 h = this.high.shiftRight(); + UInt128 l = this.low.shiftRight(); + if (this.high.isOdd()) { + l = l.or(UInt128.HIGH_BIT); + } + return UInt256.from(h, l); + } + + /** + * Logical shift {@code this} right n bits. Zeros are shifted into the leftmost bits. + * + * @return The result of logical shifting {@code this} right n bits. + */ + public UInt256 shiftRight(int n) { + if (n == 0) { + return this; + } else if (n >= SIZE || n <= -SIZE) { + return ZERO; // All bits are gone + } else if (n < 0) { + return shiftLeft(-n); // -ve right shift is left shift + } + UInt128 h = (n >= UInt128.SIZE) ? UInt128.ZERO : this.high; + UInt128 l = (n >= UInt128.SIZE) ? this.high : this.low; + int r = n % UInt128.SIZE; + if (r > 0) { + UInt128 c = h.shiftLeft(UInt128.SIZE - r); + h = h.shiftRight(r); + l = l.shiftRight(r).or(c); + } + return UInt256.from(h, l); + } + + /** + * Returns the value of {@code ~this}. + * + * @return The logical inverse of {@code this}. + */ + public UInt256 invert() { + return UInt256.from(this.high.invert(), this.low.invert()); + } + + @Override + public int compareTo(UInt256 n) { + int cmp = this.high.compareTo(n.high); + if (cmp == 0) { + cmp = this.low.compareTo(n.low); + } + return cmp; + } + + /** + * Returns the most significant word. + * + * @return the most significant word. + */ + public UInt128 getHigh() { + return this.high; + } + + /** + * Returns the least significant word. + * + * @return the least significant word. + */ + public UInt128 getLow() { + return this.low; + } + + /** + * Calculates the bitwise inclusive-or of {@code this} with {@code other} ({@code this | other}). + * + * @param other The value to inclusive-or with {@code this}. + * @return {@code this | other} + */ + public UInt256 or(UInt256 other) { + return UInt256.from(this.high.or(other.high), this.low.or(other.low)); + } + + /** + * Calculates the bitwise and of {@code this} with {@code other} ({@code this & other}). + * + * @param other The value to and with {@code this}. + * @return {@code this & other} + */ + public UInt256 and(UInt256 other) { + return UInt256.from(this.high.and(other.high), this.low.and(other.low)); + } + + /** + * Calculates the exclusive-or of {@code this} with {@code other} ({@code this ^ other}). + * + * @param other The value to exclusive-or with {@code this}. + * @return {@code this ^ other} + */ + public UInt256 xor(UInt256 other) { + return UInt256.from(this.high.xor(other.high), this.low.xor(other.low)); + } + + /** + * Returns the number of zero bits preceding the highest-order ("leftmost") one-bit in the two's + * complement binary representation of the specified {@code long} value. Returns 128 if the + * specified value has no one-bits in its two's complement representation, in other words if it is + * equal to zero. + * + *

Note that this method is closely related to the logarithm base 2. For all positive {@code + * long} values x: + * + *

    + *
  • floor(log2(x)) = {@code 255 - numberOfLeadingZeros(x)} + *
  • ceil(log2(x)) = {@code 256 - numberOfLeadingZeros(x - 1)} + *
+ * + * @return the number of zero bits preceding the highest-order ("leftmost") one-bit in the two's + * complement binary representation of the specified {@code long} value, or 256 if the value + * is equal to zero. + */ + public int numberOfLeadingZeros() { + return this.high.isZero() + ? UInt128.SIZE + this.low.numberOfLeadingZeros() + : this.high.numberOfLeadingZeros(); + } + + /** + * Return {@code true} if the {@link UInt128} has its high bit set. + * + * @return {@code true} if the {@link UInt128} has its high bit set. + */ + public boolean isHighBitSet() { + return this.high.isHighBitSet(); + } + + /** + * Returns {@code true} if {@code this} is zero. + * + * @return {@code true} if {@code this} is zero. + */ + public boolean isZero() { + return this.high.isZero() && this.low.isZero(); + } + + /** + * Returns {@code true} if {@code this} is an even number. + * + * @return {@code true} if {@code this} is an even number. + */ + public boolean isEven() { + return (this.low.low & 1) == 0; + } + + /** + * Returns {@code true} if {@code this} is an odd number. + * + * @return {@code true} if {@code this} is an odd number. + */ + public boolean isOdd() { + return (this.low.low & 1) != 0; + } + + @Override + public int hashCode() { + return this.high.hashCode() * 31 + this.low.hashCode(); + } + + @Override + public boolean equals(Object obj) { + // Note that this needs to be consistent with compareTo + if (this == obj) { + return true; + } + if (obj instanceof UInt256) { + UInt256 other = (UInt256) obj; + return Objects.equals(this.high, other.high) && Objects.equals(this.low, other.low); + } + return false; + } + + @JsonValue + public String toJson() { + return toString(10); + } + + @Override + public String toString() { + return toString(10); + } + + /** + * Returns a string representation of this object in the specified radix. + * + *

If the radix is smaller than {@code Character.MIN_RADIX} or larger than {@code + * Character.MAX_RADIX}, an {@link IllegalArgumentException} is thrown. + * + *

The characters of the result represent the magnitude of {@code this}. If the magnitude is + * zero, it is represented by a single zero character {@code '0'}; otherwise no leading zeros are + * output. + * + *

The following ASCII characters are used as digits: + * + *

+ * + * {@code 0123456789abcdefghijklmnopqrstuvwxyz} + * + *
+ * + *

If {@code radix} is N, then the first N of these characters are used + * as radix-N digits in the order shown, i.e. the digits for hexadecimal (radix 16) are + * {@code 0123456789abcdef}. + * + * @param radix the radix to use in the string representation. + * @return a string representation of the argument in the specified radix. + * @throws IllegalArgumentException if {@code radix} is less than {@code Character.MIN_RADIX} or + * greater than {@code Character.MAX_RADIX}. + * @see Character#MAX_RADIX + * @see Character#MIN_RADIX + */ + public String toString(int radix) { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new IllegalArgumentException("Illegal radix: " + radix); + } + if (isZero()) { + return "0"; + } + StringBuilder sb = new StringBuilder(); + UInt256 n = this; + UInt256 r = UInt256.from(radix); + while (!n.isZero()) { + UInt256 digit = n.remainder(r); + sb.append(Character.forDigit(digit.low.low, radix)); + n = n.divide(r); + } + return sb.reverse().toString(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256s.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256s.java index ce35b19cb7..8f7296a7fe 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256s.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt256s.java @@ -64,147 +64,147 @@ package com.radixdlt.utils; +import com.radixdlt.SecurityCritical; +import com.radixdlt.SecurityCritical.SecurityKind; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Objects; -import com.radixdlt.SecurityCritical; -import com.radixdlt.SecurityCritical.SecurityKind; - -/** - * Utility methods for converting to/from {@link UInt256}. - */ +/** Utility methods for converting to/from {@link UInt256}. */ @SecurityCritical(SecurityKind.NUMERIC) public final class UInt256s { - private UInt256s() { - throw new IllegalStateException("Can't construct"); - } - - private static final BigInteger MAX_UINT256 = toBigInteger(UInt256.MAX_VALUE); - - /** - * Returns the specified {@link UInt256} as a {@link BigInteger}. - * - * @param value The value to convert - * @return The value as a {@link BigInteger} - */ - public static BigInteger toBigInteger(UInt256 value) { - // Set sign to positive to stop BigInteger interpreting high bit as sign - return new BigInteger(1, value.toByteArray()); - } - - /** - * Returns the specified {@link BigInteger} as a {@link UInt256}. - * - * @param value The value to convert - * @return The value as a {@link UInt256} - * @throws IllegalArgumentException if {@code value} < 0, or {@code value} > {@link UInt256#MAX_VALUE}. - */ - public static UInt256 fromBigInteger(BigInteger value) { - if (value.signum() < 0) { - throw new IllegalArgumentException("value must be >= 0: " + value); - } - - if (value.compareTo(MAX_UINT256) > 0) { - throw new IllegalArgumentException("value must be <= " + MAX_UINT256 + ": " + value); - } - - final byte[] byteArray = value.toByteArray(); - - // Handle possible padded zeroes - if (byteArray.length > UInt256.BYTES) { - return UInt256.from(byteArray, byteArray.length - UInt256.BYTES); - } else { - return UInt256.from(byteArray); - } - } - - /** - * Returns the specified {@link BigDecimal} as a {@link UInt256}. - * - * @param value The value to convert - * @return The value as a {@link UInt256} - * @throws ArithmeticException if {@code value} has a nonzero fractional part. - * @throws IllegalArgumentException if {@code value} < 0, or {@code value} > {@link UInt256#MAX_VALUE}. - */ - public static UInt256 fromBigDecimal(BigDecimal value) { - return fromBigInteger(value.toBigIntegerExact()); - } - - /** - * Returns the smaller of two {@code UInt256} values. That is, the result - * the argument closer to the value of {@link UInt256#MIN_VALUE}. If the - * arguments have the same value, the result is that same value. - * - * @param a an argument. - * @param b another argument. - * @return the smaller of {@code a} and {@code b}. - */ - public static UInt256 min(UInt256 a, UInt256 b) { - int cmp = a.compareTo(b); - return (cmp <= 0) ? a : b; - } - - /** - * Returns the larger of two {@code UInt256} values. That is, the result - * the argument closer to the value of {@link UInt256#MAX_VALUE}. If the - * arguments have the same value, the result is that same value. - * - * @param a an argument. - * @param b another argument. - * @return the smaller of {@code a} and {@code b}. - */ - public static UInt256 max(UInt256 a, UInt256 b) { - int cmp = a.compareTo(b); - return (cmp >= 0) ? a : b; - } - - /** - * Euclid's algorithm to find greatest common divisor. - * - * @param x first number - * @param y second number - * @return greatest common divisor between x and y - */ - private static UInt256 gcd(UInt256 x, UInt256 y) { - return (y.isZero()) ? x : gcd(y, x.remainder(y)); - } - - /** - * Least common multiple computed via reduction by gcd. - * - * @param x first number - * @param y second number - * @return least common multiple between x and y, or {@code null} if overflow - */ - private static UInt256 lcm(UInt256 x, UInt256 y) { - UInt256 d = y.divide(gcd(x, y)); - UInt256 r = x.multiply(d); - boolean overflow = !x.isZero() && !r.divide(x).equals(d); - return overflow ? null : r; - } - - /** - * Returns a capped least common multiple between an array of {@link UInt256} numbers. - * The cap acts as a ceiling. If the result exceeds cap, then this will return null. - *

- * All numbers must be non-null and non-zero. Otherwise, result is undefined. - * - * @param cap the cap to be used for the computation - * @param numbers array of numbers of size at least 1 - * @return null, if the least common multiple is greater than cap, otherwise the least common multiple - * @throws ArrayIndexOutOfBoundsException if numbers is a zero length array - * @throws NullPointerException if cap or numbers is null - */ - public static UInt256 cappedLCM(UInt256 cap, UInt256... numbers) { - Objects.requireNonNull(cap); - UInt256 r = numbers[0]; - for (int i = 1; i < numbers.length; i++) { - r = lcm(r, numbers[i]); - if (r == null || r.compareTo(cap) > 0) { - return null; - } - } - return r; - } + private UInt256s() { + throw new IllegalStateException("Can't construct"); + } + + private static final BigInteger MAX_UINT256 = toBigInteger(UInt256.MAX_VALUE); + + /** + * Returns the specified {@link UInt256} as a {@link BigInteger}. + * + * @param value The value to convert + * @return The value as a {@link BigInteger} + */ + public static BigInteger toBigInteger(UInt256 value) { + // Set sign to positive to stop BigInteger interpreting high bit as sign + return new BigInteger(1, value.toByteArray()); + } + + /** + * Returns the specified {@link BigInteger} as a {@link UInt256}. + * + * @param value The value to convert + * @return The value as a {@link UInt256} + * @throws IllegalArgumentException if {@code value} < 0, or {@code value} > {@link + * UInt256#MAX_VALUE}. + */ + public static UInt256 fromBigInteger(BigInteger value) { + if (value.signum() < 0) { + throw new IllegalArgumentException("value must be >= 0: " + value); + } + + if (value.compareTo(MAX_UINT256) > 0) { + throw new IllegalArgumentException("value must be <= " + MAX_UINT256 + ": " + value); + } + + final byte[] byteArray = value.toByteArray(); + + // Handle possible padded zeroes + if (byteArray.length > UInt256.BYTES) { + return UInt256.from(byteArray, byteArray.length - UInt256.BYTES); + } else { + return UInt256.from(byteArray); + } + } + + /** + * Returns the specified {@link BigDecimal} as a {@link UInt256}. + * + * @param value The value to convert + * @return The value as a {@link UInt256} + * @throws ArithmeticException if {@code value} has a nonzero fractional part. + * @throws IllegalArgumentException if {@code value} < 0, or {@code value} > {@link + * UInt256#MAX_VALUE}. + */ + public static UInt256 fromBigDecimal(BigDecimal value) { + return fromBigInteger(value.toBigIntegerExact()); + } + + /** + * Returns the smaller of two {@code UInt256} values. That is, the result the argument closer to + * the value of {@link UInt256#MIN_VALUE}. If the arguments have the same value, the result is + * that same value. + * + * @param a an argument. + * @param b another argument. + * @return the smaller of {@code a} and {@code b}. + */ + public static UInt256 min(UInt256 a, UInt256 b) { + int cmp = a.compareTo(b); + return (cmp <= 0) ? a : b; + } + + /** + * Returns the larger of two {@code UInt256} values. That is, the result the argument closer to + * the value of {@link UInt256#MAX_VALUE}. If the arguments have the same value, the result is + * that same value. + * + * @param a an argument. + * @param b another argument. + * @return the smaller of {@code a} and {@code b}. + */ + public static UInt256 max(UInt256 a, UInt256 b) { + int cmp = a.compareTo(b); + return (cmp >= 0) ? a : b; + } + + /** + * Euclid's algorithm to find greatest common divisor. + * + * @param x first number + * @param y second number + * @return greatest common divisor between x and y + */ + private static UInt256 gcd(UInt256 x, UInt256 y) { + return (y.isZero()) ? x : gcd(y, x.remainder(y)); + } + + /** + * Least common multiple computed via reduction by gcd. + * + * @param x first number + * @param y second number + * @return least common multiple between x and y, or {@code null} if overflow + */ + private static UInt256 lcm(UInt256 x, UInt256 y) { + UInt256 d = y.divide(gcd(x, y)); + UInt256 r = x.multiply(d); + boolean overflow = !x.isZero() && !r.divide(x).equals(d); + return overflow ? null : r; + } + + /** + * Returns a capped least common multiple between an array of {@link UInt256} numbers. The cap + * acts as a ceiling. If the result exceeds cap, then this will return null. + * + *

All numbers must be non-null and non-zero. Otherwise, result is undefined. + * + * @param cap the cap to be used for the computation + * @param numbers array of numbers of size at least 1 + * @return null, if the least common multiple is greater than cap, otherwise the least common + * multiple + * @throws ArrayIndexOutOfBoundsException if numbers is a zero length array + * @throws NullPointerException if cap or numbers is null + */ + public static UInt256 cappedLCM(UInt256 cap, UInt256... numbers) { + Objects.requireNonNull(cap); + UInt256 r = numbers[0]; + for (int i = 1; i < numbers.length; i++) { + r = lcm(r, numbers[i]); + if (r == null || r.compareTo(cap) > 0) { + return null; + } + } + return r; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt384.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt384.java index 20ce09519a..c5d2e5145b 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt384.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UInt384.java @@ -64,842 +64,826 @@ package com.radixdlt.utils; +import static com.radixdlt.errors.ApiErrors.UNABLE_TO_PARSE_UINT; + import com.radixdlt.SecurityCritical; import com.radixdlt.SecurityCritical.SecurityKind; import com.radixdlt.utils.functional.Result; - import java.util.Arrays; import java.util.Objects; -import static com.radixdlt.errors.ApiErrors.UNABLE_TO_PARSE_UINT; - -/** - * A 384-bit unsigned integer, with comparison and some basic arithmetic - * operations. - */ +/** A 384-bit unsigned integer, with comparison and some basic arithmetic operations. */ @SecurityCritical(SecurityKind.NUMERIC) public final class UInt384 implements Comparable { - // Some sizing constants in line with Integer, Long etc - /** - * Size of this numeric type in bits. - */ - public static final int SIZE = UInt128.SIZE + UInt256.SIZE; - - /** - * Size of this numeric type in bytes. - */ - public static final int BYTES = UInt128.BYTES + UInt256.BYTES; - - /** - * A constant holding the minimum value a {@code UInt384} can - * have, 0. - */ - public static final UInt384 MIN_VALUE = new UInt384(UInt128.ZERO, UInt256.ZERO); - - /** - * Highest bit. - */ - public static final UInt384 HIGH_BIT = new UInt384(UInt128.HIGH_BIT, UInt256.ZERO); - - /** - * A constant holding the maximum value an {@code Int256} can - * have, 2384-1. - */ - public static final UInt384 MAX_VALUE = new UInt384(UInt128.MAX_VALUE, UInt256.MAX_VALUE); - - // Some commonly used values - public static final UInt384 ZERO = new UInt384(UInt128.ZERO, UInt256.ZERO); - public static final UInt384 ONE = new UInt384(UInt128.ZERO, UInt256.ONE); - public static final UInt384 TWO = new UInt384(UInt128.ZERO, UInt256.TWO); - public static final UInt384 THREE = new UInt384(UInt128.ZERO, UInt256.THREE); - public static final UInt384 FOUR = new UInt384(UInt128.ZERO, UInt256.FOUR); - public static final UInt384 FIVE = new UInt384(UInt128.ZERO, UInt256.FIVE); - public static final UInt384 SIX = new UInt384(UInt128.ZERO, UInt256.SIX); - public static final UInt384 SEVEN = new UInt384(UInt128.ZERO, UInt256.SEVEN); - public static final UInt384 EIGHT = new UInt384(UInt128.ZERO, UInt256.EIGHT); - public static final UInt384 NINE = new UInt384(UInt128.ZERO, UInt256.NINE); - public static final UInt384 TEN = new UInt384(UInt128.ZERO, UInt256.TEN); - - // Numbers in order. This is used by factory methods. - private static final UInt384[] numbers = { - ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN - }; - - // The actual value. - // @PackageLocalForTest - final UInt128 high; - // @PackageLocalForTest - final UInt256 low; - - /** - * Factory method for materialising an {@link UInt384} from a {@code short} - * value. - * - * @param value The value to be represented as an {@link UInt384}. - * @return {@code value} as an {@link UInt384} type. - */ - public static UInt384 from(short value) { - return from((long) value); - } - - /** - * Factory method for materialising an {@link UInt384} from an {@code int} value. - * - * @param value The value to be represented as an {@link UInt384}. - * @return {@code value} as an {@link UInt384} type. - */ - public static UInt384 from(int value) { - return from((long) value); - } - - /** - * Factory method for materialising an {@link UInt384} from a {@code long} value. - * Note that values are zero extended into the 256 bit value. - * - * @param value The value to be represented as an {@link UInt384}. - * @return {@code value} as an {@link UInt384} type. - */ - public static UInt384 from(long value) { - return from(UInt128.from(value)); - } - - /** - * Factory method for materialising an {@link UInt384} from an {@link UInt128} - * value. Note that values are zero extended into the 256 bit value. - * - * @param value The least significant half-word of the value. - * @return the specified value as an {@link UInt384} type. - */ - public static UInt384 from(UInt128 value) { - return from(UInt256.from(value)); - } - - /** - * Factory method for materialising an {@link UInt384} from an {@link UInt128} - * value. Note that values are zero extended into the 256 bit value. - * - * @param value The least significant word of the value. - * @return the specified value as an {@link UInt384} type. - */ - public static UInt384 from(UInt256 value) { - return from(UInt128.ZERO, value); - } - - /** - * Factory method for materialising an {@link UInt384} - * from constituent {@link UInt128} and {@link UInt256} parts. - * - * @param high The most significant half-word of the value. - * @param low The least significant word of the value. - * @return the specified values as an {@link UInt384} type. - */ - public static UInt384 from(UInt128 high, UInt256 low) { - return new UInt384(high, low); - } - - /** - * Factory method for materialising an {@link UInt384} from an array - * of bytes. The array is most-significant byte first, and must not be - * zero length. - *

If the array is smaller than {@link #BYTES}, then it is effectively - * padded with leading zero bytes. - *

If the array is longer than {@link #BYTES}, then values at index - * {@link #BYTES} and beyond are ignored. - * - * @param bytes The array of bytes to be used. - * @return {@code bytes} as an {@link UInt384} type. - * @throws IllegalArgumentException if {@code bytes} is 0 length. - * @see #toByteArray() - */ - public static UInt384 from(byte[] bytes) { - Objects.requireNonNull(bytes); - if (bytes.length == 0) { - throw new IllegalArgumentException("bytes is 0 bytes long"); - } - byte[] newBytes = extend(bytes); - return from(newBytes, 0); - } - - /** - * Factory method for materialising an {@link UInt384} from an array - * of bytes. The array is most-significant byte first. - * - * @param bytes The array of bytes to be used. - * @param offset The offset within the array to be used. - * @return {@code bytes} from {@code offset} as an {@link UInt384} type. - * @see #toByteArray() - */ - public static UInt384 from(byte[] bytes, int offset) { - UInt128 high = UInt128.from(bytes, offset); - UInt256 low = UInt256.from(bytes, offset + UInt128.BYTES); - return from(high, low); - } - - /** - * Factory method for materialising an {@link UInt384} from a string. - * Conversion is performed base 10 and leading '+' sign character is - * permitted. - * - * @param s The array of bytes to be used. - * @return {@code s} as an {@link UInt384} type. - * @throws NumberFormatException if {@code s} is not a valid - * integer number. - */ - public static UInt384 from(String s) { - Objects.requireNonNull(s); - - int len = s.length(); - if (len > 0) { - int i = 0; - char ch = s.charAt(0); - if (ch == '+') { - i += 1; // skip first char - } - if (i >= len) { - throw new NumberFormatException(s); - } - // No real effort to catch overflow here - UInt384 result = UInt384.ZERO; - while (i < len) { - int digit = Character.digit(s.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException(s); - } - result = result.multiply(UInt384.TEN).add(numbers[digit]); - } - return result; - } else { - throw new NumberFormatException(s); - } - } - - /** - * Functional style friendly version of {@link #from(String)}. - * - * @param input The string to parse - * - * @return Success {@link Result} if value can be successfully parsed and failure {@link Result} otherwise. - */ - public static Result fromString(String input) { - return Result.wrap(() -> UNABLE_TO_PARSE_UINT.with(input), () -> from(input)); - } - - // Pad short (< BYTES length) array with appropriate lead bytes. - private static byte[] extend(byte[] bytes) { - if (bytes.length >= BYTES) { - return bytes; - } - byte[] newBytes = new byte[BYTES]; - int newPos = BYTES - bytes.length; - Arrays.fill(newBytes, 0, newPos, (byte) 0); - System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); - return newBytes; - } - - private UInt384(UInt128 high, UInt256 low) { - this.high = Objects.requireNonNull(high); - this.low = Objects.requireNonNull(low); - } - - /** - * Converts {@code this} to an array of bytes. - * The most significant byte will be returned in index zero. - * The array will always be {@link #BYTES} bytes long, and - * will be zero filled to suit the actual value. - * - * @return An array of {@link #BYTES} bytes representing the - * value of this {@link UInt384}. - */ - public byte[] toByteArray() { - return toByteArray(new byte[BYTES], 0); - } - - /** - * Converts {@code this} to an array of bytes. - * The most significant byte will be returned in index {@code offset}. - * The array must be at least {@code offset + BYTES} long. - * - * @param bytes The array to place the bytes in. - * @param offset The offset within the array to place the bytes. - * @return The passed-in value of {@code bytes}. - */ - public byte[] toByteArray(byte[] bytes, int offset) { - this.high.toByteArray(bytes, offset); - this.low.toByteArray(bytes, offset + UInt128.BYTES); - return bytes; - } - - /** - * Adds {@code other} to {@code this}, returning the result. - * - * @param other The addend. - * @return An {@link UInt384} with the value {@code this + other}. - */ - public UInt384 add(UInt384 other) { - UInt256 newLow = this.low.add(other.low); - // Hacker's Delight section 2-13: - // "The following branch-free code can be used to compute the - // overflow predicate for unsigned add/subtract, with the result - // being in the sign position." - // Note that the use of method calls and the ternary operator - // very likely precludes this from being branch-free in java. - UInt128 carry = this.low.shiftRight() - .add(other.low.shiftRight()) - .add(this.low.and(other.low).and(UInt256.ONE)) - .isHighBitSet() ? UInt128.ONE : UInt128.ZERO; - UInt128 newHigh = this.high.add(other.high).add(carry); - return UInt384.from(newHigh, newLow); - } - - /** - * Adds {@code other} to {@code this}, returning the result. - * - * @param other The addend. - * @return An {@link UInt384} with the value {@code this + other}. - */ - public UInt384 add(UInt256 other) { - UInt256 newLow = this.low.add(other); - // Hacker's Delight section 2-13: - // "The following branch-free code can be used to compute the - // overflow predicate for unsigned add/subtract, with the result - // being in the sign position." - // Note that the use of method calls and the ternary operator - // very likely precludes this from being branch-free in java. - UInt128 newHigh = this.low.shiftRight() - .add(other.low.shiftRight()) - .add(this.low.and(other).and(UInt256.ONE)) - .isHighBitSet() ? this.high.increment() : this.high; - return UInt384.from(newHigh, newLow); - } - - /** - * Subtracts {@code other} from {@code this}, returning the result. - * - * @param other The subtrahend. - * @return An {@link UInt384} with the value {@code this - other}. - */ - public UInt384 subtract(UInt384 other) { - UInt256 newLow = this.low.subtract(other.low); - // Hacker's Delight section 2-13: - // "The following branch-free code can be used to compute the - // overflow predicate for unsigned add/subtract, with the result - // being in the sign position." - // Note that the use of method calls and the ternary operator - // very likely precludes this from being branch-free in java. - UInt128 carry = this.low.shiftRight() - .subtract(other.low.shiftRight()) - .subtract(this.low.invert().and(other.low).and(UInt256.ONE)) - .isHighBitSet() ? UInt128.ONE : UInt128.ZERO; - UInt128 newHigh = this.high.subtract(other.high).subtract(carry); - return UInt384.from(newHigh, newLow); - } - - /** - * Subtracts {@code other} from {@code this}, returning the result. - * - * @param other The subtrahend. - * @return An {@link UInt384} with the value {@code this - other}. - */ - public UInt384 subtract(UInt256 other) { - UInt256 newLow = this.low.subtract(other); - // Hacker's Delight section 2-13: - // "The following branch-free code can be used to compute the - // overflow predicate for unsigned add/subtract, with the result - // being in the sign position." - // Note that the use of method calls and the ternary operator - // very likely precludes this from being branch-free in java. - UInt128 newHigh = this.low.shiftRight() - .subtract(other.low.shiftRight()) - .subtract(this.low.invert().and(other).and(UInt256.ONE)) - .isHighBitSet() ? this.high.decrement() : this.high; - return UInt384.from(newHigh, newLow); - } - - /** - * Increments {@code this}. Equivalent to {@code this.add(Int256.ONE)}, but - * faster. - * - * @return This number incremented by one. - */ - public UInt384 increment() { - UInt256 l = this.low.increment(); - UInt128 h = l.isZero() ? this.high.increment() : this.high; - return UInt384.from(h, l); - } - - /** - * Decrements {@code this}. Equivalent to {@code this.subtract(Int256.ONE)}, but - * faster. - * - * @return This number decremented by one. - */ - public UInt384 decrement() { - UInt256 l = this.low.decrement(); - UInt128 h = this.low.isZero() ? this.high.decrement() : this.high; - return UInt384.from(h, l); - } - - /** - * Multiplies {@code this} by the specified multiplicand. - * - * @param multiplicand The multiplicand to multiply {@code this} by. - * @return The result {@code this * multiplicand}. - */ - public UInt384 multiply(UInt384 multiplicand) { - // Russian peasant - UInt384 result = UInt384.ZERO; - UInt384 multiplier = this; - - while (!multiplicand.isZero()) { - if (multiplicand.isOdd()) { - result = result.add(multiplier); - } - - multiplier = multiplier.shiftLeft(); - multiplicand = multiplicand.shiftRight(); - } - return result; - } - - /** - * Multiplies {@code this} by the specified multiplicand. - * - * @param multiplicand The multiplicand to multiply {@code this} by. - * @return The result {@code this * multiplicand}. - */ - public UInt384 multiply(UInt256 multiplicand) { - // Russian peasant - UInt384 result = UInt384.ZERO; - UInt384 multiplier = this; - - while (!multiplicand.isZero()) { - if (multiplicand.isOdd()) { - result = result.add(multiplier); - } - - multiplier = multiplier.shiftLeft(); - multiplicand = multiplicand.shiftRight(); - } - return result; - } - - /** - * Divides {@code this} by the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * @return The result {@code floor(this / divisor)}. - * @throws IllegalArgumentException if {@code divisor} is zero - */ - public UInt384 divide(UInt384 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - UInt384 q = UInt384.ZERO; - UInt384 r = UInt384.ZERO; - UInt384 n = this; - for (int i = 0; i < SIZE; ++i) { - r = r.shiftLeft(); - q = q.shiftLeft(); - if (n.high.isHighBitSet()) { - r = r.or(UInt384.ONE); - } - n = n.shiftLeft(); - if (r.compareTo(divisor) >= 0) { - r = r.subtract(divisor); - q = q.or(UInt384.ONE); - } - } - return q; - } - - /** - * Divides {@code this} by the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * @return The result {@code floor(this / divisor)}. - * @throws IllegalArgumentException if {@code divisor} is zero - */ - public UInt384 divide(UInt256 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - UInt384 q = UInt384.ZERO; - UInt384 r = UInt384.ZERO; - UInt384 n = this; - for (int i = 0; i < SIZE; ++i) { - r = r.shiftLeft(); - q = q.shiftLeft(); - if (n.high.isHighBitSet()) { - r = r.or(UInt384.ONE); - } - n = n.shiftLeft(); - if (!r.high.isZero() || (r.low.compareTo(divisor) >= 0)) { - r = r.subtract(divisor); - q = q.or(UInt384.ONE); - } - } - return q; - } - - /** - * Returns the remainder of the division of {@code this} by - * the specified divisor. - * - * @param divisor The divisor to divide {@code this} by. - * @return The remainder of the division {@code this / divisor}. - */ - public UInt384 remainder(UInt384 divisor) { - if (divisor.isZero()) { - throw new IllegalArgumentException("Can't divide by zero"); - } - UInt384 r = UInt384.ZERO; - UInt384 n = this; - for (int i = 0; i < SIZE; ++i) { - r = r.shiftLeft(); - if (n.high.isHighBitSet()) { - r = r.or(UInt384.ONE); - } - n = n.shiftLeft(); - if (r.compareTo(divisor) >= 0) { - r = r.subtract(divisor); - } - } - return r; - } - - /** - * Calculates {@code this}{@code exp}. - * - * @param exp the exponent to raise {@code this} to - * @return {@code this}{@code exp} - */ - public UInt384 pow(int exp) { - if (exp < 0) { - throw new IllegalArgumentException("exp must be >= 0"); - } - - // Mirrors algorithm in multiply(...) - UInt384 result = UInt384.ONE; - UInt384 base = this; - - while (exp != 0) { - if ((exp & 1) != 0) { - result = result.multiply(base); - } - - base = base.multiply(base); - exp >>>= 1; - } - return result; - } - - /** - * Calculates the integer square root, that is the largest number {@code n} - * such that {@code n * n <= this}. - * - * @return The integer square root. - */ - public UInt384 isqrt() { - UInt384 bit = UInt384.ONE.shiftLeft(SIZE - 2); - - // "bit" starts at the highest power of four <= this - UInt384 num = this; - while (bit.compareTo(num) > 0) { - bit = bit.shiftRight(2); - } - - UInt384 res = UInt384.ZERO; - while (!bit.isZero()) { - UInt384 rab = res.add(bit); - if (num.compareTo(rab) >= 0) { - num = num.subtract(rab); - res = res.shiftRight().add(bit); - } else { - res = res.shiftRight(); - } - bit = bit.shiftRight(2); - } - return res; - } - - /** - * Shifts {@code this} left 1 bit. A zero bit is moved into the - * rightmost bit. - * - * @return The result of shifting {@code this} left one bit. - */ - public UInt384 shiftLeft() { - UInt128 h = this.high.shiftLeft(); - if (this.low.isHighBitSet()) { - h = h.or(UInt128.ONE); - } - UInt256 l = this.low.shiftLeft(); - return UInt384.from(h, l); - } - - /** - * Shift {@code this} left n bits. A zero bit is moved into the - * rightmost bits. - * - * @return The result of shifting {@code this} left n bits. - */ - public UInt384 shiftLeft(int n) { - if (n == 0) { - return this; - } else if (n >= SIZE || n <= -SIZE) { - return ZERO; // All bits are gone - } else if (n < 0) { - return shiftRight(-n); // -ve left shift is right shift - } - UInt256 h = (n >= UInt256.SIZE) ? this.low : UInt256.from(this.high); - UInt256 l = (n >= UInt256.SIZE) ? UInt256.ZERO : this.low; - int r = n % UInt256.SIZE; - if (r > 0) { - UInt256 c = l.shiftRight(UInt256.SIZE - r); - h = h.shiftLeft(r).or(c); - l = l.shiftLeft(r); - } - return UInt384.from(h.low, l); - } - - /** - * Shifts {@code this} right 1 bit. A zero bit is moved into the - * into the left bit. - * - * @return The result of arithmetic shifting {@code this} right one bit. - */ - public UInt384 shiftRight() { - UInt128 h = this.high.shiftRight(); - UInt256 l = this.low.shiftRight(); - if (this.high.isOdd()) { - l = l.or(UInt256.HIGH_BIT); - } - return UInt384.from(h, l); - } - - /** - * Logical shift {@code this} right n bits. Zeros are shifted into - * the leftmost bits. - * - * @return The result of logical shifting {@code this} right n bits. - */ - public UInt384 shiftRight(int n) { - if (n == 0) { - return this; - } else if (n >= SIZE || n <= -SIZE) { - return ZERO; // All bits are gone - } else if (n < 0) { - return shiftLeft(-n); // -ve right shift is left shift - } - UInt256 h = (n >= UInt256.SIZE) ? UInt256.ZERO : UInt256.from(this.high); - UInt256 l = (n >= UInt256.SIZE) ? UInt256.from(this.high) : this.low; - int r = n % UInt256.SIZE; - if (r > 0) { - UInt256 c = h.shiftLeft(UInt256.SIZE - r); - h = h.shiftRight(r); - l = l.shiftRight(r).or(c); - } - return UInt384.from(h.low, l); - } - - /** - * Returns the value of {@code ~this}. - * - * @return The logical inverse of {@code this}. - */ - public UInt384 invert() { - return UInt384.from(this.high.invert(), this.low.invert()); - } - - @Override - public int compareTo(UInt384 n) { - int cmp = this.high.compareTo(n.high); - if (cmp == 0) { - cmp = this.low.compareTo(n.low); - } - return cmp; - } - - /** - * Returns the most significant half-word. - * - * @return the most significant half-word. - */ - public UInt128 getHigh() { - return this.high; - } - - /** - * Returns the least significant word. - * - * @return the least significant word. - */ - public UInt256 getLow() { - return this.low; - } - - /** - * Calculates the bitwise inclusive-or of {@code this} with {@code other} - * ({@code this | other}). - * - * @param other The value to inclusive-or with {@code this}. - * @return {@code this | other} - */ - public UInt384 or(UInt384 other) { - return UInt384.from(this.high.or(other.high), this.low.or(other.low)); - } - - /** - * Calculates the bitwise and of {@code this} with {@code other} - * ({@code this & other}). - * - * @param other The value to and with {@code this}. - * @return {@code this & other} - */ - public UInt384 and(UInt384 other) { - return UInt384.from(this.high.and(other.high), this.low.and(other.low)); - } - - /** - * Calculates the exclusive-or of {@code this} with {@code other} - * ({@code this ^ other}). - * - * @param other The value to exclusive-or with {@code this}. - * @return {@code this ^ other} - */ - public UInt384 xor(UInt384 other) { - return UInt384.from(this.high.xor(other.high), this.low.xor(other.low)); - } - - /** - * Returns the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code long} value. Returns 128 if the - * specified value has no one-bits in its two's complement representation, - * in other words if it is equal to zero. - * - *

Note that this method is closely related to the logarithm base 2. - * For all positive {@code long} values x: - *

    - *
  • floor(log2(x)) = {@code 255 - numberOfLeadingZeros(x)} - *
  • ceil(log2(x)) = {@code 256 - numberOfLeadingZeros(x - 1)} - *
- * - * @return the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code long} value, or 256 if the value - * is equal to zero. - */ - public int numberOfLeadingZeros() { - return this.high.isZero() - ? UInt128.SIZE + this.low.numberOfLeadingZeros() - : this.high.numberOfLeadingZeros(); - } - - /** - * Return {@code true} if the {@link UInt128} has its high bit set. - * - * @return {@code true} if the {@link UInt128} has its high bit set. - */ - public boolean isHighBitSet() { - return this.high.isHighBitSet(); - } - - /** - * Returns {@code true} if {@code this} is zero. - * - * @return {@code true} if {@code this} is zero. - */ - public boolean isZero() { - return this.high.isZero() && this.low.isZero(); - } - - /** - * Returns {@code true} if {@code this} is an even number. - * - * @return {@code true} if {@code this} is an even number. - */ - public boolean isEven() { - return this.low.isEven(); - } - - /** - * Returns {@code true} if {@code this} is an odd number. - * - * @return {@code true} if {@code this} is an odd number. - */ - public boolean isOdd() { - return this.low.isOdd(); - } - - @Override - public int hashCode() { - return Objects.hashCode(this.high) * 31 + Objects.hashCode(this.low); - } - - @Override - public boolean equals(Object obj) { - // Note that this needs to be consistent with compareTo - if (this == obj) { - return true; - } - - if (obj instanceof UInt384) { - UInt384 other = (UInt384) obj; - return Objects.equals(this.high, other.high) && Objects.equals(this.low, other.low); - } - return false; - } - - @Override - public String toString() { - return toString(10); - } - - /** - * Returns a string representation of this object in the specified radix. - *

- * If the radix is smaller than {@code Character.MIN_RADIX} or larger than - * {@code Character.MAX_RADIX}, an {@link IllegalArgumentException} is - * thrown. - *

- * The characters of the result represent the magnitude of {@code this}. - * If the magnitude is zero, it is represented by a single zero character - * {@code '0'}; otherwise no leading zeros are output. - *

- * The following ASCII characters are used as digits: - * - *

- * {@code 0123456789abcdefghijklmnopqrstuvwxyz} - *
- * - * If {@code radix} is N, then the first N of these - * characters are used as radix-N digits in the order shown, - * i.e. the digits for hexadecimal (radix 16) are {@code 0123456789abcdef}. - * - * @param radix the radix to use in the string representation. - * @return a string representation of the argument in the specified radix. - * @see Character#MAX_RADIX - * @throws IllegalArgumentException if {@code radix} is less than - * {@code Character.MIN_RADIX} or greater than - * {@code Character.MAX_RADIX}. - * @see Character#MIN_RADIX - */ - public String toString(int radix) { - if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new IllegalArgumentException("Illegal radix: " + radix); - } - if (isZero()) { - return "0"; - } - StringBuilder sb = new StringBuilder(); - UInt384 n = this; - UInt384 r = UInt384.from(radix); - while (!n.isZero()) { - UInt384 digit = n.remainder(r); - sb.append(Character.forDigit(digit.low.low.low, radix)); - n = n.divide(r); - } - return sb.reverse().toString(); - } + // Some sizing constants in line with Integer, Long etc + /** Size of this numeric type in bits. */ + public static final int SIZE = UInt128.SIZE + UInt256.SIZE; + + /** Size of this numeric type in bytes. */ + public static final int BYTES = UInt128.BYTES + UInt256.BYTES; + + /** A constant holding the minimum value a {@code UInt384} can have, 0. */ + public static final UInt384 MIN_VALUE = new UInt384(UInt128.ZERO, UInt256.ZERO); + + /** Highest bit. */ + public static final UInt384 HIGH_BIT = new UInt384(UInt128.HIGH_BIT, UInt256.ZERO); + + /** A constant holding the maximum value an {@code Int256} can have, 2384-1. */ + public static final UInt384 MAX_VALUE = new UInt384(UInt128.MAX_VALUE, UInt256.MAX_VALUE); + + // Some commonly used values + public static final UInt384 ZERO = new UInt384(UInt128.ZERO, UInt256.ZERO); + public static final UInt384 ONE = new UInt384(UInt128.ZERO, UInt256.ONE); + public static final UInt384 TWO = new UInt384(UInt128.ZERO, UInt256.TWO); + public static final UInt384 THREE = new UInt384(UInt128.ZERO, UInt256.THREE); + public static final UInt384 FOUR = new UInt384(UInt128.ZERO, UInt256.FOUR); + public static final UInt384 FIVE = new UInt384(UInt128.ZERO, UInt256.FIVE); + public static final UInt384 SIX = new UInt384(UInt128.ZERO, UInt256.SIX); + public static final UInt384 SEVEN = new UInt384(UInt128.ZERO, UInt256.SEVEN); + public static final UInt384 EIGHT = new UInt384(UInt128.ZERO, UInt256.EIGHT); + public static final UInt384 NINE = new UInt384(UInt128.ZERO, UInt256.NINE); + public static final UInt384 TEN = new UInt384(UInt128.ZERO, UInt256.TEN); + + // Numbers in order. This is used by factory methods. + private static final UInt384[] numbers = { + ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN + }; + + // The actual value. + // @PackageLocalForTest + final UInt128 high; + // @PackageLocalForTest + final UInt256 low; + + /** + * Factory method for materialising an {@link UInt384} from a {@code short} value. + * + * @param value The value to be represented as an {@link UInt384}. + * @return {@code value} as an {@link UInt384} type. + */ + public static UInt384 from(short value) { + return from((long) value); + } + + /** + * Factory method for materialising an {@link UInt384} from an {@code int} value. + * + * @param value The value to be represented as an {@link UInt384}. + * @return {@code value} as an {@link UInt384} type. + */ + public static UInt384 from(int value) { + return from((long) value); + } + + /** + * Factory method for materialising an {@link UInt384} from a {@code long} value. Note that values + * are zero extended into the 256 bit value. + * + * @param value The value to be represented as an {@link UInt384}. + * @return {@code value} as an {@link UInt384} type. + */ + public static UInt384 from(long value) { + return from(UInt128.from(value)); + } + + /** + * Factory method for materialising an {@link UInt384} from an {@link UInt128} value. Note that + * values are zero extended into the 256 bit value. + * + * @param value The least significant half-word of the value. + * @return the specified value as an {@link UInt384} type. + */ + public static UInt384 from(UInt128 value) { + return from(UInt256.from(value)); + } + + /** + * Factory method for materialising an {@link UInt384} from an {@link UInt128} value. Note that + * values are zero extended into the 256 bit value. + * + * @param value The least significant word of the value. + * @return the specified value as an {@link UInt384} type. + */ + public static UInt384 from(UInt256 value) { + return from(UInt128.ZERO, value); + } + + /** + * Factory method for materialising an {@link UInt384} from constituent {@link UInt128} and {@link + * UInt256} parts. + * + * @param high The most significant half-word of the value. + * @param low The least significant word of the value. + * @return the specified values as an {@link UInt384} type. + */ + public static UInt384 from(UInt128 high, UInt256 low) { + return new UInt384(high, low); + } + + /** + * Factory method for materialising an {@link UInt384} from an array of bytes. The array is + * most-significant byte first, and must not be zero length. + * + *

If the array is smaller than {@link #BYTES}, then it is effectively padded with leading zero + * bytes. + * + *

If the array is longer than {@link #BYTES}, then values at index {@link #BYTES} and beyond + * are ignored. + * + * @param bytes The array of bytes to be used. + * @return {@code bytes} as an {@link UInt384} type. + * @throws IllegalArgumentException if {@code bytes} is 0 length. + * @see #toByteArray() + */ + public static UInt384 from(byte[] bytes) { + Objects.requireNonNull(bytes); + if (bytes.length == 0) { + throw new IllegalArgumentException("bytes is 0 bytes long"); + } + byte[] newBytes = extend(bytes); + return from(newBytes, 0); + } + + /** + * Factory method for materialising an {@link UInt384} from an array of bytes. The array is + * most-significant byte first. + * + * @param bytes The array of bytes to be used. + * @param offset The offset within the array to be used. + * @return {@code bytes} from {@code offset} as an {@link UInt384} type. + * @see #toByteArray() + */ + public static UInt384 from(byte[] bytes, int offset) { + UInt128 high = UInt128.from(bytes, offset); + UInt256 low = UInt256.from(bytes, offset + UInt128.BYTES); + return from(high, low); + } + + /** + * Factory method for materialising an {@link UInt384} from a string. Conversion is performed base + * 10 and leading '+' sign character is permitted. + * + * @param s The array of bytes to be used. + * @return {@code s} as an {@link UInt384} type. + * @throws NumberFormatException if {@code s} is not a valid integer number. + */ + public static UInt384 from(String s) { + Objects.requireNonNull(s); + + int len = s.length(); + if (len > 0) { + int i = 0; + char ch = s.charAt(0); + if (ch == '+') { + i += 1; // skip first char + } + if (i >= len) { + throw new NumberFormatException(s); + } + // No real effort to catch overflow here + UInt384 result = UInt384.ZERO; + while (i < len) { + int digit = Character.digit(s.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException(s); + } + result = result.multiply(UInt384.TEN).add(numbers[digit]); + } + return result; + } else { + throw new NumberFormatException(s); + } + } + + /** + * Functional style friendly version of {@link #from(String)}. + * + * @param input The string to parse + * @return Success {@link Result} if value can be successfully parsed and failure {@link Result} + * otherwise. + */ + public static Result fromString(String input) { + return Result.wrap(() -> UNABLE_TO_PARSE_UINT.with(input), () -> from(input)); + } + + // Pad short (< BYTES length) array with appropriate lead bytes. + private static byte[] extend(byte[] bytes) { + if (bytes.length >= BYTES) { + return bytes; + } + byte[] newBytes = new byte[BYTES]; + int newPos = BYTES - bytes.length; + Arrays.fill(newBytes, 0, newPos, (byte) 0); + System.arraycopy(bytes, 0, newBytes, newPos, bytes.length); + return newBytes; + } + + private UInt384(UInt128 high, UInt256 low) { + this.high = Objects.requireNonNull(high); + this.low = Objects.requireNonNull(low); + } + + /** + * Converts {@code this} to an array of bytes. The most significant byte will be returned in index + * zero. The array will always be {@link #BYTES} bytes long, and will be zero filled to suit the + * actual value. + * + * @return An array of {@link #BYTES} bytes representing the value of this {@link UInt384}. + */ + public byte[] toByteArray() { + return toByteArray(new byte[BYTES], 0); + } + + /** + * Converts {@code this} to an array of bytes. The most significant byte will be returned in index + * {@code offset}. The array must be at least {@code offset + BYTES} long. + * + * @param bytes The array to place the bytes in. + * @param offset The offset within the array to place the bytes. + * @return The passed-in value of {@code bytes}. + */ + public byte[] toByteArray(byte[] bytes, int offset) { + this.high.toByteArray(bytes, offset); + this.low.toByteArray(bytes, offset + UInt128.BYTES); + return bytes; + } + + /** + * Adds {@code other} to {@code this}, returning the result. + * + * @param other The addend. + * @return An {@link UInt384} with the value {@code this + other}. + */ + public UInt384 add(UInt384 other) { + UInt256 newLow = this.low.add(other.low); + // Hacker's Delight section 2-13: + // "The following branch-free code can be used to compute the + // overflow predicate for unsigned add/subtract, with the result + // being in the sign position." + // Note that the use of method calls and the ternary operator + // very likely precludes this from being branch-free in java. + UInt128 carry = + this.low + .shiftRight() + .add(other.low.shiftRight()) + .add(this.low.and(other.low).and(UInt256.ONE)) + .isHighBitSet() + ? UInt128.ONE + : UInt128.ZERO; + UInt128 newHigh = this.high.add(other.high).add(carry); + return UInt384.from(newHigh, newLow); + } + + /** + * Adds {@code other} to {@code this}, returning the result. + * + * @param other The addend. + * @return An {@link UInt384} with the value {@code this + other}. + */ + public UInt384 add(UInt256 other) { + UInt256 newLow = this.low.add(other); + // Hacker's Delight section 2-13: + // "The following branch-free code can be used to compute the + // overflow predicate for unsigned add/subtract, with the result + // being in the sign position." + // Note that the use of method calls and the ternary operator + // very likely precludes this from being branch-free in java. + UInt128 newHigh = + this.low + .shiftRight() + .add(other.low.shiftRight()) + .add(this.low.and(other).and(UInt256.ONE)) + .isHighBitSet() + ? this.high.increment() + : this.high; + return UInt384.from(newHigh, newLow); + } + + /** + * Subtracts {@code other} from {@code this}, returning the result. + * + * @param other The subtrahend. + * @return An {@link UInt384} with the value {@code this - other}. + */ + public UInt384 subtract(UInt384 other) { + UInt256 newLow = this.low.subtract(other.low); + // Hacker's Delight section 2-13: + // "The following branch-free code can be used to compute the + // overflow predicate for unsigned add/subtract, with the result + // being in the sign position." + // Note that the use of method calls and the ternary operator + // very likely precludes this from being branch-free in java. + UInt128 carry = + this.low + .shiftRight() + .subtract(other.low.shiftRight()) + .subtract(this.low.invert().and(other.low).and(UInt256.ONE)) + .isHighBitSet() + ? UInt128.ONE + : UInt128.ZERO; + UInt128 newHigh = this.high.subtract(other.high).subtract(carry); + return UInt384.from(newHigh, newLow); + } + + /** + * Subtracts {@code other} from {@code this}, returning the result. + * + * @param other The subtrahend. + * @return An {@link UInt384} with the value {@code this - other}. + */ + public UInt384 subtract(UInt256 other) { + UInt256 newLow = this.low.subtract(other); + // Hacker's Delight section 2-13: + // "The following branch-free code can be used to compute the + // overflow predicate for unsigned add/subtract, with the result + // being in the sign position." + // Note that the use of method calls and the ternary operator + // very likely precludes this from being branch-free in java. + UInt128 newHigh = + this.low + .shiftRight() + .subtract(other.low.shiftRight()) + .subtract(this.low.invert().and(other).and(UInt256.ONE)) + .isHighBitSet() + ? this.high.decrement() + : this.high; + return UInt384.from(newHigh, newLow); + } + + /** + * Increments {@code this}. Equivalent to {@code this.add(Int256.ONE)}, but faster. + * + * @return This number incremented by one. + */ + public UInt384 increment() { + UInt256 l = this.low.increment(); + UInt128 h = l.isZero() ? this.high.increment() : this.high; + return UInt384.from(h, l); + } + + /** + * Decrements {@code this}. Equivalent to {@code this.subtract(Int256.ONE)}, but faster. + * + * @return This number decremented by one. + */ + public UInt384 decrement() { + UInt256 l = this.low.decrement(); + UInt128 h = this.low.isZero() ? this.high.decrement() : this.high; + return UInt384.from(h, l); + } + + /** + * Multiplies {@code this} by the specified multiplicand. + * + * @param multiplicand The multiplicand to multiply {@code this} by. + * @return The result {@code this * multiplicand}. + */ + public UInt384 multiply(UInt384 multiplicand) { + // Russian peasant + UInt384 result = UInt384.ZERO; + UInt384 multiplier = this; + + while (!multiplicand.isZero()) { + if (multiplicand.isOdd()) { + result = result.add(multiplier); + } + + multiplier = multiplier.shiftLeft(); + multiplicand = multiplicand.shiftRight(); + } + return result; + } + + /** + * Multiplies {@code this} by the specified multiplicand. + * + * @param multiplicand The multiplicand to multiply {@code this} by. + * @return The result {@code this * multiplicand}. + */ + public UInt384 multiply(UInt256 multiplicand) { + // Russian peasant + UInt384 result = UInt384.ZERO; + UInt384 multiplier = this; + + while (!multiplicand.isZero()) { + if (multiplicand.isOdd()) { + result = result.add(multiplier); + } + + multiplier = multiplier.shiftLeft(); + multiplicand = multiplicand.shiftRight(); + } + return result; + } + + /** + * Divides {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The result {@code floor(this / divisor)}. + * @throws IllegalArgumentException if {@code divisor} is zero + */ + public UInt384 divide(UInt384 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + UInt384 q = UInt384.ZERO; + UInt384 r = UInt384.ZERO; + UInt384 n = this; + for (int i = 0; i < SIZE; ++i) { + r = r.shiftLeft(); + q = q.shiftLeft(); + if (n.high.isHighBitSet()) { + r = r.or(UInt384.ONE); + } + n = n.shiftLeft(); + if (r.compareTo(divisor) >= 0) { + r = r.subtract(divisor); + q = q.or(UInt384.ONE); + } + } + return q; + } + + /** + * Divides {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The result {@code floor(this / divisor)}. + * @throws IllegalArgumentException if {@code divisor} is zero + */ + public UInt384 divide(UInt256 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + UInt384 q = UInt384.ZERO; + UInt384 r = UInt384.ZERO; + UInt384 n = this; + for (int i = 0; i < SIZE; ++i) { + r = r.shiftLeft(); + q = q.shiftLeft(); + if (n.high.isHighBitSet()) { + r = r.or(UInt384.ONE); + } + n = n.shiftLeft(); + if (!r.high.isZero() || (r.low.compareTo(divisor) >= 0)) { + r = r.subtract(divisor); + q = q.or(UInt384.ONE); + } + } + return q; + } + + /** + * Returns the remainder of the division of {@code this} by the specified divisor. + * + * @param divisor The divisor to divide {@code this} by. + * @return The remainder of the division {@code this / divisor}. + */ + public UInt384 remainder(UInt384 divisor) { + if (divisor.isZero()) { + throw new IllegalArgumentException("Can't divide by zero"); + } + UInt384 r = UInt384.ZERO; + UInt384 n = this; + for (int i = 0; i < SIZE; ++i) { + r = r.shiftLeft(); + if (n.high.isHighBitSet()) { + r = r.or(UInt384.ONE); + } + n = n.shiftLeft(); + if (r.compareTo(divisor) >= 0) { + r = r.subtract(divisor); + } + } + return r; + } + + /** + * Calculates {@code this}{@code exp}. + * + * @param exp the exponent to raise {@code this} to + * @return {@code this}{@code exp} + */ + public UInt384 pow(int exp) { + if (exp < 0) { + throw new IllegalArgumentException("exp must be >= 0"); + } + + // Mirrors algorithm in multiply(...) + UInt384 result = UInt384.ONE; + UInt384 base = this; + + while (exp != 0) { + if ((exp & 1) != 0) { + result = result.multiply(base); + } + + base = base.multiply(base); + exp >>>= 1; + } + return result; + } + + /** + * Calculates the integer square root, that is the largest number {@code n} such that {@code n * n + * <= this}. + * + * @return The integer square root. + */ + public UInt384 isqrt() { + UInt384 bit = UInt384.ONE.shiftLeft(SIZE - 2); + + // "bit" starts at the highest power of four <= this + UInt384 num = this; + while (bit.compareTo(num) > 0) { + bit = bit.shiftRight(2); + } + + UInt384 res = UInt384.ZERO; + while (!bit.isZero()) { + UInt384 rab = res.add(bit); + if (num.compareTo(rab) >= 0) { + num = num.subtract(rab); + res = res.shiftRight().add(bit); + } else { + res = res.shiftRight(); + } + bit = bit.shiftRight(2); + } + return res; + } + + /** + * Shifts {@code this} left 1 bit. A zero bit is moved into the rightmost bit. + * + * @return The result of shifting {@code this} left one bit. + */ + public UInt384 shiftLeft() { + UInt128 h = this.high.shiftLeft(); + if (this.low.isHighBitSet()) { + h = h.or(UInt128.ONE); + } + UInt256 l = this.low.shiftLeft(); + return UInt384.from(h, l); + } + + /** + * Shift {@code this} left n bits. A zero bit is moved into the rightmost bits. + * + * @return The result of shifting {@code this} left n bits. + */ + public UInt384 shiftLeft(int n) { + if (n == 0) { + return this; + } else if (n >= SIZE || n <= -SIZE) { + return ZERO; // All bits are gone + } else if (n < 0) { + return shiftRight(-n); // -ve left shift is right shift + } + UInt256 h = (n >= UInt256.SIZE) ? this.low : UInt256.from(this.high); + UInt256 l = (n >= UInt256.SIZE) ? UInt256.ZERO : this.low; + int r = n % UInt256.SIZE; + if (r > 0) { + UInt256 c = l.shiftRight(UInt256.SIZE - r); + h = h.shiftLeft(r).or(c); + l = l.shiftLeft(r); + } + return UInt384.from(h.low, l); + } + + /** + * Shifts {@code this} right 1 bit. A zero bit is moved into the into the left bit. + * + * @return The result of arithmetic shifting {@code this} right one bit. + */ + public UInt384 shiftRight() { + UInt128 h = this.high.shiftRight(); + UInt256 l = this.low.shiftRight(); + if (this.high.isOdd()) { + l = l.or(UInt256.HIGH_BIT); + } + return UInt384.from(h, l); + } + + /** + * Logical shift {@code this} right n bits. Zeros are shifted into the leftmost bits. + * + * @return The result of logical shifting {@code this} right n bits. + */ + public UInt384 shiftRight(int n) { + if (n == 0) { + return this; + } else if (n >= SIZE || n <= -SIZE) { + return ZERO; // All bits are gone + } else if (n < 0) { + return shiftLeft(-n); // -ve right shift is left shift + } + UInt256 h = (n >= UInt256.SIZE) ? UInt256.ZERO : UInt256.from(this.high); + UInt256 l = (n >= UInt256.SIZE) ? UInt256.from(this.high) : this.low; + int r = n % UInt256.SIZE; + if (r > 0) { + UInt256 c = h.shiftLeft(UInt256.SIZE - r); + h = h.shiftRight(r); + l = l.shiftRight(r).or(c); + } + return UInt384.from(h.low, l); + } + + /** + * Returns the value of {@code ~this}. + * + * @return The logical inverse of {@code this}. + */ + public UInt384 invert() { + return UInt384.from(this.high.invert(), this.low.invert()); + } + + @Override + public int compareTo(UInt384 n) { + int cmp = this.high.compareTo(n.high); + if (cmp == 0) { + cmp = this.low.compareTo(n.low); + } + return cmp; + } + + /** + * Returns the most significant half-word. + * + * @return the most significant half-word. + */ + public UInt128 getHigh() { + return this.high; + } + + /** + * Returns the least significant word. + * + * @return the least significant word. + */ + public UInt256 getLow() { + return this.low; + } + + /** + * Calculates the bitwise inclusive-or of {@code this} with {@code other} ({@code this | other}). + * + * @param other The value to inclusive-or with {@code this}. + * @return {@code this | other} + */ + public UInt384 or(UInt384 other) { + return UInt384.from(this.high.or(other.high), this.low.or(other.low)); + } + + /** + * Calculates the bitwise and of {@code this} with {@code other} ({@code this & other}). + * + * @param other The value to and with {@code this}. + * @return {@code this & other} + */ + public UInt384 and(UInt384 other) { + return UInt384.from(this.high.and(other.high), this.low.and(other.low)); + } + + /** + * Calculates the exclusive-or of {@code this} with {@code other} ({@code this ^ other}). + * + * @param other The value to exclusive-or with {@code this}. + * @return {@code this ^ other} + */ + public UInt384 xor(UInt384 other) { + return UInt384.from(this.high.xor(other.high), this.low.xor(other.low)); + } + + /** + * Returns the number of zero bits preceding the highest-order ("leftmost") one-bit in the two's + * complement binary representation of the specified {@code long} value. Returns 128 if the + * specified value has no one-bits in its two's complement representation, in other words if it is + * equal to zero. + * + *

Note that this method is closely related to the logarithm base 2. For all positive {@code + * long} values x: + * + *

    + *
  • floor(log2(x)) = {@code 255 - numberOfLeadingZeros(x)} + *
  • ceil(log2(x)) = {@code 256 - numberOfLeadingZeros(x - 1)} + *
+ * + * @return the number of zero bits preceding the highest-order ("leftmost") one-bit in the two's + * complement binary representation of the specified {@code long} value, or 256 if the value + * is equal to zero. + */ + public int numberOfLeadingZeros() { + return this.high.isZero() + ? UInt128.SIZE + this.low.numberOfLeadingZeros() + : this.high.numberOfLeadingZeros(); + } + + /** + * Return {@code true} if the {@link UInt128} has its high bit set. + * + * @return {@code true} if the {@link UInt128} has its high bit set. + */ + public boolean isHighBitSet() { + return this.high.isHighBitSet(); + } + + /** + * Returns {@code true} if {@code this} is zero. + * + * @return {@code true} if {@code this} is zero. + */ + public boolean isZero() { + return this.high.isZero() && this.low.isZero(); + } + + /** + * Returns {@code true} if {@code this} is an even number. + * + * @return {@code true} if {@code this} is an even number. + */ + public boolean isEven() { + return this.low.isEven(); + } + + /** + * Returns {@code true} if {@code this} is an odd number. + * + * @return {@code true} if {@code this} is an odd number. + */ + public boolean isOdd() { + return this.low.isOdd(); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.high) * 31 + Objects.hashCode(this.low); + } + + @Override + public boolean equals(Object obj) { + // Note that this needs to be consistent with compareTo + if (this == obj) { + return true; + } + + if (obj instanceof UInt384) { + UInt384 other = (UInt384) obj; + return Objects.equals(this.high, other.high) && Objects.equals(this.low, other.low); + } + return false; + } + + @Override + public String toString() { + return toString(10); + } + + /** + * Returns a string representation of this object in the specified radix. + * + *

If the radix is smaller than {@code Character.MIN_RADIX} or larger than {@code + * Character.MAX_RADIX}, an {@link IllegalArgumentException} is thrown. + * + *

The characters of the result represent the magnitude of {@code this}. If the magnitude is + * zero, it is represented by a single zero character {@code '0'}; otherwise no leading zeros are + * output. + * + *

The following ASCII characters are used as digits: + * + *

+ * + * {@code 0123456789abcdefghijklmnopqrstuvwxyz} + * + *
+ * + * If {@code radix} is N, then the first N of these characters are used as + * radix-N digits in the order shown, i.e. the digits for hexadecimal (radix 16) are + * {@code 0123456789abcdef}. + * + * @param radix the radix to use in the string representation. + * @return a string representation of the argument in the specified radix. + * @see Character#MAX_RADIX + * @throws IllegalArgumentException if {@code radix} is less than {@code Character.MIN_RADIX} or + * greater than {@code Character.MAX_RADIX}. + * @see Character#MIN_RADIX + */ + public String toString(int radix) { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new IllegalArgumentException("Illegal radix: " + radix); + } + if (isZero()) { + return "0"; + } + StringBuilder sb = new StringBuilder(); + UInt384 n = this; + UInt384 r = UInt384.from(radix); + while (!n.isZero()) { + UInt384 digit = n.remainder(r); + sb.append(Character.forDigit(digit.low.low.low, radix)); + n = n.divide(r); + } + return sb.reverse().toString(); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UIntUtils.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UIntUtils.java index adbd91d26d..e8355ef065 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/UIntUtils.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/UIntUtils.java @@ -64,173 +64,165 @@ package com.radixdlt.utils; -/** - * Utility methods for working with various UInt classes. - */ +/** Utility methods for working with various UInt classes. */ public final class UIntUtils { - private static final String OVERFLOW = "overflow"; - private static final String UNDERFLOW = "underflow"; - - // Values taken from - // https://en.wikipedia.org/wiki/Double-precision_floating-point_format - private static final int SIGNIFICAND_PREC = 53; // Including implicit leading one bit - private static final long SIGNIFICAND_MASK = (1L << (SIGNIFICAND_PREC - 1)) - 1L; - private static final long SIGNIFICAND_OVF = 1L << SIGNIFICAND_PREC; - - private static final int EXPONENT_BIAS = 1023; - - private UIntUtils() { - throw new IllegalStateException("Can't construct"); - } - - /** - * Compute {@code addend + augend}, throwing an exception if overflow - * occurs. - * - * @param addend The addend - * @param augend The augend - * @return {@code addend + augend} if no overflow - * @throws ArithmeticException if an overflow occurs - */ - public static UInt256 addWithOverflow(UInt256 addend, UInt256 augend) { - UInt256 maxAddend = UInt256.MAX_VALUE.subtract(augend); - if (maxAddend.compareTo(addend) < 0) { - throw new ArithmeticException(OVERFLOW); - } - return addend.add(augend); - } - - /** - * Compute {@code addend + augend}, throwing an exception if overflow - * occurs. - * - * @param addend The addend - * @param augend The augend - * @return {@code addend + augend} if no overflow - * @throws ArithmeticException if an overflow occurs - */ - public static UInt384 addWithOverflow(UInt384 addend, UInt256 augend) { - UInt384 maxAddend = UInt384.MAX_VALUE.subtract(augend); - if (maxAddend.compareTo(addend) < 0) { - throw new ArithmeticException(OVERFLOW); - } - return addend.add(augend); - } - - /** - * Compute {@code addend + augend}, throwing an exception if overflow - * occurs. - * - * @param addend The addend - * @param augend The augend - * @return {@code addend + augend} if no overflow - * @throws ArithmeticException if an overflow occurs - */ - public static UInt384 addWithOverflow(UInt384 addend, UInt384 augend) { - UInt384 maxAddend = UInt384.MAX_VALUE.subtract(augend); - if (maxAddend.compareTo(addend) < 0) { - throw new ArithmeticException(OVERFLOW); - } - return addend.add(augend); - } - - /** - * Compute {@code minuend - subtrahend}, throwing an exception if underflow - * occurs. - * - * @param addend The minuend - * @param augend The subtrahend - * @return {@code minuend - subtrahend} if no overflow - * @throws ArithmeticException if an underflow occurs - */ - public static UInt256 subtractWithUnderflow(UInt256 minuend, UInt256 subtrahend) { - UInt256 minMinuend = UInt256.MIN_VALUE.add(minuend); - if (minMinuend.compareTo(subtrahend) < 0) { - throw new ArithmeticException(UNDERFLOW); - } - return minuend.subtract(subtrahend); - } - - /** - * Compute {@code minuend - subtrahend}, throwing an exception if underflow - * occurs. - * - * @param addend The minuend - * @param augend The subtrahend - * @return {@code minuend - subtrahend} if no overflow - * @throws ArithmeticException if an underflow occurs - */ - public static UInt384 subtractWithUnderflow(UInt384 minuend, UInt256 subtrahend) { - UInt384 minMinuend = UInt384.MIN_VALUE.add(minuend); - if (minMinuend.compareTo(UInt384.from(subtrahend)) < 0) { - throw new ArithmeticException(UNDERFLOW); - } - return minuend.subtract(subtrahend); - } - - /** - * Compute {@code minuend - subtrahend}, throwing an exception if underflow - * occurs. - * - * @param addend The minuend - * @param augend The subtrahend - * @return {@code minuend - subtrahend} if no overflow - * @throws ArithmeticException if an underflow occurs - */ - public static UInt384 subtractWithUnderflow(UInt384 minuend, UInt384 subtrahend) { - UInt384 minMinuend = UInt384.MIN_VALUE.add(minuend); - if (minMinuend.compareTo(subtrahend) < 0) { - throw new ArithmeticException(UNDERFLOW); - } - return minuend.subtract(subtrahend); - } - - /** - * Convert {@link UInt128} to an approximate {@code double} value. - * - * @param x the value to convert - * @return the value as a {@code double} - */ - public static double toDouble(UInt128 x) { - long h = x.getHigh(); - long l = x.getLow(); - - // If it's a number that fits into a long, let the compiler convert. - if (h == 0L && l >= 0L) { - return l; - } - - // Must be at least 64 bits based on initial checks. Note that it is - // not possible for this exponent to overflow a double (128 < 1023). - int shift = bitLength(h); - long exponent = Long.SIZE + shift - 1L; - - // Merge all the bits into l, discarding lower bits - l >>>= shift; - h <<= Long.SIZE - shift; - l |= h; - - // Extract 53 bits of significand. Note that we make a - // quick stop part way through to organise rounding. - // Note that rounding is approximate, not RTNE. - l >>>= Long.SIZE - SIGNIFICAND_PREC - 1; - l += 1; - l >>>= 1; - - // If rounding has caused overflow, then shift an extra bit - if ((l & SIGNIFICAND_OVF) != 0L) { - exponent += 1; - l >>>= 1; - } - - // Assemble into a double now. - long raw = (exponent + EXPONENT_BIAS) << (SIGNIFICAND_PREC - 1); - raw |= l & SIGNIFICAND_MASK; - return Double.longBitsToDouble(raw); - } - - private static int bitLength(long n) { - return Long.SIZE - Long.numberOfLeadingZeros(n); - } + private static final String OVERFLOW = "overflow"; + private static final String UNDERFLOW = "underflow"; + + // Values taken from + // https://en.wikipedia.org/wiki/Double-precision_floating-point_format + private static final int SIGNIFICAND_PREC = 53; // Including implicit leading one bit + private static final long SIGNIFICAND_MASK = (1L << (SIGNIFICAND_PREC - 1)) - 1L; + private static final long SIGNIFICAND_OVF = 1L << SIGNIFICAND_PREC; + + private static final int EXPONENT_BIAS = 1023; + + private UIntUtils() { + throw new IllegalStateException("Can't construct"); + } + + /** + * Compute {@code addend + augend}, throwing an exception if overflow occurs. + * + * @param addend The addend + * @param augend The augend + * @return {@code addend + augend} if no overflow + * @throws ArithmeticException if an overflow occurs + */ + public static UInt256 addWithOverflow(UInt256 addend, UInt256 augend) { + UInt256 maxAddend = UInt256.MAX_VALUE.subtract(augend); + if (maxAddend.compareTo(addend) < 0) { + throw new ArithmeticException(OVERFLOW); + } + return addend.add(augend); + } + + /** + * Compute {@code addend + augend}, throwing an exception if overflow occurs. + * + * @param addend The addend + * @param augend The augend + * @return {@code addend + augend} if no overflow + * @throws ArithmeticException if an overflow occurs + */ + public static UInt384 addWithOverflow(UInt384 addend, UInt256 augend) { + UInt384 maxAddend = UInt384.MAX_VALUE.subtract(augend); + if (maxAddend.compareTo(addend) < 0) { + throw new ArithmeticException(OVERFLOW); + } + return addend.add(augend); + } + + /** + * Compute {@code addend + augend}, throwing an exception if overflow occurs. + * + * @param addend The addend + * @param augend The augend + * @return {@code addend + augend} if no overflow + * @throws ArithmeticException if an overflow occurs + */ + public static UInt384 addWithOverflow(UInt384 addend, UInt384 augend) { + UInt384 maxAddend = UInt384.MAX_VALUE.subtract(augend); + if (maxAddend.compareTo(addend) < 0) { + throw new ArithmeticException(OVERFLOW); + } + return addend.add(augend); + } + + /** + * Compute {@code minuend - subtrahend}, throwing an exception if underflow occurs. + * + * @param addend The minuend + * @param augend The subtrahend + * @return {@code minuend - subtrahend} if no overflow + * @throws ArithmeticException if an underflow occurs + */ + public static UInt256 subtractWithUnderflow(UInt256 minuend, UInt256 subtrahend) { + UInt256 minMinuend = UInt256.MIN_VALUE.add(minuend); + if (minMinuend.compareTo(subtrahend) < 0) { + throw new ArithmeticException(UNDERFLOW); + } + return minuend.subtract(subtrahend); + } + + /** + * Compute {@code minuend - subtrahend}, throwing an exception if underflow occurs. + * + * @param addend The minuend + * @param augend The subtrahend + * @return {@code minuend - subtrahend} if no overflow + * @throws ArithmeticException if an underflow occurs + */ + public static UInt384 subtractWithUnderflow(UInt384 minuend, UInt256 subtrahend) { + UInt384 minMinuend = UInt384.MIN_VALUE.add(minuend); + if (minMinuend.compareTo(UInt384.from(subtrahend)) < 0) { + throw new ArithmeticException(UNDERFLOW); + } + return minuend.subtract(subtrahend); + } + + /** + * Compute {@code minuend - subtrahend}, throwing an exception if underflow occurs. + * + * @param addend The minuend + * @param augend The subtrahend + * @return {@code minuend - subtrahend} if no overflow + * @throws ArithmeticException if an underflow occurs + */ + public static UInt384 subtractWithUnderflow(UInt384 minuend, UInt384 subtrahend) { + UInt384 minMinuend = UInt384.MIN_VALUE.add(minuend); + if (minMinuend.compareTo(subtrahend) < 0) { + throw new ArithmeticException(UNDERFLOW); + } + return minuend.subtract(subtrahend); + } + + /** + * Convert {@link UInt128} to an approximate {@code double} value. + * + * @param x the value to convert + * @return the value as a {@code double} + */ + public static double toDouble(UInt128 x) { + long h = x.getHigh(); + long l = x.getLow(); + + // If it's a number that fits into a long, let the compiler convert. + if (h == 0L && l >= 0L) { + return l; + } + + // Must be at least 64 bits based on initial checks. Note that it is + // not possible for this exponent to overflow a double (128 < 1023). + int shift = bitLength(h); + long exponent = Long.SIZE + shift - 1L; + + // Merge all the bits into l, discarding lower bits + l >>>= shift; + h <<= Long.SIZE - shift; + l |= h; + + // Extract 53 bits of significand. Note that we make a + // quick stop part way through to organise rounding. + // Note that rounding is approximate, not RTNE. + l >>>= Long.SIZE - SIGNIFICAND_PREC - 1; + l += 1; + l >>>= 1; + + // If rounding has caused overflow, then shift an extra bit + if ((l & SIGNIFICAND_OVF) != 0L) { + exponent += 1; + l >>>= 1; + } + + // Assemble into a double now. + long raw = (exponent + EXPONENT_BIAS) << (SIGNIFICAND_PREC - 1); + raw |= l & SIGNIFICAND_MASK; + return Double.longBitsToDouble(raw); + } + + private static int bitLength(long n) { + return Long.SIZE - Long.numberOfLeadingZeros(n); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Failure.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Failure.java index 4f872cc011..794efa9113 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Failure.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Failure.java @@ -67,89 +67,84 @@ import java.text.MessageFormat; import java.util.Objects; -/** - * Simplest failure descriptor. - */ +/** Simplest failure descriptor. */ public interface Failure { - String message(); - - int code(); - - default Result result() { - return Result.fail(this); - } - - default Failure with(Object... params) { - return failure(code(), message(), params); - } - - /** - * Create instance of Failure with given code, message format string and parameters. - * - * @param code format string - * @param format message format string - * @param values parameters - * - * @return created instance of Failure - * - * @see MessageFormat for supported format string options - */ - static Failure failure(int code, String format, Object... values) { - return failure(code, MessageFormat.format(format, values)); - } - - /** - * Create instance of Failure with given message and code. - * - * @param message failure message - * - * @return created instance of Failure - */ - static Failure failure(int code, String message) { - return new FailureImpl(code, message); - } - - class FailureImpl implements Failure { - private final int code; - private final String message; - - private FailureImpl(int code, String message) { - this.message = message; - this.code = code; - } - - @Override - public String message() { - return message; - } - - @Override - public int code() { - return code; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof FailureImpl)) { - return false; - } - - var failure = (FailureImpl) o; - return code == failure.code && Objects.equals(message, failure.message); - } - - @Override - public int hashCode() { - return Objects.hash(message, code); - } - - @Override - public String toString() { - return "{" + code + ", '" + message + "'}"; - } - } + String message(); + + int code(); + + default Result result() { + return Result.fail(this); + } + + default Failure with(Object... params) { + return failure(code(), message(), params); + } + + /** + * Create instance of Failure with given code, message format string and parameters. + * + * @param code format string + * @param format message format string + * @param values parameters + * @return created instance of Failure + * @see MessageFormat for supported format string options + */ + static Failure failure(int code, String format, Object... values) { + return failure(code, MessageFormat.format(format, values)); + } + + /** + * Create instance of Failure with given message and code. + * + * @param message failure message + * @return created instance of Failure + */ + static Failure failure(int code, String message) { + return new FailureImpl(code, message); + } + + class FailureImpl implements Failure { + private final int code; + private final String message; + + private FailureImpl(int code, String message) { + this.message = message; + this.code = code; + } + + @Override + public String message() { + return message; + } + + @Override + public int code() { + return code; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof FailureImpl)) { + return false; + } + + var failure = (FailureImpl) o; + return code == failure.code && Objects.equals(message, failure.message); + } + + @Override + public int hashCode() { + return Objects.hash(message, code); + } + + @Override + public String toString() { + return "{" + code + ", '" + message + "'}"; + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/FunctionalUtils.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/FunctionalUtils.java index 6f9bbb9400..3a67cc9b4a 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/FunctionalUtils.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/FunctionalUtils.java @@ -65,7 +65,6 @@ package com.radixdlt.utils.functional; import com.google.common.collect.ImmutableMap; - import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.HashSet; @@ -77,129 +76,123 @@ import java.util.stream.Stream; public interface FunctionalUtils { - /** - * Use this method when it's necessary to pick last element from the stream. - *

- * For example: - *

-	 *     var lastElement = ...
-	 *     .stream()
-	 *     .reduce(FunctionalUtils::findLast);
-	 * 
- */ - static T findLast(T first, T second) { - return second; - } + /** + * Use this method when it's necessary to pick last element from the stream. + * + *

For example: + * + *

+   *     var lastElement = ...
+   *     .stream()
+   *     .reduce(FunctionalUtils::findLast);
+   * 
+ */ + static T findLast(T first, T second) { + return second; + } - /** - * Use this method to return part of iterable starting from element - * right after one which matched the predicated. - * - * @param input Source iterable - * @param predicate Predicate to test - * - * @return List consisting of the elements from input iterable which were found - * after the predicate match. Empty list if match not found. - */ - static List skipUntil(Iterable input, Predicate predicate) { - var output = new ArrayList(); - var found = false; + /** + * Use this method to return part of iterable starting from element right after one which matched + * the predicated. + * + * @param input Source iterable + * @param predicate Predicate to test + * @return List consisting of the elements from input iterable which were found after the + * predicate match. Empty list if match not found. + */ + static List skipUntil(Iterable input, Predicate predicate) { + var output = new ArrayList(); + var found = false; - for (var info : input) { - if (predicate.test(info)) { - found = true; - continue; - } - if (found) { - output.add(info); - } - } + for (var info : input) { + if (predicate.test(info)) { + found = true; + continue; + } + if (found) { + output.add(info); + } + } - return output; - } + return output; + } - /** - * This method takes map and new entry and returns new map where existing entry with same key as new entry, - * is get replaced with new entry. If no entry with same key exists, then new entry is added to resulting map. - * Input map remains intact, returned map is a new map instance. - * - * @param newEntry the entry which will be put into new map - * @param existingMap input map - * - * @return new map with old entry replaced with new entry - */ - static Map replaceEntry(Map.Entry newEntry, Map existingMap) { - return Stream.concat( - Stream.of(newEntry), - existingMap.entrySet().stream().filter(e -> !newEntry.getKey().equals(e.getKey())) - ).collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - } + /** + * This method takes map and new entry and returns new map where existing entry with same key as + * new entry, is get replaced with new entry. If no entry with same key exists, then new entry is + * added to resulting map. Input map remains intact, returned map is a new map instance. + * + * @param newEntry the entry which will be put into new map + * @param existingMap input map + * @return new map with old entry replaced with new entry + */ + static Map replaceEntry(Map.Entry newEntry, Map existingMap) { + return Stream.concat( + Stream.of(newEntry), + existingMap.entrySet().stream().filter(e -> !newEntry.getKey().equals(e.getKey()))) + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + } - /** - * This method takes a map and returns new map with entry with specified key removed. - * - * @param keyToRemove the key to remove - * @param existingMap input map - * - * @return new map with specified key removed - */ - static Map removeKey(K keyToRemove, Map existingMap) { - return existingMap.entrySet().stream() - .filter(e -> !keyToRemove.equals(e.getKey())) - .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - } + /** + * This method takes a map and returns new map with entry with specified key removed. + * + * @param keyToRemove the key to remove + * @param existingMap input map + * @return new map with specified key removed + */ + static Map removeKey(K keyToRemove, Map existingMap) { + return existingMap.entrySet().stream() + .filter(e -> !keyToRemove.equals(e.getKey())) + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + } - /** - * Return copy of the input set with specified element removed. - * - * @param element element to remove - * @param input input set - * - * @return new set with specified element removed - */ - static Set removeElement(T element, Set input) { - return input.stream().filter(e -> !e.equals(element)).collect(Collectors.toSet()); - } + /** + * Return copy of the input set with specified element removed. + * + * @param element element to remove + * @param input input set + * @return new set with specified element removed + */ + static Set removeElement(T element, Set input) { + return input.stream().filter(e -> !e.equals(element)).collect(Collectors.toSet()); + } - /** - * Return copy of the input set with provided element added. - * - * @param element element to add - * @param input input set - * - * @return new set with provided element added - */ - static Set addElement(T element, Set input) { - return Stream.concat(input.stream(), Stream.of(element)).collect(Collectors.toSet()); - } + /** + * Return copy of the input set with provided element added. + * + * @param element element to add + * @param input input set + * @return new set with provided element added + */ + static Set addElement(T element, Set input) { + return Stream.concat(input.stream(), Stream.of(element)).collect(Collectors.toSet()); + } - /** - * Merge several sets into one. - * - * @param inputs sets to merge - * - * @return merged set - */ - @SafeVarargs - static Set mergeAll(Set... inputs) { - var output = new HashSet(); + /** + * Merge several sets into one. + * + * @param inputs sets to merge + * @return merged set + */ + @SafeVarargs + static Set mergeAll(Set... inputs) { + var output = new HashSet(); - for (var input : inputs) { - output.addAll(input); - } + for (var input : inputs) { + output.addAll(input); + } - return Set.copyOf(output); - } + return Set.copyOf(output); + } - /** - * Create new immutable map entry. - * - * @param key entry key - * @param value entry value - * - * @return created entry - */ - static Map.Entry newEntry(K key, V value) { - return new SimpleImmutableEntry<>(key, value); - } + /** + * Create new immutable map entry. + * + * @param key entry key + * @param value entry value + * @return created entry + */ + static Map.Entry newEntry(K key, V value) { + return new SimpleImmutableEntry<>(key, value); + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Functions.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Functions.java index 7ffff7e0b3..71491013ab 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Functions.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Functions.java @@ -65,143 +65,153 @@ package com.radixdlt.utils.functional; /** - * Collection of basic functions which accept 0-9 parameters and return single result. - * Note that these functions are not supposed to throw any exceptions + * Collection of basic functions which accept 0-9 parameters and return single result. Note that + * these functions are not supposed to throw any exceptions */ public interface Functions { - static T identity(T t) { - return t; - } - - - @FunctionalInterface - interface FN0 { - R apply(); - } - - @FunctionalInterface - interface FN1 { - R apply(T1 param1); - - default FN0 bind(final T1 param) { - return () -> apply(param); - } - - default FN1 then(final FN1 function) { - return v1 -> function.apply(apply(v1)); - } - - default FN1 before(final FN1 function) { - return v1 -> apply(function.apply(v1)); - } - - static FN1 id() { - return v -> v; - } - } - - @FunctionalInterface - interface FN2 { - R apply(T1 param1, T2 param2); - - default FN1 bind(final T1 param) { - return v2 -> apply(param, v2); - } - - default FN2 then(final FN1 function) { - return (v1, v2) -> function.apply(apply(v1, v2)); - } - } - - @FunctionalInterface - interface FN3 { - R apply(T1 param1, T2 param2, T3 param3); - - default FN2 bind(final T1 param) { - return (v2, v3) -> apply(param, v2, v3); - } - - default FN3 then(final FN1 function) { - return (v1, v2, v3) -> function.apply(apply(v1, v2, v3)); - } - } - - @FunctionalInterface - interface FN4 { - R apply(T1 param1, T2 param2, T3 param3, T4 param4); - - default FN3 bind(final T1 param) { - return (v2, v3, v4) -> apply(param, v2, v3, v4); - } - - default FN4 then(final FN1 function) { - return (v1, v2, v3, v4) -> function.apply(apply(v1, v2, v3, v4)); - } - } - - @FunctionalInterface - interface FN5 { - R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5); - - default FN4 bind(final T1 param) { - return (v2, v3, v4, v5) -> apply(param, v2, v3, v4, v5); - } - - default FN5 then(final FN1 function) { - return (v1, v2, v3, v4, v5) -> function.apply(apply(v1, v2, v3, v4, v5)); - } - } - - @FunctionalInterface - interface FN6 { - R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6); - - default FN5 bind(final T1 param) { - return (v2, v3, v4, v5, v6) -> apply(param, v2, v3, v4, v5, v6); - } - - default FN6 then(final FN1 function) { - return (v1, v2, v3, v4, v5, v6) -> function.apply(apply(v1, v2, v3, v4, v5, v6)); - } - } - - @FunctionalInterface - interface FN7 { - R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7); - - default FN6 bind(final T1 param) { - return (v2, v3, v4, v5, v6, v7) -> apply(param, v2, v3, v4, v5, v6, v7); - } - - default FN7 then(final FN1 function) { - return (v1, v2, v3, v4, v5, v6, v7) -> function.apply(apply(v1, v2, v3, v4, v5, v6, v7)); - } - } - - @FunctionalInterface - interface FN8 { - R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8); - - default FN7 bind(final T1 param) { - return (v2, v3, v4, v5, v6, v7, v8) -> apply(param, v2, v3, v4, v5, v6, v7, v8); - } - - default FN8 then(final FN1 function) { - return (v1, v2, v3, v4, v5, v6, v7, v8) -> function.apply(apply(v1, v2, v3, v4, v5, v6, v7, v8)); - } - } - - @FunctionalInterface - interface FN9 { - R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9); - - default FN8 bind(final T1 param) { - return (v2, v3, v4, v5, v6, v7, v8, v9) -> apply(param, v2, v3, v4, v5, v6, v7, v8, v9); - } - - default FN9 then(final FN1 function) { - return (v1, v2, v3, v4, v5, v6, v7, v8, v9) -> function.apply(apply(v1, v2, v3, v4, v5, v6, v7, v8, v9)); - } - } -} \ No newline at end of file + static T identity(T t) { + return t; + } + + @FunctionalInterface + interface FN0 { + R apply(); + } + + @FunctionalInterface + interface FN1 { + R apply(T1 param1); + + default FN0 bind(final T1 param) { + return () -> apply(param); + } + + default FN1 then(final FN1 function) { + return v1 -> function.apply(apply(v1)); + } + + default FN1 before(final FN1 function) { + return v1 -> apply(function.apply(v1)); + } + + static FN1 id() { + return v -> v; + } + } + + @FunctionalInterface + interface FN2 { + R apply(T1 param1, T2 param2); + + default FN1 bind(final T1 param) { + return v2 -> apply(param, v2); + } + + default FN2 then(final FN1 function) { + return (v1, v2) -> function.apply(apply(v1, v2)); + } + } + + @FunctionalInterface + interface FN3 { + R apply(T1 param1, T2 param2, T3 param3); + + default FN2 bind(final T1 param) { + return (v2, v3) -> apply(param, v2, v3); + } + + default FN3 then(final FN1 function) { + return (v1, v2, v3) -> function.apply(apply(v1, v2, v3)); + } + } + + @FunctionalInterface + interface FN4 { + R apply(T1 param1, T2 param2, T3 param3, T4 param4); + + default FN3 bind(final T1 param) { + return (v2, v3, v4) -> apply(param, v2, v3, v4); + } + + default FN4 then(final FN1 function) { + return (v1, v2, v3, v4) -> function.apply(apply(v1, v2, v3, v4)); + } + } + + @FunctionalInterface + interface FN5 { + R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5); + + default FN4 bind(final T1 param) { + return (v2, v3, v4, v5) -> apply(param, v2, v3, v4, v5); + } + + default FN5 then(final FN1 function) { + return (v1, v2, v3, v4, v5) -> function.apply(apply(v1, v2, v3, v4, v5)); + } + } + + @FunctionalInterface + interface FN6 { + R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6); + + default FN5 bind(final T1 param) { + return (v2, v3, v4, v5, v6) -> apply(param, v2, v3, v4, v5, v6); + } + + default FN6 then(final FN1 function) { + return (v1, v2, v3, v4, v5, v6) -> function.apply(apply(v1, v2, v3, v4, v5, v6)); + } + } + + @FunctionalInterface + interface FN7 { + R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7); + + default FN6 bind(final T1 param) { + return (v2, v3, v4, v5, v6, v7) -> apply(param, v2, v3, v4, v5, v6, v7); + } + + default FN7 then(final FN1 function) { + return (v1, v2, v3, v4, v5, v6, v7) -> function.apply(apply(v1, v2, v3, v4, v5, v6, v7)); + } + } + + @FunctionalInterface + interface FN8 { + R apply(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8); + + default FN7 bind(final T1 param) { + return (v2, v3, v4, v5, v6, v7, v8) -> apply(param, v2, v3, v4, v5, v6, v7, v8); + } + + default FN8 then(final FN1 function) { + return (v1, v2, v3, v4, v5, v6, v7, v8) -> + function.apply(apply(v1, v2, v3, v4, v5, v6, v7, v8)); + } + } + + @FunctionalInterface + interface FN9 { + R apply( + T1 param1, + T2 param2, + T3 param3, + T4 param4, + T5 param5, + T6 param6, + T7 param7, + T8 param8, + T9 param9); + + default FN8 bind(final T1 param) { + return (v2, v3, v4, v5, v6, v7, v8, v9) -> apply(param, v2, v3, v4, v5, v6, v7, v8, v9); + } + + default FN9 then(final FN1 function) { + return (v1, v2, v3, v4, v5, v6, v7, v8, v9) -> + function.apply(apply(v1, v2, v3, v4, v5, v6, v7, v8, v9)); + } + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Optionals.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Optionals.java index 9ec9ad374b..e6b3c5f93d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Optionals.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Optionals.java @@ -64,6 +64,8 @@ package com.radixdlt.utils.functional; +import static com.radixdlt.utils.functional.Tuple.tuple; + import com.radixdlt.utils.functional.Functions.FN1; import com.radixdlt.utils.functional.Functions.FN2; import com.radixdlt.utils.functional.Functions.FN3; @@ -82,213 +84,277 @@ import com.radixdlt.utils.functional.Tuple.Tuple7; import com.radixdlt.utils.functional.Tuple.Tuple8; import com.radixdlt.utils.functional.Tuple.Tuple9; - import java.util.Optional; -import static com.radixdlt.utils.functional.Tuple.tuple; - -/** - * Group methods for {@link Optional}. - */ +/** Group methods for {@link Optional}. */ public interface Optionals { - static Mapper1 allOf(Optional op1) { - return () -> op1.flatMap(v1 -> Optional.of(tuple(v1))); - } - - static Mapper2 allOf(Optional op1, Optional op2) { - return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> Optional.of(tuple(v1, v2)))); - } - - static Mapper3 allOf(Optional op1, Optional op2, Optional op3) { - return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> op3.flatMap(v3 -> Optional.of(tuple(v1, v2, v3))))); - } - - static Mapper4 allOf( - Optional op1, Optional op2, Optional op3, Optional op4 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> Optional.of(tuple(v1, v2, v3, v4)))))); - } - - static Mapper5 allOf( - Optional op1, Optional op2, Optional op3, Optional op4, Optional op5 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> Optional.of(tuple(v1, v2, v3, v4, v5))))))); - } - - static Mapper6 allOf( - Optional op1, Optional op2, Optional op3, - Optional op4, Optional op5, Optional op6 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> Optional.of(tuple(v1, v2, v3, v4, v5, v6)))))))); - } - - static Mapper7 allOf( - Optional op1, Optional op2, Optional op3, Optional op4, - Optional op5, Optional op6, Optional op7 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> op7.flatMap( - v7 -> Optional.of(tuple(v1, v2, v3, v4, v5, v6, v7))))))))); - } - - static Mapper8 allOf( - Optional op1, Optional op2, Optional op3, Optional op4, - Optional op5, Optional op6, Optional op7, Optional op8 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> op7.flatMap( - v7 -> op8.flatMap( - v8 -> Optional.of(tuple(v1, v2, v3, v4, v5, v6, v7, v8)))))))))); - } - - static Mapper9 allOf( - Optional op1, Optional op2, Optional op3, Optional op4, Optional op5, - Optional op6, Optional op7, Optional op8, Optional op9 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> op7.flatMap( - v7 -> op8.flatMap( - v8 -> op9.flatMap( - v9 -> Optional.of( - tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9) - )))))))))); - } - - interface Mapper1 { - Optional> id(); - - default Optional map(FN1 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN1, T1> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper2 { - Optional> id(); - - default Optional map(FN2 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN2, T1, T2> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper3 { - Optional> id(); - - default Optional map(FN3 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN3, T1, T2, T3> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper4 { - Optional> id(); - - default Optional map(FN4 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN4, T1, T2, T3, T4> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper5 { - Optional> id(); - - default Optional map(FN5 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN5, T1, T2, T3, T4, T5> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper6 { - Optional> id(); - - default Optional map(FN6 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN6, T1, T2, T3, T4, T5, T6> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper7 { - Optional> id(); - - default Optional map(FN7 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN7, T1, T2, T3, T4, T5, T6, T7> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper8 { - Optional> id(); - - default Optional map(FN8 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Optional flatMap(FN8, T1, T2, T3, T4, T5, T6, T7, T8> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper9 { - Optional> id(); + static Mapper1 allOf(Optional op1) { + return () -> op1.flatMap(v1 -> Optional.of(tuple(v1))); + } + + static Mapper2 allOf(Optional op1, Optional op2) { + return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> Optional.of(tuple(v1, v2)))); + } + + static Mapper3 allOf( + Optional op1, Optional op2, Optional op3) { + return () -> + op1.flatMap(v1 -> op2.flatMap(v2 -> op3.flatMap(v3 -> Optional.of(tuple(v1, v2, v3))))); + } + + static Mapper4 allOf( + Optional op1, Optional op2, Optional op3, Optional op4) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap(v3 -> op4.flatMap(v4 -> Optional.of(tuple(v1, v2, v3, v4)))))); + } + + static Mapper5 allOf( + Optional op1, Optional op2, Optional op3, Optional op4, Optional op5) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> Optional.of(tuple(v1, v2, v3, v4, v5))))))); + } + + static Mapper6 allOf( + Optional op1, + Optional op2, + Optional op3, + Optional op4, + Optional op5, + Optional op6) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + Optional.of( + tuple(v1, v2, v3, v4, v5, v6)))))))); + } + + static Mapper7 allOf( + Optional op1, + Optional op2, + Optional op3, + Optional op4, + Optional op5, + Optional op6, + Optional op7) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + op7.flatMap( + v7 -> + Optional.of( + tuple( + v1, v2, v3, v4, v5, v6, + v7))))))))); + } + + static Mapper8 allOf( + Optional op1, + Optional op2, + Optional op3, + Optional op4, + Optional op5, + Optional op6, + Optional op7, + Optional op8) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + op7.flatMap( + v7 -> + op8.flatMap( + v8 -> + Optional.of( + tuple( + v1, v2, v3, v4, v5, + v6, v7, + v8)))))))))); + } + + static Mapper9 allOf( + Optional op1, + Optional op2, + Optional op3, + Optional op4, + Optional op5, + Optional op6, + Optional op7, + Optional op8, + Optional op9) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + op7.flatMap( + v7 -> + op8.flatMap( + v8 -> + op9.flatMap( + v9 -> + Optional.of( + tuple( + v1, v2, v3, + v4, v5, v6, + v7, v8, + v9))))))))))); + } + + interface Mapper1 { + Optional> id(); + + default Optional map(FN1 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN1, T1> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper2 { + Optional> id(); + + default Optional map(FN2 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN2, T1, T2> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper3 { + Optional> id(); + + default Optional map(FN3 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN3, T1, T2, T3> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper4 { + Optional> id(); + + default Optional map(FN4 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN4, T1, T2, T3, T4> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper5 { + Optional> id(); + + default Optional map(FN5 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN5, T1, T2, T3, T4, T5> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper6 { + Optional> id(); + + default Optional map(FN6 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN6, T1, T2, T3, T4, T5, T6> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper7 { + Optional> id(); + + default Optional map(FN7 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN7, T1, T2, T3, T4, T5, T6, T7> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper8 { + Optional> id(); + + default Optional map(FN8 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Optional flatMap(FN8, T1, T2, T3, T4, T5, T6, T7, T8> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper9 { + Optional> id(); - default Optional map(FN9 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } + default Optional map(FN9 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } - default Optional flatMap(FN9, T1, T2, T3, T4, T5, T6, T7, T8, T9> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } + default Optional flatMap(FN9, T1, T2, T3, T4, T5, T6, T7, T8, T9> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Promise.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Promise.java index 3536fd04f3..2f84e2996f 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Promise.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Promise.java @@ -64,113 +64,115 @@ package com.radixdlt.utils.functional; +import static com.radixdlt.errors.InternalErrors.ASYNC_PROCESSING_ERROR; + import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; -import static com.radixdlt.errors.InternalErrors.ASYNC_PROCESSING_ERROR; - public class Promise extends CompletableFuture> { - private Promise() { - } - - private Promise(Result value) { - complete(value); - } - - public static Promise promise() { - return new Promise<>(); - } - - public static Promise promise(Function errorMapper, CompletableFuture future) { - var promise = new Promise(); - future.whenComplete( - (value, exception) -> - promise.resolve(exception != null ? Result.fail(errorMapper.apply(exception)) : Result.ok(value)) - ); - return promise; - } - - public static Promise promise(CompletableFuture> future) { - var promise = new Promise(); - future.thenAccept(promise::resolve); - return promise; - } - - public static Promise promise(Consumer> setupLambda) { - var promise = new Promise(); - setupLambda.accept(promise); - return promise; - } - - public static Promise promise(Result value) { - return new Promise<>(value); - } - - public static Promise ok(R value) { - return promise(Result.ok(value)); - } - - public static Promise failure(Failure failure) { - return promise(Result.fail(failure)); - } - - public Promise success(T value) { - complete(Result.ok(value)); - return this; - } - - public Promise fail(Failure failure) { - complete(failure.result()); - return this; - } - - public Promise resolve(Result value) { - complete(value); - return this; - } - - public Promise onResult(Consumer> action) { - whenComplete((value, exception) -> { - if (exception != null) { - action.accept(Result.fail(ASYNC_PROCESSING_ERROR.with(exception.getMessage()))); - } else { - action.accept(value); - } - }); - return this; - } - - public Promise onSuccess(Consumer action) { - return onResult(result -> result.onSuccess(action)); - } - - public Promise onFailure(Consumer action) { - return onResult(result -> result.onFailure(action)); - } - - public Promise map(Function mapper) { - var result = Promise.promise(); - - onResult(r -> result.resolve(r.map(mapper))); - - return result; - } - - @SuppressWarnings("unchecked") - public Promise flatMap(Function> mapper) { - var resultPromise = Promise.promise(); - - onResult(result -> result.fold( - failure -> resultPromise.resolve((Result) result), - success -> mapper.apply(success).onResult(resultPromise::resolve) - )); - - return resultPromise; - } - - public Promise async(Consumer> consumer) { - runAsync(() -> consumer.accept(this)); - return this; - } + private Promise() {} + + private Promise(Result value) { + complete(value); + } + + public static Promise promise() { + return new Promise<>(); + } + + public static Promise promise( + Function errorMapper, CompletableFuture future) { + var promise = new Promise(); + future.whenComplete( + (value, exception) -> + promise.resolve( + exception != null ? Result.fail(errorMapper.apply(exception)) : Result.ok(value))); + return promise; + } + + public static Promise promise(CompletableFuture> future) { + var promise = new Promise(); + future.thenAccept(promise::resolve); + return promise; + } + + public static Promise promise(Consumer> setupLambda) { + var promise = new Promise(); + setupLambda.accept(promise); + return promise; + } + + public static Promise promise(Result value) { + return new Promise<>(value); + } + + public static Promise ok(R value) { + return promise(Result.ok(value)); + } + + public static Promise failure(Failure failure) { + return promise(Result.fail(failure)); + } + + public Promise success(T value) { + complete(Result.ok(value)); + return this; + } + + public Promise fail(Failure failure) { + complete(failure.result()); + return this; + } + + public Promise resolve(Result value) { + complete(value); + return this; + } + + public Promise onResult(Consumer> action) { + whenComplete( + (value, exception) -> { + if (exception != null) { + action.accept(Result.fail(ASYNC_PROCESSING_ERROR.with(exception.getMessage()))); + } else { + action.accept(value); + } + }); + return this; + } + + public Promise onSuccess(Consumer action) { + return onResult(result -> result.onSuccess(action)); + } + + public Promise onFailure(Consumer action) { + return onResult(result -> result.onFailure(action)); + } + + public Promise map(Function mapper) { + var result = Promise.promise(); + + onResult(r -> result.resolve(r.map(mapper))); + + return result; + } + + @SuppressWarnings("unchecked") + public Promise flatMap(Function> mapper) { + var resultPromise = Promise.promise(); + + onResult( + result -> + result.fold( + failure -> resultPromise.resolve((Result) result), + success -> mapper.apply(success).onResult(resultPromise::resolve))); + + return resultPromise; + } + + public Promise async(Consumer> consumer) { + runAsync(() -> consumer.accept(this)); + return this; + } } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Result.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Result.java index 642aa58556..04a527dc8d 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Result.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Result.java @@ -64,6 +64,8 @@ package com.radixdlt.utils.functional; +import static com.radixdlt.utils.functional.Tuple.tuple; + import com.radixdlt.utils.functional.Functions.FN1; import com.radixdlt.utils.functional.Functions.FN2; import com.radixdlt.utils.functional.Functions.FN3; @@ -82,7 +84,6 @@ import com.radixdlt.utils.functional.Tuple.Tuple7; import com.radixdlt.utils.functional.Tuple.Tuple8; import com.radixdlt.utils.functional.Tuple.Tuple9; - import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -94,597 +95,642 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import static com.radixdlt.utils.functional.Tuple.tuple; - /** - * Representation of the operation result. The result can be either success or failure. - * In case of success it holds value returned by the operation. In case of failure it - * holds a failure description. + * Representation of the operation result. The result can be either success or failure. In case of + * success it holds value returned by the operation. In case of failure it holds a failure + * description. * * @param Type of value in case of success. */ public interface Result { - /** - * Handle success and failure cases and produce some resulting value. - * - * @param leftMapper Function to transform the error value. - * @param rightMapper Function to transform the success value. - * - * @return transformed value. - */ - R fold(Function leftMapper, Function rightMapper); - - /** - * Version of {@link #fold(Function, Function)} which uses one function to handle both cases. - * - * @param mapper Function to transform the error and success values. - * - * @return transformed value. - */ - default R fold(BiFunction mapper) { - return fold(f -> mapper.apply(f, null), v -> mapper.apply(null, v)); - } - - /** - * Transform operation result value into value of other type and wrap new - * value into {@link Result}. Transformation takes place if current instance - * (this) contains successful result, otherwise current instance remains - * unchanged and transformation function is not invoked. - * - * @param mapper Function to transform successful value - * - * @return transformed value (in case of success) or current instance (in case of failure) - */ - @SuppressWarnings("unchecked") - default Result map(Function mapper) { - return fold(l -> (Result) this, r -> ok(mapper.apply(r))); - } - - /** - * Transform operation result into another operation result. In case if current - * instance (this) is an error, transformation function is not invoked - * and value remains the same. - * - * @param mapper Function to apply to result - * - * @return transformed value (in case of success) or current instance (in case of failure) - */ - @SuppressWarnings("unchecked") - default Result flatMap(Function> mapper) { - return fold(t -> (Result) this, mapper); - } - - /** - * Apply consumers to result value. Note that depending on the result (success or failure) only one consumer will be - * applied at a time. - * - * @param failureConsumer Consumer for failure result - * @param successConsumer Consumer for success result - * - * @return current instance - */ - default Result apply(Consumer failureConsumer, Consumer successConsumer) { - return fold(t -> { - failureConsumer.accept(t); - return this; - }, t -> { - successConsumer.accept(t); - return this; - }); - } - - /** - * Combine current instance with another result. If current instance holds - * success then result is equivalent to current instance, otherwise other - * instance (passed as {@code replacement} parameter) is returned. - * - * @param replacement Value to return if current instance contains failure operation result - * - * @return current instance in case of success or replacement instance in case of failure. - */ - default Result or(Result replacement) { - return fold(t -> replacement, t -> this); - } - - /** - * Combine current instance with another result. If current instance holds - * success then result is equivalent to current instance, otherwise instance provided by - * specified supplier is returned. - * - * @param supplier Supplier for replacement instance if current instance contains failure operation result - * - * @return current instance in case of success or result returned by supplier in case of failure. - */ - default Result or(Supplier> supplier) { - return fold(t -> supplier.get(), t -> this); - } - - /** - * Pass successful operation result value into provided consumer. - * - * @param consumer Consumer to pass value to - * - * @return current instance for fluent call chaining - */ - Result onSuccess(Consumer consumer); - - /** - * Run provided action in case of success. - * - * @return current instance for fluent call chaining - */ - Result onSuccessDo(Runnable action); - - /** - * Run provided action in case of failure. - * - * @return current instance for fluent call chaining - */ - Result onFailureDo(Runnable action); - - /** - * Pass failure operation result value into provided consumer. - * - * @param consumer Consumer to pass value to - * - * @return current instance for fluent call chaining - */ - Result onFailure(Consumer consumer); - - /** - * Check for success. - * - * @return {@code true} if result is a success - */ - default boolean isSuccess() { - return fold(__ -> false, __ -> true); - } - - /** - * Filter contained value with given predicate. Provided failure is used for the result - * if predicate returns {@code false}. - * - * @param predicate Predicate to check - * @param failure Failure which will be used in case if predicate returns {@code false} - * - * @return the same instance if predicate returns {@code true} or new failure result with provided failure. - */ - default Result filter(Predicate predicate, Failure failure) { - return flatMap(v -> predicate.test(v) ? this : failure.with(v).result()); - } - - /** - * Convert instance into {@link Optional} of the same type. Successful instance - * is converted into present {@link Optional} and failure - into empty {@link Optional}. - * Note that during such a conversion error information may get lost. - * - * @return {@link Optional} instance which is present in case of success and missing - * in case of failure. - */ - default Optional toOptional() { - return fold(t1 -> Optional.empty(), Optional::of); - } - - /** - * Convert instance into {@link Result} - * - * @param failure failure to use when input is empty instance. - * @param source input instance of {@link Optional} - * - * @return created instance - */ - static Result fromOptional(Failure failure, Optional source) { - return source.map(Result::ok).orElseGet(failure::result); - } - - /** - * Convert instance into {@link Result} - * - * @param failure supplier of failure which is used when input is empty instance. - * @param source input instance of {@link Optional} - * - * @return created instance - */ - static Result fromOptional(Supplier failure, Optional source) { - return source.map(Result::ok).orElseGet(() -> failure.get().result()); - } - - /** - * Wrap call to function which may throw an exception. - * - * @param failure the failure to represent the error which may happen during call - * @param supplier the function to call. - * - * @return success instance if call was successful and failure instance if function threw an exception. - */ - static Result wrap(Failure failure, ThrowingSupplier supplier) { - return wrap(e -> failure.with(e.getMessage()), supplier); - } - - /** - * Wrap call to function which may throw an exception. - * - * @param failure the supplier of failure used to represent the error which may happen during call - * @param supplier the function to call. - * - * @return success instance if call was successful and failure instance if function threw an exception. - */ - static Result wrap(Supplier failure, ThrowingSupplier supplier) { - return wrap(e -> failure.get().with(e.getMessage()), supplier); - } - - /** - * Wrap call to function which may throw an exception. - * - * @param errorMapper the mapper which translates the exception into failure - * @param supplier the function to call. - * - * @return success instance if call was successful and failure instance if function threw an exception. - */ - static Result wrap(Function errorMapper, ThrowingSupplier supplier) { - try { - return ok(supplier.get()); - } catch (Throwable e) { - return errorMapper.apply(e).result(); - } - } - - /** - * Create an instance of successful operation result. - * - * @param value Operation result - * - * @return created instance - */ - static Result ok(R value) { - return new ResultOk<>(value); - } - - /** - * Create an instance of failure operation result. - * - * @param value Operation error value - * - * @return created instance - */ - static Result fail(Failure value) { - return new ResultFail<>(value); - } - - final class ResultOk implements Result { - private final R value; - - protected ResultOk(R value) { - this.value = value; - } - - @Override - public T fold( - Function leftMapper, - Function rightMapper - ) { - return rightMapper.apply(value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Result) - ? ((Result) obj).fold($ -> false, val -> Objects.equals(val, value)) - : false; - } - - @Override - public String toString() { - return new StringJoiner(", ", "Result-success(", ")") - .add(value.toString()) - .toString(); - } - - @Override - public Result onSuccess(Consumer consumer) { - consumer.accept(value); - return this; - } - - @Override - public Result onSuccessDo(Runnable action) { - action.run(); - return this; - } - - @Override - public Result onFailure(Consumer consumer) { - return this; - } - - @Override - public Result onFailureDo(Runnable action) { - return this; - } - } - - final class ResultFail implements Result { - private final Failure value; - - protected ResultFail(Failure value) { - this.value = value; - } - - @Override - public T fold( - Function leftMapper, - Function rightMapper - ) { - return leftMapper.apply(value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Result) - ? ((Result) obj).fold(val -> Objects.equals(val, value), $ -> false) - : false; - } - - @Override - public String toString() { - return new StringJoiner(", ", "Result-failure(", ")") - .add(value.toString()) - .toString(); - } - - @Override - public Result onSuccess(Consumer consumer) { - return this; - } - - @Override - public Result onSuccessDo(Runnable action) { - return this; - } - - @Override - public Result onFailure(Consumer consumer) { - consumer.accept(value); - return this; - } - - @Override - public Result onFailureDo(Runnable action) { - action.run(); - return this; - } - } - - @SuppressWarnings("unchecked") - static Result> all(Iterable> results) { - var list = new ArrayList(); - - for (var result : results) { - if (!result.isSuccess()) { - return (Result>) result; - } - - result.onSuccess(list::add); - } - - return ok(list); - } - - static Mapper1 allOf(Result op1) { - return () -> op1.flatMap(v1 -> ok(tuple(v1))); - } - - static Mapper2 allOf(Result op1, Result op2) { - return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> ok(tuple(v1, v2)))); - } - - static Mapper3 allOf(Result op1, Result op2, Result op3) { - return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> op3.flatMap(v3 -> ok(tuple(v1, v2, v3))))); - } - - static Mapper4 allOf( - Result op1, Result op2, Result op3, Result op4 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> ok(tuple(v1, v2, v3, v4)))))); - } - - static Mapper5 allOf( - Result op1, Result op2, Result op3, Result op4, Result op5 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> ok(tuple(v1, v2, v3, v4, v5))))))); - } - - static Mapper6 allOf( - Result op1, Result op2, Result op3, - Result op4, Result op5, Result op6 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> ok(tuple(v1, v2, v3, v4, v5, v6)))))))); - } - - static Mapper7 allOf( - Result op1, Result op2, Result op3, Result op4, - Result op5, Result op6, Result op7 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> op7.flatMap( - v7 -> ok(tuple(v1, v2, v3, v4, v5, v6, v7))))))))); - } - - static Mapper8 allOf( - Result op1, Result op2, Result op3, Result op4, - Result op5, Result op6, Result op7, Result op8 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> op7.flatMap( - v7 -> op8.flatMap( - v8 -> ok(tuple(v1, v2, v3, v4, v5, v6, v7, v8)))))))))); - } - - static Mapper9 allOf( - Result op1, Result op2, Result op3, Result op4, Result op5, - Result op6, Result op7, Result op8, Result op9 - ) { - return () -> op1.flatMap( - v1 -> op2.flatMap( - v2 -> op3.flatMap( - v3 -> op4.flatMap( - v4 -> op5.flatMap( - v5 -> op6.flatMap( - v6 -> op7.flatMap( - v7 -> op8.flatMap( - v8 -> op9.flatMap( - v9 -> ok(tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9) - )))))))))); - } - - interface Mapper1 { - Result> id(); - - default Result map(FN1 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN1, T1> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper2 { - Result> id(); - - default Result map(FN2 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN2, T1, T2> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper3 { - Result> id(); - - default Result map(FN3 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN3, T1, T2, T3> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper4 { - Result> id(); - - default Result map(FN4 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN4, T1, T2, T3, T4> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper5 { - Result> id(); - - default Result map(FN5 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN5, T1, T2, T3, T4, T5> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper6 { - Result> id(); - - default Result map(FN6 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN6, T1, T2, T3, T4, T5, T6> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper7 { - Result> id(); - - default Result map(FN7 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN7, T1, T2, T3, T4, T5, T6, T7> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper8 { - Result> id(); - - default Result map(FN8 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } - - default Result flatMap(FN8, T1, T2, T3, T4, T5, T6, T7, T8> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } - - interface Mapper9 { - Result> id(); + /** + * Handle success and failure cases and produce some resulting value. + * + * @param leftMapper Function to transform the error value. + * @param rightMapper Function to transform the success value. + * @return transformed value. + */ + R fold( + Function leftMapper, + Function rightMapper); + + /** + * Version of {@link #fold(Function, Function)} which uses one function to handle both cases. + * + * @param mapper Function to transform the error and success values. + * @return transformed value. + */ + default R fold(BiFunction mapper) { + return fold(f -> mapper.apply(f, null), v -> mapper.apply(null, v)); + } + + /** + * Transform operation result value into value of other type and wrap new value into {@link + * Result}. Transformation takes place if current instance (this) contains successful result, + * otherwise current instance remains unchanged and transformation function is not invoked. + * + * @param mapper Function to transform successful value + * @return transformed value (in case of success) or current instance (in case of failure) + */ + @SuppressWarnings("unchecked") + default Result map(Function mapper) { + return fold(l -> (Result) this, r -> ok(mapper.apply(r))); + } + + /** + * Transform operation result into another operation result. In case if current instance (this) is + * an error, transformation function is not invoked and value remains the same. + * + * @param mapper Function to apply to result + * @return transformed value (in case of success) or current instance (in case of failure) + */ + @SuppressWarnings("unchecked") + default Result flatMap(Function> mapper) { + return fold(t -> (Result) this, mapper); + } + + /** + * Apply consumers to result value. Note that depending on the result (success or failure) only + * one consumer will be applied at a time. + * + * @param failureConsumer Consumer for failure result + * @param successConsumer Consumer for success result + * @return current instance + */ + default Result apply( + Consumer failureConsumer, Consumer successConsumer) { + return fold( + t -> { + failureConsumer.accept(t); + return this; + }, + t -> { + successConsumer.accept(t); + return this; + }); + } + + /** + * Combine current instance with another result. If current instance holds success then result is + * equivalent to current instance, otherwise other instance (passed as {@code replacement} + * parameter) is returned. + * + * @param replacement Value to return if current instance contains failure operation result + * @return current instance in case of success or replacement instance in case of failure. + */ + default Result or(Result replacement) { + return fold(t -> replacement, t -> this); + } + + /** + * Combine current instance with another result. If current instance holds success then result is + * equivalent to current instance, otherwise instance provided by specified supplier is returned. + * + * @param supplier Supplier for replacement instance if current instance contains failure + * operation result + * @return current instance in case of success or result returned by supplier in case of failure. + */ + default Result or(Supplier> supplier) { + return fold(t -> supplier.get(), t -> this); + } + + /** + * Pass successful operation result value into provided consumer. + * + * @param consumer Consumer to pass value to + * @return current instance for fluent call chaining + */ + Result onSuccess(Consumer consumer); + + /** + * Run provided action in case of success. + * + * @return current instance for fluent call chaining + */ + Result onSuccessDo(Runnable action); + + /** + * Run provided action in case of failure. + * + * @return current instance for fluent call chaining + */ + Result onFailureDo(Runnable action); + + /** + * Pass failure operation result value into provided consumer. + * + * @param consumer Consumer to pass value to + * @return current instance for fluent call chaining + */ + Result onFailure(Consumer consumer); + + /** + * Check for success. + * + * @return {@code true} if result is a success + */ + default boolean isSuccess() { + return fold(__ -> false, __ -> true); + } + + /** + * Filter contained value with given predicate. Provided failure is used for the result if + * predicate returns {@code false}. + * + * @param predicate Predicate to check + * @param failure Failure which will be used in case if predicate returns {@code false} + * @return the same instance if predicate returns {@code true} or new failure result with provided + * failure. + */ + default Result filter(Predicate predicate, Failure failure) { + return flatMap(v -> predicate.test(v) ? this : failure.with(v).result()); + } + + /** + * Convert instance into {@link Optional} of the same type. Successful instance is converted into + * present {@link Optional} and failure - into empty {@link Optional}. Note that during such a + * conversion error information may get lost. + * + * @return {@link Optional} instance which is present in case of success and missing in case of + * failure. + */ + default Optional toOptional() { + return fold(t1 -> Optional.empty(), Optional::of); + } + + /** + * Convert instance into {@link Result} + * + * @param failure failure to use when input is empty instance. + * @param source input instance of {@link Optional} + * @return created instance + */ + static Result fromOptional(Failure failure, Optional source) { + return source.map(Result::ok).orElseGet(failure::result); + } + + /** + * Convert instance into {@link Result} + * + * @param failure supplier of failure which is used when input is empty instance. + * @param source input instance of {@link Optional} + * @return created instance + */ + static Result fromOptional(Supplier failure, Optional source) { + return source.map(Result::ok).orElseGet(() -> failure.get().result()); + } + + /** + * Wrap call to function which may throw an exception. + * + * @param failure the failure to represent the error which may happen during call + * @param supplier the function to call. + * @return success instance if call was successful and failure instance if function threw an + * exception. + */ + static Result wrap(Failure failure, ThrowingSupplier supplier) { + return wrap(e -> failure.with(e.getMessage()), supplier); + } + + /** + * Wrap call to function which may throw an exception. + * + * @param failure the supplier of failure used to represent the error which may happen during call + * @param supplier the function to call. + * @return success instance if call was successful and failure instance if function threw an + * exception. + */ + static Result wrap(Supplier failure, ThrowingSupplier supplier) { + return wrap(e -> failure.get().with(e.getMessage()), supplier); + } + + /** + * Wrap call to function which may throw an exception. + * + * @param errorMapper the mapper which translates the exception into failure + * @param supplier the function to call. + * @return success instance if call was successful and failure instance if function threw an + * exception. + */ + static Result wrap( + Function errorMapper, ThrowingSupplier supplier) { + try { + return ok(supplier.get()); + } catch (Throwable e) { + return errorMapper.apply(e).result(); + } + } + + /** + * Create an instance of successful operation result. + * + * @param value Operation result + * @return created instance + */ + static Result ok(R value) { + return new ResultOk<>(value); + } + + /** + * Create an instance of failure operation result. + * + * @param value Operation error value + * @return created instance + */ + static Result fail(Failure value) { + return new ResultFail<>(value); + } + + final class ResultOk implements Result { + private final R value; + + protected ResultOk(R value) { + this.value = value; + } + + @Override + public T fold( + Function leftMapper, + Function rightMapper) { + return rightMapper.apply(value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Result) + ? ((Result) obj).fold($ -> false, val -> Objects.equals(val, value)) + : false; + } + + @Override + public String toString() { + return new StringJoiner(", ", "Result-success(", ")").add(value.toString()).toString(); + } + + @Override + public Result onSuccess(Consumer consumer) { + consumer.accept(value); + return this; + } + + @Override + public Result onSuccessDo(Runnable action) { + action.run(); + return this; + } + + @Override + public Result onFailure(Consumer consumer) { + return this; + } + + @Override + public Result onFailureDo(Runnable action) { + return this; + } + } + + final class ResultFail implements Result { + private final Failure value; + + protected ResultFail(Failure value) { + this.value = value; + } + + @Override + public T fold( + Function leftMapper, + Function rightMapper) { + return leftMapper.apply(value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Result) + ? ((Result) obj).fold(val -> Objects.equals(val, value), $ -> false) + : false; + } + + @Override + public String toString() { + return new StringJoiner(", ", "Result-failure(", ")").add(value.toString()).toString(); + } + + @Override + public Result onSuccess(Consumer consumer) { + return this; + } + + @Override + public Result onSuccessDo(Runnable action) { + return this; + } + + @Override + public Result onFailure(Consumer consumer) { + consumer.accept(value); + return this; + } + + @Override + public Result onFailureDo(Runnable action) { + action.run(); + return this; + } + } + + @SuppressWarnings("unchecked") + static Result> all(Iterable> results) { + var list = new ArrayList(); + + for (var result : results) { + if (!result.isSuccess()) { + return (Result>) result; + } + + result.onSuccess(list::add); + } + + return ok(list); + } + + static Mapper1 allOf(Result op1) { + return () -> op1.flatMap(v1 -> ok(tuple(v1))); + } + + static Mapper2 allOf(Result op1, Result op2) { + return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> ok(tuple(v1, v2)))); + } + + static Mapper3 allOf(Result op1, Result op2, Result op3) { + return () -> op1.flatMap(v1 -> op2.flatMap(v2 -> op3.flatMap(v3 -> ok(tuple(v1, v2, v3))))); + } + + static Mapper4 allOf( + Result op1, Result op2, Result op3, Result op4) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap(v2 -> op3.flatMap(v3 -> op4.flatMap(v4 -> ok(tuple(v1, v2, v3, v4)))))); + } + + static Mapper5 allOf( + Result op1, Result op2, Result op3, Result op4, Result op5) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> op5.flatMap(v5 -> ok(tuple(v1, v2, v3, v4, v5))))))); + } + + static Mapper6 allOf( + Result op1, + Result op2, + Result op3, + Result op4, + Result op5, + Result op6) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> ok(tuple(v1, v2, v3, v4, v5, v6)))))))); + } + + static Mapper7 allOf( + Result op1, + Result op2, + Result op3, + Result op4, + Result op5, + Result op6, + Result op7) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + op7.flatMap( + v7 -> + ok( + tuple( + v1, v2, v3, v4, v5, v6, + v7))))))))); + } + + static Mapper8 allOf( + Result op1, + Result op2, + Result op3, + Result op4, + Result op5, + Result op6, + Result op7, + Result op8) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + op7.flatMap( + v7 -> + op8.flatMap( + v8 -> + ok( + tuple( + v1, v2, v3, v4, v5, + v6, v7, + v8)))))))))); + } + + static Mapper9 allOf( + Result op1, + Result op2, + Result op3, + Result op4, + Result op5, + Result op6, + Result op7, + Result op8, + Result op9) { + return () -> + op1.flatMap( + v1 -> + op2.flatMap( + v2 -> + op3.flatMap( + v3 -> + op4.flatMap( + v4 -> + op5.flatMap( + v5 -> + op6.flatMap( + v6 -> + op7.flatMap( + v7 -> + op8.flatMap( + v8 -> + op9.flatMap( + v9 -> + ok( + tuple( + v1, v2, v3, + v4, v5, v6, + v7, v8, + v9))))))))))); + } + + interface Mapper1 { + Result> id(); + + default Result map(FN1 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN1, T1> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper2 { + Result> id(); + + default Result map(FN2 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN2, T1, T2> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper3 { + Result> id(); + + default Result map(FN3 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN3, T1, T2, T3> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper4 { + Result> id(); + + default Result map(FN4 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN4, T1, T2, T3, T4> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper5 { + Result> id(); + + default Result map(FN5 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN5, T1, T2, T3, T4, T5> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper6 { + Result> id(); + + default Result map(FN6 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN6, T1, T2, T3, T4, T5, T6> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper7 { + Result> id(); + + default Result map(FN7 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN7, T1, T2, T3, T4, T5, T6, T7> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper8 { + Result> id(); + + default Result map(FN8 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } + + default Result flatMap(FN8, T1, T2, T3, T4, T5, T6, T7, T8> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } + + interface Mapper9 { + Result> id(); - default Result map(FN9 mapper) { - return id().map(tuple -> tuple.map(mapper)); - } + default Result map(FN9 mapper) { + return id().map(tuple -> tuple.map(mapper)); + } - default Result flatMap(FN9, T1, T2, T3, T4, T5, T6, T7, T8, T9> mapper) { - return id().flatMap(tuple -> tuple.map(mapper)); - } - } -} \ No newline at end of file + default Result flatMap(FN9, T1, T2, T3, T4, T5, T6, T7, T8, T9> mapper) { + return id().flatMap(tuple -> tuple.map(mapper)); + } + } +} diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/ThrowingSupplier.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/ThrowingSupplier.java index 5fcd4c61dc..7d81655172 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/ThrowingSupplier.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/ThrowingSupplier.java @@ -66,5 +66,5 @@ @FunctionalInterface public interface ThrowingSupplier { - T get() throws Throwable; + T get() throws Throwable; } diff --git a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Tuple.java b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Tuple.java index a23fc6e052..8c274d7c80 100644 --- a/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Tuple.java +++ b/radixdlt-java-common/src/main/java/com/radixdlt/utils/functional/Tuple.java @@ -74,477 +74,527 @@ import com.radixdlt.utils.functional.Functions.FN7; import com.radixdlt.utils.functional.Functions.FN8; import com.radixdlt.utils.functional.Functions.FN9; - import java.util.Objects; -/** - * Tuples of various size. - */ +/** Tuples of various size. */ public interface Tuple> { - interface Tuple0 extends Tuple { - T map(FN0 mapper); - } - - interface Tuple1 extends Tuple> { - T map(Functions.FN1 mapper); - } - - interface Tuple2 extends Tuple> { - T map(FN2 mapper); - default T1 first() { - return map((first, __) -> first); - } - - default T2 last() { - return map((__, last) -> last); - } - } - - interface Tuple3 extends Tuple> { - T map(FN3 mapper); - } - - interface Tuple4 extends Tuple> { - T map(FN4 mapper); - } - - interface Tuple5 extends Tuple> { - T map(FN5 mapper); - } - - interface Tuple6 extends Tuple> { - T map(FN6 mapper); - } - - interface Tuple7 extends Tuple> { - T map(FN7 mapper); - } - - interface Tuple8 extends Tuple> { - T map(FN8 mapper); - } - - interface Tuple9 extends Tuple> { - T map(FN9 mapper); - } - - Tuple0 UNIT = new Tuple0() { - @Override - public T map(final FN0 mapper) { - return mapper.apply(); - } - - @Override - public boolean equals(final Object obj) { - return obj instanceof Tuple0; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public String toString() { - return "Tuple()"; - } - }; - - static Tuple0 tuple() { - return UNIT; - } - - static Tuple1 tuple(final T1 param1) { - return new Tuple1<>() { - @Override - public T map(final FN1 mapper) { - return mapper.apply(param1); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple1) - ? ((Tuple1) obj).map(v1 -> Objects.equals(v1, param1)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1); - } - - @Override - public String toString() { - return "Tuple(" + param1.toString() + ")"; - } - }; - } - - static Tuple2 tuple(final T1 param1, final T2 param2) { - return new Tuple2<>() { - @Override - public T map(final FN2 mapper) { - return mapper.apply(param1, param2); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple2) - ? ((Tuple2) obj).map((v1, v2) -> Objects.equals(v1, param1) && Objects.equals(v2, param2)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2); - } - - @Override - public String toString() { - return "Tuple(" + param1.toString() + ", " + param2.toString() + ")"; - } - }; - } - - static Tuple3 tuple(final T1 param1, final T2 param2, final T3 param3) { - return new Tuple3<>() { - @Override - public T map(final FN3 mapper) { - return mapper.apply(param1, param2, param3); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple3) - ? ((Tuple3) obj) - .map((v1, v2, v3) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3); - } - - @Override - public String toString() { - return "Tuple(" + param1.toString() + "," + param2.toString() + "," + param3.toString() + ")"; - } - }; - } - - static Tuple4 tuple( - final T1 param1, final T2 param2, final T3 param3, - final T4 param4 - ) { - return new Tuple4<>() { - @Override - public T map(final FN4 mapper) { - return mapper.apply(param1, param2, param3, param4); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple4) - ? ((Tuple4) obj) - .map((v1, v2, v3, v4) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3) - && Objects.equals(v4, param4)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3, param4); - } - - @Override - public String toString() { - return "Tuple(" - + param1.toString() + "," - + param2.toString() + "," - + param3.toString() + "," - + param4.toString() + ")"; - } - }; - } - - static Tuple5 tuple( - final T1 param1, final T2 param2, final T3 param3, - final T4 param4, final T5 param5 - ) { - return new Tuple5<>() { - @Override - public T map(final FN5 mapper) { - return mapper.apply(param1, param2, param3, param4, param5); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple5) - ? ((Tuple5) obj) - .map((v1, v2, v3, v4, v5) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3) - && Objects.equals(v4, param4) - && Objects.equals(v5, param5)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3, param4, param5); - } - - @Override - public String toString() { - return "Tuple(" - + param1.toString() + "," - + param2.toString() + "," - + param3.toString() + "," - + param4.toString() + "," - + param5.toString() + ")"; - } - }; - } - - static Tuple6 tuple( - final T1 param1, final T2 param2, final T3 param3, - final T4 param4, final T5 param5, final T6 param6 - ) { - return new Tuple6<>() { - @Override - public T map(final FN6 mapper) { - return mapper.apply(param1, param2, param3, param4, param5, param6); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple6) - ? ((Tuple6) obj) - .map((v1, v2, v3, v4, v5, v6) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3) - && Objects.equals(v4, param4) - && Objects.equals(v5, param5) - && Objects.equals(v6, param6)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3, param4, param5, param6); - } - - @Override - public String toString() { - return "Tuple(" - + param1.toString() + "," - + param2.toString() + "," - + param3.toString() + "," - + param4.toString() + "," - + param5.toString() + "," - + param6.toString() + ")"; - } - }; - } - - static Tuple7 tuple( - final T1 param1, - final T2 param2, - final T3 param3, - final T4 param4, - final T5 param5, - final T6 param6, - final T7 param7 - ) { - return new Tuple7<>() { - @Override - public T map(final FN7 mapper) { - return mapper.apply(param1, param2, param3, param4, param5, param6, param7); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple7) - ? ((Tuple7) obj) - .map((v1, v2, v3, v4, v5, v6, v7) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3) - && Objects.equals(v4, param4) - && Objects.equals(v5, param5) - && Objects.equals(v6, param6) - && Objects.equals(v7, param7)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3, param4, param5, param6, param7); - } - - @Override - public String toString() { - return "Tuple(" - + param1.toString() + "," - + param2.toString() + "," - + param3.toString() + "," - + param4.toString() + "," - + param5.toString() + "," - + param6.toString() + "," - + param7.toString() + ")"; - } - }; - } - - static Tuple8 tuple( - final T1 param1, - final T2 param2, - final T3 param3, - final T4 param4, - final T5 param5, - final T6 param6, - final T7 param7, - final T8 param8 - ) { - return new Tuple8<>() { - @Override - public T map(final FN8 mapper) { - return mapper.apply(param1, param2, param3, param4, param5, param6, param7, param8); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple8) - ? ((Tuple8) obj) - .map((v1, v2, v3, v4, v5, v6, v7, v8) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3) - && Objects.equals(v4, param4) - && Objects.equals(v5, param5) - && Objects.equals(v6, param6) - && Objects.equals(v7, param7) - && Objects.equals(v8, param8)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3, param4, param5, param6, param7, param8); - } - - @Override - public String toString() { - return "Tuple(" - + param1.toString() + "," - + param2.toString() + "," - + param3.toString() + "," - + param4.toString() + "," - + param5.toString() + "," - + param6.toString() + "," - + param7.toString() + "," - + param8.toString() + ")"; - } - }; - } - - static Tuple9 tuple( - final T1 param1, - final T2 param2, - final T3 param3, - final T4 param4, - final T5 param5, - final T6 param6, - final T7 param7, - final T8 param8, - final T9 param9 - ) { - return new Tuple9<>() { - @Override - public T map(final FN9 mapper) { - return mapper.apply(param1, param2, param3, param4, param5, param6, param7, param8, param9); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - - return (obj instanceof Tuple9) - ? ((Tuple9) obj) - .map((v1, v2, v3, v4, v5, v6, v7, v8, v9) -> - Objects.equals(v1, param1) - && Objects.equals(v2, param2) - && Objects.equals(v3, param3) - && Objects.equals(v4, param4) - && Objects.equals(v5, param5) - && Objects.equals(v6, param6) - && Objects.equals(v7, param7) - && Objects.equals(v8, param8) - && Objects.equals(v9, param9)) - : false; - } - - @Override - public int hashCode() { - return Objects.hash(param1, param2, param3, param4, param5, param6, param7, param8, param9); - } - - @Override - public String toString() { - return "Tuple(" - + param1.toString() + "," - + param2.toString() + "," - + param3.toString() + "," - + param4.toString() + "," - + param5.toString() + "," - + param6.toString() + "," - + param7.toString() + "," - + param8.toString() + "," - + param9.toString() + ")"; - } - }; - } + interface Tuple0 extends Tuple { + T map(FN0 mapper); + } + + interface Tuple1 extends Tuple> { + T map(Functions.FN1 mapper); + } + + interface Tuple2 extends Tuple> { + T map(FN2 mapper); + + default T1 first() { + return map((first, __) -> first); + } + + default T2 last() { + return map((__, last) -> last); + } + } + + interface Tuple3 extends Tuple> { + T map(FN3 mapper); + } + + interface Tuple4 extends Tuple> { + T map(FN4 mapper); + } + + interface Tuple5 extends Tuple> { + T map(FN5 mapper); + } + + interface Tuple6 extends Tuple> { + T map(FN6 mapper); + } + + interface Tuple7 extends Tuple> { + T map(FN7 mapper); + } + + interface Tuple8 + extends Tuple> { + T map(FN8 mapper); + } + + interface Tuple9 + extends Tuple> { + T map(FN9 mapper); + } + + Tuple0 UNIT = + new Tuple0() { + @Override + public T map(final FN0 mapper) { + return mapper.apply(); + } + + @Override + public boolean equals(final Object obj) { + return obj instanceof Tuple0; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public String toString() { + return "Tuple()"; + } + }; + + static Tuple0 tuple() { + return UNIT; + } + + static Tuple1 tuple(final T1 param1) { + return new Tuple1<>() { + @Override + public T map(final FN1 mapper) { + return mapper.apply(param1); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple1) + ? ((Tuple1) obj).map(v1 -> Objects.equals(v1, param1)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1); + } + + @Override + public String toString() { + return "Tuple(" + param1.toString() + ")"; + } + }; + } + + static Tuple2 tuple(final T1 param1, final T2 param2) { + return new Tuple2<>() { + @Override + public T map(final FN2 mapper) { + return mapper.apply(param1, param2); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple2) + ? ((Tuple2) obj) + .map((v1, v2) -> Objects.equals(v1, param1) && Objects.equals(v2, param2)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2); + } + + @Override + public String toString() { + return "Tuple(" + param1.toString() + ", " + param2.toString() + ")"; + } + }; + } + + static Tuple3 tuple(final T1 param1, final T2 param2, final T3 param3) { + return new Tuple3<>() { + @Override + public T map(final FN3 mapper) { + return mapper.apply(param1, param2, param3); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple3) + ? ((Tuple3) obj) + .map( + (v1, v2, v3) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + ")"; + } + }; + } + + static Tuple4 tuple( + final T1 param1, final T2 param2, final T3 param3, final T4 param4) { + return new Tuple4<>() { + @Override + public T map(final FN4 mapper) { + return mapper.apply(param1, param2, param3, param4); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple4) + ? ((Tuple4) obj) + .map( + (v1, v2, v3, v4) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3) + && Objects.equals(v4, param4)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3, param4); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + "," + + param4.toString() + + ")"; + } + }; + } + + static Tuple5 tuple( + final T1 param1, final T2 param2, final T3 param3, final T4 param4, final T5 param5) { + return new Tuple5<>() { + @Override + public T map(final FN5 mapper) { + return mapper.apply(param1, param2, param3, param4, param5); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple5) + ? ((Tuple5) obj) + .map( + (v1, v2, v3, v4, v5) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3) + && Objects.equals(v4, param4) + && Objects.equals(v5, param5)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3, param4, param5); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + "," + + param4.toString() + + "," + + param5.toString() + + ")"; + } + }; + } + + static Tuple6 tuple( + final T1 param1, + final T2 param2, + final T3 param3, + final T4 param4, + final T5 param5, + final T6 param6) { + return new Tuple6<>() { + @Override + public T map(final FN6 mapper) { + return mapper.apply(param1, param2, param3, param4, param5, param6); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple6) + ? ((Tuple6) obj) + .map( + (v1, v2, v3, v4, v5, v6) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3) + && Objects.equals(v4, param4) + && Objects.equals(v5, param5) + && Objects.equals(v6, param6)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3, param4, param5, param6); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + "," + + param4.toString() + + "," + + param5.toString() + + "," + + param6.toString() + + ")"; + } + }; + } + + static Tuple7 tuple( + final T1 param1, + final T2 param2, + final T3 param3, + final T4 param4, + final T5 param5, + final T6 param6, + final T7 param7) { + return new Tuple7<>() { + @Override + public T map(final FN7 mapper) { + return mapper.apply(param1, param2, param3, param4, param5, param6, param7); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple7) + ? ((Tuple7) obj) + .map( + (v1, v2, v3, v4, v5, v6, v7) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3) + && Objects.equals(v4, param4) + && Objects.equals(v5, param5) + && Objects.equals(v6, param6) + && Objects.equals(v7, param7)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3, param4, param5, param6, param7); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + "," + + param4.toString() + + "," + + param5.toString() + + "," + + param6.toString() + + "," + + param7.toString() + + ")"; + } + }; + } + + static Tuple8 tuple( + final T1 param1, + final T2 param2, + final T3 param3, + final T4 param4, + final T5 param5, + final T6 param6, + final T7 param7, + final T8 param8) { + return new Tuple8<>() { + @Override + public T map(final FN8 mapper) { + return mapper.apply(param1, param2, param3, param4, param5, param6, param7, param8); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple8) + ? ((Tuple8) obj) + .map( + (v1, v2, v3, v4, v5, v6, v7, v8) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3) + && Objects.equals(v4, param4) + && Objects.equals(v5, param5) + && Objects.equals(v6, param6) + && Objects.equals(v7, param7) + && Objects.equals(v8, param8)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3, param4, param5, param6, param7, param8); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + "," + + param4.toString() + + "," + + param5.toString() + + "," + + param6.toString() + + "," + + param7.toString() + + "," + + param8.toString() + + ")"; + } + }; + } + + static Tuple9 tuple( + final T1 param1, + final T2 param2, + final T3 param3, + final T4 param4, + final T5 param5, + final T6 param6, + final T7 param7, + final T8 param8, + final T9 param9) { + return new Tuple9<>() { + @Override + public T map(final FN9 mapper) { + return mapper.apply(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + return (obj instanceof Tuple9) + ? ((Tuple9) obj) + .map( + (v1, v2, v3, v4, v5, v6, v7, v8, v9) -> + Objects.equals(v1, param1) + && Objects.equals(v2, param2) + && Objects.equals(v3, param3) + && Objects.equals(v4, param4) + && Objects.equals(v5, param5) + && Objects.equals(v6, param6) + && Objects.equals(v7, param7) + && Objects.equals(v8, param8) + && Objects.equals(v9, param9)) + : false; + } + + @Override + public int hashCode() { + return Objects.hash(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } + + @Override + public String toString() { + return "Tuple(" + + param1.toString() + + "," + + param2.toString() + + "," + + param3.toString() + + "," + + param4.toString() + + "," + + param5.toString() + + "," + + param6.toString() + + "," + + param7.toString() + + "," + + param8.toString() + + "," + + param9.toString() + + ")"; + } + }; + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/TestSetupUtils.java b/radixdlt-java-common/src/test/java/com/radixdlt/TestSetupUtils.java index 10e986d60b..44bd0ebf31 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/TestSetupUtils.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/TestSetupUtils.java @@ -1,134 +1,129 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt; - -import java.security.Security; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -/** - * Some utilities to help with initialisation of other sub-systems - * that tests use. - */ -public final class TestSetupUtils { - private static final int HEXDUMP_LINESIZE = 0x10; - private static final AtomicBoolean bouncyCastleInstalled = new AtomicBoolean(false); - - private TestSetupUtils() { - throw new IllegalStateException("Can't construct"); - } - - /** - * Install the Bouncy Castle crypto provider used for various hashing - * and symmetric/asymmetric key functions. - */ - public static void installBouncyCastleProvider() { - if (bouncyCastleInstalled.compareAndSet(false, true)) { - Security.insertProviderAt(new BouncyCastleProvider(), 1); - } - } - - /** - * Useful method for discovering why things went wrong - outputs - * a hexdump to {@code System.out}. - * - * @param bytes bytes to dump - */ - public static void hexdump(byte[] bytes) { - for (int index = 0; index < bytes.length; index += HEXDUMP_LINESIZE) { - int thisLen = Math.min(HEXDUMP_LINESIZE, bytes.length - index); - System.out.format("%04X:", index); - int ofs = 0; - for (; ofs < thisLen; ++ofs) { - if (ofs == HEXDUMP_LINESIZE / 2) { - System.out.format("-%02X", bytes[index + ofs] & 0xFF); - } else { - System.out.format(" %02X", bytes[index + ofs] & 0xFF); - } - } - while (ofs < HEXDUMP_LINESIZE) { - System.out.print(" "); - ofs += 1; - } - System.out.print(" |"); - for (ofs = 0; ofs < thisLen; ++ofs) { - System.out.print(toPrintable(bytes[index + ofs])); - } - while (ofs < HEXDUMP_LINESIZE) { - System.out.print(' '); - ofs += 1; - } - System.out.println('|'); - } - } - - private static char toPrintable(byte b) { - if (b >= 0x20 && b < 0x7F) { - return (char) b; - } - return '.'; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt; + +import java.security.Security; +import java.util.concurrent.atomic.AtomicBoolean; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** Some utilities to help with initialisation of other sub-systems that tests use. */ +public final class TestSetupUtils { + private static final int HEXDUMP_LINESIZE = 0x10; + private static final AtomicBoolean bouncyCastleInstalled = new AtomicBoolean(false); + + private TestSetupUtils() { + throw new IllegalStateException("Can't construct"); + } + + /** + * Install the Bouncy Castle crypto provider used for various hashing and symmetric/asymmetric key + * functions. + */ + public static void installBouncyCastleProvider() { + if (bouncyCastleInstalled.compareAndSet(false, true)) { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + } + + /** + * Useful method for discovering why things went wrong - outputs a hexdump to {@code System.out}. + * + * @param bytes bytes to dump + */ + public static void hexdump(byte[] bytes) { + for (int index = 0; index < bytes.length; index += HEXDUMP_LINESIZE) { + int thisLen = Math.min(HEXDUMP_LINESIZE, bytes.length - index); + System.out.format("%04X:", index); + int ofs = 0; + for (; ofs < thisLen; ++ofs) { + if (ofs == HEXDUMP_LINESIZE / 2) { + System.out.format("-%02X", bytes[index + ofs] & 0xFF); + } else { + System.out.format(" %02X", bytes[index + ofs] & 0xFF); + } + } + while (ofs < HEXDUMP_LINESIZE) { + System.out.print(" "); + ofs += 1; + } + System.out.print(" |"); + for (ofs = 0; ofs < thisLen; ++ofs) { + System.out.print(toPrintable(bytes[index + ofs])); + } + while (ofs < HEXDUMP_LINESIZE) { + System.out.print(' '); + ofs += 1; + } + System.out.println('|'); + } + } + + private static char toPrintable(byte b) { + if (b >= 0x20 && b < 0x7F) { + return (char) b; + } + return '.'; + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECDSASignatureTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECDSASignatureTest.java index b5a8bad859..fea9a989e0 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECDSASignatureTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECDSASignatureTest.java @@ -64,169 +64,213 @@ package com.radixdlt.crypto; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.radixdlt.TestSetupUtils; import com.radixdlt.utils.Bytes; -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; -import org.junit.BeforeClass; -import org.junit.Test; - import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; +import org.junit.BeforeClass; +import org.junit.Test; public class ECDSASignatureTest { - @BeforeClass - public static void startRadixTest() { - TestSetupUtils.installBouncyCastleProvider(); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(ECDSASignature.class) - .suppress(Warning.NONFINAL_FIELDS) // serialization prevents us from making `r` and `s` final. - .verify(); - } - - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException1() { - ECDSASignature.deserialize(null, BigInteger.ONE.toByteArray(), 1); - } - - @Test(expected = NullPointerException.class) - public void deserializationWithNullThrowsException2() { - ECDSASignature.deserialize(BigInteger.ONE.toByteArray(), null, 1); - } - - @Test - public void test_rfc6979_determinstic_signatures() throws Exception { - - /// Sanity checks of Signing implementation of RFC6979 - Deterministic usage of ECDSA: https://tools.ietf.org/html/rfc6979 - /// Test vectors: https://github.com/trezor/trezor-crypto/blob/957b8129bded180c8ac3106e61ff79a1a3df8893/tests/test_check.c#L1959-L1965 - /// Signature data from: https://github.com/oleganza/CoreBitcoin/blob/master/CoreBitcoinTestsOSX/BTCKeyTests.swift - - //CHECKSTYLE:OFF - //language=JSON - String rfc6979TestVectors = "[\n" + - " {\n" + - " \"expectedDer\": \"3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b3842022050" - + "09fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124\",\n" + - " \"expectedSignatureR\": \"af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b3842\",\n" + - " \"expectedSignatureS\": \"5009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124\",\n" + - " \"privateKey\": \"CCA9FBCC1B41E5A95D369EAA6DDCFF73B61A4EFAA279CFC6567E8DAA39CBAF50\",\n" + - " \"expectedPublicKeyCompressed\": \"0391f1ed66d63e12df118095ae010152f6cf65ffee656831f3000c28c4421d8e5b\",\n" + - " \"expectedK\": \"2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3\",\n" + - " \"message\": \"sample\",\n" + - " \"expectedHashOfMessage\": \"af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf\"\n" + - " },\n" + - " {\n" + - " \"expectedDer\": \"3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8022024" - + "42ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5\",\n" + - " \"expectedSignatureR\": \"934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8\",\n" + - " \"expectedSignatureS\": \"2442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5\",\n" + - " \"privateKey\": \"0000000000000000000000000000000000000000000000000000000000000001\",\n" + - " \"expectedPublicKeyCompressed\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n" + - " \"expectedK\": \"8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15\",\n" + - " \"message\": \"Satoshi Nakamoto\",\n" + - " \"expectedHashOfMessage\": \"a0dc65ffca799873cbea0ac274015b9526505daaaed385155425f7337704883e\"\n" + - " },\n" + - " {\n" + - " \"expectedDer\": \"3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b" - + "39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5\",\n" + - " \"expectedSignatureR\": \"fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d0\",\n" + - " \"expectedSignatureS\": \"6b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5\",\n" + - " \"privateKey\": \"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140\",\n" + - " \"expectedPublicKeyCompressed\": \"0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n" + - " \"expectedK\": \"33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90\",\n" + - " \"message\": \"Satoshi Nakamoto\",\n" + - " \"expectedHashOfMessage\": \"a0dc65ffca799873cbea0ac274015b9526505daaaed385155425f7337704883e\"\n" + - " },\n" + - " {\n" + - " \"expectedDer\": \"304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058df" - + "cc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea\",\n" + - " \"expectedSignatureR\": \"7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c\",\n" + - " \"expectedSignatureS\": \"58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea\",\n" + - " \"privateKey\": \"f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181\",\n" + - " \"expectedPublicKeyCompressed\": \"0292df7b245b81aa637ab4e867c8d511008f79161a97d64f2ac709600352f7acbc\",\n" + - " \"expectedK\": \"525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1\",\n" + - " \"message\": \"Alan Turing\",\n" + - " \"expectedHashOfMessage\": \"4ba38d48a60f1b29e9eb726eaff08b2e83d8d81e031666fee50e85900d7dc1ef\"\n" + - " },\n" + - " {\n" + - " \"expectedDer\": \"30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b022054" - + "7fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21\",\n" + - " \"expectedSignatureR\": \"8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b\",\n" + - " \"expectedSignatureS\": \"547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21\",\n" + - " \"privateKey\": \"0000000000000000000000000000000000000000000000000000000000000001\",\n" + - " \"expectedPublicKeyCompressed\": \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n" + - " \"expectedK\": \"38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3\",\n" + - " \"message\": \"All those moments will be lost in time, like tears in rain. Time to die...\",\n" + - " \"expectedHashOfMessage\": \"7d1833f54854ac51659521afcd0ec6dca2ce2351429614bfa28a756b1b3c637f\"\n" + - " },\n" + - " {\n" + - " \"expectedDer\": \"3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b022027" - + "9fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6\",\n" + - " \"expectedSignatureR\": \"b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b\",\n" + - " \"expectedSignatureS\": \"279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6\",\n" + - " \"privateKey\": \"e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2\",\n" + - " \"expectedPublicKeyCompressed\": \"03567b7512001f3cc4dcb8b8096c046fff571ab07adb2126cd42908f2ff1ca424a\",\n" + - " \"expectedK\": \"1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d\",\n" + - " \"message\": \"There is a computer disease that anybody who works with computers knows about. It's " - + "a very serious disease and it interferes completely with the work. The trouble with computers is " - + "that you 'play' with them!\",\n" + - " \"expectedHashOfMessage\": \"1609a53bb33ef00e0cc1e784b436d7924956d87ec2b399574378312f07cba3e8\"\n" + - " }\n" + - "]"; - //CHECKSTYLE:ON - - var vectors = new ObjectMapper() - .readValue(rfc6979TestVectors, new TypeReference>>() {}); - - assertEquals(6, vectors.size()); - - for (var vector : vectors) { - var keyPair = ECKeyPair.fromPrivateKey(Bytes.fromHexString(vector.get("privateKey"))); - var publicKey = keyPair.getPublicKey(); - - assertEquals(vector.get("expectedPublicKeyCompressed"), Bytes.toHexString(publicKey.getCompressedBytes())); - - var messageUnhashedPlaintext = vector.get("message"); - var messageUnhashed = messageUnhashedPlaintext.getBytes(StandardCharsets.UTF_8); - var message = sha2bits256Once(messageUnhashed); - - assertEquals(vector.get("expectedHashOfMessage"), Bytes.toHexString(message)); - - var signature = keyPair.sign(message, true, true); - - assertEquals(vector.get("expectedSignatureR"), signature.getR().toString(16)); - assertEquals(vector.get("expectedSignatureS"), signature.getS().toString(16)); - assertTrue("Should verify", publicKey.verify(message, signature)); - - var expectedSignatureDERBytes = Bytes.fromHexString(vector.get("expectedDer")); - var sigFromDER = ECDSASignature.decodeFromDER(expectedSignatureDERBytes); - - //Signature from DER has no `v` byte, so comparing using `equals` fails. - assertEquals(sigFromDER.getR(), signature.getR()); - assertEquals(sigFromDER.getS(), signature.getS()); - } - } - - private byte[] sha2bits256Once(byte[] data) { - MessageDigest hasher = null; - try { - hasher = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Should always be able to sha256 hash", e); - } - hasher.update(data); - return hasher.digest(); - } + @BeforeClass + public static void startRadixTest() { + TestSetupUtils.installBouncyCastleProvider(); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(ECDSASignature.class) + .suppress( + Warning.NONFINAL_FIELDS) // serialization prevents us from making `r` and `s` final. + .verify(); + } + + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException1() { + ECDSASignature.deserialize(null, BigInteger.ONE.toByteArray(), 1); + } + + @Test(expected = NullPointerException.class) + public void deserializationWithNullThrowsException2() { + ECDSASignature.deserialize(BigInteger.ONE.toByteArray(), null, 1); + } + + @Test + public void test_rfc6979_determinstic_signatures() throws Exception { + + /// Sanity checks of Signing implementation of RFC6979 - Deterministic usage of ECDSA: + // https://tools.ietf.org/html/rfc6979 + /// Test vectors: + // https://github.com/trezor/trezor-crypto/blob/957b8129bded180c8ac3106e61ff79a1a3df8893/tests/test_check.c#L1959-L1965 + /// Signature data from: + // https://github.com/oleganza/CoreBitcoin/blob/master/CoreBitcoinTestsOSX/BTCKeyTests.swift + + // CHECKSTYLE:OFF + // language=JSON + String rfc6979TestVectors = + "[\n" + + " {\n" + + " \"expectedDer\":" + + " \"3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124\",\n" + + " \"expectedSignatureR\":" + + " \"af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b3842\",\n" + + " \"expectedSignatureS\":" + + " \"5009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124\",\n" + + " \"privateKey\":" + + " \"CCA9FBCC1B41E5A95D369EAA6DDCFF73B61A4EFAA279CFC6567E8DAA39CBAF50\",\n" + + " \"expectedPublicKeyCompressed\":" + + " \"0391f1ed66d63e12df118095ae010152f6cf65ffee656831f3000c28c4421d8e5b\",\n" + + " \"expectedK\":" + + " \"2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3\",\n" + + " \"message\": \"sample\",\n" + + " \"expectedHashOfMessage\":" + + " \"af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf\"\n" + + " },\n" + + " {\n" + + " \"expectedDer\":" + + " \"3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5\",\n" + + " \"expectedSignatureR\":" + + " \"934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8\",\n" + + " \"expectedSignatureS\":" + + " \"2442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5\",\n" + + " \"privateKey\":" + + " \"0000000000000000000000000000000000000000000000000000000000000001\",\n" + + " \"expectedPublicKeyCompressed\":" + + " \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n" + + " \"expectedK\":" + + " \"8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15\",\n" + + " \"message\": \"Satoshi Nakamoto\",\n" + + " \"expectedHashOfMessage\":" + + " \"a0dc65ffca799873cbea0ac274015b9526505daaaed385155425f7337704883e\"\n" + + " },\n" + + " {\n" + + " \"expectedDer\":" + + " \"3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5\",\n" + + " \"expectedSignatureR\":" + + " \"fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d0\",\n" + + " \"expectedSignatureS\":" + + " \"6b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5\",\n" + + " \"privateKey\":" + + " \"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140\",\n" + + " \"expectedPublicKeyCompressed\":" + + " \"0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n" + + " \"expectedK\":" + + " \"33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90\",\n" + + " \"message\": \"Satoshi Nakamoto\",\n" + + " \"expectedHashOfMessage\":" + + " \"a0dc65ffca799873cbea0ac274015b9526505daaaed385155425f7337704883e\"\n" + + " },\n" + + " {\n" + + " \"expectedDer\":" + + " \"304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea\",\n" + + " \"expectedSignatureR\":" + + " \"7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c\",\n" + + " \"expectedSignatureS\":" + + " \"58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea\",\n" + + " \"privateKey\":" + + " \"f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181\",\n" + + " \"expectedPublicKeyCompressed\":" + + " \"0292df7b245b81aa637ab4e867c8d511008f79161a97d64f2ac709600352f7acbc\",\n" + + " \"expectedK\":" + + " \"525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1\",\n" + + " \"message\": \"Alan Turing\",\n" + + " \"expectedHashOfMessage\":" + + " \"4ba38d48a60f1b29e9eb726eaff08b2e83d8d81e031666fee50e85900d7dc1ef\"\n" + + " },\n" + + " {\n" + + " \"expectedDer\":" + + " \"30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21\",\n" + + " \"expectedSignatureR\":" + + " \"8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b\",\n" + + " \"expectedSignatureS\":" + + " \"547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21\",\n" + + " \"privateKey\":" + + " \"0000000000000000000000000000000000000000000000000000000000000001\",\n" + + " \"expectedPublicKeyCompressed\":" + + " \"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n" + + " \"expectedK\":" + + " \"38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3\",\n" + + " \"message\": \"All those moments will be lost in time, like tears in rain. Time" + + " to die...\",\n" + + " \"expectedHashOfMessage\":" + + " \"7d1833f54854ac51659521afcd0ec6dca2ce2351429614bfa28a756b1b3c637f\"\n" + + " },\n" + + " {\n" + + " \"expectedDer\":" + + " \"3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6\",\n" + + " \"expectedSignatureR\":" + + " \"b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b\",\n" + + " \"expectedSignatureS\":" + + " \"279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6\",\n" + + " \"privateKey\":" + + " \"e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2\",\n" + + " \"expectedPublicKeyCompressed\":" + + " \"03567b7512001f3cc4dcb8b8096c046fff571ab07adb2126cd42908f2ff1ca424a\",\n" + + " \"expectedK\":" + + " \"1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d\",\n" + + " \"message\": \"There is a computer disease that anybody who works with computers" + + " knows about. It's a very serious disease and it interferes completely with the" + + " work. The trouble with computers is that you 'play' with them!\",\n" + + " \"expectedHashOfMessage\":" + + " \"1609a53bb33ef00e0cc1e784b436d7924956d87ec2b399574378312f07cba3e8\"\n" + + " }\n" + + "]"; + // CHECKSTYLE:ON + + var vectors = + new ObjectMapper() + .readValue(rfc6979TestVectors, new TypeReference>>() {}); + + assertEquals(6, vectors.size()); + + for (var vector : vectors) { + var keyPair = ECKeyPair.fromPrivateKey(Bytes.fromHexString(vector.get("privateKey"))); + var publicKey = keyPair.getPublicKey(); + + assertEquals( + vector.get("expectedPublicKeyCompressed"), + Bytes.toHexString(publicKey.getCompressedBytes())); + + var messageUnhashedPlaintext = vector.get("message"); + var messageUnhashed = messageUnhashedPlaintext.getBytes(StandardCharsets.UTF_8); + var message = sha2bits256Once(messageUnhashed); + + assertEquals(vector.get("expectedHashOfMessage"), Bytes.toHexString(message)); + + var signature = keyPair.sign(message, true, true); + + assertEquals(vector.get("expectedSignatureR"), signature.getR().toString(16)); + assertEquals(vector.get("expectedSignatureS"), signature.getS().toString(16)); + assertTrue("Should verify", publicKey.verify(message, signature)); + + var expectedSignatureDERBytes = Bytes.fromHexString(vector.get("expectedDer")); + var sigFromDER = ECDSASignature.decodeFromDER(expectedSignatureDERBytes); + + // Signature from DER has no `v` byte, so comparing using `equals` fails. + assertEquals(sigFromDER.getR(), signature.getR()); + assertEquals(sigFromDER.getS(), signature.getS()); + } + } + + private byte[] sha2bits256Once(byte[] data) { + MessageDigest hasher = null; + try { + hasher = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Should always be able to sha256 hash", e); + } + hasher.update(data); + return hasher.digest(); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyPairTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyPairTest.java index 0297c547e0..98a42e635a 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyPairTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyPairTest.java @@ -1,177 +1,179 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.crypto; - -import org.junit.BeforeClass; -import org.junit.Test; - -import com.radixdlt.TestSetupUtils; - -import java.nio.charset.StandardCharsets; - -import nl.jqno.equalsverifier.EqualsVerifier; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - -public class ECKeyPairTest { - - @BeforeClass - public static void beforeClass() { - TestSetupUtils.installBouncyCastleProvider(); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(ECKeyPair.class) - .withIgnoredFields("publicKey") // public key is derived from used private key. - .verify(); - } - - @Test - public void checkKeyIntegrity() throws Exception { - final int iterations = 5000; - - for (int i = 0; i < iterations; i++) { - var key = ECKeyPair.generateNew(); - - var priv = key.getPrivateKey(); - var pub = key.getPublicKey().getBytes(); - - key = ECKeyPair.fromPrivateKey(priv); - - assertArrayEquals(priv, key.getPrivateKey()); - assertArrayEquals(pub, key.getPublicKey().getBytes()); - } - } - - @Test - public void signAndVerify() throws Exception { - final int iterations = 2000; - var helloWorld = "Hello World"; - - for (int i = 0; i < iterations; i++) { - var key = ECKeyPair.generateNew(); - var priv = key.getPrivateKey(); - var pub = key.getPublicKey().getBytes(); - - var keyPair = ECKeyPair.fromPrivateKey(priv); - var signature = keyPair.sign(HashUtils.sha256(helloWorld.getBytes(StandardCharsets.UTF_8)).asBytes()); - - var pubKey = ECPublicKey.fromBytes(pub); - assertTrue(pubKey.verify(HashUtils.sha256(helloWorld.getBytes(StandardCharsets.UTF_8)).asBytes(), signature)); - } - } - - @Test - public void checkKeyPairEquals() { - EqualsVerifier.forClass(ECKeyPair.class) - .withIgnoredFields("publicKey") // Computed - .verify(); - } - - @Test - public void when_generating_two_default_key_pairs__they_should_have_different_private_keys() { - var privateKey1 = ECKeyPair.generateNew().getPrivateKey(); - var privateKey2 = ECKeyPair.generateNew().getPrivateKey(); - - assertThat(privateKey1).isNotEqualTo(privateKey2); - } - - @Test - public void when_generating_two_key_pairs_from_same_seed__they_should_have_same_private_keys() { - var seed = "seed".getBytes(); - var privateKey1 = ECKeyPair.fromSeed(seed).getPrivateKey(); - var privateKey2 = ECKeyPair.fromSeed(seed).getPrivateKey(); - - assertThat(privateKey1).isEqualTo(privateKey2); - } - - @Test - public void when_signing_some_hash_with_a_seeded_key_pair__another_key_pair_from_same_seed_can_verify_the_signature() { - var seed = "seed".getBytes(); - var keyPair1 = ECKeyPair.fromSeed(seed); - var keyPair2 = ECKeyPair.fromSeed(seed); - - var hash1 = HashUtils.random256(); - var hash2 = HashUtils.random256(); - var signature1 = keyPair1.sign(hash1); - var signature2 = keyPair2.sign(hash2); - - // Assert that KeyPair1 can be used to verify the signature of Hash2 - assertTrue(keyPair1.getPublicKey().verify(hash2, signature2)); - - // Assert that KeyPair2 can be used to verify the signature of Hash1 - assertTrue(keyPair2.getPublicKey().verify(hash1, signature1)); - } - - @Test - public void validateSeedBeforeUse() { - assertThatThrownBy(() -> ECKeyPair.fromSeed(null)).isInstanceOf(NullPointerException.class); - assertThatThrownBy(() -> ECKeyPair.fromSeed(new byte[]{})).isInstanceOf(IllegalArgumentException.class); - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.crypto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + +import com.radixdlt.TestSetupUtils; +import java.nio.charset.StandardCharsets; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ECKeyPairTest { + + @BeforeClass + public static void beforeClass() { + TestSetupUtils.installBouncyCastleProvider(); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(ECKeyPair.class) + .withIgnoredFields("publicKey") // public key is derived from used private key. + .verify(); + } + + @Test + public void checkKeyIntegrity() throws Exception { + final int iterations = 5000; + + for (int i = 0; i < iterations; i++) { + var key = ECKeyPair.generateNew(); + + var priv = key.getPrivateKey(); + var pub = key.getPublicKey().getBytes(); + + key = ECKeyPair.fromPrivateKey(priv); + + assertArrayEquals(priv, key.getPrivateKey()); + assertArrayEquals(pub, key.getPublicKey().getBytes()); + } + } + + @Test + public void signAndVerify() throws Exception { + final int iterations = 2000; + var helloWorld = "Hello World"; + + for (int i = 0; i < iterations; i++) { + var key = ECKeyPair.generateNew(); + var priv = key.getPrivateKey(); + var pub = key.getPublicKey().getBytes(); + + var keyPair = ECKeyPair.fromPrivateKey(priv); + var signature = + keyPair.sign(HashUtils.sha256(helloWorld.getBytes(StandardCharsets.UTF_8)).asBytes()); + + var pubKey = ECPublicKey.fromBytes(pub); + assertTrue( + pubKey.verify( + HashUtils.sha256(helloWorld.getBytes(StandardCharsets.UTF_8)).asBytes(), signature)); + } + } + + @Test + public void checkKeyPairEquals() { + EqualsVerifier.forClass(ECKeyPair.class) + .withIgnoredFields("publicKey") // Computed + .verify(); + } + + @Test + public void when_generating_two_default_key_pairs__they_should_have_different_private_keys() { + var privateKey1 = ECKeyPair.generateNew().getPrivateKey(); + var privateKey2 = ECKeyPair.generateNew().getPrivateKey(); + + assertThat(privateKey1).isNotEqualTo(privateKey2); + } + + @Test + public void when_generating_two_key_pairs_from_same_seed__they_should_have_same_private_keys() { + var seed = "seed".getBytes(); + var privateKey1 = ECKeyPair.fromSeed(seed).getPrivateKey(); + var privateKey2 = ECKeyPair.fromSeed(seed).getPrivateKey(); + + assertThat(privateKey1).isEqualTo(privateKey2); + } + + @Test + public void + when_signing_some_hash_with_a_seeded_key_pair__another_key_pair_from_same_seed_can_verify_the_signature() { + var seed = "seed".getBytes(); + var keyPair1 = ECKeyPair.fromSeed(seed); + var keyPair2 = ECKeyPair.fromSeed(seed); + + var hash1 = HashUtils.random256(); + var hash2 = HashUtils.random256(); + var signature1 = keyPair1.sign(hash1); + var signature2 = keyPair2.sign(hash2); + + // Assert that KeyPair1 can be used to verify the signature of Hash2 + assertTrue(keyPair1.getPublicKey().verify(hash2, signature2)); + + // Assert that KeyPair2 can be used to verify the signature of Hash1 + assertTrue(keyPair2.getPublicKey().verify(hash1, signature1)); + } + + @Test + public void validateSeedBeforeUse() { + assertThatThrownBy(() -> ECKeyPair.fromSeed(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ECKeyPair.fromSeed(new byte[] {})) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyUtilsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyUtilsTest.java index 344562d26d..3993f7523f 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyUtilsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECKeyUtilsTest.java @@ -64,174 +64,177 @@ package com.radixdlt.crypto; -import org.junit.Assert; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; - import java.math.BigInteger; import java.util.Arrays; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.Assert; +import org.junit.Test; public class ECKeyUtilsTest { - @Test - public void testGreaterOrEqualModulus() { - byte[] modulus = ECKeyUtils.adjustArray(ECKeyUtils.domain().getN().toByteArray(), ECKeyPair.BYTES); - assertEquals(ECKeyPair.BYTES, modulus.length); - assertTrue(ECKeyUtils.greaterOrEqualOrder(modulus)); - - byte[] goodKey = ECKeyUtils.adjustArray(ECKeyUtils.domain().getN().subtract(BigInteger.ONE).toByteArray(), ECKeyPair.BYTES); - assertFalse(ECKeyUtils.greaterOrEqualOrder(goodKey)); - - byte[] badKey = new byte[ECKeyPair.BYTES]; - Arrays.fill(badKey, (byte) 0xFF); - assertTrue(ECKeyUtils.greaterOrEqualOrder(badKey)); - } - - @Test(expected = IllegalArgumentException.class) - public void testGreaterOrEqualModulusFail() { - ECKeyUtils.greaterOrEqualOrder(new byte[1]); - } - - @Test - public void testAdjustArray() { - // Test that all smaller or equal lengths are padded correctly - for (int i = 0; i <= ECKeyPair.BYTES; i += 1) { - byte[] testArray = new byte[i]; - for (int j = 0; j < i; j += 1) { - testArray[j] = (byte) (255 - j); - } - byte[] paddedArray = ECKeyUtils.adjustArray(testArray, ECKeyPair.BYTES); - assertEquals(ECKeyPair.BYTES, paddedArray.length); - int padding = ECKeyPair.BYTES - i; - for (int j = 0; j < i; j += 1) { - // Long constants because there is no assertEquals(int, int) - assertEquals(255L - j, paddedArray[padding + j] & 0xFFL); - } - } - // Test that longer length is truncated correctly - byte[] testArray = new byte[ECKeyPair.BYTES + 1]; - for (int i = 0; i < ECKeyPair.BYTES; i += 1) { - testArray[i + 1] = (byte) (255 - i); - } - byte[] truncatedArray = ECKeyUtils.adjustArray(testArray, ECKeyPair.BYTES); - assertEquals(ECKeyPair.BYTES, truncatedArray.length); - for (int i = 0; i < ECKeyPair.BYTES; i += 1) { - // Long constants because there is no assertEquals(int, int) - assertEquals(255L - i, truncatedArray[i] & 0xFFL); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testAdjustArrayFail() { - // Test that longer length without leading zeros throws exception - byte[] testArray = new byte[ECKeyPair.BYTES + 1]; - Arrays.fill(testArray, (byte) 1); - ECKeyUtils.adjustArray(testArray, ECKeyPair.BYTES); - } - - @Test(expected = PublicKeyException.class) - public void testValidatePublicFailForNullInput() throws PublicKeyException { - ECKeyUtils.validatePublic(null); - } - - @Test(expected = PublicKeyException.class) - public void testValidatePublicFailForEmptyInput() throws PublicKeyException { - ECKeyUtils.validatePublic(new byte[]{}); - } - - @Test - public void testValidatePublicPassForType2Key() throws PublicKeyException { - var key = new byte[ECPublicKey.COMPRESSED_BYTES]; - key[0] = 0x02; - ECKeyUtils.validatePublic(key); - } - - @Test(expected = PublicKeyException.class) - public void testValidatePublicFailForType2Key() throws PublicKeyException { - var key = new byte[ECPublicKey.COMPRESSED_BYTES + 1]; - key[0] = 0x02; - ECKeyUtils.validatePublic(key); - } - - @Test - public void testValidatePublicPassForType3Key() throws PublicKeyException { - var key = new byte[ECPublicKey.COMPRESSED_BYTES]; - key[0] = 0x03; - ECKeyUtils.validatePublic(key); - } - - @Test(expected = PublicKeyException.class) - public void testValidatePublicFailForType3Key() throws PublicKeyException { - var key = new byte[ECPublicKey.COMPRESSED_BYTES + 1]; - key[0] = 0x03; - ECKeyUtils.validatePublic(key); - } - - @Test - public void testValidatePublicPassForType4Key() throws PublicKeyException { - var key = new byte[ECPublicKey.UNCOMPRESSED_BYTES]; - key[0] = 0x04; - ECKeyUtils.validatePublic(key); - } - - @Test(expected = PublicKeyException.class) - public void testValidatePublicFailForType4Key() throws PublicKeyException { - var key = new byte[ECPublicKey.UNCOMPRESSED_BYTES + 1]; - key[0] = 0x04; - ECKeyUtils.validatePublic(key); - } - - @Test(expected = PublicKeyException.class) - public void testValidatePublicFailForUnknownTypeKey() throws PublicKeyException { - var key = new byte[ECPublicKey.UNCOMPRESSED_BYTES]; - key[0] = 0x05; - ECKeyUtils.validatePublic(key); - } - - @Test(expected = PrivateKeyException.class) - public void testValidatePrivateFailForNullInput() throws PrivateKeyException { - ECKeyUtils.validatePrivate(null); - } - - @Test(expected = PrivateKeyException.class) - public void testValidatePrivateFailForEmptyInput() throws PrivateKeyException { - ECKeyUtils.validatePrivate(new byte[]{}); - } - - @Test(expected = PrivateKeyException.class) - public void testValidatePrivateFailForShortInput() throws PrivateKeyException { - ECKeyUtils.validatePrivate(new byte[ECKeyPair.BYTES - 1]); - } - - @Test(expected = PrivateKeyException.class) - public void testValidatePrivateFailForZeroBytes() throws PrivateKeyException { - ECKeyUtils.validatePrivate(new byte[ECKeyPair.BYTES]); - } - - @Test(expected = PrivateKeyException.class) - public void testValidatePrivateFailForIncorrectOrder() throws PrivateKeyException { - var key = new byte[ECKeyPair.BYTES]; - Arrays.fill(key, (byte) -1); - ECKeyUtils.validatePrivate(key); - } - - @Test - public void testToRecoverable() throws PublicKeyException { - var keyPair = ECKeyPair.generateNew(); - var hash = HashUtils.transactionIdHash("123456".getBytes()); - var signature = keyPair.sign(hash); - - ECKeyUtils.toRecoverable(signature, hash.asBytes(), keyPair.getPublicKey()) - .onFailureDo(Assert::fail) - .onSuccess(sig -> ECPublicKey.recoverFrom(hash, sig).ifPresentOrElse( - recovered -> assertEquals(recovered, keyPair.getPublicKey()), - Assert::fail - )); - } + @Test + public void testGreaterOrEqualModulus() { + byte[] modulus = + ECKeyUtils.adjustArray(ECKeyUtils.domain().getN().toByteArray(), ECKeyPair.BYTES); + assertEquals(ECKeyPair.BYTES, modulus.length); + assertTrue(ECKeyUtils.greaterOrEqualOrder(modulus)); + + byte[] goodKey = + ECKeyUtils.adjustArray( + ECKeyUtils.domain().getN().subtract(BigInteger.ONE).toByteArray(), ECKeyPair.BYTES); + assertFalse(ECKeyUtils.greaterOrEqualOrder(goodKey)); + + byte[] badKey = new byte[ECKeyPair.BYTES]; + Arrays.fill(badKey, (byte) 0xFF); + assertTrue(ECKeyUtils.greaterOrEqualOrder(badKey)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGreaterOrEqualModulusFail() { + ECKeyUtils.greaterOrEqualOrder(new byte[1]); + } + + @Test + public void testAdjustArray() { + // Test that all smaller or equal lengths are padded correctly + for (int i = 0; i <= ECKeyPair.BYTES; i += 1) { + byte[] testArray = new byte[i]; + for (int j = 0; j < i; j += 1) { + testArray[j] = (byte) (255 - j); + } + byte[] paddedArray = ECKeyUtils.adjustArray(testArray, ECKeyPair.BYTES); + assertEquals(ECKeyPair.BYTES, paddedArray.length); + int padding = ECKeyPair.BYTES - i; + for (int j = 0; j < i; j += 1) { + // Long constants because there is no assertEquals(int, int) + assertEquals(255L - j, paddedArray[padding + j] & 0xFFL); + } + } + // Test that longer length is truncated correctly + byte[] testArray = new byte[ECKeyPair.BYTES + 1]; + for (int i = 0; i < ECKeyPair.BYTES; i += 1) { + testArray[i + 1] = (byte) (255 - i); + } + byte[] truncatedArray = ECKeyUtils.adjustArray(testArray, ECKeyPair.BYTES); + assertEquals(ECKeyPair.BYTES, truncatedArray.length); + for (int i = 0; i < ECKeyPair.BYTES; i += 1) { + // Long constants because there is no assertEquals(int, int) + assertEquals(255L - i, truncatedArray[i] & 0xFFL); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testAdjustArrayFail() { + // Test that longer length without leading zeros throws exception + byte[] testArray = new byte[ECKeyPair.BYTES + 1]; + Arrays.fill(testArray, (byte) 1); + ECKeyUtils.adjustArray(testArray, ECKeyPair.BYTES); + } + + @Test(expected = PublicKeyException.class) + public void testValidatePublicFailForNullInput() throws PublicKeyException { + ECKeyUtils.validatePublic(null); + } + + @Test(expected = PublicKeyException.class) + public void testValidatePublicFailForEmptyInput() throws PublicKeyException { + ECKeyUtils.validatePublic(new byte[] {}); + } + + @Test + public void testValidatePublicPassForType2Key() throws PublicKeyException { + var key = new byte[ECPublicKey.COMPRESSED_BYTES]; + key[0] = 0x02; + ECKeyUtils.validatePublic(key); + } + + @Test(expected = PublicKeyException.class) + public void testValidatePublicFailForType2Key() throws PublicKeyException { + var key = new byte[ECPublicKey.COMPRESSED_BYTES + 1]; + key[0] = 0x02; + ECKeyUtils.validatePublic(key); + } + + @Test + public void testValidatePublicPassForType3Key() throws PublicKeyException { + var key = new byte[ECPublicKey.COMPRESSED_BYTES]; + key[0] = 0x03; + ECKeyUtils.validatePublic(key); + } + + @Test(expected = PublicKeyException.class) + public void testValidatePublicFailForType3Key() throws PublicKeyException { + var key = new byte[ECPublicKey.COMPRESSED_BYTES + 1]; + key[0] = 0x03; + ECKeyUtils.validatePublic(key); + } + + @Test + public void testValidatePublicPassForType4Key() throws PublicKeyException { + var key = new byte[ECPublicKey.UNCOMPRESSED_BYTES]; + key[0] = 0x04; + ECKeyUtils.validatePublic(key); + } + + @Test(expected = PublicKeyException.class) + public void testValidatePublicFailForType4Key() throws PublicKeyException { + var key = new byte[ECPublicKey.UNCOMPRESSED_BYTES + 1]; + key[0] = 0x04; + ECKeyUtils.validatePublic(key); + } + + @Test(expected = PublicKeyException.class) + public void testValidatePublicFailForUnknownTypeKey() throws PublicKeyException { + var key = new byte[ECPublicKey.UNCOMPRESSED_BYTES]; + key[0] = 0x05; + ECKeyUtils.validatePublic(key); + } + + @Test(expected = PrivateKeyException.class) + public void testValidatePrivateFailForNullInput() throws PrivateKeyException { + ECKeyUtils.validatePrivate(null); + } + + @Test(expected = PrivateKeyException.class) + public void testValidatePrivateFailForEmptyInput() throws PrivateKeyException { + ECKeyUtils.validatePrivate(new byte[] {}); + } + + @Test(expected = PrivateKeyException.class) + public void testValidatePrivateFailForShortInput() throws PrivateKeyException { + ECKeyUtils.validatePrivate(new byte[ECKeyPair.BYTES - 1]); + } + + @Test(expected = PrivateKeyException.class) + public void testValidatePrivateFailForZeroBytes() throws PrivateKeyException { + ECKeyUtils.validatePrivate(new byte[ECKeyPair.BYTES]); + } + + @Test(expected = PrivateKeyException.class) + public void testValidatePrivateFailForIncorrectOrder() throws PrivateKeyException { + var key = new byte[ECKeyPair.BYTES]; + Arrays.fill(key, (byte) -1); + ECKeyUtils.validatePrivate(key); + } + + @Test + public void testToRecoverable() throws PublicKeyException { + var keyPair = ECKeyPair.generateNew(); + var hash = HashUtils.transactionIdHash("123456".getBytes()); + var signature = keyPair.sign(hash); + + ECKeyUtils.toRecoverable(signature, hash.asBytes(), keyPair.getPublicKey()) + .onFailureDo(Assert::fail) + .onSuccess( + sig -> + ECPublicKey.recoverFrom(hash, sig) + .ifPresentOrElse( + recovered -> assertEquals(recovered, keyPair.getPublicKey()), + Assert::fail)); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECPublicKeyTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECPublicKeyTest.java index 5ba46f59a6..c740035df4 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECPublicKeyTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/ECPublicKeyTest.java @@ -71,17 +71,17 @@ import org.junit.Test; public class ECPublicKeyTest { - @Test - public void equalsContract() throws PublicKeyException { - final var p1 = ECKeyPair.generateNew().getPublicKey().getEcPoint(); - final var p2 = ECKeyPair.generateNew().getPublicKey().getEcPoint(); - var key = Bytes.fromBase64String("AtuRjZPGw0b0BIYx46e0iKCaFU5EPnPx7/wLk6Vcursg"); - ECPublicKey pk = ECPublicKey.fromBytes(key); - EqualsVerifier.forClass(ECPublicKey.class) - .withNonnullFields("ecPoint", "compressed") - .withIgnoredFields("uid", "ecPoint", "uncompressedBytes") // cached value - .withPrefabValues(ECPoint.class, p1, p2) - .withCachedHashCode("hashCode", "computeHashCode", pk) - .verify(); - } + @Test + public void equalsContract() throws PublicKeyException { + final var p1 = ECKeyPair.generateNew().getPublicKey().getEcPoint(); + final var p2 = ECKeyPair.generateNew().getPublicKey().getEcPoint(); + var key = Bytes.fromBase64String("AtuRjZPGw0b0BIYx46e0iKCaFU5EPnPx7/wLk6Vcursg"); + ECPublicKey pk = ECPublicKey.fromBytes(key); + EqualsVerifier.forClass(ECPublicKey.class) + .withNonnullFields("ecPoint", "compressed") + .withIgnoredFields("uid", "ecPoint", "uncompressedBytes") // cached value + .withPrefabValues(ECPoint.class, p1, p2) + .withCachedHashCode("hashCode", "computeHashCode", pk) + .verify(); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/HashUtilsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/HashUtilsTest.java index 838a2b41e0..f60b0e1f85 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/HashUtilsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/HashUtilsTest.java @@ -64,15 +64,14 @@ package com.radixdlt.crypto; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + import com.google.common.base.Strings; import com.google.common.hash.HashCode; import com.radixdlt.TestSetupUtils; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.Longs; -import org.assertj.core.api.Assertions; -import org.junit.BeforeClass; -import org.junit.Test; - import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -80,186 +79,164 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import org.assertj.core.api.Assertions; +import org.junit.BeforeClass; +import org.junit.Test; public class HashUtilsTest { - @BeforeClass - public static void setupBouncyCastle() { - TestSetupUtils.installBouncyCastleProvider(); - } - - @Test - public void verify_that_hexstring_remains_the_same_as_passed_in_constructor() { - String hex = deadbeefString(); - HashCode hash = HashCode.fromString(hex); - assertEquals(hex, hash.toString()); - } - - @Test - public void verify_that_tobytearray_returns_same_bytes_as_passed_in_constructor() { - String hex = deadbeefString(); - HashCode hash = HashCode.fromString(hex); - byte[] expectedBytes = Bytes.fromHexString(hex); - assertArrayEquals(expectedBytes, hash.asBytes()); - } - - @Test - public void testHashValues256() { - assertArrayEquals( - Bytes.fromHexString("7ef0ca626bbb058dd443bb78e33b888bdec8295c96e51f5545f96370870c10b9"), - HashUtils.sha256(Longs.toByteArray(0L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString("3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284"), - HashUtils.sha256(Longs.toByteArray(1L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString("d03524a98786b780bd640979dc49316702799f46bb8029e29aaf3e7d3b8b7c3e"), - HashUtils.sha256(Longs.toByteArray(10L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString("43c1dd54b91ef646f74dc83f2238292bc3340e34cfdb2057aba0193a662409c5"), - HashUtils.sha256(Longs.toByteArray(100L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString("4bba9cc5d36a21b9d09cd16bbd83d0472f9b95afd2d7aa1621bb6fa580c99bfc"), - HashUtils.sha256(Longs.toByteArray(1_000L)).asBytes() - ); - } - - @Test - public void testHashValues512() { - assertArrayEquals( - Bytes.fromHexString( - "d6f117761cef5715fcb3fe49a3cf2ebb268acec9e9d87a1e8812a8aa811a1d02ed636b9d04694c160fd071e687772d0cc2e1" - + "c3990da4435409c7b1f7b87fa632" - ), - HashUtils.sha512(Longs.toByteArray(0L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString( - "ec9d8eba8da254c20b3681454bdb3425ba144b7d36421ceffa796bad78d7e66c3439c73a6bbb2d985883b0ff081cfa3ebbac" - + "90c580065bad0eb1e9bee74eb0c9" - ), - HashUtils.sha512(Longs.toByteArray(1L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString( - "78a037d80204606de0f166a625d3fdc81de417da21bf0f5d7c9b756d73a4decd770c349f21fd5141329d0f2a639c143b3094" - + "2cc044ff7d0d95209107c38e045c" - ), - HashUtils.sha512(Longs.toByteArray(10L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString( - "1cb75a3020fda027b89157eebde70134c281e719f700e84b9f12607b3ab3ae286c34c144e8b444eb0fd163948d00bcae900b" - + "2c08d263c7127fc1bf85f43c28a0" - ), - HashUtils.sha512(Longs.toByteArray(100L)).asBytes() - ); - assertArrayEquals( - Bytes.fromHexString( - "66c0a8301d6a3cc9ad2151af74ad748d10ecfe83a5b1c765a5e31c72916f238f1de2006f2fcb12f634948e13200f354c6f47" - + "fc183c7208c1a022575e761c4222" - ), - HashUtils.sha512(Longs.toByteArray(1_000L)).asBytes() - ); - } - - @Test - public void test_sha256_hash_as_reference_for_other_libraries() { - byte[] data = "Hello Radix".getBytes(StandardCharsets.UTF_8); - - byte[] singleHash = sha2bits256Once(data).asBytes(); - byte[] doubleHash = HashUtils.sha256(data).asBytes(); - - // These hashes as just the result of running the sha256 once and output the values - // These are then used as reference for other libraries, especially Swift which - // lacks native Sha256 methods. - assertEquals( - "374d9dc94c1252acf828cdfb94946cf808cb112aa9760a2e6216c14b4891f934", - Bytes.toHexString(singleHash) - ); - assertEquals( - "fd6be8b4b12276857ac1b63594bf38c01327bd6e8ae0eb4b0c6e253563cc8cc7", - Bytes.toHexString(doubleHash) - ); - } - - @Test - public void verify_hash256_once() { - testEncodeToHashedBytesFromString( - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - this::sha2bits256Once, - "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" - ); - } - - @Test - public void verify_hash256_twice() { - testEncodeToHashedBytesFromString( - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - HashUtils::sha256, - "0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af" - ); - } - - @Test - public void birthday_attack_test() { - // 32-bit hashes + 300000 hashes will have a collision 99.997% of the time. - // Practically high enough to catch any issues we have with hash collisions - final int numHashes = 300000; - Set hashes = Stream.generate(() -> HashUtils.random256()) - .limit(numHashes) - .collect(Collectors.toSet()); - - Assertions.assertThat(hashes) - .hasSize(numHashes) - .doesNotContainNull(); - } - - @Test - public void zero_should_indeed_be_zero() { - byte[] output = HashUtils.zero256().asBytes(); - assertEquals(32, output.length); - for (int i = 0; i < output.length; i++) { - assertEquals((byte) 0, output[i]); - } - } - - @Test - public void compare_hash_codes() { - assertEquals(-1, HashUtils.compare(HashCode.fromLong(1), HashCode.fromLong(2))); - assertEquals(0, HashUtils.compare(HashCode.fromLong(3), HashCode.fromLong(3))); - assertEquals(1, HashUtils.compare(HashCode.fromLong(5), HashCode.fromLong(4))); - } - - private void testEncodeToHashedBytesFromString(String message, Function hashFunction, String expectedHashHex) { - byte[] unhashedData = message.getBytes(StandardCharsets.UTF_8); - HashCode hashedData = hashFunction.apply(unhashedData); - assertEquals(expectedHashHex, Bytes.toHexString(hashedData.asBytes())); - } - - private HashCode deadbeef() { - return HashCode.fromString(deadbeefString()); - } - - private String deadbeefString() { - return Strings.repeat("deadbeef", 8); - } - - private HashCode sha2bits256Once(byte[] data) { - MessageDigest hasher = null; - try { - hasher = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Should always be able to sha256 hash", e); - } - hasher.update(data); - return HashCode.fromBytes(hasher.digest()); - } + @BeforeClass + public static void setupBouncyCastle() { + TestSetupUtils.installBouncyCastleProvider(); + } + + @Test + public void verify_that_hexstring_remains_the_same_as_passed_in_constructor() { + String hex = deadbeefString(); + HashCode hash = HashCode.fromString(hex); + assertEquals(hex, hash.toString()); + } + + @Test + public void verify_that_tobytearray_returns_same_bytes_as_passed_in_constructor() { + String hex = deadbeefString(); + HashCode hash = HashCode.fromString(hex); + byte[] expectedBytes = Bytes.fromHexString(hex); + assertArrayEquals(expectedBytes, hash.asBytes()); + } + + @Test + public void testHashValues256() { + assertArrayEquals( + Bytes.fromHexString("7ef0ca626bbb058dd443bb78e33b888bdec8295c96e51f5545f96370870c10b9"), + HashUtils.sha256(Longs.toByteArray(0L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString("3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284"), + HashUtils.sha256(Longs.toByteArray(1L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString("d03524a98786b780bd640979dc49316702799f46bb8029e29aaf3e7d3b8b7c3e"), + HashUtils.sha256(Longs.toByteArray(10L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString("43c1dd54b91ef646f74dc83f2238292bc3340e34cfdb2057aba0193a662409c5"), + HashUtils.sha256(Longs.toByteArray(100L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString("4bba9cc5d36a21b9d09cd16bbd83d0472f9b95afd2d7aa1621bb6fa580c99bfc"), + HashUtils.sha256(Longs.toByteArray(1_000L)).asBytes()); + } + + @Test + public void testHashValues512() { + assertArrayEquals( + Bytes.fromHexString( + "d6f117761cef5715fcb3fe49a3cf2ebb268acec9e9d87a1e8812a8aa811a1d02ed636b9d04694c160fd071e687772d0cc2e1" + + "c3990da4435409c7b1f7b87fa632"), + HashUtils.sha512(Longs.toByteArray(0L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString( + "ec9d8eba8da254c20b3681454bdb3425ba144b7d36421ceffa796bad78d7e66c3439c73a6bbb2d985883b0ff081cfa3ebbac" + + "90c580065bad0eb1e9bee74eb0c9"), + HashUtils.sha512(Longs.toByteArray(1L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString( + "78a037d80204606de0f166a625d3fdc81de417da21bf0f5d7c9b756d73a4decd770c349f21fd5141329d0f2a639c143b3094" + + "2cc044ff7d0d95209107c38e045c"), + HashUtils.sha512(Longs.toByteArray(10L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString( + "1cb75a3020fda027b89157eebde70134c281e719f700e84b9f12607b3ab3ae286c34c144e8b444eb0fd163948d00bcae900b" + + "2c08d263c7127fc1bf85f43c28a0"), + HashUtils.sha512(Longs.toByteArray(100L)).asBytes()); + assertArrayEquals( + Bytes.fromHexString( + "66c0a8301d6a3cc9ad2151af74ad748d10ecfe83a5b1c765a5e31c72916f238f1de2006f2fcb12f634948e13200f354c6f47" + + "fc183c7208c1a022575e761c4222"), + HashUtils.sha512(Longs.toByteArray(1_000L)).asBytes()); + } + + @Test + public void test_sha256_hash_as_reference_for_other_libraries() { + byte[] data = "Hello Radix".getBytes(StandardCharsets.UTF_8); + + byte[] singleHash = sha2bits256Once(data).asBytes(); + byte[] doubleHash = HashUtils.sha256(data).asBytes(); + + // These hashes as just the result of running the sha256 once and output the values + // These are then used as reference for other libraries, especially Swift which + // lacks native Sha256 methods. + assertEquals( + "374d9dc94c1252acf828cdfb94946cf808cb112aa9760a2e6216c14b4891f934", + Bytes.toHexString(singleHash)); + assertEquals( + "fd6be8b4b12276857ac1b63594bf38c01327bd6e8ae0eb4b0c6e253563cc8cc7", + Bytes.toHexString(doubleHash)); + } + + @Test + public void verify_hash256_once() { + testEncodeToHashedBytesFromString( + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + this::sha2bits256Once, + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); + } + + @Test + public void verify_hash256_twice() { + testEncodeToHashedBytesFromString( + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + HashUtils::sha256, + "0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af"); + } + + @Test + public void birthday_attack_test() { + // 32-bit hashes + 300000 hashes will have a collision 99.997% of the time. + // Practically high enough to catch any issues we have with hash collisions + final int numHashes = 300000; + Set hashes = + Stream.generate(() -> HashUtils.random256()).limit(numHashes).collect(Collectors.toSet()); + + Assertions.assertThat(hashes).hasSize(numHashes).doesNotContainNull(); + } + + @Test + public void zero_should_indeed_be_zero() { + byte[] output = HashUtils.zero256().asBytes(); + assertEquals(32, output.length); + for (int i = 0; i < output.length; i++) { + assertEquals((byte) 0, output[i]); + } + } + + @Test + public void compare_hash_codes() { + assertEquals(-1, HashUtils.compare(HashCode.fromLong(1), HashCode.fromLong(2))); + assertEquals(0, HashUtils.compare(HashCode.fromLong(3), HashCode.fromLong(3))); + assertEquals(1, HashUtils.compare(HashCode.fromLong(5), HashCode.fromLong(4))); + } + + private void testEncodeToHashedBytesFromString( + String message, Function hashFunction, String expectedHashHex) { + byte[] unhashedData = message.getBytes(StandardCharsets.UTF_8); + HashCode hashedData = hashFunction.apply(unhashedData); + assertEquals(expectedHashHex, Bytes.toHexString(hashedData.asBytes())); + } + + private HashCode deadbeef() { + return HashCode.fromString(deadbeefString()); + } + + private String deadbeefString() { + return Strings.repeat("deadbeef", 8); + } + + private HashCode sha2bits256Once(byte[] data) { + MessageDigest hasher = null; + try { + hasher = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Should always be able to sha256 hash", e); + } + hasher.update(data); + return HashCode.fromBytes(hasher.digest()); + } } - diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/RadixKeyStoreTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/RadixKeyStoreTest.java index 1e13cbd03e..71e37d272c 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/RadixKeyStoreTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/RadixKeyStoreTest.java @@ -64,15 +64,17 @@ package com.radixdlt.crypto; -import org.bouncycastle.jcajce.PKCS12Key; -import org.junit.BeforeClass; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import com.radixdlt.TestSetupUtils; import com.radixdlt.crypto.exception.KeyStoreException; import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -86,222 +88,197 @@ import java.security.cert.Certificate; import java.security.spec.ECGenParameterSpec; import java.util.stream.IntStream; - import javax.crypto.SecretKey; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.bouncycastle.jcajce.PKCS12Key; +import org.junit.BeforeClass; +import org.junit.Test; public class RadixKeyStoreTest { - private static final String TEST_SECRET = "secret"; - private static final String TEST_KS_FILENAME = "testfile.ks"; - - @BeforeClass - public static void setup() { - TestSetupUtils.installBouncyCastleProvider(); - } - - /** - * Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. - */ - @Test - public void testFromFileCreate() throws IOException, KeyStoreException { - File file = newFile(TEST_KS_FILENAME); - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { - assertTrue(file.exists()); - assertTrue(ks.toString().contains(file.toString())); - } - } - - /** - * Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. - */ - @Test - public void testFromFileNotFound() throws IOException { - File file = newFile(TEST_KS_FILENAME); - assertThatThrownBy(() -> RadixKeyStore.fromFile(file, null, false)) - .isInstanceOf(FileNotFoundException.class); - } - - /** - * Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. - */ - @Test - public void testFromFileReload1() throws IOException, KeyStoreException, PrivateKeyException, PublicKeyException { - File file = newFile(TEST_KS_FILENAME); - ECKeyPair kp1 = ECKeyPair.generateNew(); - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { - assertTrue(file.exists()); - ks.writeKeyPair("test", kp1); - } - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, false)) { - ECKeyPair kp2 = ks.readKeyPair("test", false); - assertArrayEquals(kp1.getPrivateKey(), kp2.getPrivateKey()); - assertArrayEquals(kp1.getPublicKey().getBytes(), kp2.getPublicKey().getBytes()); - } - } - - /** - * Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. - */ - @Test - public void testFromFileReload2() throws IOException, KeyStoreException, PrivateKeyException, PublicKeyException { - File file = newFile(TEST_KS_FILENAME); - final ECKeyPair kp1; - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { - assertTrue(file.exists()); - kp1 = ks.readKeyPair("test", true); - } - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, new char[0], false)) { - ECKeyPair kp2 = ks.readKeyPair("test", false); - assertArrayEquals(kp1.getPrivateKey(), kp2.getPrivateKey()); - assertArrayEquals(kp1.getPublicKey().getBytes(), kp2.getPublicKey().getBytes()); - } - } - - /** - * Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. - */ - @Test - public void testFromFileWrongFilePassword() throws IOException, KeyStoreException { - File file = newFile(TEST_KS_FILENAME); - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, "secret1".toCharArray(), true)) { - assertTrue(file.exists()); - assertTrue(ks.toString().contains(file.toString())); - } - assertThatThrownBy(() -> RadixKeyStore.fromFile(file, "secret2".toCharArray(), false)) - .isInstanceOf(IOException.class) - .hasMessageContaining("wrong password") - .hasNoCause(); - } - - /** - * Test method for {@link RadixKeyStore#readKeyPair(String, boolean)}. - */ - @Test - public void testReadKeyPairFail() throws IOException, KeyStoreException { - File file = newFile(TEST_KS_FILENAME); - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { - assertTrue(file.exists()); - assertThatThrownBy(() -> ks.readKeyPair("notexist", false)) - .isInstanceOf(KeyStoreException.class) - .hasMessageContaining("No such entry") - .hasNoCause(); - } - } - - /** - * Test method for {@link RadixKeyStore#readKeyPair(String, boolean)}. - */ - @Test - public void testReadKeyPairSuccess() throws IOException, KeyStoreException, PrivateKeyException, PublicKeyException { - final File file = newFile("keystore.ks"); - final var originalKeypair = ECKeyPair.generateNew(); - final var storePassword = "nopass".toCharArray(); - final var keyPairName = "node"; - - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, storePassword, true)) { - assertTrue(file.exists()); - - ks.writeKeyPair(keyPairName, originalKeypair); - } - - final File renamedFile = new File(TEST_KS_FILENAME); - file.renameTo(renamedFile); - - try (RadixKeyStore ks = RadixKeyStore.fromFile(renamedFile, storePassword, false)) { - var loadedKeypair = ks.readKeyPair(keyPairName, false); - assertThat(loadedKeypair).isEqualTo(originalKeypair); - } - } - - /** - * Test method for {@link RadixKeyStore#close()}. - */ - @Test - public void testClose() throws IOException, KeyStoreException { - File file = newFile(TEST_KS_FILENAME); - @SuppressWarnings("resource") - RadixKeyStore ks = RadixKeyStore.fromFile(file, "testpassword".toCharArray(), true); - assertTrue(file.exists()); - ks.close(); - char[] pwd = ks.storePassword(); - assertEquals(12, pwd.length); - assertTrue(IntStream.range(0, pwd.length).map(i -> pwd[i]).allMatch(i -> i == ' ')); - } - - /** - * Test method for {@link RadixKeyStore#toString()}. - */ - @Test - public void testToString() throws IOException, KeyStoreException { - File file = newFile(TEST_KS_FILENAME); - try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { - assertThat(ks.toString()).contains(file.toString()); - assertThat(ks.toString()).contains(RadixKeyStore.class.getSimpleName()); - } - } - - /** - * Test method for ensuring that only secp256k1 keys are allowed. - */ - @Test - public void testInvalidECKey() throws IOException, GeneralSecurityException, KeyStoreException { - File file = newFile(TEST_KS_FILENAME); - - KeyStore jks = KeyStore.getInstance("pkcs12"); - jks.load(null, TEST_SECRET.toCharArray()); - - // Only secp256k1 curve is supported - use a different curve here - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom()); - - KeyPair keyPair = keyGen.generateKeyPair(); - PrivateKey privKey = keyPair.getPrivate(); - - Certificate[] chain = { - RadixKeyStore.selfSignedCert(keyPair, 1000, "CN=Invalid") - }; - - KeyStore.PrivateKeyEntry entry = new KeyStore.PrivateKeyEntry(privKey, chain); - jks.setEntry("test", entry, new KeyStore.PasswordProtection(new char[0])); - - try (RadixKeyStore rks = new RadixKeyStore(file, jks, TEST_SECRET.toCharArray())) { - assertThatThrownBy(() -> rks.readKeyPair("test", false)) - .isInstanceOf(KeyStoreException.class) - .hasMessageContaining("Unknown curve") - .hasNoCause(); - } - } - - /** - * Test method for ensuring that only PrivateKeyEntry values are allowed. - */ - @Test - public void testInvalidEntry() throws IOException, GeneralSecurityException { - KeyStore jks = KeyStore.getInstance("pkcs12"); - jks.load(null, "password".toCharArray()); - - SecretKey sk = new PKCS12Key(TEST_SECRET.toCharArray()); - KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(sk); - - assertThatThrownBy(() -> jks.setEntry("test", entry, new KeyStore.PasswordProtection(new char[0]))) - .isInstanceOf(java.security.KeyStoreException.class) - .hasMessageContaining("PKCS12 does not support non-PrivateKeys") - .hasNoCause(); - } - - private static File newFile(String filename) throws IOException { - File file = new File(filename); - if (!Files.deleteIfExists(file.toPath())) { - // In this case we are fine if "file" does not exist and wasn't deleted. - } - assertFalse(file.exists()); - return file; - } + private static final String TEST_SECRET = "secret"; + private static final String TEST_KS_FILENAME = "testfile.ks"; + + @BeforeClass + public static void setup() { + TestSetupUtils.installBouncyCastleProvider(); + } + + /** Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. */ + @Test + public void testFromFileCreate() throws IOException, KeyStoreException { + File file = newFile(TEST_KS_FILENAME); + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { + assertTrue(file.exists()); + assertTrue(ks.toString().contains(file.toString())); + } + } + + /** Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. */ + @Test + public void testFromFileNotFound() throws IOException { + File file = newFile(TEST_KS_FILENAME); + assertThatThrownBy(() -> RadixKeyStore.fromFile(file, null, false)) + .isInstanceOf(FileNotFoundException.class); + } + + /** Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. */ + @Test + public void testFromFileReload1() + throws IOException, KeyStoreException, PrivateKeyException, PublicKeyException { + File file = newFile(TEST_KS_FILENAME); + ECKeyPair kp1 = ECKeyPair.generateNew(); + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { + assertTrue(file.exists()); + ks.writeKeyPair("test", kp1); + } + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, false)) { + ECKeyPair kp2 = ks.readKeyPair("test", false); + assertArrayEquals(kp1.getPrivateKey(), kp2.getPrivateKey()); + assertArrayEquals(kp1.getPublicKey().getBytes(), kp2.getPublicKey().getBytes()); + } + } + + /** Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. */ + @Test + public void testFromFileReload2() + throws IOException, KeyStoreException, PrivateKeyException, PublicKeyException { + File file = newFile(TEST_KS_FILENAME); + final ECKeyPair kp1; + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { + assertTrue(file.exists()); + kp1 = ks.readKeyPair("test", true); + } + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, new char[0], false)) { + ECKeyPair kp2 = ks.readKeyPair("test", false); + assertArrayEquals(kp1.getPrivateKey(), kp2.getPrivateKey()); + assertArrayEquals(kp1.getPublicKey().getBytes(), kp2.getPublicKey().getBytes()); + } + } + + /** Test method for {@link RadixKeyStore#fromFile(java.io.File, char[], boolean)}. */ + @Test + public void testFromFileWrongFilePassword() throws IOException, KeyStoreException { + File file = newFile(TEST_KS_FILENAME); + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, "secret1".toCharArray(), true)) { + assertTrue(file.exists()); + assertTrue(ks.toString().contains(file.toString())); + } + assertThatThrownBy(() -> RadixKeyStore.fromFile(file, "secret2".toCharArray(), false)) + .isInstanceOf(IOException.class) + .hasMessageContaining("wrong password") + .hasNoCause(); + } + + /** Test method for {@link RadixKeyStore#readKeyPair(String, boolean)}. */ + @Test + public void testReadKeyPairFail() throws IOException, KeyStoreException { + File file = newFile(TEST_KS_FILENAME); + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { + assertTrue(file.exists()); + assertThatThrownBy(() -> ks.readKeyPair("notexist", false)) + .isInstanceOf(KeyStoreException.class) + .hasMessageContaining("No such entry") + .hasNoCause(); + } + } + + /** Test method for {@link RadixKeyStore#readKeyPair(String, boolean)}. */ + @Test + public void testReadKeyPairSuccess() + throws IOException, KeyStoreException, PrivateKeyException, PublicKeyException { + final File file = newFile("keystore.ks"); + final var originalKeypair = ECKeyPair.generateNew(); + final var storePassword = "nopass".toCharArray(); + final var keyPairName = "node"; + + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, storePassword, true)) { + assertTrue(file.exists()); + + ks.writeKeyPair(keyPairName, originalKeypair); + } + + final File renamedFile = new File(TEST_KS_FILENAME); + file.renameTo(renamedFile); + + try (RadixKeyStore ks = RadixKeyStore.fromFile(renamedFile, storePassword, false)) { + var loadedKeypair = ks.readKeyPair(keyPairName, false); + assertThat(loadedKeypair).isEqualTo(originalKeypair); + } + } + + /** Test method for {@link RadixKeyStore#close()}. */ + @Test + public void testClose() throws IOException, KeyStoreException { + File file = newFile(TEST_KS_FILENAME); + @SuppressWarnings("resource") + RadixKeyStore ks = RadixKeyStore.fromFile(file, "testpassword".toCharArray(), true); + assertTrue(file.exists()); + ks.close(); + char[] pwd = ks.storePassword(); + assertEquals(12, pwd.length); + assertTrue(IntStream.range(0, pwd.length).map(i -> pwd[i]).allMatch(i -> i == ' ')); + } + + /** Test method for {@link RadixKeyStore#toString()}. */ + @Test + public void testToString() throws IOException, KeyStoreException { + File file = newFile(TEST_KS_FILENAME); + try (RadixKeyStore ks = RadixKeyStore.fromFile(file, null, true)) { + assertThat(ks.toString()).contains(file.toString()); + assertThat(ks.toString()).contains(RadixKeyStore.class.getSimpleName()); + } + } + + /** Test method for ensuring that only secp256k1 keys are allowed. */ + @Test + public void testInvalidECKey() throws IOException, GeneralSecurityException, KeyStoreException { + File file = newFile(TEST_KS_FILENAME); + + KeyStore jks = KeyStore.getInstance("pkcs12"); + jks.load(null, TEST_SECRET.toCharArray()); + + // Only secp256k1 curve is supported - use a different curve here + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + keyGen.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom()); + + KeyPair keyPair = keyGen.generateKeyPair(); + PrivateKey privKey = keyPair.getPrivate(); + + Certificate[] chain = {RadixKeyStore.selfSignedCert(keyPair, 1000, "CN=Invalid")}; + + KeyStore.PrivateKeyEntry entry = new KeyStore.PrivateKeyEntry(privKey, chain); + jks.setEntry("test", entry, new KeyStore.PasswordProtection(new char[0])); + + try (RadixKeyStore rks = new RadixKeyStore(file, jks, TEST_SECRET.toCharArray())) { + assertThatThrownBy(() -> rks.readKeyPair("test", false)) + .isInstanceOf(KeyStoreException.class) + .hasMessageContaining("Unknown curve") + .hasNoCause(); + } + } + + /** Test method for ensuring that only PrivateKeyEntry values are allowed. */ + @Test + public void testInvalidEntry() throws IOException, GeneralSecurityException { + KeyStore jks = KeyStore.getInstance("pkcs12"); + jks.load(null, "password".toCharArray()); + + SecretKey sk = new PKCS12Key(TEST_SECRET.toCharArray()); + KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(sk); + + assertThatThrownBy( + () -> jks.setEntry("test", entry, new KeyStore.PasswordProtection(new char[0]))) + .isInstanceOf(java.security.KeyStoreException.class) + .hasMessageContaining("PKCS12 does not support non-PrivateKeys") + .hasNoCause(); + } + + private static File newFile(String filename) throws IOException { + File file = new File(filename); + if (!Files.deleteIfExists(file.toPath())) { + // In this case we are fine if "file" does not exist and wasn't deleted. + } + assertFalse(file.exists()); + return file; + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SHAHashHandlerTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SHAHashHandlerTest.java index 7be2bc898f..e97eceeed8 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SHAHashHandlerTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SHAHashHandlerTest.java @@ -61,160 +61,158 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.crypto; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import com.radixdlt.utils.Bytes; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; - import org.junit.Test; -import com.radixdlt.utils.Bytes; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - /** * Test vectors for 2 rounds of SHA-256 and SHA-512. - *

- * Test vectors used have been taken from - * - * https://www.di-mgt.com.au/sha_testvectors.html - * . - *

- * Note that the final 1GiB data test vector has been omitted for memory space reasons. + * + *

Test vectors used have been taken from + * https://www.di-mgt.com.au/sha_testvectors.html . + * + *

Note that the final 1GiB data test vector has been omitted for memory space reasons. */ public class SHAHashHandlerTest { - private final MessageDigest sha256Digester = getDigester("SHA-256"); - private final MessageDigest sha512Digester = getDigester("SHA-512"); - private final SHAHashHandler hashHandler = new SHAHashHandler(); - - - @Test - public void testVector1Sha256() { - // Input message: "abc", the bit string (0x)616263 of length 24 bits. - testSha256("abc", sha256("ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad")); - } - - @Test - public void testVector2Sha256() { - // Input message: the empty string "", the bit string of length 0. - testSha256("", sha256("e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855")); - } - - @Test - public void testVector3Sha256() { - // Input message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" (length 448 bits). - testSha256( - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - sha256("248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1") - ); - } - - @Test - public void testVector4Sha256() { - // Input message: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" - // (length 896 bits). - testSha256( - "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - sha256("cf5b16a7 78af8380 036ce59e 7b049237 0b249b11 e8f07a51 afac4503 7afee9d1") - ); - } - - @Test - public void testVector5Sha256() { - // Input message: one million (1,000,000) repetitions of the character "a" (0x61). - testSha256( - "a".repeat(1_000_000), - sha256("cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0") - ); - } - - @Test - public void testVector1Sha512() { - // Input message: "abc", the bit string (0x)616263 of length 24 bits. - testSha512( - "abc", - sha512("ddaf35a193617aba cc417349ae204131 12e6fa4e89a97ea2 0a9eeee64b55d39a" - + " 2192992a274fc1a8 36ba3c23a3feebbd 454d4423643ce80e 2a9ac94fa54ca49f") - ); - } - - @Test - public void testVector2Sha512() { - // Input message: the empty string "", the bit string of length 0. - testSha512( - "", - sha512("cf83e1357eefb8bd f1542850d66d8007 d620e4050b5715dc 83f4a921d36ce9ce" - + " 47d0d13c5d85f2b0 ff8318d2877eec2f 63b931bd47417a81 a538327af927da3e") - ); - } - - @Test - public void testVector3Sha512() { - // Input message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" (length 448 bits). - testSha512( - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - sha512("204a8fc6dda82f0a 0ced7beb8e08a416 57c16ef468b228a8 279be331a703c335" - + " 96fd15c13b1b07f9 aa1d3bea57789ca0 31ad85c7a71dd703 54ec631238ca3445") - ); - } - - @Test - public void testVector4Sha512() { - // Input message: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" - // (length 896 bits). - testSha512( - "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - sha512("8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018" - + " 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909") - ); - } - - @Test - public void testVector5Sha512() { - // Input message: one million (1,000,000) repetitions of the character "a" (0x61). - testSha512( - "a".repeat(1_000_000), - sha512("e718483d0ce76964 4e2e42c7bc15b463 8e1f98b13b204428 5632a803afa973eb" - + " de0ff244877ea60a 4cb0432ce577c31b eb009c5c2c49aa2e 4eadb217ad8cc09b") - ); - } - - private void testSha256(String charsToHash, byte[] bytesToCompare) { - assertEquals(32, bytesToCompare.length); - byte[] hashedBytes = hashHandler.hash256(charsToHash.getBytes(StandardCharsets.US_ASCII)); - assertEquals(32, hashedBytes.length); - assertArrayEquals(bytesToCompare, hashedBytes); - } - - private void testSha512(String charsToHash, byte[] bytesToCompare) { - assertEquals(64, bytesToCompare.length); - byte[] hashedBytes = hashHandler.hash512(charsToHash.getBytes(StandardCharsets.US_ASCII)); - assertEquals(64, hashedBytes.length); - assertArrayEquals(bytesToCompare, hashedBytes); - } - - private byte[] sha256(String hexString) { - byte[] bytes = Bytes.fromHexString(hexString.replaceAll(" ", "")); - // Perform the second round of sha-256 - this.sha256Digester.reset(); - return this.sha256Digester.digest(bytes); - } - - private byte[] sha512(String hexString) { - byte[] bytes = Bytes.fromHexString(hexString.replaceAll(" ", "")); - // Perform the second round of sha-512 - this.sha512Digester.reset(); - return this.sha512Digester.digest(bytes); - } - - private static MessageDigest getDigester(String algorithm) { - try { - return MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException("No such algorithm: " + algorithm, e); - } - } + private final MessageDigest sha256Digester = getDigester("SHA-256"); + private final MessageDigest sha512Digester = getDigester("SHA-512"); + private final SHAHashHandler hashHandler = new SHAHashHandler(); + + @Test + public void testVector1Sha256() { + // Input message: "abc", the bit string (0x)616263 of length 24 bits. + testSha256( + "abc", sha256("ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad")); + } + + @Test + public void testVector2Sha256() { + // Input message: the empty string "", the bit string of length 0. + testSha256( + "", sha256("e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855")); + } + + @Test + public void testVector3Sha256() { + // Input message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" (length 448 bits). + testSha256( + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + sha256("248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1")); + } + + @Test + public void testVector4Sha256() { + // Input message: + // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + // (length 896 bits). + testSha256( + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + sha256("cf5b16a7 78af8380 036ce59e 7b049237 0b249b11 e8f07a51 afac4503 7afee9d1")); + } + + @Test + public void testVector5Sha256() { + // Input message: one million (1,000,000) repetitions of the character "a" (0x61). + testSha256( + "a".repeat(1_000_000), + sha256("cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0")); + } + + @Test + public void testVector1Sha512() { + // Input message: "abc", the bit string (0x)616263 of length 24 bits. + testSha512( + "abc", + sha512( + "ddaf35a193617aba cc417349ae204131 12e6fa4e89a97ea2 0a9eeee64b55d39a" + + " 2192992a274fc1a8 36ba3c23a3feebbd 454d4423643ce80e 2a9ac94fa54ca49f")); + } + + @Test + public void testVector2Sha512() { + // Input message: the empty string "", the bit string of length 0. + testSha512( + "", + sha512( + "cf83e1357eefb8bd f1542850d66d8007 d620e4050b5715dc 83f4a921d36ce9ce" + + " 47d0d13c5d85f2b0 ff8318d2877eec2f 63b931bd47417a81 a538327af927da3e")); + } + + @Test + public void testVector3Sha512() { + // Input message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" (length 448 bits). + testSha512( + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + sha512( + "204a8fc6dda82f0a 0ced7beb8e08a416 57c16ef468b228a8 279be331a703c335" + + " 96fd15c13b1b07f9 aa1d3bea57789ca0 31ad85c7a71dd703 54ec631238ca3445")); + } + + @Test + public void testVector4Sha512() { + // Input message: + // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + // (length 896 bits). + testSha512( + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + sha512( + "8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018" + + " 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909")); + } + + @Test + public void testVector5Sha512() { + // Input message: one million (1,000,000) repetitions of the character "a" (0x61). + testSha512( + "a".repeat(1_000_000), + sha512( + "e718483d0ce76964 4e2e42c7bc15b463 8e1f98b13b204428 5632a803afa973eb" + + " de0ff244877ea60a 4cb0432ce577c31b eb009c5c2c49aa2e 4eadb217ad8cc09b")); + } + + private void testSha256(String charsToHash, byte[] bytesToCompare) { + assertEquals(32, bytesToCompare.length); + byte[] hashedBytes = hashHandler.hash256(charsToHash.getBytes(StandardCharsets.US_ASCII)); + assertEquals(32, hashedBytes.length); + assertArrayEquals(bytesToCompare, hashedBytes); + } + + private void testSha512(String charsToHash, byte[] bytesToCompare) { + assertEquals(64, bytesToCompare.length); + byte[] hashedBytes = hashHandler.hash512(charsToHash.getBytes(StandardCharsets.US_ASCII)); + assertEquals(64, hashedBytes.length); + assertArrayEquals(bytesToCompare, hashedBytes); + } + + private byte[] sha256(String hexString) { + byte[] bytes = Bytes.fromHexString(hexString.replaceAll(" ", "")); + // Perform the second round of sha-256 + this.sha256Digester.reset(); + return this.sha256Digester.digest(bytes); + } + + private byte[] sha512(String hexString) { + byte[] bytes = Bytes.fromHexString(hexString.replaceAll(" ", "")); + // Perform the second round of sha-512 + this.sha512Digester.reset(); + return this.sha512Digester.digest(bytes); + } + + private static MessageDigest getDigester(String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("No such algorithm: " + algorithm, e); + } + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SecureRandomTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SecureRandomTest.java index c03b0f70cc..2ffa2bf602 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SecureRandomTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/crypto/SecureRandomTest.java @@ -65,22 +65,23 @@ package com.radixdlt.crypto; import com.radixdlt.TestSetupUtils; +import java.security.SecureRandom; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import java.security.SecureRandom; - public class SecureRandomTest { - @Before - public void setUp() { - TestSetupUtils.installBouncyCastleProvider(); - } + @Before + public void setUp() { + TestSetupUtils.installBouncyCastleProvider(); + } - @Test - public void verifySecureRandomUsingBouncyCastleProviderByDefault() { - Assert.assertTrue("BouncyCastleProvider should be used by default", new SecureRandom().getProvider() instanceof BouncyCastleProvider); - } -} \ No newline at end of file + @Test + public void verifySecureRandomUsingBouncyCastleProviderByDefault() { + Assert.assertTrue( + "BouncyCastleProvider should be used by default", + new SecureRandom().getProvider() instanceof BouncyCastleProvider); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/errors/ApiErrorsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/errors/ApiErrorsTest.java index d63c456603..07e86bf611 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/errors/ApiErrorsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/errors/ApiErrorsTest.java @@ -64,35 +64,28 @@ package com.radixdlt.errors; -import org.junit.Ignore; -import org.junit.Test; - import com.radixdlt.utils.functional.Functions; - import java.util.stream.Collectors; import java.util.stream.Stream; +import org.junit.Ignore; +import org.junit.Test; public class ApiErrorsTest { - @Test - public void ensureUniqueCodes() { - //noinspection ResultOfMethodCallIgnored - Stream.of(ApiErrors.values()) - .collect(Collectors.toMap(ApiErrors::code, Functions::identity)); - } + @Test + public void ensureUniqueCodes() { + //noinspection ResultOfMethodCallIgnored + Stream.of(ApiErrors.values()).collect(Collectors.toMap(ApiErrors::code, Functions::identity)); + } - @Test - @Ignore("Used only to generate list of error codes for the spec") - public void listErrors() { - Stream.of(ApiErrors.values()) - .forEach(ApiErrorsTest::printError); - } + @Test + @Ignore("Used only to generate list of error codes for the spec") + public void listErrors() { + Stream.of(ApiErrors.values()).forEach(ApiErrorsTest::printError); + } - private static void printError(ApiErrors error) { - System.out.printf( - "{\n\t\"code\": %d,\n\t\"message\": \"%s\",\n\t\"data\": \"%s\"\n},\n", - error.code(), - error.name(), - error.message() - ); - } -} \ No newline at end of file + private static void printError(ApiErrors error) { + System.out.printf( + "{\n\t\"code\": %d,\n\t\"message\": \"%s\",\n\t\"data\": \"%s\"\n},\n", + error.code(), error.name(), error.message()); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/errors/ClientErrorsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/errors/ClientErrorsTest.java index 7975325c5a..fb963493fc 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/errors/ClientErrorsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/errors/ClientErrorsTest.java @@ -64,19 +64,16 @@ package com.radixdlt.errors; -import org.junit.Test; - import com.radixdlt.utils.functional.Functions; - import java.util.stream.Collectors; import java.util.stream.Stream; +import org.junit.Test; public class ClientErrorsTest { - @Test - public void ensureUniqueCodes() { - //noinspection ResultOfMethodCallIgnored - Stream.of(ClientErrors.values()) - .collect(Collectors.toMap(ClientErrors::code, Functions::identity)); - } - -} \ No newline at end of file + @Test + public void ensureUniqueCodes() { + //noinspection ResultOfMethodCallIgnored + Stream.of(ClientErrors.values()) + .collect(Collectors.toMap(ClientErrors::code, Functions::identity)); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/errors/InternalErrorsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/errors/InternalErrorsTest.java index efd5079dc3..5b776a22f8 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/errors/InternalErrorsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/errors/InternalErrorsTest.java @@ -64,18 +64,16 @@ package com.radixdlt.errors; -import org.junit.Test; - import com.radixdlt.utils.functional.Functions; - import java.util.stream.Collectors; import java.util.stream.Stream; +import org.junit.Test; public class InternalErrorsTest { - @Test - public void ensureUniqueCodes() { - //noinspection ResultOfMethodCallIgnored - Stream.of(InternalErrors.values()) - .collect(Collectors.toMap(InternalErrors::code, Functions::identity)); - } -} \ No newline at end of file + @Test + public void ensureUniqueCodes() { + //noinspection ResultOfMethodCallIgnored + Stream.of(InternalErrors.values()) + .collect(Collectors.toMap(InternalErrors::code, Functions::identity)); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AIDTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AIDTest.java index 2fa2c120da..0bd0174ef7 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AIDTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AIDTest.java @@ -1,126 +1,125 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.identifiers; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Test; - - -public class AIDTest { - @Test - public void testIllegalConstruction() { - assertThatThrownBy(() -> AID.from((byte[]) null)).isInstanceOf(NullPointerException.class); - assertThatThrownBy(() -> AID.from((String) null)).isInstanceOf(NullPointerException.class); - - assertThatThrownBy(() -> AID.from(new byte[7])).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> AID.from("deadbeef")).isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void testCreateEquivalence() { - byte[] bytes1 = new byte[AID.BYTES]; - for (int i = 0; i < AID.BYTES; i++) { - bytes1[i] = (byte) i; - } - byte[] bytes2 = new byte[AID.BYTES]; - for (int i = 0; i < AID.BYTES; i++) { - bytes2[i] = (byte) (AID.BYTES - i); - } - - AID aid1 = AID.from(bytes1); - assertArrayEquals(bytes1, aid1.getBytes()); - byte[] bytes1Copy = new byte[AID.BYTES]; - aid1.copyTo(bytes1Copy, 0); - assertArrayEquals(bytes1Copy, bytes1); - - AID aid2 = AID.from(bytes2); - assertArrayEquals(bytes2, aid2.getBytes()); - - assertNotEquals(aid1, aid2); - } - - - @Test - public void equalsContract() { - EqualsVerifier.forClass(AID.class).verify(); - } - - @Test - public void testArrayOffsetFactory() { - byte[] bytes = new byte[AID.BYTES * 2]; - AID aid0 = AID.from(bytes, 0); - assertEquals(AID.ZERO, aid0); - AID aid1 = AID.from(bytes, AID.BYTES); - assertEquals(AID.ZERO, aid1); - - assertThatThrownBy(() -> AID.from(bytes, -1)).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> AID.from(bytes, AID.BYTES + 1)).isInstanceOf(IllegalArgumentException.class); - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.identifiers; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class AIDTest { + @Test + public void testIllegalConstruction() { + assertThatThrownBy(() -> AID.from((byte[]) null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> AID.from((String) null)).isInstanceOf(NullPointerException.class); + + assertThatThrownBy(() -> AID.from(new byte[7])).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> AID.from("deadbeef")).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testCreateEquivalence() { + byte[] bytes1 = new byte[AID.BYTES]; + for (int i = 0; i < AID.BYTES; i++) { + bytes1[i] = (byte) i; + } + byte[] bytes2 = new byte[AID.BYTES]; + for (int i = 0; i < AID.BYTES; i++) { + bytes2[i] = (byte) (AID.BYTES - i); + } + + AID aid1 = AID.from(bytes1); + assertArrayEquals(bytes1, aid1.getBytes()); + byte[] bytes1Copy = new byte[AID.BYTES]; + aid1.copyTo(bytes1Copy, 0); + assertArrayEquals(bytes1Copy, bytes1); + + AID aid2 = AID.from(bytes2); + assertArrayEquals(bytes2, aid2.getBytes()); + + assertNotEquals(aid1, aid2); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(AID.class).verify(); + } + + @Test + public void testArrayOffsetFactory() { + byte[] bytes = new byte[AID.BYTES * 2]; + AID aid0 = AID.from(bytes, 0); + assertEquals(AID.ZERO, aid0); + AID aid1 = AID.from(bytes, AID.BYTES); + assertEquals(AID.ZERO, aid1); + + assertThatThrownBy(() -> AID.from(bytes, -1)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> AID.from(bytes, AID.BYTES + 1)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AccountAddressingTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AccountAddressingTest.java index 61e4aab697..acaff221e4 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AccountAddressingTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/AccountAddressingTest.java @@ -64,91 +64,95 @@ package com.radixdlt.identifiers; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.utils.Bytes; - import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Test; public class AccountAddressingTest { - private final AccountAddressing accountAddresses = AccountAddressing.bech32("brx"); - private final BiMap privateKeyToAccountAddress = HashBiMap.create( - Map.of( - "00", "brx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjqllv9", - "deadbeef", "brx1qspsel805pa0nhtdhemshp7hm0wjcvd60a8ulre6zxtd2qh3x4smq3sraak9a", - "deadbeefdeadbeef", "brx1qsp7gnv7g60plkk9lgskjghdlevyve6rtrzggk7x3fwmp4yfyjza7gcumgm9f", - "bead", "brx1qsppw0z477r695m9f9qjs3nj2vmdkd3rg4mfx7tf5v0gasrhz32jefqwxg7ul", - "aaaaaaaaaaaaaaaa", "brx1qspqsfad7e5k2k9agq74g40al0j9cllv7w0ylatvhy7m64wyrwymy5g7md96s" - ) - ); + private final AccountAddressing accountAddresses = AccountAddressing.bech32("brx"); + private final BiMap privateKeyToAccountAddress = + HashBiMap.create( + Map.of( + "00", "brx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjqllv9", + "deadbeef", "brx1qspsel805pa0nhtdhemshp7hm0wjcvd60a8ulre6zxtd2qh3x4smq3sraak9a", + "deadbeefdeadbeef", + "brx1qsp7gnv7g60plkk9lgskjghdlevyve6rtrzggk7x3fwmp4yfyjza7gcumgm9f", + "bead", "brx1qsppw0z477r695m9f9qjs3nj2vmdkd3rg4mfx7tf5v0gasrhz32jefqwxg7ul", + "aaaaaaaaaaaaaaaa", + "brx1qspqsfad7e5k2k9agq74g40al0j9cllv7w0ylatvhy7m64wyrwymy5g7md96s")); - private final BiMap reAddrToAccountAddress = HashBiMap.create( - Map.of( - "04" + "02".repeat(33), "brx1qspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs7cr9az" - ) - ); + private final BiMap reAddrToAccountAddress = + HashBiMap.create( + Map.of( + "04" + "02".repeat(33), + "brx1qspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs7cr9az")); - private final Map invalidAddresses = Map.of( - "vb1qvz3anvawgvm7pwvjs7xmjg48dvndczkgnufh475k2tqa2vm5c6cq9u3702", "invalid hrp", - "brx1xhv8x3", "invalid address length 0", - "brx1qsqsyqcyq5rqzjh9c6", "invalid length for address type 4" - ); + private final Map invalidAddresses = + Map.of( + "vb1qvz3anvawgvm7pwvjs7xmjg48dvndczkgnufh475k2tqa2vm5c6cq9u3702", "invalid hrp", + "brx1xhv8x3", "invalid address length 0", + "brx1qsqsyqcyq5rqzjh9c6", "invalid length for address type 4"); - @Test - public void test_validator_privkey_to_address_serialization() { - privateKeyToAccountAddress.forEach((privHex, expectedAddress) -> { - var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); - var publicKey = keyPair.getPublicKey(); - var addr = REAddr.ofPubKeyAccount(publicKey); - var accountAddress = accountAddresses.of(addr); - assertThat(accountAddress).isEqualTo(expectedAddress); - }); - } + @Test + public void test_validator_privkey_to_address_serialization() { + privateKeyToAccountAddress.forEach( + (privHex, expectedAddress) -> { + var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); + var publicKey = keyPair.getPublicKey(); + var addr = REAddr.ofPubKeyAccount(publicKey); + var accountAddress = accountAddresses.of(addr); + assertThat(accountAddress).isEqualTo(expectedAddress); + }); + } - @Test - public void test_re_addr_to_address_serialization() { - reAddrToAccountAddress.forEach((hex, expectedAddress) -> { - var addr = REAddr.of(Bytes.fromHexString(hex)); - var accountAddr = accountAddresses.of(addr); - assertThat(accountAddr).isEqualTo(expectedAddress); - }); - } + @Test + public void test_re_addr_to_address_serialization() { + reAddrToAccountAddress.forEach( + (hex, expectedAddress) -> { + var addr = REAddr.of(Bytes.fromHexString(hex)); + var accountAddr = accountAddresses.of(addr); + assertThat(accountAddr).isEqualTo(expectedAddress); + }); + } - @Test - public void test_priv_key_address_deserialization() { - for (var e : privateKeyToAccountAddress.entrySet()) { - var address = e.getValue(); - var privHex = e.getKey(); - var reAddr = accountAddresses.parseOrThrow(address, IllegalStateException::new); - var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); - var pubKey = keyPair.getPublicKey(); - assertThat(reAddr).isEqualTo(REAddr.ofPubKeyAccount(pubKey)); - } - } + @Test + public void test_priv_key_address_deserialization() { + for (var e : privateKeyToAccountAddress.entrySet()) { + var address = e.getValue(); + var privHex = e.getKey(); + var reAddr = accountAddresses.parseOrThrow(address, IllegalStateException::new); + var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); + var pubKey = keyPair.getPublicKey(); + assertThat(reAddr).isEqualTo(REAddr.ofPubKeyAccount(pubKey)); + } + } - @Test - public void test_re_addr_from_address_deserialization() { - for (var e : reAddrToAccountAddress.entrySet()) { - var address = e.getValue(); - var hex = e.getKey(); - var reAddr = REAddr.of(Bytes.fromHexString(hex)); - assertThat(reAddr).isEqualTo(accountAddresses.parseOrThrow(address, IllegalStateException::new)); - } - } + @Test + public void test_re_addr_from_address_deserialization() { + for (var e : reAddrToAccountAddress.entrySet()) { + var address = e.getValue(); + var hex = e.getKey(); + var reAddr = REAddr.of(Bytes.fromHexString(hex)); + assertThat(reAddr) + .isEqualTo(accountAddresses.parseOrThrow(address, IllegalStateException::new)); + } + } - @Test - public void test_invalid_addresses() { - for (var e : invalidAddresses.entrySet()) { - var address = e.getKey(); - var expectedError = e.getValue(); - assertThatThrownBy(() -> accountAddresses.parseOrThrow(address, IllegalStateException::new), expectedError) - .isInstanceOf(IllegalStateException.class); - } - } -} \ No newline at end of file + @Test + public void test_invalid_addresses() { + for (var e : invalidAddresses.entrySet()) { + var address = e.getKey(); + var expectedError = e.getValue(); + assertThatThrownBy( + () -> accountAddresses.parseOrThrow(address, IllegalStateException::new), + expectedError) + .isInstanceOf(IllegalStateException.class); + } + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/EUIDTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/EUIDTest.java index 68d31eac3f..845543603e 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/EUIDTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/EUIDTest.java @@ -64,12 +64,12 @@ package com.radixdlt.identifiers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.base.Strings; import com.google.common.hash.HashCode; @@ -77,186 +77,183 @@ import com.radixdlt.utils.UInt128; import java.util.Arrays; import java.util.List; - import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.Test; public class EUIDTest { - private static final byte[] ONE = new byte[32]; - private static final byte[] NEGATIVE_ONE = new byte[32]; - static { - ONE[EUID.BYTES - 1] = 1; - Arrays.fill(NEGATIVE_ONE, (byte) 0xff); - } + private static final byte[] ONE = new byte[32]; + private static final byte[] NEGATIVE_ONE = new byte[32]; + + static { + ONE[EUID.BYTES - 1] = 1; + Arrays.fill(NEGATIVE_ONE, (byte) 0xff); + } - @Test - public void equalsContract() { - EqualsVerifier.forClass(EUID.class).verify(); - } + @Test + public void equalsContract() { + EqualsVerifier.forClass(EUID.class).verify(); + } - @Test - public void testOffsetBytesConstructor() { - EUID expected = new EUID("dead000000000000000000000000beef"); - byte[] tooManyBytes = Bytes.fromHexString("11dead000000000000000000000000beef"); - EUID offsetted = new EUID(tooManyBytes, 1); // remember 2 hex chars == 1 byte. - assertEquals(expected, offsetted); - } + @Test + public void testOffsetBytesConstructor() { + EUID expected = new EUID("dead000000000000000000000000beef"); + byte[] tooManyBytes = Bytes.fromHexString("11dead000000000000000000000000beef"); + EUID offsetted = new EUID(tooManyBytes, 1); // remember 2 hex chars == 1 byte. + assertEquals(expected, offsetted); + } - @Test - public void verify_that_exception_is_thrown_when_calling_constructor_with_too_short_hexstring() { - assertThatThrownBy(() -> new EUID("deadbeef")) - .isInstanceOf(IllegalArgumentException.class); - } + @Test + public void verify_that_exception_is_thrown_when_calling_constructor_with_too_short_hexstring() { + assertThatThrownBy(() -> new EUID("deadbeef")).isInstanceOf(IllegalArgumentException.class); + } - @Test - public void verify_that_exception_is_thrown_when_calling_constructor_with_empty_byte_array() { - assertThatThrownBy(() -> new EUID(new byte[0])) - .isInstanceOf(IllegalArgumentException.class); - } + @Test + public void verify_that_exception_is_thrown_when_calling_constructor_with_empty_byte_array() { + assertThatThrownBy(() -> new EUID(new byte[0])).isInstanceOf(IllegalArgumentException.class); + } - @Test - public void testZero() { - assertTrue(EUID.ZERO.isZero()); - assertFalse(EUID.ONE.isZero()); - } + @Test + public void testZero() { + assertTrue(EUID.ZERO.isZero()); + assertFalse(EUID.ONE.isZero()); + } - @Test - public void testGetValue() { - assertEquals(UInt128.ZERO, EUID.ZERO.getValue()); - assertEquals(UInt128.ONE, EUID.ONE.getValue()); - assertEquals(UInt128.TWO, EUID.TWO.getValue()); - } + @Test + public void testGetValue() { + assertEquals(UInt128.ZERO, EUID.ZERO.getValue()); + assertEquals(UInt128.ONE, EUID.ONE.getValue()); + assertEquals(UInt128.TWO, EUID.TWO.getValue()); + } - @Test - public void testGetLow() { - assertEquals(1L, EUID.ONE.getLow()); - assertEquals(0L, EUID.ZERO.getLow()); - assertEquals(2L, EUID.TWO.getLow()); - } + @Test + public void testGetLow() { + assertEquals(1L, EUID.ONE.getLow()); + assertEquals(0L, EUID.ZERO.getLow()); + assertEquals(2L, EUID.TWO.getLow()); + } - @Test - public void verify_that_tobytearray_returns_same_as_passed_in_constructor() { - byte[] bytes = Bytes.fromHexString("dead000000000000000000000000beef"); - EUID euid = new EUID(bytes); - assertArrayEquals(bytes, euid.toByteArray()); - } + @Test + public void verify_that_tobytearray_returns_same_as_passed_in_constructor() { + byte[] bytes = Bytes.fromHexString("dead000000000000000000000000beef"); + EUID euid = new EUID(bytes); + assertArrayEquals(bytes, euid.toByteArray()); + } - @Test - public void testCompare() { - EUID low = new EUID(Strings.repeat("1", 32)); - EUID high = new EUID(Strings.repeat("9", 32)); - assertThat(low).isLessThan(high); - } + @Test + public void testCompare() { + EUID low = new EUID(Strings.repeat("1", 32)); + EUID high = new EUID(Strings.repeat("9", 32)); + assertThat(low).isLessThan(high); + } - @Test - public void testEqualityWithDifferentConstructors() { - List> equalIds = - Arrays.asList( - Arrays.asList( - new EUID(new byte[] {0, 1}), - new EUID(new byte[] {1}), - new EUID(1), - new EUID(1L), - EUID.valueOf("00000000000000000000000000000001") - ), - Arrays.asList( - new EUID(new byte[] {(byte) 0xff, (byte) 0xff}), - new EUID(new byte[] {(byte) 0xff}), - new EUID(-1), - new EUID(-1L), - EUID.valueOf("ffffffffffffffffffffffffffffffff") - ) - ); + @Test + public void testEqualityWithDifferentConstructors() { + List> equalIds = + Arrays.asList( + Arrays.asList( + new EUID(new byte[] {0, 1}), + new EUID(new byte[] {1}), + new EUID(1), + new EUID(1L), + EUID.valueOf("00000000000000000000000000000001")), + Arrays.asList( + new EUID(new byte[] {(byte) 0xff, (byte) 0xff}), + new EUID(new byte[] {(byte) 0xff}), + new EUID(-1), + new EUID(-1L), + EUID.valueOf("ffffffffffffffffffffffffffffffff"))); - for (int i = 0; i < equalIds.size(); i++) { - for (int j = 0; j < equalIds.get(i).size(); j++) { - for (int k = 0; k < equalIds.get(i).size(); k++) { - EUID first = equalIds.get(i).get(j); - EUID second = equalIds.get(i).get(k); - assertEquals("Equality Test on Index " + i + " " + j + " " + k, first, second); - assertEquals("Hash Test on Index " + i + " " + j + " " + k, first.hashCode(), second.hashCode()); - assertEquals("String Test on Index " + i + " " + j + " " + k, first.toString(), second.toString()); - } - } - } - } + for (int i = 0; i < equalIds.size(); i++) { + for (int j = 0; j < equalIds.get(i).size(); j++) { + for (int k = 0; k < equalIds.get(i).size(); k++) { + EUID first = equalIds.get(i).get(j); + EUID second = equalIds.get(i).get(k); + assertEquals("Equality Test on Index " + i + " " + j + " " + k, first, second); + assertEquals( + "Hash Test on Index " + i + " " + j + " " + k, first.hashCode(), second.hashCode()); + assertEquals( + "String Test on Index " + i + " " + j + " " + k, first.toString(), second.toString()); + } + } + } + } - @Test - public void trimAndExpand() { - EUID small = new EUID(Integer.MAX_VALUE); - EUID large = new EUID(Long.MIN_VALUE); + @Test + public void trimAndExpand() { + EUID small = new EUID(Integer.MAX_VALUE); + EUID large = new EUID(Long.MIN_VALUE); - EUID expanded = new EUID(Arrays.copyOfRange(small.toByteArray(), EUID.BYTES - Long.BYTES, EUID.BYTES)); - EUID trimmed = new EUID(Arrays.copyOfRange(large.toByteArray(), EUID.BYTES - Long.BYTES, EUID.BYTES - Integer.BYTES)); + EUID expanded = + new EUID(Arrays.copyOfRange(small.toByteArray(), EUID.BYTES - Long.BYTES, EUID.BYTES)); + EUID trimmed = + new EUID( + Arrays.copyOfRange( + large.toByteArray(), EUID.BYTES - Long.BYTES, EUID.BYTES - Integer.BYTES)); - assertEquals(small, expanded); - assertEquals(new EUID(Integer.MIN_VALUE), trimmed); - } + assertEquals(small, expanded); + assertEquals(new EUID(Integer.MIN_VALUE), trimmed); + } - /** - * Test compareDistances(). - */ - @Test - public void testCompareDistances() { - assertThat(EUID.ZERO.compareDistances(EUID.ONE, EUID.ONE)).isZero(); + /** Test compareDistances(). */ + @Test + public void testCompareDistances() { + assertThat(EUID.ZERO.compareDistances(EUID.ONE, EUID.ONE)).isZero(); - // Both to right of origin - assertThat(EUID.ZERO.compareDistances(EUID.TWO, EUID.ONE)).isPositive(); - assertThat(EUID.ZERO.compareDistances(EUID.ONE, EUID.TWO)).isNegative(); - EUID minusOne = new EUID(-1); - EUID minusTwo = new EUID(-2); - // Both to left of origin - assertThat(EUID.ZERO.compareDistances(minusTwo, minusOne)).isPositive(); - assertThat(EUID.ZERO.compareDistances(minusOne, minusTwo)).isNegative(); + // Both to right of origin + assertThat(EUID.ZERO.compareDistances(EUID.TWO, EUID.ONE)).isPositive(); + assertThat(EUID.ZERO.compareDistances(EUID.ONE, EUID.TWO)).isNegative(); + EUID minusOne = new EUID(-1); + EUID minusTwo = new EUID(-2); + // Both to left of origin + assertThat(EUID.ZERO.compareDistances(minusTwo, minusOne)).isPositive(); + assertThat(EUID.ZERO.compareDistances(minusOne, minusTwo)).isNegative(); - // Origin between values, but different in most significant bits. - assertThat(EUID.ZERO.compareDistances(EUID.TWO, minusOne)).isPositive(); - assertThat(EUID.ZERO.compareDistances(minusOne, EUID.TWO)).isNegative(); - assertThat(EUID.ZERO.compareDistances(minusTwo, EUID.ONE)).isPositive(); - assertThat(EUID.ZERO.compareDistances(EUID.ONE, minusTwo)).isNegative(); + // Origin between values, but different in most significant bits. + assertThat(EUID.ZERO.compareDistances(EUID.TWO, minusOne)).isPositive(); + assertThat(EUID.ZERO.compareDistances(minusOne, EUID.TWO)).isNegative(); + assertThat(EUID.ZERO.compareDistances(minusTwo, EUID.ONE)).isPositive(); + assertThat(EUID.ZERO.compareDistances(EUID.ONE, minusTwo)).isNegative(); - // Origin between values, but only different in least significant bit - EUID three = new EUID(3L); - EUID minusThree = new EUID(-3L); - assertThat(EUID.ZERO.compareDistances(three, minusTwo)).isPositive(); - assertThat(EUID.ZERO.compareDistances(minusThree, EUID.TWO)).isPositive(); - assertThat(EUID.ZERO.compareDistances(minusTwo, three)).isNegative(); - assertThat(EUID.ZERO.compareDistances(EUID.TWO, minusThree)).isNegative(); + // Origin between values, but only different in least significant bit + EUID three = new EUID(3L); + EUID minusThree = new EUID(-3L); + assertThat(EUID.ZERO.compareDistances(three, minusTwo)).isPositive(); + assertThat(EUID.ZERO.compareDistances(minusThree, EUID.TWO)).isPositive(); + assertThat(EUID.ZERO.compareDistances(minusTwo, three)).isNegative(); + assertThat(EUID.ZERO.compareDistances(EUID.TWO, minusThree)).isNegative(); - // Check that wrap / ring behaviour works - EUID max = new EUID(UInt128.MAX_VALUE); - EUID maxP2 = new EUID(UInt128.MAX_VALUE.add(UInt128.TWO)); - EUID maxM3 = new EUID(UInt128.MAX_VALUE.subtract(UInt128.THREE)); - assertThat(max.compareDistances(maxP2, maxM3)).isNegative(); - assertThat(max.compareDistances(maxM3, maxP2)).isPositive(); + // Check that wrap / ring behaviour works + EUID max = new EUID(UInt128.MAX_VALUE); + EUID maxP2 = new EUID(UInt128.MAX_VALUE.add(UInt128.TWO)); + EUID maxM3 = new EUID(UInt128.MAX_VALUE.subtract(UInt128.THREE)); + assertThat(max.compareDistances(maxP2, maxM3)).isNegative(); + assertThat(max.compareDistances(maxM3, maxP2)).isPositive(); - EUID min = new EUID(UInt128.MIN_VALUE); - EUID minP3 = new EUID(UInt128.MIN_VALUE.add(UInt128.THREE)); - EUID minM2 = new EUID(UInt128.MIN_VALUE.subtract(UInt128.TWO)); - assertThat(min.compareDistances(minM2, minP3)).isNegative(); - assertThat(min.compareDistances(minP3, minM2)).isPositive(); - } + EUID min = new EUID(UInt128.MIN_VALUE); + EUID minP3 = new EUID(UInt128.MIN_VALUE.add(UInt128.THREE)); + EUID minM2 = new EUID(UInt128.MIN_VALUE.subtract(UInt128.TWO)); + assertThat(min.compareDistances(minM2, minP3)).isNegative(); + assertThat(min.compareDistances(minP3, minM2)).isPositive(); + } - /** - * Test routingDistanceFrom(...). - */ - @Test - public void testRoutingDistanceFrom() { - EUID minusOne = new EUID(-1L); - assertEquals(UInt128.SIZE, EUID.ZERO.routingDistanceFrom(EUID.ZERO)); - assertEquals(UInt128.SIZE, EUID.ONE.routingDistanceFrom(EUID.ONE)); - assertEquals(UInt128.SIZE, EUID.TWO.routingDistanceFrom(EUID.TWO)); + /** Test routingDistanceFrom(...). */ + @Test + public void testRoutingDistanceFrom() { + EUID minusOne = new EUID(-1L); + assertEquals(UInt128.SIZE, EUID.ZERO.routingDistanceFrom(EUID.ZERO)); + assertEquals(UInt128.SIZE, EUID.ONE.routingDistanceFrom(EUID.ONE)); + assertEquals(UInt128.SIZE, EUID.TWO.routingDistanceFrom(EUID.TWO)); - assertEquals(UInt128.SIZE - 1, EUID.ZERO.routingDistanceFrom(EUID.ONE)); - assertEquals(UInt128.SIZE - 2, EUID.ZERO.routingDistanceFrom(EUID.TWO)); + assertEquals(UInt128.SIZE - 1, EUID.ZERO.routingDistanceFrom(EUID.ONE)); + assertEquals(UInt128.SIZE - 2, EUID.ZERO.routingDistanceFrom(EUID.TWO)); - assertEquals(0, EUID.ZERO.routingDistanceFrom(minusOne)); - } + assertEquals(0, EUID.ZERO.routingDistanceFrom(minusOne)); + } - @Test - public void testCreateFromHashCode() { - HashCode hash = HashCode.fromLong(1234); - assertEquals(EUID.fromHash(hash), new EUID(hash.asBytes())); - } + @Test + public void testCreateFromHashCode() { + HashCode hash = HashCode.fromLong(1234); + assertEquals(EUID.fromHash(hash), new EUID(hash.asBytes())); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ResourceAddressingTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ResourceAddressingTest.java index 79889c49e4..d5e9cb1423 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ResourceAddressingTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ResourceAddressingTest.java @@ -64,7 +64,8 @@ package com.radixdlt.identifiers; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -72,104 +73,107 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.utils.Bytes; import com.radixdlt.utils.Pair; - import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Test; public class ResourceAddressingTest { - private final ResourceAddressing resourceAddressing = ResourceAddressing.bech32("_rb"); - private final BiMap, String> reAddressToRri = HashBiMap.create( - Map.of( - Pair.of("xrd", "01"), "xrd_rb1qya85pwq", - Pair.of("usdc", "03" + "00".repeat(26)), "usdc_rb1qvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gwwwd" - ) - ); - - private final Map invalidRris = Map.of( - "xrd1pzdsczc", "no _rb suffix", - "xrd_rb1avu205I", "invalid address type (0)", - "usdc_rb1qg8vs72e", "invalid address type (2)", - "usdc_rb1qqqsqs6ztc", "invalid length for address type 1", - "usdc_rb1qvgxjc9r", "invalid length for address type 3" - ); - - private final BiMap, String> privateKeyAndNameToRri = HashBiMap.create( - Map.of( - Pair.of(1, "foo"), "foo_rb1qv9ee5j4qun9frqj2mcg79maqq55n46u5ypn2j0g9c3q32j6y3", - Pair.of(1, "bar"), "bar_rb1qwaa87cznx0nmeq08dya2ae43u92g4g0nkfktd9u9lpq6hgjca", - Pair.of(2, "foo"), "foo_rb1qvmf6ak360gxjfhxeh0x5tn99gjzzh5d7u3kvktj26rsu5qa3u", - Pair.of(2, "bar"), "bar_rb1qd3t7gnvwxddj2wxg5dl4adr7er9uw62g7x0ku6hyw4qfk0pfz" - ) - ); - - private final Map systemRris = Map.of( - "xrd", "xrd_rb1qya85pwq", - "eth", "eth_rb1qynl40gy", - "btc", "btc_rb1qytls7qn" - ); - - @Test - public void test_rri_serialization() { - reAddressToRri.forEach((pair, expected) -> { - var reAddr = REAddr.of(Bytes.fromHexString(pair.getSecond())); - var rri = resourceAddressing.of(pair.getFirst(), reAddr); - assertThat(expected).isEqualTo(rri); - }); - } - - @Test - public void test_rri_deserialization() throws Exception { - for (var e : reAddressToRri.entrySet()) { - var expected = e.getKey(); - var rri = e.getValue(); - var pair = resourceAddressing.parseOrThrow(rri, IllegalStateException::new); - var expectedAddr = REAddr.of(Bytes.fromHexString(expected.getSecond())); - - pair.map((symbol, address) -> { - assertThat(expected.getFirst()).isEqualTo(symbol); - assertThat(expectedAddr).isEqualTo(address); - return null; - }); - } - } - - @Test - public void test_invalid_rris() { - for (var e : invalidRris.entrySet()) { - assertThatThrownBy(() -> resourceAddressing.parseOrThrow(e.getKey(), IllegalStateException::new), e.getValue()) - .isInstanceOf(IllegalStateException.class); - } - } - - private ECPublicKey publicKeyOfPrivateKey(int privateKeyScalar) { - assertThat(privateKeyScalar).isLessThanOrEqualTo(9); - try { - return ECKeyPair.fromPrivateKey(Bytes.fromHexString("0".repeat(63) + privateKeyScalar)).getPublicKey(); - } catch (Exception e) { - throw new IllegalStateException("bad key"); - } - } - - private String rriFromPKAndName(int privateKey, String name) { - var reAddr = REAddr.ofHashedKey(publicKeyOfPrivateKey(privateKey), name); - return resourceAddressing.of(name, reAddr); - } - - @Test - public void test_system_rris() { - var systemTokenREAddr = REAddr.ofNativeToken(); - for (var e : systemRris.entrySet()) { - var rri = resourceAddressing.of(e.getKey(), systemTokenREAddr); - assertThat(rri).isEqualTo(e.getValue()); - } - } - @Test - public void test_rri_from_pubkey_serialization() { - privateKeyAndNameToRri.forEach((pair, expected) -> { - var rri = rriFromPKAndName(pair.getFirst(), pair.getSecond()); - assertThat(rri).isEqualTo(expected); - }); - } -} \ No newline at end of file + private final ResourceAddressing resourceAddressing = ResourceAddressing.bech32("_rb"); + private final BiMap, String> reAddressToRri = + HashBiMap.create( + Map.of( + Pair.of("xrd", "01"), "xrd_rb1qya85pwq", + Pair.of("usdc", "03" + "00".repeat(26)), + "usdc_rb1qvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gwwwd")); + + private final Map invalidRris = + Map.of( + "xrd1pzdsczc", "no _rb suffix", + "xrd_rb1avu205I", "invalid address type (0)", + "usdc_rb1qg8vs72e", "invalid address type (2)", + "usdc_rb1qqqsqs6ztc", "invalid length for address type 1", + "usdc_rb1qvgxjc9r", "invalid length for address type 3"); + + private final BiMap, String> privateKeyAndNameToRri = + HashBiMap.create( + Map.of( + Pair.of(1, "foo"), "foo_rb1qv9ee5j4qun9frqj2mcg79maqq55n46u5ypn2j0g9c3q32j6y3", + Pair.of(1, "bar"), "bar_rb1qwaa87cznx0nmeq08dya2ae43u92g4g0nkfktd9u9lpq6hgjca", + Pair.of(2, "foo"), "foo_rb1qvmf6ak360gxjfhxeh0x5tn99gjzzh5d7u3kvktj26rsu5qa3u", + Pair.of(2, "bar"), "bar_rb1qd3t7gnvwxddj2wxg5dl4adr7er9uw62g7x0ku6hyw4qfk0pfz")); + + private final Map systemRris = + Map.of( + "xrd", "xrd_rb1qya85pwq", + "eth", "eth_rb1qynl40gy", + "btc", "btc_rb1qytls7qn"); + + @Test + public void test_rri_serialization() { + reAddressToRri.forEach( + (pair, expected) -> { + var reAddr = REAddr.of(Bytes.fromHexString(pair.getSecond())); + var rri = resourceAddressing.of(pair.getFirst(), reAddr); + assertThat(expected).isEqualTo(rri); + }); + } + + @Test + public void test_rri_deserialization() throws Exception { + for (var e : reAddressToRri.entrySet()) { + var expected = e.getKey(); + var rri = e.getValue(); + var pair = resourceAddressing.parseOrThrow(rri, IllegalStateException::new); + var expectedAddr = REAddr.of(Bytes.fromHexString(expected.getSecond())); + + pair.map( + (symbol, address) -> { + assertThat(expected.getFirst()).isEqualTo(symbol); + assertThat(expectedAddr).isEqualTo(address); + return null; + }); + } + } + + @Test + public void test_invalid_rris() { + for (var e : invalidRris.entrySet()) { + assertThatThrownBy( + () -> resourceAddressing.parseOrThrow(e.getKey(), IllegalStateException::new), + e.getValue()) + .isInstanceOf(IllegalStateException.class); + } + } + + private ECPublicKey publicKeyOfPrivateKey(int privateKeyScalar) { + assertThat(privateKeyScalar).isLessThanOrEqualTo(9); + try { + return ECKeyPair.fromPrivateKey(Bytes.fromHexString("0".repeat(63) + privateKeyScalar)) + .getPublicKey(); + } catch (Exception e) { + throw new IllegalStateException("bad key"); + } + } + + private String rriFromPKAndName(int privateKey, String name) { + var reAddr = REAddr.ofHashedKey(publicKeyOfPrivateKey(privateKey), name); + return resourceAddressing.of(name, reAddr); + } + + @Test + public void test_system_rris() { + var systemTokenREAddr = REAddr.ofNativeToken(); + for (var e : systemRris.entrySet()) { + var rri = resourceAddressing.of(e.getKey(), systemTokenREAddr); + assertThat(rri).isEqualTo(e.getValue()); + } + } + + @Test + public void test_rri_from_pubkey_serialization() { + privateKeyAndNameToRri.forEach( + (pair, expected) -> { + var rri = rriFromPKAndName(pair.getFirst(), pair.getSecond()); + assertThat(rri).isEqualTo(expected); + }); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ValidatorAddressingTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ValidatorAddressingTest.java index 0cc01cc966..7e50663aed 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ValidatorAddressingTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/identifiers/ValidatorAddressingTest.java @@ -64,66 +64,67 @@ package com.radixdlt.identifiers; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.serialization.DeserializeException; import com.radixdlt.utils.Bytes; - import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.Test; public class ValidatorAddressingTest { - private final ValidatorAddressing validatorAddresses = ValidatorAddressing.bech32("vb"); - private final BiMap privateKeyToValidatorId = HashBiMap.create( - Map.of( - "00", "vb1qvz3anvawgvm7pwvjs7xmjg48dvndczkgnufh475k2tqa2vm5c6cq9u3702", - "deadbeef", "vb1qvx0emaq0tua6md7wu9c047mm5krrwnlfl8c7ws3jm2s9uf4vxcyvrwrazy", - "deadbeefdeadbeef", "vb1q0jym8jxnc0a4306y95j9m07tprxws6ccjz9h352tkcdfzfysh0jxll64dl", - "bead", "vb1qgtnc40hs73dxe2fgy5yvujnxmdnvg69w6fhj6drr68vqac525k2gkfdady", - "aaaaaaaaaaaaaaaa", "vb1qgyz0t0kd9j4302q8429tl0mu3w8lm8nne8l2m9e8k74t3qm3xe9z8l2049" - ) - ); + private final ValidatorAddressing validatorAddresses = ValidatorAddressing.bech32("vb"); + private final BiMap privateKeyToValidatorId = + HashBiMap.create( + Map.of( + "00", "vb1qvz3anvawgvm7pwvjs7xmjg48dvndczkgnufh475k2tqa2vm5c6cq9u3702", + "deadbeef", "vb1qvx0emaq0tua6md7wu9c047mm5krrwnlfl8c7ws3jm2s9uf4vxcyvrwrazy", + "deadbeefdeadbeef", "vb1q0jym8jxnc0a4306y95j9m07tprxws6ccjz9h352tkcdfzfysh0jxll64dl", + "bead", "vb1qgtnc40hs73dxe2fgy5yvujnxmdnvg69w6fhj6drr68vqac525k2gkfdady", + "aaaaaaaaaaaaaaaa", + "vb1qgyz0t0kd9j4302q8429tl0mu3w8lm8nne8l2m9e8k74t3qm3xe9z8l2049")); - private final Map invalidAddresses = Map.of( - "vb1qvx0emaq0tua6md7wu9c047mm5krrwnlfl8c7ws3jm2s9uf4vxcyvrwrazz", "Bad checksum", - "xrd_rr1gd5j68", "Bad hrp", - "vb1qqweu28r", "Not enough bytes for public key" - ); + private final Map invalidAddresses = + Map.of( + "vb1qvx0emaq0tua6md7wu9c047mm5krrwnlfl8c7ws3jm2s9uf4vxcyvrwrazz", "Bad checksum", + "xrd_rr1gd5j68", "Bad hrp", + "vb1qqweu28r", "Not enough bytes for public key"); - @Test - public void test_validator_address_serialization() { - privateKeyToValidatorId.forEach((privHex, expectedAddress) -> { - var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); - var publicKey = keyPair.getPublicKey(); - var validatorAddress = validatorAddresses.of(publicKey); - assertThat(validatorAddress).isEqualTo(expectedAddress); - }); - } + @Test + public void test_validator_address_serialization() { + privateKeyToValidatorId.forEach( + (privHex, expectedAddress) -> { + var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); + var publicKey = keyPair.getPublicKey(); + var validatorAddress = validatorAddresses.of(publicKey); + assertThat(validatorAddress).isEqualTo(expectedAddress); + }); + } - @Test - public void test_correct_validator_address_deserialization() throws DeserializeException { - for (var e : privateKeyToValidatorId.entrySet()) { - var address = e.getValue(); - var privHex = e.getKey(); - var pubKey = validatorAddresses.parseOrThrow(address, IllegalStateException::new); - var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); - var expectedPubKey = keyPair.getPublicKey(); - assertThat(pubKey).isEqualTo(expectedPubKey); - } - } + @Test + public void test_correct_validator_address_deserialization() throws DeserializeException { + for (var e : privateKeyToValidatorId.entrySet()) { + var address = e.getValue(); + var privHex = e.getKey(); + var pubKey = validatorAddresses.parseOrThrow(address, IllegalStateException::new); + var keyPair = ECKeyPair.fromSeed(Bytes.fromHexString(privHex)); + var expectedPubKey = keyPair.getPublicKey(); + assertThat(pubKey).isEqualTo(expectedPubKey); + } + } - @Test - public void test_invalid_addresses() { - for (var e : invalidAddresses.entrySet()) { - var address = e.getKey(); - var expectedError = e.getValue(); - assertThatThrownBy(() -> validatorAddresses.parseOrThrow(address, IllegalStateException::new), expectedError) - .isInstanceOf(IllegalStateException.class); - } - } -} \ No newline at end of file + @Test + public void test_invalid_addresses() { + for (var e : invalidAddresses.entrySet()) { + var address = e.getKey(); + var expectedError = e.getValue(); + assertThatThrownBy( + () -> validatorAddresses.parseOrThrow(address, IllegalStateException::new), + expectedError) + .isInstanceOf(IllegalStateException.class); + } + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/InterfaceSerializationTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/InterfaceSerializationTest.java index 54fe19100f..ed52758cb6 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/InterfaceSerializationTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/InterfaceSerializationTest.java @@ -1,133 +1,134 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.middleware2; - -import com.radixdlt.serialization.DeserializeException; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.Serialization; -import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; -import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class InterfaceSerializationTest { - private final Serialization serialization = Serialization.create( - ClasspathScanningSerializerIds.create(), - ClasspathScanningSerializationPolicy.create() - ); - - @Test - public void one_subclass_can_be_serialized_and_deserialized_via_interface() throws Exception { - final var clientAtom = TestClientAtom.create("metadata"); - - final var json = serialization.toDson(clientAtom, Output.ALL); - final var obj = serialization.fromDson(json, TestLedgerAtom.class); - - assertEquals(obj, clientAtom); - } - - @Test - public void sibling_class_can_be_serialized_and_deserialized_via_interface() throws Exception { - final var clientAtom = TestDifferentClientAtom.create("datameta"); - - final var json = serialization.toDson(clientAtom, Output.ALL); - final var obj = serialization.fromDson(json, TestLedgerAtom.class); - - assertEquals(obj, clientAtom); - } - - @Test - public void deeper_inherited_class_can_be_serialized_and_deserialized_via_interface() throws Exception { - final var clientAtom = TestExtendedClientAtom.create("meta", "extra"); - - final var json = serialization.toDson(clientAtom, Output.ALL); - final var obj = serialization.fromDson(json, TestLedgerAtom.class); - - assertEquals(obj, clientAtom); - } - - @Test(expected = DeserializeException.class) - public void deeper_inherited_class_with_null_can_be_serialized_and_deserialized_via_interface() throws Exception { - final var clientAtom = new TestExtendedClientAtom("meta"); - - final var json = serialization.toDson(clientAtom, Output.ALL); - final var obj = serialization.fromDson(json, TestLedgerAtom.class); - - assertEquals(obj, clientAtom); - } - - @Test - public void embedded_interface_can_be_serialized_and_deserialized() throws Exception { - final var clientAtom = TestExtendedClientAtom.create("meta", "extra"); - final var container = TestEmbeddedInterfaceAtom.create(clientAtom); - - final var json = serialization.toDson(container, Output.ALL); - final var obj = serialization.fromDson(json, TestEmbeddedInterfaceAtom.class); - - assertEquals(obj, container); - assertEquals(obj.ledgerAtom(), clientAtom); - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2; + +import static org.junit.Assert.assertEquals; + +import com.radixdlt.serialization.DeserializeException; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.Serialization; +import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; +import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; +import org.junit.Test; + +public class InterfaceSerializationTest { + private final Serialization serialization = + Serialization.create( + ClasspathScanningSerializerIds.create(), ClasspathScanningSerializationPolicy.create()); + + @Test + public void one_subclass_can_be_serialized_and_deserialized_via_interface() throws Exception { + final var clientAtom = TestClientAtom.create("metadata"); + + final var json = serialization.toDson(clientAtom, Output.ALL); + final var obj = serialization.fromDson(json, TestLedgerAtom.class); + + assertEquals(obj, clientAtom); + } + + @Test + public void sibling_class_can_be_serialized_and_deserialized_via_interface() throws Exception { + final var clientAtom = TestDifferentClientAtom.create("datameta"); + + final var json = serialization.toDson(clientAtom, Output.ALL); + final var obj = serialization.fromDson(json, TestLedgerAtom.class); + + assertEquals(obj, clientAtom); + } + + @Test + public void deeper_inherited_class_can_be_serialized_and_deserialized_via_interface() + throws Exception { + final var clientAtom = TestExtendedClientAtom.create("meta", "extra"); + + final var json = serialization.toDson(clientAtom, Output.ALL); + final var obj = serialization.fromDson(json, TestLedgerAtom.class); + + assertEquals(obj, clientAtom); + } + + @Test(expected = DeserializeException.class) + public void deeper_inherited_class_with_null_can_be_serialized_and_deserialized_via_interface() + throws Exception { + final var clientAtom = new TestExtendedClientAtom("meta"); + + final var json = serialization.toDson(clientAtom, Output.ALL); + final var obj = serialization.fromDson(json, TestLedgerAtom.class); + + assertEquals(obj, clientAtom); + } + + @Test + public void embedded_interface_can_be_serialized_and_deserialized() throws Exception { + final var clientAtom = TestExtendedClientAtom.create("meta", "extra"); + final var container = TestEmbeddedInterfaceAtom.create(clientAtom); + + final var json = serialization.toDson(container, Output.ALL); + final var obj = serialization.fromDson(json, TestEmbeddedInterfaceAtom.class); + + assertEquals(obj, container); + assertEquals(obj.ledgerAtom(), clientAtom); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestClientAtom.java b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestClientAtom.java index e863e854e3..84c05d838d 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestClientAtom.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestClientAtom.java @@ -1,143 +1,141 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.middleware2; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.identifiers.AID; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; - -@SerializerId2("client_atom") -public class TestClientAtom implements TestLedgerAtom { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput({Output.ALL}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("metadata") - @DsonOutput({Output.ALL}) - private final String metaData; - - @JsonProperty("aid") - @DsonOutput({Output.ALL}) - private final AID aid; - - @JsonCreator - protected TestClientAtom( - @JsonProperty("aid") AID aid, - @JsonProperty("metadata") String metaData - ) { - this.aid = aid; - this.metaData = metaData == null ? "no metadata" : metaData; - } - - public static TestClientAtom create(String metadata) { - var id = AID.from(HashUtils.random(AID.BYTES).asBytes()); - return new TestClientAtom(id, metadata); - } - - public AID aid() { - return aid; - } - - public String metaData() { - return metaData; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TestClientAtom)) { - return false; - } - - TestClientAtom that = (TestClientAtom) o; - - if (!metaData.equals(that.metaData)) { - return false; - } - - return aid.equals(that.aid); - } - - @Override - public int hashCode() { - int result = metaData.hashCode(); - result = 31 * result + aid.hashCode(); - return result; - } - - @Override - public String toString() { - return "ClientAtom(metaData: '" + metaData + "', aid: " + aid + ')'; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.identifiers.AID; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; + +@SerializerId2("client_atom") +public class TestClientAtom implements TestLedgerAtom { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput({Output.ALL}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("metadata") + @DsonOutput({Output.ALL}) + private final String metaData; + + @JsonProperty("aid") + @DsonOutput({Output.ALL}) + private final AID aid; + + @JsonCreator + protected TestClientAtom( + @JsonProperty("aid") AID aid, @JsonProperty("metadata") String metaData) { + this.aid = aid; + this.metaData = metaData == null ? "no metadata" : metaData; + } + + public static TestClientAtom create(String metadata) { + var id = AID.from(HashUtils.random(AID.BYTES).asBytes()); + return new TestClientAtom(id, metadata); + } + + public AID aid() { + return aid; + } + + public String metaData() { + return metaData; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TestClientAtom)) { + return false; + } + + TestClientAtom that = (TestClientAtom) o; + + if (!metaData.equals(that.metaData)) { + return false; + } + + return aid.equals(that.aid); + } + + @Override + public int hashCode() { + int result = metaData.hashCode(); + result = 31 * result + aid.hashCode(); + return result; + } + + @Override + public String toString() { + return "ClientAtom(metaData: '" + metaData + "', aid: " + aid + ')'; + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestDifferentClientAtom.java b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestDifferentClientAtom.java index 8cdf846807..0e43786ef5 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestDifferentClientAtom.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestDifferentClientAtom.java @@ -1,143 +1,141 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.middleware2; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.identifiers.AID; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; - -@SerializerId2("different_client_atom") -public class TestDifferentClientAtom implements TestLedgerAtom { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput({Output.ALL}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("datameta") - @DsonOutput({Output.ALL}) - private final String metaData; - - @JsonProperty("dia") - @DsonOutput({Output.ALL}) - private final AID aid; - - @JsonCreator - private TestDifferentClientAtom( - @JsonProperty("dia") AID aid, - @JsonProperty("datameta") String metaData - ) { - this.aid = aid; - this.metaData = metaData == null ? "no metadata" : metaData; - } - - public static TestDifferentClientAtom create(String metadata) { - var id = AID.from(HashUtils.random(AID.BYTES).asBytes()); - return new TestDifferentClientAtom(id, metadata); - } - - public AID aid() { - return aid; - } - - public String metaData() { - return metaData; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TestDifferentClientAtom)) { - return false; - } - - TestDifferentClientAtom that = (TestDifferentClientAtom) o; - - if (!metaData.equals(that.metaData)) { - return false; - } - - return aid.equals(that.aid); - } - - @Override - public int hashCode() { - int result = metaData.hashCode(); - result = 31 * result + aid.hashCode(); - return result; - } - - @Override - public String toString() { - return "DifferentClientAtom(metaData: '" + metaData + "', aid: " + aid + ')'; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.identifiers.AID; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; + +@SerializerId2("different_client_atom") +public class TestDifferentClientAtom implements TestLedgerAtom { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput({Output.ALL}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("datameta") + @DsonOutput({Output.ALL}) + private final String metaData; + + @JsonProperty("dia") + @DsonOutput({Output.ALL}) + private final AID aid; + + @JsonCreator + private TestDifferentClientAtom( + @JsonProperty("dia") AID aid, @JsonProperty("datameta") String metaData) { + this.aid = aid; + this.metaData = metaData == null ? "no metadata" : metaData; + } + + public static TestDifferentClientAtom create(String metadata) { + var id = AID.from(HashUtils.random(AID.BYTES).asBytes()); + return new TestDifferentClientAtom(id, metadata); + } + + public AID aid() { + return aid; + } + + public String metaData() { + return metaData; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TestDifferentClientAtom)) { + return false; + } + + TestDifferentClientAtom that = (TestDifferentClientAtom) o; + + if (!metaData.equals(that.metaData)) { + return false; + } + + return aid.equals(that.aid); + } + + @Override + public int hashCode() { + int result = metaData.hashCode(); + result = 31 * result + aid.hashCode(); + return result; + } + + @Override + public String toString() { + return "DifferentClientAtom(metaData: '" + metaData + "', aid: " + aid + ')'; + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestEmbeddedInterfaceAtom.java b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestEmbeddedInterfaceAtom.java index ccf1737cfe..d605b4c141 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestEmbeddedInterfaceAtom.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestEmbeddedInterfaceAtom.java @@ -1,52 +1,116 @@ -package com.radixdlt.middleware2; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; - -@SerializerId2("embedded_interface_atom") -public class TestEmbeddedInterfaceAtom { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput({DsonOutput.Output.ALL}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("atom") - @DsonOutput({DsonOutput.Output.ALL}) - private final TestLedgerAtom ledgerAtom; - - @JsonCreator - private TestEmbeddedInterfaceAtom(@JsonProperty("atom") TestLedgerAtom ledgerAtom) { - this.ledgerAtom = ledgerAtom; - } - - public static TestEmbeddedInterfaceAtom create(final TestLedgerAtom ledgerAtom) { - return new TestEmbeddedInterfaceAtom(ledgerAtom); - } - - public TestLedgerAtom ledgerAtom() { - return ledgerAtom; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - return o instanceof TestEmbeddedInterfaceAtom - && ledgerAtom.equals(((TestEmbeddedInterfaceAtom) o).ledgerAtom); - } - - @Override - public int hashCode() { - return ledgerAtom.hashCode(); - } - - @Override - public String toString() { - return "TestEmbeddedInterfaceAtom(" + ledgerAtom + ')'; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; + +@SerializerId2("embedded_interface_atom") +public class TestEmbeddedInterfaceAtom { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput({DsonOutput.Output.ALL}) + SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("atom") + @DsonOutput({DsonOutput.Output.ALL}) + private final TestLedgerAtom ledgerAtom; + + @JsonCreator + private TestEmbeddedInterfaceAtom(@JsonProperty("atom") TestLedgerAtom ledgerAtom) { + this.ledgerAtom = ledgerAtom; + } + + public static TestEmbeddedInterfaceAtom create(final TestLedgerAtom ledgerAtom) { + return new TestEmbeddedInterfaceAtom(ledgerAtom); + } + + public TestLedgerAtom ledgerAtom() { + return ledgerAtom; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + return o instanceof TestEmbeddedInterfaceAtom + && ledgerAtom.equals(((TestEmbeddedInterfaceAtom) o).ledgerAtom); + } + + @Override + public int hashCode() { + return ledgerAtom.hashCode(); + } + + @Override + public String toString() { + return "TestEmbeddedInterfaceAtom(" + ledgerAtom + ')'; + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestExtendedClientAtom.java b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestExtendedClientAtom.java index 602e6c8d45..2ba66d606e 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestExtendedClientAtom.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestExtendedClientAtom.java @@ -1,120 +1,127 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.middleware2; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.identifiers.AID; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.SerializerId2; - -import static java.util.Objects.requireNonNull; - -@SerializerId2("extended_client_atom") -public class TestExtendedClientAtom extends TestClientAtom { - @JsonProperty("extra") - @DsonOutput({DsonOutput.Output.ALL}) - private final String extra; - - @JsonCreator - private TestExtendedClientAtom( - @JsonProperty("aid") AID aid, - @JsonProperty("metadata") String metaData, - @JsonProperty("extra") String extra - ) { - super(aid, metaData); - this.extra = requireNonNull(extra); - } - - public TestExtendedClientAtom(String metaData) { - super(AID.from(HashUtils.random(AID.BYTES).asBytes()), metaData); - this.extra = null; - } - - public static TestExtendedClientAtom create(String metadata, String extra) { - var id = AID.from(HashUtils.random(AID.BYTES).asBytes()); - return new TestExtendedClientAtom(id, metadata, extra); - } - - public String extra() { - return extra; - } - - @Override - public boolean equals(Object o) { - return super.equals(o) && (o instanceof TestExtendedClientAtom) && (extra.equals(((TestExtendedClientAtom) o).extra)); - } - - @Override - public int hashCode() { - return 31 * super.hashCode() + extra.hashCode(); - } - - @Override - public String toString() { - return "ExtendedClientAtom(aid: " + aid() + ", metaData: '" + metaData() + "', extra: '" + extra + "')"; - } -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.crypto.HashUtils; +import com.radixdlt.identifiers.AID; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.SerializerId2; + +@SerializerId2("extended_client_atom") +public class TestExtendedClientAtom extends TestClientAtom { + @JsonProperty("extra") + @DsonOutput({DsonOutput.Output.ALL}) + private final String extra; + + @JsonCreator + private TestExtendedClientAtom( + @JsonProperty("aid") AID aid, + @JsonProperty("metadata") String metaData, + @JsonProperty("extra") String extra) { + super(aid, metaData); + this.extra = requireNonNull(extra); + } + + public TestExtendedClientAtom(String metaData) { + super(AID.from(HashUtils.random(AID.BYTES).asBytes()), metaData); + this.extra = null; + } + + public static TestExtendedClientAtom create(String metadata, String extra) { + var id = AID.from(HashUtils.random(AID.BYTES).asBytes()); + return new TestExtendedClientAtom(id, metadata, extra); + } + + public String extra() { + return extra; + } + + @Override + public boolean equals(Object o) { + return super.equals(o) + && (o instanceof TestExtendedClientAtom) + && (extra.equals(((TestExtendedClientAtom) o).extra)); + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + extra.hashCode(); + } + + @Override + public String toString() { + return "ExtendedClientAtom(aid: " + + aid() + + ", metaData: '" + + metaData() + + "', extra: '" + + extra + + "')"; + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestLedgerAtom.java b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestLedgerAtom.java index b4a745f8ed..d3518c1399 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestLedgerAtom.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/middleware2/TestLedgerAtom.java @@ -1,71 +1,70 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.middleware2; - -import com.radixdlt.serialization.SerializerRoot; - -@SerializerRoot -public interface TestLedgerAtom { -} +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.middleware2; + +import com.radixdlt.serialization.SerializerRoot; + +@SerializerRoot +public interface TestLedgerAtom {} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ApiSerializationModifierTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ApiSerializationModifierTest.java index af94118b8c..5953904963 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ApiSerializationModifierTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ApiSerializationModifierTest.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.serialization; +import static org.junit.Assert.*; + import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; @@ -7,55 +73,57 @@ import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; - public class ApiSerializationModifierTest { - @SerializeWithHid - private static final class DummyWithHid { - @JsonProperty - public int getDummy() { - return 2; - } + @SerializeWithHid + private static final class DummyWithHid { + @JsonProperty + public int getDummy() { + return 2; } + } - private static final class DummyWithoutHid { - @JsonProperty - public int getDummy() { - return 2; - } + private static final class DummyWithoutHid { + @JsonProperty + public int getDummy() { + return 2; } + } - private Serialization serialization; + private Serialization serialization; - @Before - public void setup() { - serialization = Serialization.create(ClasspathScanningSerializerIds.create(), ClasspathScanningSerializationPolicy.create()); - } + @Before + public void setup() { + serialization = + Serialization.create( + ClasspathScanningSerializerIds.create(), ClasspathScanningSerializationPolicy.create()); + } - @Test - public void testIncludeHidFieldInJson() { - JSONObject output = serialization.toJsonObject(new DummyWithHid(), DsonOutput.Output.API); - assertEquals(":hsh:b0aace23265c295eb13464b5b97cf57d1a227a02c4c7042ab7daae1df1eb6e6a", output.getString("hid")); - } + @Test + public void testIncludeHidFieldInJson() { + JSONObject output = serialization.toJsonObject(new DummyWithHid(), DsonOutput.Output.API); + assertEquals( + ":hsh:b0aace23265c295eb13464b5b97cf57d1a227a02c4c7042ab7daae1df1eb6e6a", + output.getString("hid")); + } - @Test - public void testIncludeHidFieldInDson() throws DeserializeException { - byte[] output = serialization.toDson(new DummyWithHid(), DsonOutput.Output.API); - JSONObject obj = serialization.fromDson(output, JSONObject.class); - assertTrue(obj.has("hid")); - } + @Test + public void testIncludeHidFieldInDson() throws DeserializeException { + byte[] output = serialization.toDson(new DummyWithHid(), DsonOutput.Output.API); + JSONObject obj = serialization.fromDson(output, JSONObject.class); + assertTrue(obj.has("hid")); + } - @Test - public void testDontIncludeHidFieldInJson() { - JSONObject output = serialization.toJsonObject(new DummyWithoutHid(), DsonOutput.Output.API); - assertFalse(output.has("hid")); - } + @Test + public void testDontIncludeHidFieldInJson() { + JSONObject output = serialization.toJsonObject(new DummyWithoutHid(), DsonOutput.Output.API); + assertFalse(output.has("hid")); + } - @Test - public void testDontIncludeHidFieldInDson() throws DeserializeException { - byte[] output = serialization.toDson(new DummyWithoutHid(), DsonOutput.Output.API); - JSONObject obj = serialization.fromDson(output, JSONObject.class); - assertFalse(obj.has("hid")); - } + @Test + public void testDontIncludeHidFieldInDson() throws DeserializeException { + byte[] output = serialization.toDson(new DummyWithoutHid(), DsonOutput.Output.API); + JSONObject obj = serialization.fromDson(output, JSONObject.class); + assertFalse(obj.has("hid")); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/CBORLengthTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/CBORLengthTest.java index c6afecc72e..addcb3c67f 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/CBORLengthTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/CBORLengthTest.java @@ -75,26 +75,26 @@ public class CBORLengthTest { - private ObjectMapper cborMapper; + private ObjectMapper cborMapper; - @Before - public void setupCborMapper() { - cborMapper = new ObjectMapper(new CBORFactory()); - } + @Before + public void setupCborMapper() { + cborMapper = new ObjectMapper(new CBORFactory()); + } - @Test - // Check that length is encoded in a single byte for 23 character strings - public void testJacksonEncodesCBORLengthsCorrectly() throws JsonProcessingException { - byte[] expectedData = Bytes.fromHexString("7772616469782e7061727469636c65732e6d657373616765"); - byte[] data = cborMapper.writeValueAsBytes("radix.particles.message"); - assertArrayEquals(expectedData, data); - } + @Test + // Check that length is encoded in a single byte for 23 character strings + public void testJacksonEncodesCBORLengthsCorrectly() throws JsonProcessingException { + byte[] expectedData = Bytes.fromHexString("7772616469782e7061727469636c65732e6d657373616765"); + byte[] data = cborMapper.writeValueAsBytes("radix.particles.message"); + assertArrayEquals(expectedData, data); + } - @Test - // Check that the value 23 is encoded in a single byte for integers - public void testJacksonEncodesCBORIntsCorrectly() throws JsonProcessingException { - byte[] expectedData = Bytes.fromHexString("17"); - byte[] data = cborMapper.writeValueAsBytes(23); - assertArrayEquals(expectedData, data); - } + @Test + // Check that the value 23 is encoded in a single byte for integers + public void testJacksonEncodesCBORIntsCorrectly() throws JsonProcessingException { + byte[] expectedData = Bytes.fromHexString("17"); + byte[] data = cborMapper.writeValueAsBytes(23); + assertArrayEquals(expectedData, data); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ECDSASignatureSerializationTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ECDSASignatureSerializationTest.java index 8c7f911571..e6e2316437 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ECDSASignatureSerializationTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/ECDSASignatureSerializationTest.java @@ -1,30 +1,91 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.serialization; import com.radixdlt.TestSetupUtils; import com.radixdlt.crypto.ECDSASignature; -import org.junit.BeforeClass; - import java.math.BigInteger; import java.util.Random; import java.util.function.Supplier; +import org.junit.BeforeClass; -/** - * JSON Serialization round trip of {@link ECDSASignature} - */ +/** JSON Serialization round trip of {@link ECDSASignature} */ public class ECDSASignatureSerializationTest extends SerializeObjectEngine { + public ECDSASignatureSerializationTest() { + super(ECDSASignature.class, ECDSASignatureSerializationTest::getECDSASignature); + } - public ECDSASignatureSerializationTest() { - super(ECDSASignature.class, ECDSASignatureSerializationTest::getECDSASignature); - } - - @BeforeClass - public static void startRadixTest() { - TestSetupUtils.installBouncyCastleProvider(); - } + @BeforeClass + public static void startRadixTest() { + TestSetupUtils.installBouncyCastleProvider(); + } - private static ECDSASignature getECDSASignature() { - Supplier randomBigInt = () -> BigInteger.valueOf(new Random().nextLong()); - return ECDSASignature.create(randomBigInt.get(), randomBigInt.get(), randomBigInt.get().signum()); - } + private static ECDSASignature getECDSASignature() { + Supplier randomBigInt = () -> BigInteger.valueOf(new Random().nextLong()); + return ECDSASignature.create( + randomBigInt.get(), randomBigInt.get(), randomBigInt.get().signum()); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/MapHelperTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/MapHelperTest.java index 7733cb9693..9b97b290cc 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/MapHelperTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/MapHelperTest.java @@ -70,67 +70,69 @@ import java.util.Map; import org.junit.Test; -/** - * Basic tests for {@link MapHelper}. - */ +/** Basic tests for {@link MapHelper}. */ public class MapHelperTest { - @Test - public void testMapOf1() { - Map map = MapHelper.mapOf("k1", 1L); - assertEquals(map, ImmutableMap.of("k1", 1L)); - } + @Test + public void testMapOf1() { + Map map = MapHelper.mapOf("k1", 1L); + assertEquals(map, ImmutableMap.of("k1", 1L)); + } - @Test - public void testMapOf2() { - Map map = MapHelper.mapOf("k1", 1L, "k2", 2L); - assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L)); - } + @Test + public void testMapOf2() { + Map map = MapHelper.mapOf("k1", 1L, "k2", 2L); + assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L)); + } - @Test - public void testMapOf3() { - Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L); - assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L, "k3", 3L)); - } + @Test + public void testMapOf3() { + Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L); + assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L, "k3", 3L)); + } - @Test - public void testMapOf4() { - Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L); - assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L)); - } + @Test + public void testMapOf4() { + Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L); + assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L)); + } - @Test - public void testMapOf5() { - Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L); - assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L)); - } + @Test + public void testMapOf5() { + Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L); + assertEquals(map, ImmutableMap.of("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L)); + } - @Test - public void testMapOf6() { - Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L, "k6", 6L); - ImmutableMap imap = new ImmutableMap.Builder() - .put("k1", 1L) - .put("k2", 2L) - .put("k3", 3L) - .put("k4", 4L) - .put("k5", 5L) - .put("k6", 6L) - .build(); - assertEquals(map, imap); - } + @Test + public void testMapOf6() { + Map map = + MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L, "k6", 6L); + ImmutableMap imap = + new ImmutableMap.Builder() + .put("k1", 1L) + .put("k2", 2L) + .put("k3", 3L) + .put("k4", 4L) + .put("k5", 5L) + .put("k6", 6L) + .build(); + assertEquals(map, imap); + } - @Test - public void testMapOf7() { - Map map = MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L, "k6", 6L, "k7", 7L); - ImmutableMap imap = new ImmutableMap.Builder() - .put("k1", 1L) - .put("k2", 2L) - .put("k3", 3L) - .put("k4", 4L) - .put("k5", 5L) - .put("k6", 6L) - .put("k7", 7L) - .build(); - assertEquals(map, imap); - } + @Test + public void testMapOf7() { + Map map = + MapHelper.mapOf("k1", 1L, "k2", 2L, "k3", 3L, "k4", 4L, "k5", 5L, "k6", 6L, "k7", 7L); + ImmutableMap imap = + new ImmutableMap.Builder() + .put("k1", 1L) + .put("k2", 2L) + .put("k3", 3L) + .put("k4", 4L) + .put("k5", 5L) + .put("k6", 6L) + .put("k7", 7L) + .build(); + assertEquals(map, imap); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java index 66c51ec3f2..922dca9dd7 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializationTestUtilsEngine.java @@ -1,39 +1,101 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.serialization; +import static org.junit.Assert.fail; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; - import java.io.IOException; -import static org.junit.Assert.fail; - -/** - * Utilities for test encoding and decoding with serializers. - */ +/** Utilities for test encoding and decoding with serializers. */ final class SerializationTestUtilsEngine { - private SerializationTestUtilsEngine() { - throw new IllegalStateException("Can't construct"); - } + private SerializationTestUtilsEngine() { + throw new IllegalStateException("Can't construct"); + } - static void testEncodeDecode(T target, Class cls, Serialization serialization, DsonOutput.Output output) - throws Exception { - String json1 = serialization.toJson(target, output); - T newTarget = serialization.fromJson(json1, cls); - String json2 = serialization.toJson(newTarget, output); - compareJson(json1, json2); - } + static void testEncodeDecode( + T target, Class cls, Serialization serialization, DsonOutput.Output output) + throws Exception { + String json1 = serialization.toJson(target, output); + T newTarget = serialization.fromJson(json1, cls); + String json2 = serialization.toJson(newTarget, output); + compareJson(json1, json2); + } - static void compareJson(String s1Json, String s2Json) throws IOException { - ObjectMapper om = new ObjectMapper(); - om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); - om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); - JsonNode s1Tree = om.readTree(s1Json); - JsonNode s2Tree = om.readTree(s2Json); - if (!s1Tree.equals(s2Tree)) { - fail("Not equivalent JSON"); - } + static void compareJson(String s1Json, String s2Json) throws IOException { + ObjectMapper om = new ObjectMapper(); + om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); + JsonNode s1Tree = om.readTree(s1Json); + JsonNode s2Tree = om.readTree(s2Json); + if (!s1Tree.equals(s2Tree)) { + fail("Not equivalent JSON"); } + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java index 22f9fe3ba6..64f8346774 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/SerializeObjectEngine.java @@ -1,66 +1,127 @@ -package com.radixdlt.serialization; - -import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; -import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; -import org.junit.Test; +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ -import java.util.function.Supplier; +package com.radixdlt.serialization; import static com.radixdlt.serialization.SerializationTestUtilsEngine.testEncodeDecode; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeFalse; +import com.radixdlt.serialization.core.ClasspathScanningSerializationPolicy; +import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; +import java.util.function.Supplier; +import org.junit.Test; + /** * Raft of tests for serialization of objects. - *

- * Note that the tests that round-trip types through the serializer - * are not run for {@link Polymorphic} types, as these types do not - * serialize to themselves, but to one of their superclasses. + * + *

Note that the tests that round-trip types through the serializer are not run for {@link + * Polymorphic} types, as these types do not serialize to themselves, but to one of their + * superclasses. * * @param The type under test. */ public abstract class SerializeObjectEngine { - private final Class cls; - private final Supplier factory; - private final Serialization serialization = Serialization.create( - ClasspathScanningSerializerIds.create(), - ClasspathScanningSerializationPolicy.create() - ); - + private final Class cls; + private final Supplier factory; + private final Serialization serialization = + Serialization.create( + ClasspathScanningSerializerIds.create(), ClasspathScanningSerializationPolicy.create()); - protected SerializeObjectEngine(Class cls, Supplier factory) { - this.cls = cls; - this.factory = factory; - } + protected SerializeObjectEngine(Class cls, Supplier factory) { + this.cls = cls; + this.factory = factory; + } - @Test - public void testNONEIsEmpty() throws Exception { - String s2Json = this.serialization.toJson(factory.get(), DsonOutput.Output.NONE); - assertEquals("{}", s2Json); - } + @Test + public void testNONEIsEmpty() throws Exception { + String s2Json = this.serialization.toJson(factory.get(), DsonOutput.Output.NONE); + assertEquals("{}", s2Json); + } - @Test - public void testEncodeDecodeALL() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.ALL); - } + @Test + public void testEncodeDecodeALL() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.ALL); + } - @Test - public void testEncodeDecodeAPI() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.API); - } + @Test + public void testEncodeDecodeAPI() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.API); + } - @Test - public void testEncodeDecodePERSIST() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.PERSIST); - } + @Test + public void testEncodeDecodePERSIST() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.PERSIST); + } - @Test - public void testEncodeDecodeWIRE() throws Exception { - assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); - testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.WIRE); - } + @Test + public void testEncodeDecodeWIRE() throws Exception { + assumeFalse("Not applicable for polymorphic classes", Polymorphic.class.isAssignableFrom(cls)); + testEncodeDecode(factory.get(), cls, this.serialization, DsonOutput.Output.WIRE); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfiguratorTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfiguratorTest.java index aeadd3fa0f..1056f0ad6d 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfiguratorTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/serialization/mapper/RadixObjectMapperConfiguratorTest.java @@ -1,242 +1,238 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -package com.radixdlt.serialization.mapper; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableList; -import com.radixdlt.serialization.ClassScanningSerializationPolicy; -import com.radixdlt.serialization.DsonOutput; -import com.radixdlt.serialization.DsonOutput.Output; -import com.radixdlt.serialization.Serialization; -import com.radixdlt.serialization.SerializerConstants; -import com.radixdlt.serialization.SerializerDummy; -import com.radixdlt.serialization.SerializerId2; -import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class RadixObjectMapperConfiguratorTest { - private final Serialization serialization = Serialization.create( - ClasspathScanningSerializerIds.create(), - new ClassScanningSerializationPolicy( - ImmutableList.of(OrderedPropertyBean.class, OrderedPropertyBean2.class) - ) { } - ); - - @Test - public void propertyOrderingIsCorrect() { - var inputBean = - new OrderedPropertyBean("1", "2", "3", "4").propertyC("5"); - - var serialized = serialization.toDson(inputBean, Output.ALL); - - // Deciphered expected value (remaining parts are omitted since they're irrelevant for test): - // (leading character contains encoded length, a = 1, j = 10, v = 22) - // aa -> field name "a" - // a3 -> field content "3" - // ab -> field name "b" - // a1 -> field content "1" - // ac -> field name "c" - // a5 -> field content "5" - // bsz -> field name "bsz" - // vtest.property_ordering -> field content "test.property_ordering" - // ax -> field name "x" - // a4 -> field content "4" - // az -> field name "z" - // a2 -> field content "2" - assertEquals(">aaa3aba1aca5bszvtest.property_orderingaxa4aza2~", toText(serialized)); - } - - @Test - public void propertyOrderingForDerivedBeanIsCorrect() { - var inputBean = - new OrderedPropertyBean2("1", "2", "3", "4").propertyG("6"); - - var serialized = serialization.toDson(inputBean, Output.ALL); - - //Deciphered expected value (remaining parts are omitted since they're irrelevant for test): - // (leading character(s) contains encoded length, a = 1, j = 10, x? = 31) - // aa -> field name "a" - // a3 -> field content "3" - // ab -> field name "b" - // a2 -> field content "2" - // ac -> field name "c" - // a1 -> field content "1" - // af -> field name "f" - // a4 -> field content "4" - // ag -> field name "g" - // a6 -> field content "6" - // bsz -> field name "sz" - // x?test.extended_property_ordering -> field content "test.extended_property_ordering" - assertEquals(">aaa3aba2aca1afa4aga6bszx?test.extended_property_ordering~", toText(serialized)); - } - - //Somewhat artificial transformation to keep output within printable range - private static String toText(byte[] input) { - var output = new char[input.length]; - - for (int i = 0; i < input.length; i++) { - output[i] = input[i] > 0 ? input[i] < 32 ? '?' : (char) input[i] : (char) (127 + input[i]); - } - return new String(output); - } - - @SerializerId2("test.property_ordering") - public static class OrderedPropertyBean { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("z") - @DsonOutput(Output.ALL) - private final String propertyZ; - - @JsonProperty("x") - @DsonOutput(Output.ALL) - private final String propertyX; - - @JsonProperty("a") - @DsonOutput(Output.ALL) - private final String propertyA; - - @JsonProperty("c") - @DsonOutput(Output.ALL) - private String propertyC; - - @JsonProperty("b") - @DsonOutput(Output.ALL) - private final String propertyB; - - @JsonCreator - public OrderedPropertyBean( - @JsonProperty("b") String propertyB, - @JsonProperty("z") String propertyZ, - @JsonProperty("a") String propertyA, - @JsonProperty("x") String propertyX) { - this.propertyZ = propertyZ; - this.propertyX = propertyX; - this.propertyA = propertyA; - this.propertyB = propertyB; - } - - public OrderedPropertyBean propertyC(String propertyC) { - this.propertyC = propertyC; - return this; - } - } - - public abstract static class AbstractOrderedPropertyBean { - @JsonProperty("a") - @DsonOutput(Output.ALL) - private final String propertyA; - - @JsonProperty("c") - @DsonOutput(Output.ALL) - private final String propertyC; - - @JsonProperty("b") - @DsonOutput(Output.ALL) - private final String propertyB; - - public AbstractOrderedPropertyBean(String propertyA, String propertyC, String propertyB) { - this.propertyA = propertyA; - this.propertyC = propertyC; - this.propertyB = propertyB; - } - } - - @SerializerId2("test.extended_property_ordering") - public static class OrderedPropertyBean2 extends AbstractOrderedPropertyBean { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(Output.ALL) - private SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("f") - @DsonOutput(Output.ALL) - private final String propertyF; - - @JsonProperty("g") - @DsonOutput(Output.ALL) - private String propertyG; - - @JsonCreator - public OrderedPropertyBean2( - @JsonProperty("b") String propertyB, - @JsonProperty("c") String propertyC, - @JsonProperty("a") String propertyA, - @JsonProperty("f") String propertyF - ) { - super(propertyA, propertyB, propertyC); - this.propertyF = propertyF; - } - - public OrderedPropertyBean2 propertyG(String propertyG) { - this.propertyG = propertyG; - return this; - } - } -} \ No newline at end of file +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +package com.radixdlt.serialization.mapper; + +import static org.junit.Assert.assertEquals; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.radixdlt.serialization.ClassScanningSerializationPolicy; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.Serialization; +import com.radixdlt.serialization.SerializerConstants; +import com.radixdlt.serialization.SerializerDummy; +import com.radixdlt.serialization.SerializerId2; +import com.radixdlt.serialization.core.ClasspathScanningSerializerIds; +import org.junit.Test; + +public class RadixObjectMapperConfiguratorTest { + private final Serialization serialization = + Serialization.create( + ClasspathScanningSerializerIds.create(), + new ClassScanningSerializationPolicy( + ImmutableList.of(OrderedPropertyBean.class, OrderedPropertyBean2.class)) {}); + + @Test + public void propertyOrderingIsCorrect() { + var inputBean = new OrderedPropertyBean("1", "2", "3", "4").propertyC("5"); + + var serialized = serialization.toDson(inputBean, Output.ALL); + + // Deciphered expected value (remaining parts are omitted since they're irrelevant for test): + // (leading character contains encoded length, a = 1, j = 10, v = 22) + // aa -> field name "a" + // a3 -> field content "3" + // ab -> field name "b" + // a1 -> field content "1" + // ac -> field name "c" + // a5 -> field content "5" + // bsz -> field name "bsz" + // vtest.property_ordering -> field content "test.property_ordering" + // ax -> field name "x" + // a4 -> field content "4" + // az -> field name "z" + // a2 -> field content "2" + assertEquals(">aaa3aba1aca5bszvtest.property_orderingaxa4aza2~", toText(serialized)); + } + + @Test + public void propertyOrderingForDerivedBeanIsCorrect() { + var inputBean = new OrderedPropertyBean2("1", "2", "3", "4").propertyG("6"); + + var serialized = serialization.toDson(inputBean, Output.ALL); + + // Deciphered expected value (remaining parts are omitted since they're irrelevant for test): + // (leading character(s) contains encoded length, a = 1, j = 10, x? = 31) + // aa -> field name "a" + // a3 -> field content "3" + // ab -> field name "b" + // a2 -> field content "2" + // ac -> field name "c" + // a1 -> field content "1" + // af -> field name "f" + // a4 -> field content "4" + // ag -> field name "g" + // a6 -> field content "6" + // bsz -> field name "sz" + // x?test.extended_property_ordering -> field content "test.extended_property_ordering" + assertEquals(">aaa3aba2aca1afa4aga6bszx?test.extended_property_ordering~", toText(serialized)); + } + + // Somewhat artificial transformation to keep output within printable range + private static String toText(byte[] input) { + var output = new char[input.length]; + + for (int i = 0; i < input.length; i++) { + output[i] = input[i] > 0 ? input[i] < 32 ? '?' : (char) input[i] : (char) (127 + input[i]); + } + return new String(output); + } + + @SerializerId2("test.property_ordering") + public static class OrderedPropertyBean { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("z") + @DsonOutput(Output.ALL) + private final String propertyZ; + + @JsonProperty("x") + @DsonOutput(Output.ALL) + private final String propertyX; + + @JsonProperty("a") + @DsonOutput(Output.ALL) + private final String propertyA; + + @JsonProperty("c") + @DsonOutput(Output.ALL) + private String propertyC; + + @JsonProperty("b") + @DsonOutput(Output.ALL) + private final String propertyB; + + @JsonCreator + public OrderedPropertyBean( + @JsonProperty("b") String propertyB, + @JsonProperty("z") String propertyZ, + @JsonProperty("a") String propertyA, + @JsonProperty("x") String propertyX) { + this.propertyZ = propertyZ; + this.propertyX = propertyX; + this.propertyA = propertyA; + this.propertyB = propertyB; + } + + public OrderedPropertyBean propertyC(String propertyC) { + this.propertyC = propertyC; + return this; + } + } + + public abstract static class AbstractOrderedPropertyBean { + @JsonProperty("a") + @DsonOutput(Output.ALL) + private final String propertyA; + + @JsonProperty("c") + @DsonOutput(Output.ALL) + private final String propertyC; + + @JsonProperty("b") + @DsonOutput(Output.ALL) + private final String propertyB; + + public AbstractOrderedPropertyBean(String propertyA, String propertyC, String propertyB) { + this.propertyA = propertyA; + this.propertyC = propertyC; + this.propertyB = propertyB; + } + } + + @SerializerId2("test.extended_property_ordering") + public static class OrderedPropertyBean2 extends AbstractOrderedPropertyBean { + @JsonProperty(SerializerConstants.SERIALIZER_NAME) + @DsonOutput(Output.ALL) + private SerializerDummy serializer = SerializerDummy.DUMMY; + + @JsonProperty("f") + @DsonOutput(Output.ALL) + private final String propertyF; + + @JsonProperty("g") + @DsonOutput(Output.ALL) + private String propertyG; + + @JsonCreator + public OrderedPropertyBean2( + @JsonProperty("b") String propertyB, + @JsonProperty("c") String propertyC, + @JsonProperty("a") String propertyA, + @JsonProperty("f") String propertyF) { + super(propertyA, propertyB, propertyC); + this.propertyF = propertyF; + } + + public OrderedPropertyBean2 propertyG(String propertyG) { + this.propertyG = propertyG; + return this; + } + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/BytesTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/BytesTest.java index 052aec0e1f..d1564aabfc 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/BytesTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/BytesTest.java @@ -70,210 +70,173 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import org.junit.Test; - import java.math.BigInteger; +import org.junit.Test; public class BytesTest { - /** - * Test that partial array compares for equality work. - */ - @Test - public void testArrayEquals() { - byte[] array1 = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - }; - byte[] array2 = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - }; - - // Parts of array the same at non-matching indices - assertTrue(Bytes.arrayEquals(array1, 1, 5, array2, 0, 5)); - // Mismatch due to length - assertFalse(Bytes.arrayEquals(array1, 1, 5, array2, 0, 4)); - // Mismatch due to non-equal data - assertFalse(Bytes.arrayEquals(array1, 0, 5, array2, 0, 5)); - } - - /** - * Test that hash codes for partial arrays are equal. - */ - @Test - public void testHashCode() { - byte[] array1 = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - }; - byte[] array2 = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - }; - - for (int i = 0; i < 10; ++i) { - assertEquals(Bytes.hashCode(array1, 1, i), Bytes.hashCode(array2, 0, i)); - } - } - - /** - * Test conversion from byte to hex string. - */ - @Test - public void testToHexStringByte() { - for (int i = 0; i < 0x100; ++i) { - String base = String.format("%02x", i); - String convert = Bytes.toHexString((byte) i); - assertEquals(base, convert); - } - } - - /** - * Test conversion from array of bytes to hex string. - */ - @Test - public void testToHexStringByteArray() { - byte[] bytes = new byte[256]; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 256; ++i) { - bytes[i] = (byte) i; - sb.append(String.format("%02x", i)); - } - String base = sb.toString(); - assertEquals(base, Bytes.toHexString(bytes)); - } - - /** - * Test conversion from partial array to hex string. - */ - @Test - public void testToHexStringPartialByteArray() { - byte[] bytes = new byte[256]; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 256; ++i) { - bytes[i] = (byte) i; - sb.append(String.format("%02x", i)); - } - String base = sb.toString(); - - // Offset 0, varying lengths - for (int i = 0; i < 256; ++i) { - assertEquals(base.substring(0, i * 2), Bytes.toHexString(bytes, 0, i)); - } - // Varying offsets, fixed length - for (int i = 0; i < 200; ++i) { - assertEquals(base.substring(i * 2, i * 2 + 20), Bytes.toHexString(bytes, i, 10)); - } - } - - /** - * Various test cases for conversion of string to byte array. - */ - @Test - public void testFromHexString() { - // Single byte - byte[] expected1 = { - (byte) 0xAA - }; - assertArrayEquals(expected1, Bytes.fromHexString("AA")); - assertArrayEquals(expected1, Bytes.fromHexString("aa")); - assertArrayEquals(expected1, Bytes.fromHexString("aA")); - // two bytes - byte[] expected2 = { - (byte) 0xAB, (byte) 0xCD - }; - assertArrayEquals(expected2, Bytes.fromHexString("ABCD")); - assertArrayEquals(expected2, Bytes.fromHexString("abcd")); - // two and a half bytes - byte[] expected3 = { - (byte) 0x0A, (byte) 0xBC, (byte) 0xDE - }; - assertArrayEquals(expected3, Bytes.fromHexString("ABCDE")); - assertArrayEquals(expected3, Bytes.fromHexString("abcde")); - // four bytes - byte[] expected4 = { - (byte) 0xAB, (byte) 0xCD, (byte) 0xEF - }; - assertArrayEquals(expected4, Bytes.fromHexString("ABCDEF")); - assertArrayEquals(expected4, Bytes.fromHexString("abcdef")); - // eight bytes - byte[] expected8 = { - 0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF - }; - assertArrayEquals(expected8, Bytes.fromHexString("0123456789ABCDEF")); - assertArrayEquals(expected8, Bytes.fromHexString("0123456789abcdef")); - - // Invalid characters - assertThatThrownBy(() -> Bytes.fromHexString("!")).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> Bytes.fromHexString(":")).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> Bytes.fromHexString("[")).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> Bytes.fromHexString("~")).isInstanceOf(IllegalArgumentException.class); - } - - /** - * Various test cases for trimLeadingZeros. - */ - @Test - public void testTrimLeadingZeros() { - assertEquals(null, Bytes.trimLeadingZeros(null)); // Null -> noop - - byte[] emptyBytes = new byte[0]; - assertEquals(emptyBytes, Bytes.trimLeadingZeros(emptyBytes)); // Empty -> noop - - // All size 1 byte arrays -> noop - for (int i = 0; i < 255; ++i) { - byte[] oneByte = new byte[1]; - oneByte[0] = (byte) i; - assertEquals(oneByte, Bytes.trimLeadingZeros(oneByte)); - } - - // All size 2 byte arrays -> trimmed - for (int i = 0; i < 255; ++i) { - byte[] oneByte = new byte[1]; - byte[] twoBytes = new byte[2]; - oneByte[0] = (byte) i; - twoBytes[1] = (byte) i; - assertArrayEquals(oneByte, Bytes.trimLeadingZeros(twoBytes)); - } - - byte[] noLeadingZeros = new byte[] { - 1, 2, 3, 4, 5, 6, 7, 8, 9 - }; - assertEquals(noLeadingZeros, Bytes.trimLeadingZeros(noLeadingZeros)); - - byte[] singleZero = new byte[1]; - byte[] severalZeros = new byte[10]; - assertArrayEquals(singleZero, Bytes.trimLeadingZeros(severalZeros)); - - byte[] zeroRemoved = new byte[] { - 1, 2, 3, 4, 5, 6, 7, 8, 9 - }; - byte[] withZero = new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - }; - assertArrayEquals(zeroRemoved, Bytes.trimLeadingZeros(withZero)); - } - - @Test - public void testBigIntegerToBytes() { - final var one32 = new byte[] { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - }; - assertArrayEquals(one32, Bytes.bigIntegerToBytes(BigInteger.ONE, 32)); - - final var test2 = new byte[] { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, (byte) 2, (byte) 1 - }; - assertArrayEquals(test2, Bytes.bigIntegerToBytes(BigInteger.valueOf(513), 32)); - } - - @Test - public void testXor() { - final var b1 = new byte[] {1, 0, 1, 1, 1, 0, 0}; - final var b2 = new byte[] {0, 0, 0, 0, 1, 0, 1}; - assertArrayEquals(new byte[] {1, 0, 1, 1, 0, 0, 1}, Bytes.xor(b1, b2)); - } + /** Test that partial array compares for equality work. */ + @Test + public void testArrayEquals() { + byte[] array1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] array2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + // Parts of array the same at non-matching indices + assertTrue(Bytes.arrayEquals(array1, 1, 5, array2, 0, 5)); + // Mismatch due to length + assertFalse(Bytes.arrayEquals(array1, 1, 5, array2, 0, 4)); + // Mismatch due to non-equal data + assertFalse(Bytes.arrayEquals(array1, 0, 5, array2, 0, 5)); + } + + /** Test that hash codes for partial arrays are equal. */ + @Test + public void testHashCode() { + byte[] array1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] array2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + for (int i = 0; i < 10; ++i) { + assertEquals(Bytes.hashCode(array1, 1, i), Bytes.hashCode(array2, 0, i)); + } + } + + /** Test conversion from byte to hex string. */ + @Test + public void testToHexStringByte() { + for (int i = 0; i < 0x100; ++i) { + String base = String.format("%02x", i); + String convert = Bytes.toHexString((byte) i); + assertEquals(base, convert); + } + } + + /** Test conversion from array of bytes to hex string. */ + @Test + public void testToHexStringByteArray() { + byte[] bytes = new byte[256]; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 256; ++i) { + bytes[i] = (byte) i; + sb.append(String.format("%02x", i)); + } + String base = sb.toString(); + assertEquals(base, Bytes.toHexString(bytes)); + } + + /** Test conversion from partial array to hex string. */ + @Test + public void testToHexStringPartialByteArray() { + byte[] bytes = new byte[256]; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 256; ++i) { + bytes[i] = (byte) i; + sb.append(String.format("%02x", i)); + } + String base = sb.toString(); + + // Offset 0, varying lengths + for (int i = 0; i < 256; ++i) { + assertEquals(base.substring(0, i * 2), Bytes.toHexString(bytes, 0, i)); + } + // Varying offsets, fixed length + for (int i = 0; i < 200; ++i) { + assertEquals(base.substring(i * 2, i * 2 + 20), Bytes.toHexString(bytes, i, 10)); + } + } + + /** Various test cases for conversion of string to byte array. */ + @Test + public void testFromHexString() { + // Single byte + byte[] expected1 = {(byte) 0xAA}; + assertArrayEquals(expected1, Bytes.fromHexString("AA")); + assertArrayEquals(expected1, Bytes.fromHexString("aa")); + assertArrayEquals(expected1, Bytes.fromHexString("aA")); + // two bytes + byte[] expected2 = {(byte) 0xAB, (byte) 0xCD}; + assertArrayEquals(expected2, Bytes.fromHexString("ABCD")); + assertArrayEquals(expected2, Bytes.fromHexString("abcd")); + // two and a half bytes + byte[] expected3 = {(byte) 0x0A, (byte) 0xBC, (byte) 0xDE}; + assertArrayEquals(expected3, Bytes.fromHexString("ABCDE")); + assertArrayEquals(expected3, Bytes.fromHexString("abcde")); + // four bytes + byte[] expected4 = {(byte) 0xAB, (byte) 0xCD, (byte) 0xEF}; + assertArrayEquals(expected4, Bytes.fromHexString("ABCDEF")); + assertArrayEquals(expected4, Bytes.fromHexString("abcdef")); + // eight bytes + byte[] expected8 = {0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF}; + assertArrayEquals(expected8, Bytes.fromHexString("0123456789ABCDEF")); + assertArrayEquals(expected8, Bytes.fromHexString("0123456789abcdef")); + + // Invalid characters + assertThatThrownBy(() -> Bytes.fromHexString("!")).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> Bytes.fromHexString(":")).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> Bytes.fromHexString("[")).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> Bytes.fromHexString("~")).isInstanceOf(IllegalArgumentException.class); + } + + /** Various test cases for trimLeadingZeros. */ + @Test + public void testTrimLeadingZeros() { + assertEquals(null, Bytes.trimLeadingZeros(null)); // Null -> noop + + byte[] emptyBytes = new byte[0]; + assertEquals(emptyBytes, Bytes.trimLeadingZeros(emptyBytes)); // Empty -> noop + + // All size 1 byte arrays -> noop + for (int i = 0; i < 255; ++i) { + byte[] oneByte = new byte[1]; + oneByte[0] = (byte) i; + assertEquals(oneByte, Bytes.trimLeadingZeros(oneByte)); + } + + // All size 2 byte arrays -> trimmed + for (int i = 0; i < 255; ++i) { + byte[] oneByte = new byte[1]; + byte[] twoBytes = new byte[2]; + oneByte[0] = (byte) i; + twoBytes[1] = (byte) i; + assertArrayEquals(oneByte, Bytes.trimLeadingZeros(twoBytes)); + } + + byte[] noLeadingZeros = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9}; + assertEquals(noLeadingZeros, Bytes.trimLeadingZeros(noLeadingZeros)); + + byte[] singleZero = new byte[1]; + byte[] severalZeros = new byte[10]; + assertArrayEquals(singleZero, Bytes.trimLeadingZeros(severalZeros)); + + byte[] zeroRemoved = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] withZero = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + assertArrayEquals(zeroRemoved, Bytes.trimLeadingZeros(withZero)); + } + + @Test + public void testBigIntegerToBytes() { + final var one32 = + new byte[] { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 + }; + assertArrayEquals(one32, Bytes.bigIntegerToBytes(BigInteger.ONE, 32)); + + final var test2 = + new byte[] { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, (byte) 2, (byte) 1 + }; + assertArrayEquals(test2, Bytes.bigIntegerToBytes(BigInteger.valueOf(513), 32)); + } + + @Test + public void testXor() { + final var b1 = new byte[] {1, 0, 1, 1, 1, 0, 0}; + final var b2 = new byte[] {0, 0, 0, 0, 1, 0, 1}; + assertArrayEquals(new byte[] {1, 0, 1, 1, 0, 0, 1}, Bytes.xor(b1, b2)); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/InstantsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/InstantsTest.java index 8a9dfdc9aa..0b722df706 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/InstantsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/InstantsTest.java @@ -64,27 +64,26 @@ package com.radixdlt.utils; -import org.junit.Test; - -import java.time.Instant; - import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import java.time.Instant; +import org.junit.Test; + public class InstantsTest { - @Test - public void toBytesWorksCorrectly() { - var instant = Instant.ofEpochSecond(1620640302L, 820166000); - var expected = new byte[] {0, 0, 0, 0, 96, -103, 2, 46, 48, -30, -67, 112}; + @Test + public void toBytesWorksCorrectly() { + var instant = Instant.ofEpochSecond(1620640302L, 820166000); + var expected = new byte[] {0, 0, 0, 0, 96, -103, 2, 46, 48, -30, -67, 112}; - assertArrayEquals(expected, Instants.toBytes(instant)); - } + assertArrayEquals(expected, Instants.toBytes(instant)); + } - @Test - public void fromBytesWorksCorrectly() { - var expected = Instant.ofEpochSecond(1620640302L, 820166000); - var bytes = new byte[] {0, 0, 0, 0, 96, -103, 2, 46, 48, -30, -67, 112}; + @Test + public void fromBytesWorksCorrectly() { + var expected = Instant.ofEpochSecond(1620640302L, 820166000); + var bytes = new byte[] {0, 0, 0, 0, 96, -103, 2, 46, 48, -30, -67, 112}; - assertEquals(expected, Instants.fromBytes(bytes)); - } -} \ No newline at end of file + assertEquals(expected, Instants.fromBytes(bytes)); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/LongsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/LongsTest.java index dcfd043ffc..860914da83 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/LongsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/LongsTest.java @@ -69,12 +69,12 @@ import org.junit.Test; public class LongsTest { - @Test - public void testLongsToBytes() { - byte[] b1 = Longs.toByteArray(1L); - byte[] b2 = Longs.toByteArray(2L); - long[] longs = Longs.fromBytes(Arrays.concatenate(b1, b2)); - Assert.assertEquals(longs[0], 1L); - Assert.assertEquals(longs[1], 2L); - } -} \ No newline at end of file + @Test + public void testLongsToBytes() { + byte[] b1 = Longs.toByteArray(1L); + byte[] b2 = Longs.toByteArray(2L); + long[] longs = Longs.fromBytes(Arrays.concatenate(b1, b2)); + Assert.assertEquals(longs[0], 1L); + Assert.assertEquals(longs[1], 2L); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/PairTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/PairTest.java index c286bf4485..77faea1c85 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/PairTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/PairTest.java @@ -64,83 +64,80 @@ package com.radixdlt.utils; -import java.util.function.Function; - -import org.junit.Before; -import org.junit.Test; - import static org.junit.Assert.*; +import java.util.function.Function; import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Before; +import org.junit.Test; public class PairTest { - private Pair testPair; - - @Before - public void setUp() throws Exception { - this.testPair = Pair.of(1234, "abcdef"); - } - - @Test - public void equalsVerifier() { - EqualsVerifier.forClass(Pair.class) - .verify(); - } - - @Test - public void testConstruction() { - assertEquals(1234, this.testPair.getFirst().intValue()); - assertEquals("abcdef", this.testPair.getSecond()); - } - - @Test - public void testMapFirst() { - Function mapper = Number::toString; - Pair newPair = this.testPair.mapFirst(mapper); - assertEquals("1234", newPair.getFirst()); - assertEquals("abcdef", newPair.getSecond()); - } - - @Test - public void testMapSecond() { - Function mapper = s -> Integer.parseInt(s.toString(), 16); - Pair newPair = this.testPair.mapSecond(mapper); - assertEquals(1234, newPair.getFirst().intValue()); - assertEquals(0xabcdef, newPair.getSecond().intValue()); - } - - @Test - public void testFirstNonNull() { - assertTrue(this.testPair.firstNonNull()); - assertFalse(this.testPair.firstIsNull()); - } - - @Test - public void testFirstIsNull() { - Pair p = Pair.of(null, null); - assertTrue(p.firstIsNull()); - assertFalse(p.firstNonNull()); - } - - @Test - public void testSecondNonNull() { - assertTrue(this.testPair.secondNonNull()); - assertFalse(this.testPair.secondIsNull()); - } - - @Test - public void testSecondIsNull() { - Pair p = Pair.of(null, null); - assertTrue(p.secondIsNull()); - assertFalse(p.secondNonNull()); - } - - @Test - public void testToString() { - String s = this.testPair.toString(); - assertTrue(s.startsWith(Pair.class.getSimpleName())); - assertTrue(s.contains(this.testPair.getFirst().toString())); - assertTrue(s.contains(this.testPair.getSecond())); - } + private Pair testPair; + + @Before + public void setUp() throws Exception { + this.testPair = Pair.of(1234, "abcdef"); + } + + @Test + public void equalsVerifier() { + EqualsVerifier.forClass(Pair.class).verify(); + } + + @Test + public void testConstruction() { + assertEquals(1234, this.testPair.getFirst().intValue()); + assertEquals("abcdef", this.testPair.getSecond()); + } + + @Test + public void testMapFirst() { + Function mapper = Number::toString; + Pair newPair = this.testPair.mapFirst(mapper); + assertEquals("1234", newPair.getFirst()); + assertEquals("abcdef", newPair.getSecond()); + } + + @Test + public void testMapSecond() { + Function mapper = s -> Integer.parseInt(s.toString(), 16); + Pair newPair = this.testPair.mapSecond(mapper); + assertEquals(1234, newPair.getFirst().intValue()); + assertEquals(0xabcdef, newPair.getSecond().intValue()); + } + + @Test + public void testFirstNonNull() { + assertTrue(this.testPair.firstNonNull()); + assertFalse(this.testPair.firstIsNull()); + } + + @Test + public void testFirstIsNull() { + Pair p = Pair.of(null, null); + assertTrue(p.firstIsNull()); + assertFalse(p.firstNonNull()); + } + + @Test + public void testSecondNonNull() { + assertTrue(this.testPair.secondNonNull()); + assertFalse(this.testPair.secondIsNull()); + } + + @Test + public void testSecondIsNull() { + Pair p = Pair.of(null, null); + assertTrue(p.secondIsNull()); + assertFalse(p.secondNonNull()); + } + + @Test + public void testToString() { + String s = this.testPair.toString(); + assertTrue(s.startsWith(Pair.class.getSimpleName())); + assertTrue(s.contains(this.testPair.getFirst().toString())); + assertTrue(s.contains(this.testPair.getSecond())); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt128Test.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt128Test.java index 2976f07a3d..42e7cf8995 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt128Test.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt128Test.java @@ -64,600 +64,531 @@ package com.radixdlt.utils; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.util.Arrays; - -import org.junit.Test; - -import com.google.common.math.BigIntegerMath; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import com.google.common.math.BigIntegerMath; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Arrays; import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; -/** - * Basic unit tests for {@link UInt128}. - */ +/** Basic unit tests for {@link UInt128}. */ public class UInt128Test { - // Range of numbers to be tested at extremes for Integer and Long. - private static final int TEST_RANGE = 1_000_000; - - /** - * Exhaustively test construction and accessors from {@link Number} - * for all {@code short} values, from {@link Short.MIN_VALUE} up to - * and including {@link Short.MAX_VALUE}. - */ - @Test - public void testShortValues() { - for (int i = Short.MIN_VALUE; i <= Short.MAX_VALUE; ++i) { - short s = (short) i; - UInt128 int128 = UInt128.from(s); - assertEquals(s & 0xFFFFL, int128.getLow()); - assertEquals(0, int128.getHigh()); - } - } - - /** - * Test construction and accessors from {@link Number} for two ranges - * of {@code int} values from {@code Integer.MIN_VALUE} through to - * {@code Integer.MIN_VALUE + TEST_RANGE}, and also from - * {@code Integer.MAX_VALUE} down to - * {@code Integer.MAX_VALUE - TEST_RANGE}. - */ - @Test - public void testIntValue() { - // Here we will just assume that testing some values near the - // extremes of the range will suffice. - for (int i = 0; i <= TEST_RANGE; ++i) { - int ii = Integer.MIN_VALUE + i; - UInt128 int128 = UInt128.from(ii); - assertEquals(ii & 0xFFFF_FFFFL, int128.getLow()); - assertEquals(0, int128.getHigh()); - } - for (int i = 0; i <= TEST_RANGE; ++i) { - int ii = Integer.MAX_VALUE - i; - UInt128 int128 = UInt128.from(ii); - assertEquals(ii & 0xFFFF_FFFFL, int128.getLow()); - assertEquals(0, int128.getHigh()); - } - } - - /** - * Test construction and accessors from {@link Number} for two ranges - * of {@code long} values from {@code Long.MIN_VALUE} through to - * {@code Long.MIN_VALUE + TEST_RANGE}, and also from - * {@code Long.MAX_VALUE} down to - * {@code Long.MAX_VALUE - TEST_RANGE}. - */ - @Test - public void testLongValue() { - // Here we will just assume that testing some values near the - // extremes of the range will suffice. - for (int i = 0; i < TEST_RANGE; ++i) { - long l = Long.MIN_VALUE + i; - UInt128 int128 = UInt128.from(l); - assertEquals(l, int128.getLow()); - assertEquals(0L, int128.getHigh()); - } - for (int i = 0; i < TEST_RANGE; ++i) { - long l = Long.MAX_VALUE - i; - UInt128 int128 = UInt128.from(l); - assertEquals(l, int128.getLow()); - assertEquals(0L, int128.getHigh()); - } - } - - /** - * Test to ensure that addition is functioning properly, including - * negative numbers, and overflow between the two underlying long - * values. - */ - @Test - public void testAddition() { - // Some basics - // 0 + 1 = 1 - assertEquals(1L, UInt128.ZERO.add(UInt128.ONE).getLow()); - // 1 + 1 = 2 - assertEquals(2L, UInt128.ONE.add(UInt128.ONE).getLow()); - // 0 + MAX_VALUE = MAX_VALUE - assertEquals(-1L, UInt128.ZERO.add(UInt128.MAX_VALUE).getLow()); - // MAX_VALUE + 1 = 0 (wrap) - assertEquals(0L, UInt128.MAX_VALUE.add(UInt128.ONE).getLow()); - // MAX_VALUE + MAX_VALUE = MAX_VALUE - 1 (wrap) - assertEquals(-2L, UInt128.MAX_VALUE.add(UInt128.MAX_VALUE).getLow()); - - // Test adding with carry. - UInt128 carry1 = UInt128.from(0x0000_0000_0000_0000L, 0xFFFF_FFFF_FFFF_FFFFL).add(UInt128.ONE); - assertEquals(0x0000_0000_0000_0001L, carry1.getHigh()); - assertEquals(0x0000_0000_0000_0000L, carry1.getLow()); - UInt128 carry2 = UInt128.ZERO.add(UInt128.MAX_VALUE); - assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getHigh()); - assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getLow()); - } - - /** - * Test to ensure that subtraction is functioning properly, including - * negative numbers, and overflow between the two underlying long - * values. - */ - @Test - public void testSubtraction() { - // Some basics - // 0 - 1 = MAX_VALUE (wrap) - assertEquals(-1L, UInt128.ZERO.subtract(UInt128.ONE).getLow()); - // 1 - 1 = 0 - assertEquals(0L, UInt128.ONE.subtract(UInt128.ONE).getLow()); - // 0 - MAX_VALUE = 1 (wrap) - assertEquals(1L, UInt128.ZERO.subtract(UInt128.MAX_VALUE).getLow()); - // MAX_VALUE - 1 = MAX_VALUE-1 - assertEquals(-2L, UInt128.MAX_VALUE.subtract(UInt128.ONE).getLow()); - // MAX_VALUE - MAX_VALUE = 0 - assertEquals(0L, UInt128.MAX_VALUE.subtract(UInt128.MAX_VALUE).getLow()); - - // Test adding with carry. - UInt128 carry1 = UInt128.from(0x0000_0000_0000_0001L, 0x0000_0000_0000_0000L).subtract(UInt128.ONE); - assertEquals(0x0000_0000_0000_0000L, carry1.getHigh()); - assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry1.getLow()); - UInt128 carry2 = UInt128.ZERO.subtract(UInt128.ONE); - assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getHigh()); - assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getLow()); - } - - /** - * Test multiplication. - */ - @Test - public void testMultiplication() { - // Some basics - assertEquals(UInt128.ZERO, UInt128.ZERO.multiply(UInt128.ONE)); - assertEquals(UInt128.ZERO, UInt128.ONE.multiply(UInt128.ZERO)); - assertEquals(UInt128.MAX_VALUE, UInt128.ONE.multiply(UInt128.MAX_VALUE)); - assertEquals(UInt128.MAX_VALUE, UInt128.MAX_VALUE.multiply(UInt128.ONE)); - assertEquals(UInt128.ONE, UInt128.MAX_VALUE.multiply(UInt128.MAX_VALUE)); - - // Some values in the long range - assertEquals(UInt128.from(12345678L * 13L), UInt128.from(12345678L).multiply(UInt128.from(13L))); - } - - /** - * Test division. - */ - @Test - public void testDivision() { - // Some basics - assertEquals(UInt128.ZERO, UInt128.ZERO.divide(UInt128.ONE)); - assertEquals(UInt128.ZERO, UInt128.ONE.divide(UInt128.MAX_VALUE)); - assertEquals(UInt128.MAX_VALUE, UInt128.MAX_VALUE.divide(UInt128.ONE)); - assertEquals(UInt128.ONE, UInt128.MAX_VALUE.divide(UInt128.MAX_VALUE)); - - // Some values in the long range - assertEquals(UInt128.from(12345678L / 13L), UInt128.from(12345678L).divide(UInt128.from(13L))); - } - - /** - * Test remainder. - */ - @Test - public void testRemainder() { - // Some basics - UInt128 two = UInt128.from(2); - assertEquals(UInt128.ZERO, UInt128.ZERO.remainder(UInt128.ONE)); - assertEquals(UInt128.ONE, UInt128.ONE.remainder(UInt128.MAX_VALUE)); - assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.remainder(UInt128.ONE)); - assertEquals(UInt128.ONE, UInt128.MAX_VALUE.remainder(two)); - assertEquals(UInt128.ONE, UInt128.ONE.remainder(two)); - - // Some values in the long range - assertEquals(UInt128.from(12345678L % 13L), UInt128.from(12345678L).remainder(UInt128.from(13L))); - } - - /** - * Basic tests for comparisons. - */ - @Test - public void testCompare() { - assertThat(UInt128.ZERO) - .isEqualByComparingTo(UInt128.ZERO) - .isLessThan(UInt128.ONE) - .isLessThan(UInt128.MAX_VALUE); - assertThat(UInt128.MAX_VALUE).isGreaterThan(UInt128.ONE); - assertThat(UInt128.ONE).isGreaterThan(UInt128.ZERO); - - UInt128 i63 = UInt128.from(0x0000_0000_0000_0000L, 0x8000_0000_0000_0000L); - UInt128 i64 = i63.add(i63); - UInt128 i65 = i64.add(i64); - assertThat(i64).isGreaterThan(i63); // In case something has gone horribly wrong. - assertThat(i64.add(i63)).isGreaterThan(i64); - assertThat(i65).isGreaterThan(i64.add(i63)); - } - - /** - * Basic tests for equals(...) method. - */ - @Test - public void testEquals() { - assertNotEquals(UInt128.ZERO, null); // Nothing should be equal to null - assertEquals(UInt128.ZERO, UInt128.ZERO); // Same object check - UInt128 i63a = UInt128.from(0x0000_0000_0000_0000L, 0x8000_0000_0000_0000L); - UInt128 i63b = UInt128.from(0x0000_0000_0000_0000L, 0x8000_0000_0000_0000L); - assertEquals(i63a, i63b); - assertNotEquals(i63a, i63a.add(i63b)); - assertNotEquals(i63a, UInt128.ZERO); - } - - /** - * Tests for numberOfLeadingZeros(). - */ - @Test - public void testNumberOfLeadingZeros() { - assertEquals(UInt128.SIZE, UInt128.ZERO.numberOfLeadingZeros()); - assertEquals(UInt128.SIZE - 1, UInt128.ONE.numberOfLeadingZeros()); - assertEquals(0, UInt128.MAX_VALUE.numberOfLeadingZeros()); - assertEquals(63, UInt128.from(0x0000_0000_0000_0001L, 0x0000_0000_0000_0000).numberOfLeadingZeros()); - } - - /** - * Tests for bit operations. - */ - @Test - public void testBitOperations() { - // Use bit positions in both high and low word for tests - UInt128 b0 = UInt128.ONE; - UInt128 b64 = UInt128.from(0x0000_0000_0000_0001L, 0x0000_0000_0000_0000); - // Basic sanity checks to make sure nothing is horribly wrong - assertThat(b64).isGreaterThan(b0); - assertThat(b0).isGreaterThan(UInt128.ZERO); - assertThat(b64).isGreaterThan(UInt128.ZERO); - - // Now for the real tests - assertEquals(b0, UInt128.ZERO.or(b0)); - assertEquals(b0, b0.or(b0)); - assertEquals(b64, UInt128.ZERO.or(b64)); - assertEquals(b64, b64.or(b64)); - assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.or(UInt128.MAX_VALUE)); - assertEquals(UInt128.MAX_VALUE, UInt128.MAX_VALUE.or(UInt128.MAX_VALUE)); - - assertEquals(b0, UInt128.MAX_VALUE.and(b0)); - assertEquals(b64, UInt128.MAX_VALUE.and(b64)); - assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.and(UInt128.ZERO)); - - assertEquals(b0, UInt128.ZERO.xor(b0)); - assertEquals(UInt128.ZERO, b0.xor(b0)); - assertEquals(b64, UInt128.ZERO.xor(b64)); - assertEquals(UInt128.ZERO, b64.xor(b64)); - assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.xor(UInt128.MAX_VALUE)); - assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.xor(UInt128.MAX_VALUE)); - } - - /** - * Test creation from byte array. - */ - @Test - public void testCreateFromByteArray() { - byte[] m1 = { - -1 - }; - byte[] p1 = { - 1 - }; - byte[] bytesArray = new byte[UInt128.BYTES]; - Arrays.fill(bytesArray, (byte) 0); - bytesArray[UInt128.BYTES - 1] = 1; - UInt128 m1Bits128 = UInt128.from(m1); - UInt128 p1Bits128 = UInt128.from(p1); - UInt128 bytesArrayBits128 = UInt128.from(bytesArray); - - assertEquals(UInt128.from(255), m1Bits128); // Zero extension happened correctly - assertEquals(UInt128.ONE, p1Bits128); // Zero fill happened correctly - assertEquals(UInt128.ONE, bytesArrayBits128); // Correct size array OK - } - - /** - * Test toByteArray(...) methods. - */ - @Test - public void testToByteArray() { - UInt128 bitPattern = UInt128.from(0x0001_0203_0405_0607L, 0x0809_0A0B_0C0D_0E0FL); - byte[] bytes2 = new byte[UInt128.BYTES * 3]; - Arrays.fill(bytes2, (byte) -1); - - // Make sure we got the value in big-endian order - byte[] bytes = bitPattern.toByteArray(); - for (int i = 0; i < UInt128.BYTES; ++i) { - assertEquals(i, bytes[i]); - } - - bitPattern.toByteArray(bytes2, UInt128.BYTES); - // Make sure we didn't overwrite bytes outside our range - for (int i = 0; i < UInt128.BYTES; ++i) { - assertEquals(-1, bytes2[i]); - assertEquals(-1, bytes2[i + UInt128.BYTES * 2]); - } - // Make sure we got the value in big-endian order - for (int i = 0; i < UInt128.BYTES; ++i) { - assertEquals(i, bytes2[UInt128.BYTES + i]); - } - } - - /** - * Test shiftLeft and shiftRight. - */ - @Test - public void testShifts() { - // Basic cases, left shift - assertEquals(UInt128.ZERO, UInt128.ZERO.shiftLeft()); - // Zero extend on left - assertEquals(UInt128.from(-1L, -2L), UInt128.MAX_VALUE.shiftLeft()); - assertEquals(UInt128.from(0L, 2L), UInt128.ONE.shiftLeft()); - // Make sure bit crosses word boundary correctly - assertEquals(UInt128.from(1L, 0L), UInt128.from(0L, 1L << (Long.SIZE - 1)).shiftLeft()); - - // Basic cases, right shift - assertEquals(UInt128.ZERO, UInt128.ZERO.shiftRight()); - // Sign extend on right - assertEquals(UInt128.from(0x7FFF_FFFF_FFFF_FFFFL, -1), UInt128.MAX_VALUE.shiftRight()); - assertEquals(UInt128.ZERO, UInt128.ONE.shiftRight()); - assertEquals(UInt128.ONE, UInt128.from(0L, 2L).shiftRight()); - // Make sure bit crosses word boundary correctly - assertEquals(UInt128.from(0L, 0x8000_0000_0000_0000L), UInt128.from(1L, 0L).shiftRight()); - } - - /** - * Test multi shiftLeft. - */ - @Test - public void testMultiShiftLeft() { - for (int i = 0; i < UInt128.SIZE; ++i) { - UInt128 powNum = UInt128.TWO.pow(i); - UInt128 shiftNum = UInt128.ONE.shiftLeft(i); - assertEquals(powNum, shiftNum); - } - assertEquals(UInt128.ZERO, UInt128.ONE.shiftLeft(UInt128.SIZE)); - assertEquals(UInt128.ZERO, UInt128.ONE.shiftLeft(-UInt128.SIZE)); - assertEquals(UInt128.ONE, UInt128.TWO.shiftLeft(-1)); - } - - /** - * Test multi shiftRight. - */ - @Test - public void testMultiShiftRight() { - for (int i = 0; i < UInt128.SIZE; ++i) { - UInt128 powNum = UInt128.TWO.pow(UInt128.SIZE - (i + 1)); - UInt128 shiftNum = UInt128.HIGH_BIT.shiftRight(i); - assertEquals(powNum, shiftNum); - } - assertEquals(UInt128.ZERO, UInt128.HIGH_BIT.shiftRight(UInt128.SIZE)); - assertEquals(UInt128.ZERO, UInt128.HIGH_BIT.shiftRight(-UInt128.SIZE)); - assertEquals(UInt128.TWO, UInt128.ONE.shiftRight(-1)); - } - - /** - * Integer square root. - */ - @Test - public void testIntegerSquareRoot() { - long max = 1 << 53; // Precision of double - for (long n = 0; n < max; n += 997) { - long lsqrt = (long) Math.floor(Math.sqrt(n)); - UInt128 nn = UInt128.from(n); - UInt128 isqrt = nn.isqrt(); - assertEquals(UInt128.from(lsqrt), isqrt); - } - for (int n = 0; n < UInt128.SIZE; ++n) { - UInt128 num = UInt128.ONE.shiftLeft(n); - byte[] bytes = num.toByteArray(); - BigInteger nsqrt = BigIntegerMath.sqrt(new BigInteger(1, bytes), RoundingMode.FLOOR); - byte[] otherBytes = num.isqrt().toByteArray(); - BigInteger isqrt = new BigInteger(1, otherBytes); - assertEquals(nsqrt, isqrt); - } - } - - /** - * Test isEven(), isOdd(), isZero(), isHighBitSet() predicates. - */ - @Test - public void testPredicates() { - // Basic tests for odd/even - assertTrue(UInt128.ONE.isOdd()); - assertFalse(UInt128.ONE.isEven()); - assertTrue(UInt128.MAX_VALUE.isOdd()); - assertFalse(UInt128.MAX_VALUE.isEven()); - UInt128 two = UInt128.ONE.add(UInt128.ONE); - assertFalse(two.isOdd()); - assertTrue(two.isEven()); - UInt128 minusTwo = UInt128.MAX_VALUE.add(UInt128.MAX_VALUE); - assertFalse(minusTwo.isOdd()); - assertTrue(minusTwo.isEven()); - - assertFalse(UInt128.ONE.isZero()); - assertFalse(UInt128.MAX_VALUE.isZero()); - assertTrue(UInt128.ZERO.isZero()); - - assertFalse(UInt128.ZERO.isHighBitSet()); - assertFalse(UInt128.TWO.pow(126).isHighBitSet()); - assertFalse(UInt128.TWO.pow(127).decrement().isHighBitSet()); - assertTrue(UInt128.MAX_VALUE.isHighBitSet()); - assertTrue(UInt128.ONE.invert().isHighBitSet()); - assertTrue(UInt128.TWO.pow(127).isHighBitSet()); - assertTrue(UInt128.HIGH_BIT.isHighBitSet()); - } - - /** - * Test toString() method. - */ - @Test - public void testToString() { - // Some basics - assertEquals("0", UInt128.ZERO.toString()); - assertEquals("1", UInt128.ONE.toString()); - assertEquals("340282366920938463463374607431768211455", UInt128.MAX_VALUE.toString()); - assertEquals("10", UInt128.TEN.toString()); - - assertEquals("12345678", UInt128.from(12345678L).toString()); - assertEquals("18446744073697205938", UInt128.from(-12345678L).toString()); - UInt128 maxPositive = UInt128.from(0x7FFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFFFL); - assertEquals(new BigInteger(maxPositive.toString()).toString(), maxPositive.toString()); - UInt128 maxNegative = UInt128.from(0x8000_0000_0000_0000L, 0x0000_0000_0000_0000L); - assertEquals(new BigInteger(maxNegative.toString()).toString(), maxNegative.toString()); - } - - /** - * Test materialising {@code UInt128} values from {@code String} values. - */ - @Test - public void testFromString() { - testRoundTrip("0"); - testRoundTrip("123456789"); - testRoundTrip("123456789123456789"); - testRoundTrip("123456789123456789123456789123456789"); - assertEquals( - UInt128.from(0x7FFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFFFL), - UInt128.from(BigInteger.ONE.shiftLeft(127).subtract(BigInteger.ONE).toString()) - ); - assertEquals(UInt128.from(0x8000_0000_0000_0000L, 0x0000_0000_0000_0000L), UInt128.from(BigInteger.ONE.shiftLeft(127).toString())); - } - - /** - * Test increment. - */ - @Test - public void testIncrement() { - assertEquals(UInt128.ONE, UInt128.ZERO.increment()); - assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.increment()); // wrap - assertEquals(UInt128.ONE.shiftLeft(Integer.SIZE), UInt128.from(0, 0, 0, -1).increment()); - assertEquals(UInt128.ONE.shiftLeft(Integer.SIZE * 2), UInt128.from(0, 0, -1, -1).increment()); - assertEquals(UInt128.ONE.shiftLeft(Integer.SIZE * 3), UInt128.from(0, -1, -1, -1).increment()); - } - - /** - * Test decrement. - */ - @Test - public void testDecrement() { - assertEquals(UInt128.ZERO, UInt128.ONE.decrement()); - assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.decrement()); // wrap - assertEquals(UInt128.from(0, 0, 0, -1), UInt128.ONE.shiftLeft(Integer.SIZE).decrement()); - assertEquals(UInt128.from(0, 0, -1, -1), UInt128.ONE.shiftLeft(Integer.SIZE * 2).decrement()); - assertEquals(UInt128.from(0, -1, -1, -1), UInt128.ONE.shiftLeft(Integer.SIZE * 3).decrement()); - // Failure case found during review - assertEquals(UInt128.from(-1, -1, 0, -1), UInt128.from(-1, -1, 1, 0).decrement()); - } - - /** - * Tests bitwise inversion. - */ - @Test - public void testInvert() { - assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.invert()); - assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.invert()); - assertEquals(UInt128.MAX_VALUE.decrement(), UInt128.ONE.invert()); - assertEquals(UInt128.ONE, UInt128.MAX_VALUE.decrement().invert()); - assertEquals(UInt128.from(Long.MAX_VALUE, -1L), UInt128.from(Long.MIN_VALUE, 0L).invert()); - assertEquals(UInt128.from(Long.MIN_VALUE, 0L), UInt128.from(Long.MAX_VALUE, -1L).invert()); - } - - /** - * Test getLowestSetBit. - */ - @Test - public void testGetLowestSetBit() { - assertEquals(-1, UInt128.ZERO.getLowestSetBit()); - assertEquals(0, UInt128.ONE.getLowestSetBit()); - assertEquals(1, UInt128.TWO.getLowestSetBit()); - assertEquals(2, UInt128.FOUR.getLowestSetBit()); - assertEquals(3, UInt128.EIGHT.getLowestSetBit()); - assertEquals(64, UInt128.from(1, 0).getLowestSetBit()); - assertEquals(65, UInt128.from(2, 0).getLowestSetBit()); - assertEquals(66, UInt128.from(4, 0).getLowestSetBit()); - assertEquals(67, UInt128.from(8, 0).getLowestSetBit()); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(UInt128.class).verify(); - } - - /** - * Test div 0. - */ - @Test(expected = IllegalArgumentException.class) - public void testDiv0() { - UInt128.ONE.divide(UInt128.ZERO); - } - - /** - * Test rem 0. - */ - @Test(expected = IllegalArgumentException.class) - public void testRem0() { - UInt128.ONE.remainder(UInt128.ZERO); - } - - /** - * NumberFormatException on empty string. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionOnEmpty() { - UInt128.from(""); - } - - /** - * NumberFormatException if no actual number. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionIfNoNumber() { - UInt128.from("+"); - } - - /** - * NumberFormatException if invalid digit. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionIfInvalidDigit() { - UInt128.from("+a"); - } - - /** - * IllegalArgumentException if byte array is empty. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionIfByteArrayEmpty() { - UInt128.from(new byte[0]); - } - - /** - * IllegalArgumentException on radix too big. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnRadixTooBig() { - UInt128.ONE.toString(Character.MAX_RADIX + 1); - } - - /** - * IllegalArgumentException on radix too small. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnRadixTooSmall() { - UInt128.ONE.toString(Character.MIN_RADIX - 1); - } - - /** - * IllegalArgumentException on negative exponent for pow. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnNegativeExponent() { - UInt128.ONE.pow(-1); - } - - private static void testRoundTrip(String s) { - assertEquals(s, UInt128.from(s).toString()); - } + // Range of numbers to be tested at extremes for Integer and Long. + private static final int TEST_RANGE = 1_000_000; + + /** + * Exhaustively test construction and accessors from {@link Number} for all {@code short} values, + * from {@link Short.MIN_VALUE} up to and including {@link Short.MAX_VALUE}. + */ + @Test + public void testShortValues() { + for (int i = Short.MIN_VALUE; i <= Short.MAX_VALUE; ++i) { + short s = (short) i; + UInt128 int128 = UInt128.from(s); + assertEquals(s & 0xFFFFL, int128.getLow()); + assertEquals(0, int128.getHigh()); + } + } + + /** + * Test construction and accessors from {@link Number} for two ranges of {@code int} values from + * {@code Integer.MIN_VALUE} through to {@code Integer.MIN_VALUE + TEST_RANGE}, and also from + * {@code Integer.MAX_VALUE} down to {@code Integer.MAX_VALUE - TEST_RANGE}. + */ + @Test + public void testIntValue() { + // Here we will just assume that testing some values near the + // extremes of the range will suffice. + for (int i = 0; i <= TEST_RANGE; ++i) { + int ii = Integer.MIN_VALUE + i; + UInt128 int128 = UInt128.from(ii); + assertEquals(ii & 0xFFFF_FFFFL, int128.getLow()); + assertEquals(0, int128.getHigh()); + } + for (int i = 0; i <= TEST_RANGE; ++i) { + int ii = Integer.MAX_VALUE - i; + UInt128 int128 = UInt128.from(ii); + assertEquals(ii & 0xFFFF_FFFFL, int128.getLow()); + assertEquals(0, int128.getHigh()); + } + } + + /** + * Test construction and accessors from {@link Number} for two ranges of {@code long} values from + * {@code Long.MIN_VALUE} through to {@code Long.MIN_VALUE + TEST_RANGE}, and also from {@code + * Long.MAX_VALUE} down to {@code Long.MAX_VALUE - TEST_RANGE}. + */ + @Test + public void testLongValue() { + // Here we will just assume that testing some values near the + // extremes of the range will suffice. + for (int i = 0; i < TEST_RANGE; ++i) { + long l = Long.MIN_VALUE + i; + UInt128 int128 = UInt128.from(l); + assertEquals(l, int128.getLow()); + assertEquals(0L, int128.getHigh()); + } + for (int i = 0; i < TEST_RANGE; ++i) { + long l = Long.MAX_VALUE - i; + UInt128 int128 = UInt128.from(l); + assertEquals(l, int128.getLow()); + assertEquals(0L, int128.getHigh()); + } + } + + /** + * Test to ensure that addition is functioning properly, including negative numbers, and overflow + * between the two underlying long values. + */ + @Test + public void testAddition() { + // Some basics + // 0 + 1 = 1 + assertEquals(1L, UInt128.ZERO.add(UInt128.ONE).getLow()); + // 1 + 1 = 2 + assertEquals(2L, UInt128.ONE.add(UInt128.ONE).getLow()); + // 0 + MAX_VALUE = MAX_VALUE + assertEquals(-1L, UInt128.ZERO.add(UInt128.MAX_VALUE).getLow()); + // MAX_VALUE + 1 = 0 (wrap) + assertEquals(0L, UInt128.MAX_VALUE.add(UInt128.ONE).getLow()); + // MAX_VALUE + MAX_VALUE = MAX_VALUE - 1 (wrap) + assertEquals(-2L, UInt128.MAX_VALUE.add(UInt128.MAX_VALUE).getLow()); + + // Test adding with carry. + UInt128 carry1 = UInt128.from(0x0000_0000_0000_0000L, 0xFFFF_FFFF_FFFF_FFFFL).add(UInt128.ONE); + assertEquals(0x0000_0000_0000_0001L, carry1.getHigh()); + assertEquals(0x0000_0000_0000_0000L, carry1.getLow()); + UInt128 carry2 = UInt128.ZERO.add(UInt128.MAX_VALUE); + assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getHigh()); + assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getLow()); + } + + /** + * Test to ensure that subtraction is functioning properly, including negative numbers, and + * overflow between the two underlying long values. + */ + @Test + public void testSubtraction() { + // Some basics + // 0 - 1 = MAX_VALUE (wrap) + assertEquals(-1L, UInt128.ZERO.subtract(UInt128.ONE).getLow()); + // 1 - 1 = 0 + assertEquals(0L, UInt128.ONE.subtract(UInt128.ONE).getLow()); + // 0 - MAX_VALUE = 1 (wrap) + assertEquals(1L, UInt128.ZERO.subtract(UInt128.MAX_VALUE).getLow()); + // MAX_VALUE - 1 = MAX_VALUE-1 + assertEquals(-2L, UInt128.MAX_VALUE.subtract(UInt128.ONE).getLow()); + // MAX_VALUE - MAX_VALUE = 0 + assertEquals(0L, UInt128.MAX_VALUE.subtract(UInt128.MAX_VALUE).getLow()); + + // Test adding with carry. + UInt128 carry1 = + UInt128.from(0x0000_0000_0000_0001L, 0x0000_0000_0000_0000L).subtract(UInt128.ONE); + assertEquals(0x0000_0000_0000_0000L, carry1.getHigh()); + assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry1.getLow()); + UInt128 carry2 = UInt128.ZERO.subtract(UInt128.ONE); + assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getHigh()); + assertEquals(0xFFFF_FFFF_FFFF_FFFFL, carry2.getLow()); + } + + /** Test multiplication. */ + @Test + public void testMultiplication() { + // Some basics + assertEquals(UInt128.ZERO, UInt128.ZERO.multiply(UInt128.ONE)); + assertEquals(UInt128.ZERO, UInt128.ONE.multiply(UInt128.ZERO)); + assertEquals(UInt128.MAX_VALUE, UInt128.ONE.multiply(UInt128.MAX_VALUE)); + assertEquals(UInt128.MAX_VALUE, UInt128.MAX_VALUE.multiply(UInt128.ONE)); + assertEquals(UInt128.ONE, UInt128.MAX_VALUE.multiply(UInt128.MAX_VALUE)); + + // Some values in the long range + assertEquals( + UInt128.from(12345678L * 13L), UInt128.from(12345678L).multiply(UInt128.from(13L))); + } + + /** Test division. */ + @Test + public void testDivision() { + // Some basics + assertEquals(UInt128.ZERO, UInt128.ZERO.divide(UInt128.ONE)); + assertEquals(UInt128.ZERO, UInt128.ONE.divide(UInt128.MAX_VALUE)); + assertEquals(UInt128.MAX_VALUE, UInt128.MAX_VALUE.divide(UInt128.ONE)); + assertEquals(UInt128.ONE, UInt128.MAX_VALUE.divide(UInt128.MAX_VALUE)); + + // Some values in the long range + assertEquals(UInt128.from(12345678L / 13L), UInt128.from(12345678L).divide(UInt128.from(13L))); + } + + /** Test remainder. */ + @Test + public void testRemainder() { + // Some basics + UInt128 two = UInt128.from(2); + assertEquals(UInt128.ZERO, UInt128.ZERO.remainder(UInt128.ONE)); + assertEquals(UInt128.ONE, UInt128.ONE.remainder(UInt128.MAX_VALUE)); + assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.remainder(UInt128.ONE)); + assertEquals(UInt128.ONE, UInt128.MAX_VALUE.remainder(two)); + assertEquals(UInt128.ONE, UInt128.ONE.remainder(two)); + + // Some values in the long range + assertEquals( + UInt128.from(12345678L % 13L), UInt128.from(12345678L).remainder(UInt128.from(13L))); + } + + /** Basic tests for comparisons. */ + @Test + public void testCompare() { + assertThat(UInt128.ZERO) + .isEqualByComparingTo(UInt128.ZERO) + .isLessThan(UInt128.ONE) + .isLessThan(UInt128.MAX_VALUE); + assertThat(UInt128.MAX_VALUE).isGreaterThan(UInt128.ONE); + assertThat(UInt128.ONE).isGreaterThan(UInt128.ZERO); + + UInt128 i63 = UInt128.from(0x0000_0000_0000_0000L, 0x8000_0000_0000_0000L); + UInt128 i64 = i63.add(i63); + UInt128 i65 = i64.add(i64); + assertThat(i64).isGreaterThan(i63); // In case something has gone horribly wrong. + assertThat(i64.add(i63)).isGreaterThan(i64); + assertThat(i65).isGreaterThan(i64.add(i63)); + } + + /** Basic tests for equals(...) method. */ + @Test + public void testEquals() { + assertNotEquals(UInt128.ZERO, null); // Nothing should be equal to null + assertEquals(UInt128.ZERO, UInt128.ZERO); // Same object check + UInt128 i63a = UInt128.from(0x0000_0000_0000_0000L, 0x8000_0000_0000_0000L); + UInt128 i63b = UInt128.from(0x0000_0000_0000_0000L, 0x8000_0000_0000_0000L); + assertEquals(i63a, i63b); + assertNotEquals(i63a, i63a.add(i63b)); + assertNotEquals(i63a, UInt128.ZERO); + } + + /** Tests for numberOfLeadingZeros(). */ + @Test + public void testNumberOfLeadingZeros() { + assertEquals(UInt128.SIZE, UInt128.ZERO.numberOfLeadingZeros()); + assertEquals(UInt128.SIZE - 1, UInt128.ONE.numberOfLeadingZeros()); + assertEquals(0, UInt128.MAX_VALUE.numberOfLeadingZeros()); + assertEquals( + 63, UInt128.from(0x0000_0000_0000_0001L, 0x0000_0000_0000_0000).numberOfLeadingZeros()); + } + + /** Tests for bit operations. */ + @Test + public void testBitOperations() { + // Use bit positions in both high and low word for tests + UInt128 b0 = UInt128.ONE; + UInt128 b64 = UInt128.from(0x0000_0000_0000_0001L, 0x0000_0000_0000_0000); + // Basic sanity checks to make sure nothing is horribly wrong + assertThat(b64).isGreaterThan(b0); + assertThat(b0).isGreaterThan(UInt128.ZERO); + assertThat(b64).isGreaterThan(UInt128.ZERO); + + // Now for the real tests + assertEquals(b0, UInt128.ZERO.or(b0)); + assertEquals(b0, b0.or(b0)); + assertEquals(b64, UInt128.ZERO.or(b64)); + assertEquals(b64, b64.or(b64)); + assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.or(UInt128.MAX_VALUE)); + assertEquals(UInt128.MAX_VALUE, UInt128.MAX_VALUE.or(UInt128.MAX_VALUE)); + + assertEquals(b0, UInt128.MAX_VALUE.and(b0)); + assertEquals(b64, UInt128.MAX_VALUE.and(b64)); + assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.and(UInt128.ZERO)); + + assertEquals(b0, UInt128.ZERO.xor(b0)); + assertEquals(UInt128.ZERO, b0.xor(b0)); + assertEquals(b64, UInt128.ZERO.xor(b64)); + assertEquals(UInt128.ZERO, b64.xor(b64)); + assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.xor(UInt128.MAX_VALUE)); + assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.xor(UInt128.MAX_VALUE)); + } + + /** Test creation from byte array. */ + @Test + public void testCreateFromByteArray() { + byte[] m1 = {-1}; + byte[] p1 = {1}; + byte[] bytesArray = new byte[UInt128.BYTES]; + Arrays.fill(bytesArray, (byte) 0); + bytesArray[UInt128.BYTES - 1] = 1; + UInt128 m1Bits128 = UInt128.from(m1); + UInt128 p1Bits128 = UInt128.from(p1); + UInt128 bytesArrayBits128 = UInt128.from(bytesArray); + + assertEquals(UInt128.from(255), m1Bits128); // Zero extension happened correctly + assertEquals(UInt128.ONE, p1Bits128); // Zero fill happened correctly + assertEquals(UInt128.ONE, bytesArrayBits128); // Correct size array OK + } + + /** Test toByteArray(...) methods. */ + @Test + public void testToByteArray() { + UInt128 bitPattern = UInt128.from(0x0001_0203_0405_0607L, 0x0809_0A0B_0C0D_0E0FL); + byte[] bytes2 = new byte[UInt128.BYTES * 3]; + Arrays.fill(bytes2, (byte) -1); + + // Make sure we got the value in big-endian order + byte[] bytes = bitPattern.toByteArray(); + for (int i = 0; i < UInt128.BYTES; ++i) { + assertEquals(i, bytes[i]); + } + + bitPattern.toByteArray(bytes2, UInt128.BYTES); + // Make sure we didn't overwrite bytes outside our range + for (int i = 0; i < UInt128.BYTES; ++i) { + assertEquals(-1, bytes2[i]); + assertEquals(-1, bytes2[i + UInt128.BYTES * 2]); + } + // Make sure we got the value in big-endian order + for (int i = 0; i < UInt128.BYTES; ++i) { + assertEquals(i, bytes2[UInt128.BYTES + i]); + } + } + + /** Test shiftLeft and shiftRight. */ + @Test + public void testShifts() { + // Basic cases, left shift + assertEquals(UInt128.ZERO, UInt128.ZERO.shiftLeft()); + // Zero extend on left + assertEquals(UInt128.from(-1L, -2L), UInt128.MAX_VALUE.shiftLeft()); + assertEquals(UInt128.from(0L, 2L), UInt128.ONE.shiftLeft()); + // Make sure bit crosses word boundary correctly + assertEquals(UInt128.from(1L, 0L), UInt128.from(0L, 1L << (Long.SIZE - 1)).shiftLeft()); + + // Basic cases, right shift + assertEquals(UInt128.ZERO, UInt128.ZERO.shiftRight()); + // Sign extend on right + assertEquals(UInt128.from(0x7FFF_FFFF_FFFF_FFFFL, -1), UInt128.MAX_VALUE.shiftRight()); + assertEquals(UInt128.ZERO, UInt128.ONE.shiftRight()); + assertEquals(UInt128.ONE, UInt128.from(0L, 2L).shiftRight()); + // Make sure bit crosses word boundary correctly + assertEquals(UInt128.from(0L, 0x8000_0000_0000_0000L), UInt128.from(1L, 0L).shiftRight()); + } + + /** Test multi shiftLeft. */ + @Test + public void testMultiShiftLeft() { + for (int i = 0; i < UInt128.SIZE; ++i) { + UInt128 powNum = UInt128.TWO.pow(i); + UInt128 shiftNum = UInt128.ONE.shiftLeft(i); + assertEquals(powNum, shiftNum); + } + assertEquals(UInt128.ZERO, UInt128.ONE.shiftLeft(UInt128.SIZE)); + assertEquals(UInt128.ZERO, UInt128.ONE.shiftLeft(-UInt128.SIZE)); + assertEquals(UInt128.ONE, UInt128.TWO.shiftLeft(-1)); + } + + /** Test multi shiftRight. */ + @Test + public void testMultiShiftRight() { + for (int i = 0; i < UInt128.SIZE; ++i) { + UInt128 powNum = UInt128.TWO.pow(UInt128.SIZE - (i + 1)); + UInt128 shiftNum = UInt128.HIGH_BIT.shiftRight(i); + assertEquals(powNum, shiftNum); + } + assertEquals(UInt128.ZERO, UInt128.HIGH_BIT.shiftRight(UInt128.SIZE)); + assertEquals(UInt128.ZERO, UInt128.HIGH_BIT.shiftRight(-UInt128.SIZE)); + assertEquals(UInt128.TWO, UInt128.ONE.shiftRight(-1)); + } + + /** Integer square root. */ + @Test + public void testIntegerSquareRoot() { + long max = 1 << 53; // Precision of double + for (long n = 0; n < max; n += 997) { + long lsqrt = (long) Math.floor(Math.sqrt(n)); + UInt128 nn = UInt128.from(n); + UInt128 isqrt = nn.isqrt(); + assertEquals(UInt128.from(lsqrt), isqrt); + } + for (int n = 0; n < UInt128.SIZE; ++n) { + UInt128 num = UInt128.ONE.shiftLeft(n); + byte[] bytes = num.toByteArray(); + BigInteger nsqrt = BigIntegerMath.sqrt(new BigInteger(1, bytes), RoundingMode.FLOOR); + byte[] otherBytes = num.isqrt().toByteArray(); + BigInteger isqrt = new BigInteger(1, otherBytes); + assertEquals(nsqrt, isqrt); + } + } + + /** Test isEven(), isOdd(), isZero(), isHighBitSet() predicates. */ + @Test + public void testPredicates() { + // Basic tests for odd/even + assertTrue(UInt128.ONE.isOdd()); + assertFalse(UInt128.ONE.isEven()); + assertTrue(UInt128.MAX_VALUE.isOdd()); + assertFalse(UInt128.MAX_VALUE.isEven()); + UInt128 two = UInt128.ONE.add(UInt128.ONE); + assertFalse(two.isOdd()); + assertTrue(two.isEven()); + UInt128 minusTwo = UInt128.MAX_VALUE.add(UInt128.MAX_VALUE); + assertFalse(minusTwo.isOdd()); + assertTrue(minusTwo.isEven()); + + assertFalse(UInt128.ONE.isZero()); + assertFalse(UInt128.MAX_VALUE.isZero()); + assertTrue(UInt128.ZERO.isZero()); + + assertFalse(UInt128.ZERO.isHighBitSet()); + assertFalse(UInt128.TWO.pow(126).isHighBitSet()); + assertFalse(UInt128.TWO.pow(127).decrement().isHighBitSet()); + assertTrue(UInt128.MAX_VALUE.isHighBitSet()); + assertTrue(UInt128.ONE.invert().isHighBitSet()); + assertTrue(UInt128.TWO.pow(127).isHighBitSet()); + assertTrue(UInt128.HIGH_BIT.isHighBitSet()); + } + + /** Test toString() method. */ + @Test + public void testToString() { + // Some basics + assertEquals("0", UInt128.ZERO.toString()); + assertEquals("1", UInt128.ONE.toString()); + assertEquals("340282366920938463463374607431768211455", UInt128.MAX_VALUE.toString()); + assertEquals("10", UInt128.TEN.toString()); + + assertEquals("12345678", UInt128.from(12345678L).toString()); + assertEquals("18446744073697205938", UInt128.from(-12345678L).toString()); + UInt128 maxPositive = UInt128.from(0x7FFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFFFL); + assertEquals(new BigInteger(maxPositive.toString()).toString(), maxPositive.toString()); + UInt128 maxNegative = UInt128.from(0x8000_0000_0000_0000L, 0x0000_0000_0000_0000L); + assertEquals(new BigInteger(maxNegative.toString()).toString(), maxNegative.toString()); + } + + /** Test materialising {@code UInt128} values from {@code String} values. */ + @Test + public void testFromString() { + testRoundTrip("0"); + testRoundTrip("123456789"); + testRoundTrip("123456789123456789"); + testRoundTrip("123456789123456789123456789123456789"); + assertEquals( + UInt128.from(0x7FFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFFFL), + UInt128.from(BigInteger.ONE.shiftLeft(127).subtract(BigInteger.ONE).toString())); + assertEquals( + UInt128.from(0x8000_0000_0000_0000L, 0x0000_0000_0000_0000L), + UInt128.from(BigInteger.ONE.shiftLeft(127).toString())); + } + + /** Test increment. */ + @Test + public void testIncrement() { + assertEquals(UInt128.ONE, UInt128.ZERO.increment()); + assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.increment()); // wrap + assertEquals(UInt128.ONE.shiftLeft(Integer.SIZE), UInt128.from(0, 0, 0, -1).increment()); + assertEquals(UInt128.ONE.shiftLeft(Integer.SIZE * 2), UInt128.from(0, 0, -1, -1).increment()); + assertEquals(UInt128.ONE.shiftLeft(Integer.SIZE * 3), UInt128.from(0, -1, -1, -1).increment()); + } + + /** Test decrement. */ + @Test + public void testDecrement() { + assertEquals(UInt128.ZERO, UInt128.ONE.decrement()); + assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.decrement()); // wrap + assertEquals(UInt128.from(0, 0, 0, -1), UInt128.ONE.shiftLeft(Integer.SIZE).decrement()); + assertEquals(UInt128.from(0, 0, -1, -1), UInt128.ONE.shiftLeft(Integer.SIZE * 2).decrement()); + assertEquals(UInt128.from(0, -1, -1, -1), UInt128.ONE.shiftLeft(Integer.SIZE * 3).decrement()); + // Failure case found during review + assertEquals(UInt128.from(-1, -1, 0, -1), UInt128.from(-1, -1, 1, 0).decrement()); + } + + /** Tests bitwise inversion. */ + @Test + public void testInvert() { + assertEquals(UInt128.MAX_VALUE, UInt128.ZERO.invert()); + assertEquals(UInt128.ZERO, UInt128.MAX_VALUE.invert()); + assertEquals(UInt128.MAX_VALUE.decrement(), UInt128.ONE.invert()); + assertEquals(UInt128.ONE, UInt128.MAX_VALUE.decrement().invert()); + assertEquals(UInt128.from(Long.MAX_VALUE, -1L), UInt128.from(Long.MIN_VALUE, 0L).invert()); + assertEquals(UInt128.from(Long.MIN_VALUE, 0L), UInt128.from(Long.MAX_VALUE, -1L).invert()); + } + + /** Test getLowestSetBit. */ + @Test + public void testGetLowestSetBit() { + assertEquals(-1, UInt128.ZERO.getLowestSetBit()); + assertEquals(0, UInt128.ONE.getLowestSetBit()); + assertEquals(1, UInt128.TWO.getLowestSetBit()); + assertEquals(2, UInt128.FOUR.getLowestSetBit()); + assertEquals(3, UInt128.EIGHT.getLowestSetBit()); + assertEquals(64, UInt128.from(1, 0).getLowestSetBit()); + assertEquals(65, UInt128.from(2, 0).getLowestSetBit()); + assertEquals(66, UInt128.from(4, 0).getLowestSetBit()); + assertEquals(67, UInt128.from(8, 0).getLowestSetBit()); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(UInt128.class).verify(); + } + + /** Test div 0. */ + @Test(expected = IllegalArgumentException.class) + public void testDiv0() { + UInt128.ONE.divide(UInt128.ZERO); + } + + /** Test rem 0. */ + @Test(expected = IllegalArgumentException.class) + public void testRem0() { + UInt128.ONE.remainder(UInt128.ZERO); + } + + /** NumberFormatException on empty string. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionOnEmpty() { + UInt128.from(""); + } + + /** NumberFormatException if no actual number. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionIfNoNumber() { + UInt128.from("+"); + } + + /** NumberFormatException if invalid digit. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionIfInvalidDigit() { + UInt128.from("+a"); + } + + /** IllegalArgumentException if byte array is empty. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionIfByteArrayEmpty() { + UInt128.from(new byte[0]); + } + + /** IllegalArgumentException on radix too big. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnRadixTooBig() { + UInt128.ONE.toString(Character.MAX_RADIX + 1); + } + + /** IllegalArgumentException on radix too small. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnRadixTooSmall() { + UInt128.ONE.toString(Character.MIN_RADIX - 1); + } + + /** IllegalArgumentException on negative exponent for pow. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnNegativeExponent() { + UInt128.ONE.pow(-1); + } + + private static void testRoundTrip(String s) { + assertEquals(s, UInt128.from(s).toString()); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256Test.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256Test.java index 46d2fc39e2..828628dae6 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256Test.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256Test.java @@ -64,14 +64,6 @@ package com.radixdlt.utils; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.util.Arrays; - -import org.junit.Test; - -import com.google.common.math.BigIntegerMath; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -79,577 +71,556 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.common.math.BigIntegerMath; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Arrays; import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; -/** - * Basic unit tests for {@link UInt256}. - */ +/** Basic unit tests for {@link UInt256}. */ public class UInt256Test { - // Range of numbers to be tested at extremes for Integer and Long. - private static final int TEST_RANGE = 1_000_000; - - /** - * Test construction from byte arrays where bytes.length < UInt256.BYTES. - */ - @Test - public void when_constructing_uint256_from_byte_arrays__values_compare_equal() { - assertEquals(UInt256.from(0x00000000_00000001L), new UInt256(bytes(1))); - assertEquals(UInt256.from(0x00000000_00000102L), new UInt256(bytes(1, 2))); - assertEquals(UInt256.from(0x00000000_00010203L), new UInt256(bytes(1, 2, 3))); - assertEquals(UInt256.from(0x00000000_01020304L), new UInt256(bytes(1, 2, 3, 4))); - assertEquals(UInt256.from(0x00000001_02030405L), new UInt256(bytes(1, 2, 3, 4, 5))); - assertEquals(UInt256.from(0x00000102_03040506L), new UInt256(bytes(1, 2, 3, 4, 5, 6))); - assertEquals(UInt256.from(0x00010203_04050607L), new UInt256(bytes(1, 2, 3, 4, 5, 6, 7))); - assertEquals(UInt256.from(0x01020304_05060708L), new UInt256(bytes(1, 2, 3, 4, 5, 6, 7, 8))); - } - - private byte[] bytes(int... bs) { - byte[] bytes = new byte[bs.length]; - for (int i = 0; i < bs.length; ++i) { - bytes[i] = (byte) bs[i]; - } - return bytes; - } - - /** - * Exhaustively test construction for all non-negative {@code short} - * values, from {@code 0} up to and including {@link Short.MAX_VALUE}. - */ - @Test - public void when_constructing_int256_from_short_values__values_compare_equal() { - for (int i = 0; i <= Short.MAX_VALUE; ++i) { - short s = (short) i; - UInt256 int256 = UInt256.from(s); - assertEqualToLong(s, int256); - } - } - - /** - * Test construction for two ranges of {@code int} values from {@code 0} - * through to {@code TEST_RANGE}, and also from {@code Integer.MAX_VALUE} - * down to {@code Integer.MAX_VALUE - TEST_RANGE}. - */ - @Test - public void when_constructing_int256_from_int_values__values_compare_equal() { - // Here we will just assume that testing some values near the - // extremes of the range will suffice. - for (int i = 0; i <= TEST_RANGE; ++i) { - UInt256 int256 = UInt256.from(i); - assertEqualToLong(i, int256); - } - for (int i = 0; i <= TEST_RANGE; ++i) { - int ii = Integer.MAX_VALUE - i; - UInt256 int256 = UInt256.from(ii); - assertEqualToLong(ii, int256); - } - } - - /** - * Test construction for two ranges of {@code long} values from {@code 0} - * through to {@code TEST_RANGE}, and also from {@code Long.MAX_VALUE} - * down to {@code Long.MAX_VALUE - TEST_RANGE}. - */ - @Test - public void when_constructing_int256_from_long_values__accessors_compare_equal() { - // Here we will just assume that testing some values near the - // extremes of the range will suffice. - for (int i = 0; i < TEST_RANGE; ++i) { - long l = i; - UInt256 int256 = UInt256.from(l); - assertEqualToLong(l, int256); - } - for (int i = 0; i < TEST_RANGE; ++i) { - long l = Long.MAX_VALUE - i; - UInt256 int256 = UInt256.from(l); - assertEqualToLong(l, int256); - } - } - - @Test - public void when_performing_basic_addition__the_correct_values_are_returned() { - // Some basics - // 0 + 1 = 1 - assertEqualToLong(1L, UInt256.ZERO.add(UInt256.ONE)); - // 1 + 1 = 2 - assertEqualToLong(2L, UInt256.ONE.add(UInt256.ONE)); - // max + 1 = 0 (overflow) - assertEqualToLong(0L, UInt256.MAX_VALUE.add(UInt256.ONE)); - // 1 + max = 0 (overflow) - assertEqualToLong(0L, UInt256.ONE.add(UInt256.MAX_VALUE)); - - // The half-add methods too - // 0 + 1 = 1 - assertEqualToLong(1L, UInt256.ZERO.add(UInt128.ONE)); - // 1 + 1 = 2 - assertEqualToLong(2L, UInt256.ONE.add(UInt128.ONE)); - // max + 1 = 0 (overflow) - assertEqualToLong(0L, UInt256.MAX_VALUE.add(UInt128.ONE)); - } - - @Test - public void when_performing_addition_overflowing_between_words__the_correct_values_are_returned() { - // Test adding with carry. - UInt256 carry1 = UInt256.from(UInt128.ZERO, UInt128.MAX_VALUE).add(UInt256.ONE); - assertEquals(UInt128.ONE, carry1.getHigh()); - assertEquals(UInt128.ZERO, carry1.getLow()); - - // And also for the half-add method - // Test adding with carry. - UInt256 carry2 = UInt256.from(UInt128.ZERO, UInt128.MAX_VALUE).add(UInt128.ONE); - assertEquals(UInt128.ONE, carry2.getHigh()); - assertEquals(UInt128.ZERO, carry2.getLow()); - } - - @Test - public void when_performing_basic_subtraction__the_correct_values_are_returned() { - // Some basics - // 1 - 1 = 0 - assertEqualToLong(0L, UInt256.ONE.subtract(UInt256.ONE)); - // 2 - 1 = 1 - assertEqualToLong(1L, UInt256.TWO.subtract(UInt256.ONE)); - // 0 - 1 = max (underflow) - assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.subtract(UInt256.ONE)); - // 0 - max = 1 (underflow) - assertEqualToLong(1L, UInt256.ZERO.subtract(UInt256.MAX_VALUE)); - - // Half subtract methods also - // 1 - 1 = 0 - assertEqualToLong(0L, UInt256.ONE.subtract(UInt128.ONE)); - // 2 - 1 = 1 - assertEqualToLong(1L, UInt256.TWO.subtract(UInt128.ONE)); - // 0 - 1 = max (underflow) - assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.subtract(UInt128.ONE)); - } - - @Test - public void when_performing_subtraction_underflowing_between_words__the_correct_value_is_returned() { - // Test subtraction with carry. - UInt256 carry1 = UInt256.from(UInt128.ONE, UInt128.ZERO).subtract(UInt256.ONE); - assertEquals(UInt128.ZERO, carry1.high); - assertEquals(UInt128.MAX_VALUE, carry1.low); - UInt256 carry2 = UInt256.ZERO.subtract(UInt256.ONE); // underflow - assertEquals(UInt128.MAX_VALUE, carry2.high); - assertEquals(UInt128.MAX_VALUE, carry2.low); - - // also with half-subtract methods - UInt256 carry3 = UInt256.from(UInt128.ONE, UInt128.ZERO).subtract(UInt128.ONE); - assertEquals(UInt128.ZERO, carry3.high); - assertEquals(UInt128.MAX_VALUE, carry3.low); - UInt256 carry4 = UInt256.ZERO.subtract(UInt128.ONE); // underflow - assertEquals(UInt128.MAX_VALUE, carry4.high); - assertEquals(UInt128.MAX_VALUE, carry4.low); - } - - @Test - public void when_incrementing_int256__the_correct_values_are_returned() { - assertEquals(UInt256.ONE, UInt256.ZERO.increment()); - assertEquals(UInt256.ZERO, UInt256.MAX_VALUE.increment()); // Internal and full overflow - } - - @Test - public void when_decrementing_int256__the_correct_values_are_returned() { - assertEquals(UInt256.ZERO, UInt256.ONE.decrement()); - assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.decrement()); // Internal and full overflow - } - - @Test - public void when_multiplying_two_values__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt256.ZERO, UInt256.ZERO.multiply(UInt256.ZERO)); - assertEquals(UInt256.ZERO, UInt256.ZERO.multiply(UInt256.ONE)); - assertEquals(UInt256.ZERO, UInt256.ONE.multiply(UInt256.ZERO)); - assertEquals(UInt256.ONE, UInt256.ONE.multiply(UInt256.ONE)); - - // Some values in the long range - assertEquals(UInt256.from(12345678L * 13L), UInt256.from(12345678L).multiply(UInt256.from(13L))); - } - - @Test - public void when_dividing_one_value_by_another__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt256.ZERO, UInt256.ZERO.divide(UInt256.ONE)); - assertEquals(UInt256.ONE, UInt256.ONE.divide(UInt256.ONE)); - - // Some values in the long range - assertEquals(UInt256.from(12345678L / 13L), UInt256.from(12345678L).divide(UInt256.from(13L))); - } - - @Test(expected = IllegalArgumentException.class) - public void when_dividing_by_zero__an_exception_is_thrown() { - UInt256.ONE.divide(UInt256.ZERO); - fail(); - } - - @Test - public void when_computing_the_remainder_of_dividing_one_value_by_another__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt256.ZERO, UInt256.ZERO.remainder(UInt256.ONE)); - assertEquals(UInt256.ZERO, UInt256.ONE.remainder(UInt256.ONE)); - assertEquals(UInt256.ONE, UInt256.ONE.remainder(UInt256.TWO)); - - // Some values in the long range - assertEquals(UInt256.from(12345678L % 13L), UInt256.from(12345678L).remainder(UInt256.from(13L))); - } - - @Test(expected = IllegalArgumentException.class) - public void when_computing_the_remainder_of_dividing_by_zero__an_exception_is_thrown() { - UInt256.ONE.remainder(UInt256.ZERO); - fail(); - } - - @Test - public void when_comparing_int256_values_using_compareTo__the_correct_value_is_returned() { - assertThat(UInt256.ZERO) - .isEqualByComparingTo(UInt256.ZERO) - .isLessThan(UInt256.ONE) - .isLessThan(UInt256.MAX_VALUE); - assertThat(UInt256.ONE).isGreaterThan(UInt256.ZERO); - assertThat(UInt256.MAX_VALUE).isGreaterThan(UInt256.ZERO); - - UInt256 i127 = UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT); - UInt256 i128 = i127.add(i127); - UInt256 i129 = i128.add(i128); - assertThat(i128).isGreaterThan(i127); // In case something has gone horribly wrong. - assertThat(i128.add(i127)).isGreaterThan(i128); - assertThat(i129).isGreaterThan(i128.add(i127)); - } - - @Test - public void when_comparing_int256_values_using_equals__the_correct_value_is_returned() { - assertNotEquals(UInt256.ZERO, null); // Nothing should be equal to null - assertEquals(UInt256.ZERO, UInt256.ZERO); // Same object check - UInt256 i127a = UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT); - UInt256 i127b = UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT); - assertEquals(i127a, i127b); - assertNotEquals(i127a, i127b.add(i127b)); - assertNotEquals(i127a, UInt256.ZERO); - } - - @Test - public void when_calculating_leading_zeros__the_correct_value_is_returned() { - assertEquals(UInt256.SIZE, UInt256.ZERO.numberOfLeadingZeros()); - assertEquals(UInt256.SIZE - 1, UInt256.ONE.numberOfLeadingZeros()); - assertEquals(0, UInt256.MAX_VALUE.numberOfLeadingZeros()); - assertEquals(UInt256.SIZE / 2 - 1, UInt256.from(UInt128.ONE, UInt128.ZERO).numberOfLeadingZeros()); - } - - @Test - public void when_binary_oring_two_values__the_correct_value_is_returned() { - // Use bit positions in both high and low word for tests - UInt256 b0 = UInt256.ONE; - UInt256 b128 = UInt256.from(UInt128.ONE, UInt128.ZERO); - // Basic sanity checks to make sure nothing is horribly wrong - assertThat(b128).isGreaterThan(b0); - assertThat(b0).isGreaterThan(UInt256.ZERO); - assertThat(b128).isGreaterThan(UInt256.ZERO); - - // Now for the real tests - assertEquals(b0, UInt256.ZERO.or(b0)); - assertEquals(b0, b0.or(b0)); - assertEquals(b128, UInt256.ZERO.or(b128)); - assertEquals(b128, b128.or(b128)); - assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.or(UInt256.MAX_VALUE)); - assertEquals(UInt256.MAX_VALUE, UInt256.MAX_VALUE.or(UInt256.MAX_VALUE)); - } - - @Test - public void when_binary_anding_two_values__the_correct_value_is_returned() { - // Use bit positions in both high and low word for tests - UInt256 b0 = UInt256.ONE; - UInt256 b128 = UInt256.from(UInt128.ONE, UInt128.ZERO); - assertEquals(b0, UInt256.MAX_VALUE.and(b0)); - assertEquals(b128, UInt256.MAX_VALUE.and(b128)); - assertEquals(UInt256.ZERO, UInt256.MAX_VALUE.and(UInt256.ZERO)); - } - - @Test - public void when_binary_xoring_two_values__the_correct_value_is_returned() { - // Use bit positions in both high and low word for tests - UInt256 b0 = UInt256.ONE; - UInt256 b128 = UInt256.from(UInt128.ONE, UInt128.ZERO); - assertEquals(b0, UInt256.ZERO.xor(b0)); - assertEquals(UInt256.ZERO, b0.xor(b0)); - assertEquals(b128, UInt256.ZERO.xor(b128)); - assertEquals(UInt256.ZERO, b128.xor(b128)); - assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.xor(UInt256.MAX_VALUE)); - assertEquals(UInt256.ZERO, UInt256.MAX_VALUE.xor(UInt256.MAX_VALUE)); - } - - @Test - public void when_creating_int256_from_byte_array__the_correct_value_is_created() { - byte[] m1 = { - -1 - }; - byte[] p1 = { - 1 - }; - byte[] bytesArray = new byte[UInt256.BYTES]; - Arrays.fill(bytesArray, (byte) 0); - bytesArray[UInt256.BYTES - 1] = 1; - UInt256 m1Bits128 = UInt256.from(m1); - UInt256 p1Bits128 = UInt256.from(p1); - UInt256 bytesArrayBits128 = UInt256.from(bytesArray); - - assertEquals(UInt256.from(255), m1Bits128); // Sign extension did not happen - assertEquals(UInt256.ONE, p1Bits128); // Zero fill happened correctly - assertEquals(UInt256.ONE, bytesArrayBits128); // Correct size array OK - } - - @Test - public void when_converting_int256_to_byte_array__the_correct_values_are_returned() { - UInt128 bp0 = UInt128.from(0x0001_0203_0405_0607L, 0x0809_0A0B_0C0D_0E0FL); - UInt128 bp1 = UInt128.from(0x1011_1213_1415_1617L, 0x1819_1A1B_1C1D_1E1FL); - UInt256 bitPattern = UInt256.from(bp0, bp1); - byte[] bytes2 = new byte[UInt256.BYTES * 3]; - Arrays.fill(bytes2, (byte) -1); - - // Make sure we got the value in big-endian order - byte[] bytes = bitPattern.toByteArray(); - for (int i = 0; i < UInt256.BYTES; ++i) { - assertEquals(i, bytes[i]); - } - - bitPattern.toByteArray(bytes2, UInt256.BYTES); - // Make sure we didn't overwrite bytes outside our range - for (int i = 0; i < UInt256.BYTES; ++i) { - assertEquals(-1, bytes2[i]); - assertEquals(-1, bytes2[i + UInt256.BYTES * 2]); - } - // Make sure we got the value in big-endian order - for (int i = 0; i < UInt256.BYTES; ++i) { - assertEquals(i, bytes2[UInt256.BYTES + i]); - } - } - - @Test - public void when_performing_binary_shifts__the_correct_value_is_returned() { - final UInt128 minusTwo = UInt128.ZERO.decrement().decrement(); - final UInt128 maxSigned = UInt128.HIGH_BIT.decrement(); - - // Basic cases, left shift - assertEquals(UInt256.ZERO, UInt256.ZERO.shiftLeft()); - // Zero extend on left - assertEquals(UInt256.from(UInt128.MAX_VALUE, minusTwo), UInt256.MAX_VALUE.shiftLeft()); - assertEquals(UInt256.from(2), UInt256.ONE.shiftLeft()); - // Make sure bit crosses word boundary correctly - assertEquals(UInt256.from(UInt128.ONE, UInt128.ZERO), UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT).shiftLeft()); - - // Basic cases, right shift - assertEquals(UInt256.ZERO, UInt256.ZERO.shiftRight()); - // Zeros inserted at right - assertEquals(UInt256.from(maxSigned, UInt128.MAX_VALUE), UInt256.MAX_VALUE.shiftRight()); - assertEquals(UInt256.ZERO, UInt256.ONE.shiftRight()); - assertEquals(UInt256.ONE, UInt256.from(2).shiftRight()); - // Make sure bit crosses word boundary correctly - assertEquals(UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT), UInt256.from(UInt128.ONE, UInt128.ZERO).shiftRight()); - } - - /** - * Test multi shiftLeft. - */ - @Test - public void when_performing_binary_left_shifts_by_n__the_correct_value_is_returned() { - for (int i = 0; i < UInt256.SIZE; ++i) { - UInt256 powNum = UInt256.TWO.pow(i); - UInt256 shiftNum = UInt256.ONE.shiftLeft(i); - assertEquals(powNum, shiftNum); - } - assertEquals(UInt256.ZERO, UInt256.ONE.shiftLeft(UInt256.SIZE)); - assertEquals(UInt256.ZERO, UInt256.ONE.shiftLeft(-UInt256.SIZE)); - assertEquals(UInt256.ONE, UInt256.TWO.shiftLeft(-1)); - } - - /** - * Test multi shiftRight. - */ - @Test - public void when_performing_binary_right_shifts_by_n__the_correct_value_is_returned() { - for (int i = 0; i < UInt256.SIZE; ++i) { - UInt256 powNum = UInt256.TWO.pow(UInt256.SIZE - (i + 1)); - UInt256 shiftNum = UInt256.HIGH_BIT.shiftRight(i); - assertEquals(powNum, shiftNum); - } - assertEquals(UInt256.ZERO, UInt256.HIGH_BIT.shiftRight(UInt256.SIZE)); - assertEquals(UInt256.ZERO, UInt256.HIGH_BIT.shiftRight(-UInt256.SIZE)); - assertEquals(UInt256.TWO, UInt256.ONE.shiftRight(-1)); - } - - /** - * Integer square root. - */ - @Test - public void when_performing_integer_square_root__the_correct_value_is_returned() { - long max = 100_000_000L; // Needs to be not more than 1L << 53, ie precision of double - for (long n = 0; n < max; n += 997) { - long lsqrt = (long) Math.floor(Math.sqrt(n)); - UInt256 nn = UInt256.from(n); - UInt256 isqrt = nn.isqrt(); - assertEquals(UInt256.from(lsqrt), isqrt); - } - for (int n = 0; n < UInt256.SIZE; ++n) { - UInt256 num = UInt256.ONE.shiftLeft(n); - byte[] bytes = num.toByteArray(); - BigInteger nsqrt = BigIntegerMath.sqrt(new BigInteger(1, bytes), RoundingMode.FLOOR); - byte[] otherBytes = num.isqrt().toByteArray(); - BigInteger isqrt = new BigInteger(1, otherBytes); - assertEquals(nsqrt, isqrt); - } - } - - @Test - public void when_performing_bitwise_inversion__the_correct_value_is_returned() { - assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.invert()); - } - - @Test - public void when_using_predicates__the_correct_value_is_returned() { - // Basic tests for odd/even - assertTrue(UInt256.ONE.isOdd()); - assertFalse(UInt256.ONE.isEven()); - assertTrue(UInt256.MAX_VALUE.isOdd()); - assertFalse(UInt256.MAX_VALUE.isEven()); - UInt256 two = UInt256.ONE.add(UInt256.ONE); - assertFalse(two.isOdd()); - assertTrue(two.isEven()); - UInt256 minusTwo = UInt256.MAX_VALUE.add(UInt256.MAX_VALUE); - assertFalse(minusTwo.isOdd()); - assertTrue(minusTwo.isEven()); - - assertFalse(UInt256.ONE.isZero()); - assertFalse(UInt256.MAX_VALUE.isZero()); - assertTrue(UInt256.ZERO.isZero()); - } - - @Test - public void when_converting_int256_to_string__the_correct_value_is_returned() { - // Some basics - assertEquals("0", UInt256.ZERO.toString()); - assertEquals("1", UInt256.ONE.toString()); - assertEquals("10", UInt256.TEN.toString()); - - assertEquals("12345678", UInt256.from(12345678L).toString()); - UInt256 maxPositive = UInt256.from(UInt128.MAX_VALUE, UInt128.MAX_VALUE); - // Need to zero extend here to make positive - byte[] bytes = maxPositive.toByteArray(); - assertEquals(new BigInteger(1, bytes).toString(), maxPositive.toString()); - - assertFalse(UInt256.ZERO.isHighBitSet()); - assertFalse(UInt256.TWO.pow(254).isHighBitSet()); - assertFalse(UInt256.TWO.pow(255).decrement().isHighBitSet()); - assertTrue(UInt256.MAX_VALUE.isHighBitSet()); - assertTrue(UInt256.ONE.invert().isHighBitSet()); - assertTrue(UInt256.TWO.pow(255).isHighBitSet()); - assertTrue(UInt256.HIGH_BIT.isHighBitSet()); - } - - @Test - public void when_converting_string_to_int256__the_correct_value_is_returned() { - testRoundTrip("0"); - testRoundTrip("123456789"); - testRoundTrip("123456789123456789"); - testRoundTrip("123456789123456789123456789123456789"); - assertEquals( - UInt256.from(UInt128.MAX_VALUE, UInt128.MAX_VALUE), - UInt256.from(BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).toString()) - ); - } - - @Test - public void when_calculating_powers__the_correct_value_is_returned() { - assertEquals(UInt256.from(1L << 9), UInt256.TWO.pow(9)); - assertEquals(UInt256.from(10_000_000_000L), UInt256.TEN.pow(10)); - assertEquals(UInt256.ONE, UInt256.ZERO.pow(0)); // At least in the limit - assertEquals(UInt256.ONE, UInt256.ONE.pow(0)); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(UInt256.class) - .withNonnullFields("high", "low") - .verify(); - } - - /** - * Test div 0. - */ - @Test(expected = IllegalArgumentException.class) - public void testDiv0() { - UInt256.ONE.divide(UInt256.ZERO); - } - - /** - * Test rem 0. - */ - @Test(expected = IllegalArgumentException.class) - public void testRem0() { - UInt256.ONE.remainder(UInt256.ZERO); - } - - /** - * NumberFormatException on empty string. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionOnEmpty() { - UInt256.from(""); - } - - /** - * NumberFormatException if no actual number. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionIfNoNumber() { - UInt256.from("+"); - } - - /** - * NumberFormatException if invalid digit. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionIfInvalidDigit() { - UInt256.from("+a"); - } - - /** - * IllegalArgumentException if byte array is empty. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionIfByteArrayEmpty() { - UInt256.from(new byte[0]); - } - - /** - * IllegalArgumentException on radix too big. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnRadixTooBig() { - UInt256.ONE.toString(Character.MAX_RADIX + 1); - } - - /** - * IllegalArgumentException on radix too small. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnRadixTooSmall() { - UInt256.ONE.toString(Character.MIN_RADIX - 1); - } - - /** - * IllegalArgumentException on negative exponent for pow. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnNegativeExponent() { - UInt256.ONE.pow(-1); - } - - private static void testRoundTrip(String s) { - assertEquals(s, UInt256.from(s).toString()); - } - - private static void assertEqualToLong(long expectedValue, UInt256 testValue) { - assertEquals(0, testValue.high.getHigh()); - assertEquals(0, testValue.high.getLow()); - assertEquals(0, testValue.low.getHigh()); - assertEquals(expectedValue, testValue.low.getLow()); - } + // Range of numbers to be tested at extremes for Integer and Long. + private static final int TEST_RANGE = 1_000_000; + + /** Test construction from byte arrays where bytes.length < UInt256.BYTES. */ + @Test + public void when_constructing_uint256_from_byte_arrays__values_compare_equal() { + assertEquals(UInt256.from(0x00000000_00000001L), new UInt256(bytes(1))); + assertEquals(UInt256.from(0x00000000_00000102L), new UInt256(bytes(1, 2))); + assertEquals(UInt256.from(0x00000000_00010203L), new UInt256(bytes(1, 2, 3))); + assertEquals(UInt256.from(0x00000000_01020304L), new UInt256(bytes(1, 2, 3, 4))); + assertEquals(UInt256.from(0x00000001_02030405L), new UInt256(bytes(1, 2, 3, 4, 5))); + assertEquals(UInt256.from(0x00000102_03040506L), new UInt256(bytes(1, 2, 3, 4, 5, 6))); + assertEquals(UInt256.from(0x00010203_04050607L), new UInt256(bytes(1, 2, 3, 4, 5, 6, 7))); + assertEquals(UInt256.from(0x01020304_05060708L), new UInt256(bytes(1, 2, 3, 4, 5, 6, 7, 8))); + } + + private byte[] bytes(int... bs) { + byte[] bytes = new byte[bs.length]; + for (int i = 0; i < bs.length; ++i) { + bytes[i] = (byte) bs[i]; + } + return bytes; + } + + /** + * Exhaustively test construction for all non-negative {@code short} values, from {@code 0} up to + * and including {@link Short.MAX_VALUE}. + */ + @Test + public void when_constructing_int256_from_short_values__values_compare_equal() { + for (int i = 0; i <= Short.MAX_VALUE; ++i) { + short s = (short) i; + UInt256 int256 = UInt256.from(s); + assertEqualToLong(s, int256); + } + } + + /** + * Test construction for two ranges of {@code int} values from {@code 0} through to {@code + * TEST_RANGE}, and also from {@code Integer.MAX_VALUE} down to {@code Integer.MAX_VALUE - + * TEST_RANGE}. + */ + @Test + public void when_constructing_int256_from_int_values__values_compare_equal() { + // Here we will just assume that testing some values near the + // extremes of the range will suffice. + for (int i = 0; i <= TEST_RANGE; ++i) { + UInt256 int256 = UInt256.from(i); + assertEqualToLong(i, int256); + } + for (int i = 0; i <= TEST_RANGE; ++i) { + int ii = Integer.MAX_VALUE - i; + UInt256 int256 = UInt256.from(ii); + assertEqualToLong(ii, int256); + } + } + + /** + * Test construction for two ranges of {@code long} values from {@code 0} through to {@code + * TEST_RANGE}, and also from {@code Long.MAX_VALUE} down to {@code Long.MAX_VALUE - TEST_RANGE}. + */ + @Test + public void when_constructing_int256_from_long_values__accessors_compare_equal() { + // Here we will just assume that testing some values near the + // extremes of the range will suffice. + for (int i = 0; i < TEST_RANGE; ++i) { + long l = i; + UInt256 int256 = UInt256.from(l); + assertEqualToLong(l, int256); + } + for (int i = 0; i < TEST_RANGE; ++i) { + long l = Long.MAX_VALUE - i; + UInt256 int256 = UInt256.from(l); + assertEqualToLong(l, int256); + } + } + + @Test + public void when_performing_basic_addition__the_correct_values_are_returned() { + // Some basics + // 0 + 1 = 1 + assertEqualToLong(1L, UInt256.ZERO.add(UInt256.ONE)); + // 1 + 1 = 2 + assertEqualToLong(2L, UInt256.ONE.add(UInt256.ONE)); + // max + 1 = 0 (overflow) + assertEqualToLong(0L, UInt256.MAX_VALUE.add(UInt256.ONE)); + // 1 + max = 0 (overflow) + assertEqualToLong(0L, UInt256.ONE.add(UInt256.MAX_VALUE)); + + // The half-add methods too + // 0 + 1 = 1 + assertEqualToLong(1L, UInt256.ZERO.add(UInt128.ONE)); + // 1 + 1 = 2 + assertEqualToLong(2L, UInt256.ONE.add(UInt128.ONE)); + // max + 1 = 0 (overflow) + assertEqualToLong(0L, UInt256.MAX_VALUE.add(UInt128.ONE)); + } + + @Test + public void + when_performing_addition_overflowing_between_words__the_correct_values_are_returned() { + // Test adding with carry. + UInt256 carry1 = UInt256.from(UInt128.ZERO, UInt128.MAX_VALUE).add(UInt256.ONE); + assertEquals(UInt128.ONE, carry1.getHigh()); + assertEquals(UInt128.ZERO, carry1.getLow()); + + // And also for the half-add method + // Test adding with carry. + UInt256 carry2 = UInt256.from(UInt128.ZERO, UInt128.MAX_VALUE).add(UInt128.ONE); + assertEquals(UInt128.ONE, carry2.getHigh()); + assertEquals(UInt128.ZERO, carry2.getLow()); + } + + @Test + public void when_performing_basic_subtraction__the_correct_values_are_returned() { + // Some basics + // 1 - 1 = 0 + assertEqualToLong(0L, UInt256.ONE.subtract(UInt256.ONE)); + // 2 - 1 = 1 + assertEqualToLong(1L, UInt256.TWO.subtract(UInt256.ONE)); + // 0 - 1 = max (underflow) + assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.subtract(UInt256.ONE)); + // 0 - max = 1 (underflow) + assertEqualToLong(1L, UInt256.ZERO.subtract(UInt256.MAX_VALUE)); + + // Half subtract methods also + // 1 - 1 = 0 + assertEqualToLong(0L, UInt256.ONE.subtract(UInt128.ONE)); + // 2 - 1 = 1 + assertEqualToLong(1L, UInt256.TWO.subtract(UInt128.ONE)); + // 0 - 1 = max (underflow) + assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.subtract(UInt128.ONE)); + } + + @Test + public void + when_performing_subtraction_underflowing_between_words__the_correct_value_is_returned() { + // Test subtraction with carry. + UInt256 carry1 = UInt256.from(UInt128.ONE, UInt128.ZERO).subtract(UInt256.ONE); + assertEquals(UInt128.ZERO, carry1.high); + assertEquals(UInt128.MAX_VALUE, carry1.low); + UInt256 carry2 = UInt256.ZERO.subtract(UInt256.ONE); // underflow + assertEquals(UInt128.MAX_VALUE, carry2.high); + assertEquals(UInt128.MAX_VALUE, carry2.low); + + // also with half-subtract methods + UInt256 carry3 = UInt256.from(UInt128.ONE, UInt128.ZERO).subtract(UInt128.ONE); + assertEquals(UInt128.ZERO, carry3.high); + assertEquals(UInt128.MAX_VALUE, carry3.low); + UInt256 carry4 = UInt256.ZERO.subtract(UInt128.ONE); // underflow + assertEquals(UInt128.MAX_VALUE, carry4.high); + assertEquals(UInt128.MAX_VALUE, carry4.low); + } + + @Test + public void when_incrementing_int256__the_correct_values_are_returned() { + assertEquals(UInt256.ONE, UInt256.ZERO.increment()); + assertEquals(UInt256.ZERO, UInt256.MAX_VALUE.increment()); // Internal and full overflow + } + + @Test + public void when_decrementing_int256__the_correct_values_are_returned() { + assertEquals(UInt256.ZERO, UInt256.ONE.decrement()); + assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.decrement()); // Internal and full overflow + } + + @Test + public void when_multiplying_two_values__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt256.ZERO, UInt256.ZERO.multiply(UInt256.ZERO)); + assertEquals(UInt256.ZERO, UInt256.ZERO.multiply(UInt256.ONE)); + assertEquals(UInt256.ZERO, UInt256.ONE.multiply(UInt256.ZERO)); + assertEquals(UInt256.ONE, UInt256.ONE.multiply(UInt256.ONE)); + + // Some values in the long range + assertEquals( + UInt256.from(12345678L * 13L), UInt256.from(12345678L).multiply(UInt256.from(13L))); + } + + @Test + public void when_dividing_one_value_by_another__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt256.ZERO, UInt256.ZERO.divide(UInt256.ONE)); + assertEquals(UInt256.ONE, UInt256.ONE.divide(UInt256.ONE)); + + // Some values in the long range + assertEquals(UInt256.from(12345678L / 13L), UInt256.from(12345678L).divide(UInt256.from(13L))); + } + + @Test(expected = IllegalArgumentException.class) + public void when_dividing_by_zero__an_exception_is_thrown() { + UInt256.ONE.divide(UInt256.ZERO); + fail(); + } + + @Test + public void + when_computing_the_remainder_of_dividing_one_value_by_another__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt256.ZERO, UInt256.ZERO.remainder(UInt256.ONE)); + assertEquals(UInt256.ZERO, UInt256.ONE.remainder(UInt256.ONE)); + assertEquals(UInt256.ONE, UInt256.ONE.remainder(UInt256.TWO)); + + // Some values in the long range + assertEquals( + UInt256.from(12345678L % 13L), UInt256.from(12345678L).remainder(UInt256.from(13L))); + } + + @Test(expected = IllegalArgumentException.class) + public void when_computing_the_remainder_of_dividing_by_zero__an_exception_is_thrown() { + UInt256.ONE.remainder(UInt256.ZERO); + fail(); + } + + @Test + public void when_comparing_int256_values_using_compareTo__the_correct_value_is_returned() { + assertThat(UInt256.ZERO) + .isEqualByComparingTo(UInt256.ZERO) + .isLessThan(UInt256.ONE) + .isLessThan(UInt256.MAX_VALUE); + assertThat(UInt256.ONE).isGreaterThan(UInt256.ZERO); + assertThat(UInt256.MAX_VALUE).isGreaterThan(UInt256.ZERO); + + UInt256 i127 = UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT); + UInt256 i128 = i127.add(i127); + UInt256 i129 = i128.add(i128); + assertThat(i128).isGreaterThan(i127); // In case something has gone horribly wrong. + assertThat(i128.add(i127)).isGreaterThan(i128); + assertThat(i129).isGreaterThan(i128.add(i127)); + } + + @Test + public void when_comparing_int256_values_using_equals__the_correct_value_is_returned() { + assertNotEquals(UInt256.ZERO, null); // Nothing should be equal to null + assertEquals(UInt256.ZERO, UInt256.ZERO); // Same object check + UInt256 i127a = UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT); + UInt256 i127b = UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT); + assertEquals(i127a, i127b); + assertNotEquals(i127a, i127b.add(i127b)); + assertNotEquals(i127a, UInt256.ZERO); + } + + @Test + public void when_calculating_leading_zeros__the_correct_value_is_returned() { + assertEquals(UInt256.SIZE, UInt256.ZERO.numberOfLeadingZeros()); + assertEquals(UInt256.SIZE - 1, UInt256.ONE.numberOfLeadingZeros()); + assertEquals(0, UInt256.MAX_VALUE.numberOfLeadingZeros()); + assertEquals( + UInt256.SIZE / 2 - 1, UInt256.from(UInt128.ONE, UInt128.ZERO).numberOfLeadingZeros()); + } + + @Test + public void when_binary_oring_two_values__the_correct_value_is_returned() { + // Use bit positions in both high and low word for tests + UInt256 b0 = UInt256.ONE; + UInt256 b128 = UInt256.from(UInt128.ONE, UInt128.ZERO); + // Basic sanity checks to make sure nothing is horribly wrong + assertThat(b128).isGreaterThan(b0); + assertThat(b0).isGreaterThan(UInt256.ZERO); + assertThat(b128).isGreaterThan(UInt256.ZERO); + + // Now for the real tests + assertEquals(b0, UInt256.ZERO.or(b0)); + assertEquals(b0, b0.or(b0)); + assertEquals(b128, UInt256.ZERO.or(b128)); + assertEquals(b128, b128.or(b128)); + assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.or(UInt256.MAX_VALUE)); + assertEquals(UInt256.MAX_VALUE, UInt256.MAX_VALUE.or(UInt256.MAX_VALUE)); + } + + @Test + public void when_binary_anding_two_values__the_correct_value_is_returned() { + // Use bit positions in both high and low word for tests + UInt256 b0 = UInt256.ONE; + UInt256 b128 = UInt256.from(UInt128.ONE, UInt128.ZERO); + assertEquals(b0, UInt256.MAX_VALUE.and(b0)); + assertEquals(b128, UInt256.MAX_VALUE.and(b128)); + assertEquals(UInt256.ZERO, UInt256.MAX_VALUE.and(UInt256.ZERO)); + } + + @Test + public void when_binary_xoring_two_values__the_correct_value_is_returned() { + // Use bit positions in both high and low word for tests + UInt256 b0 = UInt256.ONE; + UInt256 b128 = UInt256.from(UInt128.ONE, UInt128.ZERO); + assertEquals(b0, UInt256.ZERO.xor(b0)); + assertEquals(UInt256.ZERO, b0.xor(b0)); + assertEquals(b128, UInt256.ZERO.xor(b128)); + assertEquals(UInt256.ZERO, b128.xor(b128)); + assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.xor(UInt256.MAX_VALUE)); + assertEquals(UInt256.ZERO, UInt256.MAX_VALUE.xor(UInt256.MAX_VALUE)); + } + + @Test + public void when_creating_int256_from_byte_array__the_correct_value_is_created() { + byte[] m1 = {-1}; + byte[] p1 = {1}; + byte[] bytesArray = new byte[UInt256.BYTES]; + Arrays.fill(bytesArray, (byte) 0); + bytesArray[UInt256.BYTES - 1] = 1; + UInt256 m1Bits128 = UInt256.from(m1); + UInt256 p1Bits128 = UInt256.from(p1); + UInt256 bytesArrayBits128 = UInt256.from(bytesArray); + + assertEquals(UInt256.from(255), m1Bits128); // Sign extension did not happen + assertEquals(UInt256.ONE, p1Bits128); // Zero fill happened correctly + assertEquals(UInt256.ONE, bytesArrayBits128); // Correct size array OK + } + + @Test + public void when_converting_int256_to_byte_array__the_correct_values_are_returned() { + UInt128 bp0 = UInt128.from(0x0001_0203_0405_0607L, 0x0809_0A0B_0C0D_0E0FL); + UInt128 bp1 = UInt128.from(0x1011_1213_1415_1617L, 0x1819_1A1B_1C1D_1E1FL); + UInt256 bitPattern = UInt256.from(bp0, bp1); + byte[] bytes2 = new byte[UInt256.BYTES * 3]; + Arrays.fill(bytes2, (byte) -1); + + // Make sure we got the value in big-endian order + byte[] bytes = bitPattern.toByteArray(); + for (int i = 0; i < UInt256.BYTES; ++i) { + assertEquals(i, bytes[i]); + } + + bitPattern.toByteArray(bytes2, UInt256.BYTES); + // Make sure we didn't overwrite bytes outside our range + for (int i = 0; i < UInt256.BYTES; ++i) { + assertEquals(-1, bytes2[i]); + assertEquals(-1, bytes2[i + UInt256.BYTES * 2]); + } + // Make sure we got the value in big-endian order + for (int i = 0; i < UInt256.BYTES; ++i) { + assertEquals(i, bytes2[UInt256.BYTES + i]); + } + } + + @Test + public void when_performing_binary_shifts__the_correct_value_is_returned() { + final UInt128 minusTwo = UInt128.ZERO.decrement().decrement(); + final UInt128 maxSigned = UInt128.HIGH_BIT.decrement(); + + // Basic cases, left shift + assertEquals(UInt256.ZERO, UInt256.ZERO.shiftLeft()); + // Zero extend on left + assertEquals(UInt256.from(UInt128.MAX_VALUE, minusTwo), UInt256.MAX_VALUE.shiftLeft()); + assertEquals(UInt256.from(2), UInt256.ONE.shiftLeft()); + // Make sure bit crosses word boundary correctly + assertEquals( + UInt256.from(UInt128.ONE, UInt128.ZERO), + UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT).shiftLeft()); + + // Basic cases, right shift + assertEquals(UInt256.ZERO, UInt256.ZERO.shiftRight()); + // Zeros inserted at right + assertEquals(UInt256.from(maxSigned, UInt128.MAX_VALUE), UInt256.MAX_VALUE.shiftRight()); + assertEquals(UInt256.ZERO, UInt256.ONE.shiftRight()); + assertEquals(UInt256.ONE, UInt256.from(2).shiftRight()); + // Make sure bit crosses word boundary correctly + assertEquals( + UInt256.from(UInt128.ZERO, UInt128.HIGH_BIT), + UInt256.from(UInt128.ONE, UInt128.ZERO).shiftRight()); + } + + /** Test multi shiftLeft. */ + @Test + public void when_performing_binary_left_shifts_by_n__the_correct_value_is_returned() { + for (int i = 0; i < UInt256.SIZE; ++i) { + UInt256 powNum = UInt256.TWO.pow(i); + UInt256 shiftNum = UInt256.ONE.shiftLeft(i); + assertEquals(powNum, shiftNum); + } + assertEquals(UInt256.ZERO, UInt256.ONE.shiftLeft(UInt256.SIZE)); + assertEquals(UInt256.ZERO, UInt256.ONE.shiftLeft(-UInt256.SIZE)); + assertEquals(UInt256.ONE, UInt256.TWO.shiftLeft(-1)); + } + + /** Test multi shiftRight. */ + @Test + public void when_performing_binary_right_shifts_by_n__the_correct_value_is_returned() { + for (int i = 0; i < UInt256.SIZE; ++i) { + UInt256 powNum = UInt256.TWO.pow(UInt256.SIZE - (i + 1)); + UInt256 shiftNum = UInt256.HIGH_BIT.shiftRight(i); + assertEquals(powNum, shiftNum); + } + assertEquals(UInt256.ZERO, UInt256.HIGH_BIT.shiftRight(UInt256.SIZE)); + assertEquals(UInt256.ZERO, UInt256.HIGH_BIT.shiftRight(-UInt256.SIZE)); + assertEquals(UInt256.TWO, UInt256.ONE.shiftRight(-1)); + } + + /** Integer square root. */ + @Test + public void when_performing_integer_square_root__the_correct_value_is_returned() { + long max = 100_000_000L; // Needs to be not more than 1L << 53, ie precision of double + for (long n = 0; n < max; n += 997) { + long lsqrt = (long) Math.floor(Math.sqrt(n)); + UInt256 nn = UInt256.from(n); + UInt256 isqrt = nn.isqrt(); + assertEquals(UInt256.from(lsqrt), isqrt); + } + for (int n = 0; n < UInt256.SIZE; ++n) { + UInt256 num = UInt256.ONE.shiftLeft(n); + byte[] bytes = num.toByteArray(); + BigInteger nsqrt = BigIntegerMath.sqrt(new BigInteger(1, bytes), RoundingMode.FLOOR); + byte[] otherBytes = num.isqrt().toByteArray(); + BigInteger isqrt = new BigInteger(1, otherBytes); + assertEquals(nsqrt, isqrt); + } + } + + @Test + public void when_performing_bitwise_inversion__the_correct_value_is_returned() { + assertEquals(UInt256.MAX_VALUE, UInt256.ZERO.invert()); + } + + @Test + public void when_using_predicates__the_correct_value_is_returned() { + // Basic tests for odd/even + assertTrue(UInt256.ONE.isOdd()); + assertFalse(UInt256.ONE.isEven()); + assertTrue(UInt256.MAX_VALUE.isOdd()); + assertFalse(UInt256.MAX_VALUE.isEven()); + UInt256 two = UInt256.ONE.add(UInt256.ONE); + assertFalse(two.isOdd()); + assertTrue(two.isEven()); + UInt256 minusTwo = UInt256.MAX_VALUE.add(UInt256.MAX_VALUE); + assertFalse(minusTwo.isOdd()); + assertTrue(minusTwo.isEven()); + + assertFalse(UInt256.ONE.isZero()); + assertFalse(UInt256.MAX_VALUE.isZero()); + assertTrue(UInt256.ZERO.isZero()); + } + + @Test + public void when_converting_int256_to_string__the_correct_value_is_returned() { + // Some basics + assertEquals("0", UInt256.ZERO.toString()); + assertEquals("1", UInt256.ONE.toString()); + assertEquals("10", UInt256.TEN.toString()); + + assertEquals("12345678", UInt256.from(12345678L).toString()); + UInt256 maxPositive = UInt256.from(UInt128.MAX_VALUE, UInt128.MAX_VALUE); + // Need to zero extend here to make positive + byte[] bytes = maxPositive.toByteArray(); + assertEquals(new BigInteger(1, bytes).toString(), maxPositive.toString()); + + assertFalse(UInt256.ZERO.isHighBitSet()); + assertFalse(UInt256.TWO.pow(254).isHighBitSet()); + assertFalse(UInt256.TWO.pow(255).decrement().isHighBitSet()); + assertTrue(UInt256.MAX_VALUE.isHighBitSet()); + assertTrue(UInt256.ONE.invert().isHighBitSet()); + assertTrue(UInt256.TWO.pow(255).isHighBitSet()); + assertTrue(UInt256.HIGH_BIT.isHighBitSet()); + } + + @Test + public void when_converting_string_to_int256__the_correct_value_is_returned() { + testRoundTrip("0"); + testRoundTrip("123456789"); + testRoundTrip("123456789123456789"); + testRoundTrip("123456789123456789123456789123456789"); + assertEquals( + UInt256.from(UInt128.MAX_VALUE, UInt128.MAX_VALUE), + UInt256.from(BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).toString())); + } + + @Test + public void when_calculating_powers__the_correct_value_is_returned() { + assertEquals(UInt256.from(1L << 9), UInt256.TWO.pow(9)); + assertEquals(UInt256.from(10_000_000_000L), UInt256.TEN.pow(10)); + assertEquals(UInt256.ONE, UInt256.ZERO.pow(0)); // At least in the limit + assertEquals(UInt256.ONE, UInt256.ONE.pow(0)); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(UInt256.class).withNonnullFields("high", "low").verify(); + } + + /** Test div 0. */ + @Test(expected = IllegalArgumentException.class) + public void testDiv0() { + UInt256.ONE.divide(UInt256.ZERO); + } + + /** Test rem 0. */ + @Test(expected = IllegalArgumentException.class) + public void testRem0() { + UInt256.ONE.remainder(UInt256.ZERO); + } + + /** NumberFormatException on empty string. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionOnEmpty() { + UInt256.from(""); + } + + /** NumberFormatException if no actual number. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionIfNoNumber() { + UInt256.from("+"); + } + + /** NumberFormatException if invalid digit. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionIfInvalidDigit() { + UInt256.from("+a"); + } + + /** IllegalArgumentException if byte array is empty. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionIfByteArrayEmpty() { + UInt256.from(new byte[0]); + } + + /** IllegalArgumentException on radix too big. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnRadixTooBig() { + UInt256.ONE.toString(Character.MAX_RADIX + 1); + } + + /** IllegalArgumentException on radix too small. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnRadixTooSmall() { + UInt256.ONE.toString(Character.MIN_RADIX - 1); + } + + /** IllegalArgumentException on negative exponent for pow. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnNegativeExponent() { + UInt256.ONE.pow(-1); + } + + private static void testRoundTrip(String s) { + assertEquals(s, UInt256.from(s).toString()); + } + + private static void assertEqualToLong(long expectedValue, UInt256 testValue) { + assertEquals(0, testValue.high.getHigh()); + assertEquals(0, testValue.high.getLow()); + assertEquals(0, testValue.low.getHigh()); + assertEquals(expectedValue, testValue.low.getLow()); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256sTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256sTest.java index 640089b9a1..7a19e7dadf 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256sTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt256sTest.java @@ -64,6 +64,8 @@ package com.radixdlt.utils; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -78,218 +80,199 @@ import java.util.function.LongFunction; import java.util.stream.LongStream; import java.util.stream.Stream; - import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - public class UInt256sTest { - @Test - public void when_constructing_uint256_from_big_integer__values_compare_equal() { - for (int pow2 = 0; pow2 <= 255; pow2++) { - assertEquals( - UInt256s.fromBigInteger(BigInteger.valueOf(2).pow(pow2)), - UInt256.TWO.pow(pow2) - ); - } - } - - @Test - public void when_constructing_uint256_from_negative_big_integer__exception_is_thrown() { - assertThatThrownBy(() -> UInt256s.fromBigInteger(BigInteger.valueOf(-1L))) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void when_constructing_uint256_from_too_large_biginteger__exception_is_thrown() { - BigInteger tooBig = BigInteger.valueOf(2).pow(256); - assertThatThrownBy(() -> UInt256s.fromBigInteger(tooBig)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void when_constructing_uint256_from_bigdecimal__values_compare_equal() { - for (int pow2 = 0; pow2 <= 255; pow2++) { - assertEquals( - UInt256s.fromBigDecimal(BigDecimal.valueOf(2).pow(pow2)), - UInt256.TWO.pow(pow2) - ); - } - } - - @Test - public void when_constructing_uint256_from_negative_bigdecimal__exception_is_thrown() { - assertThatThrownBy(() -> UInt256s.fromBigDecimal(BigDecimal.valueOf(-1))) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void when_constructing_uint256_from_too_large_bigdecimal__exception_is_thrown() { - BigDecimal tooBig = BigDecimal.valueOf(2).pow(256); - assertThatThrownBy(() -> UInt256s.fromBigDecimal(tooBig)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void when_constructing_uint256_from_fractional_bigdecimal__exception_is_thrown() { - BigDecimal fractional = BigDecimal.valueOf(Math.PI); - assertThatThrownBy(() -> UInt256s.fromBigDecimal(fractional)) - .isInstanceOf(ArithmeticException.class); - } - - @Test - public void test_min_cases() { - assertEquals(UInt256.ZERO, UInt256s.min(UInt256.ONE, UInt256.ZERO)); - assertEquals(UInt256.ZERO, UInt256s.min(UInt256.ZERO, UInt256.ONE)); - - assertEquals(UInt256.TEN, UInt256s.min(UInt256.MAX_VALUE, UInt256.TEN)); - assertEquals(UInt256.TEN, UInt256s.min(UInt256.TEN, UInt256.MAX_VALUE)); - - assertEquals(UInt256.ONE, UInt256s.min(UInt256.ONE, UInt256.ONE)); - } - - @Test - public void test_max_cases() { - assertEquals(UInt256.ONE, UInt256s.max(UInt256.ONE, UInt256.ZERO)); - assertEquals(UInt256.ONE, UInt256s.max(UInt256.ZERO, UInt256.ONE)); - - assertEquals(UInt256.MAX_VALUE, UInt256s.max(UInt256.MAX_VALUE, UInt256.TEN)); - assertEquals(UInt256.MAX_VALUE, UInt256s.max(UInt256.TEN, UInt256.MAX_VALUE)); - - assertEquals(UInt256.ONE, UInt256s.max(UInt256.ONE, UInt256.ONE)); - } - - @Test - public void lcm_edgecases() { - // Null cap - assertThatThrownBy(() -> UInt256s.cappedLCM(null, UInt256.ONE, UInt256.TWO)) - .isInstanceOf(NullPointerException.class); - - // Empty varargs - assertThatThrownBy(() -> UInt256s.cappedLCM(UInt256.MAX_VALUE)) - .isInstanceOf(ArrayIndexOutOfBoundsException.class); - - // Exceeds cap - assertNull(UInt256s.cappedLCM(UInt256.ONE, UInt256.TWO, UInt256.THREE)); - - // Zero element - assertEquals(UInt256.ZERO, UInt256s.cappedLCM(UInt256.MAX_VALUE, UInt256.ONE, UInt256.ZERO)); - - // Overflow - lcm of two largest primes less than UInt256.MAX_VALUE - UInt256 largePrime1 = UInt256s.fromBigInteger( - new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639747") - ); - UInt256 largePrime2 = UInt256s.fromBigInteger( - new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639579") - ); - assertNull(UInt256s.cappedLCM(UInt256.MAX_VALUE, largePrime1, largePrime2)); - } - - private static void testPrimeLCM( - IntFunction arrayBuilder, - LongFunction builder, - BinaryOperator multiply, - T one, - T max, - Function toBI, - BiFunction lcmFunction - ) { - List primeList = Arrays.asList( - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, - 31, 37, 41, 43, 47, 53, 59, 61, 67, - 71, 73, 79, 83, 89, 97, 101, 103, 107, - 109, 113, 127, 131, 137, 139, 149 - ); - IntFunction primeNumbers = size -> primeList.stream() - .mapToLong(i -> i) - .limit(size); - - BigInteger maxBI = toBI.apply(max); - for (int size = 1; size < primeList.size(); size++) { - T[] values = primeNumbers.apply(size) - .mapToObj(builder) - .toArray(arrayBuilder); - - BigInteger biExpected = primeNumbers.apply(size) - .mapToObj(BigInteger::valueOf) - .reduce(BigInteger.ONE, BigInteger::multiply); - - final T expected; - if (biExpected.compareTo(maxBI) > 0) { - expected = null; - } else { - expected = primeNumbers.apply(size) - .mapToObj(builder) - .reduce(one, multiply); - } - - assertThat(lcmFunction.apply(max, values)) - .isEqualTo(expected); - } - } - - private static void testGeometricLCM( - IntFunction arrayBuilder, - LongFunction builder, - BinaryOperator multiply, - BiFunction pow, - T max, - Function toBI, - BiFunction lcmFunction - ) { - - BigInteger maxBI = toBI.apply(max); - for (int exponent = 1; exponent < 12; exponent++) { - for (int base = 2; base < 1024; base++) { - final T baseNum = builder.apply(base); - final T[] values = Stream.iterate(baseNum, n -> multiply.apply(n, baseNum)) - .limit(exponent) - .toArray(arrayBuilder); - - final BigInteger biExpected = BigInteger.valueOf(base).pow(exponent); - final T expected; - if (biExpected.compareTo(maxBI) > 0) { - expected = null; - } else { - expected = pow.apply(builder.apply(base), exponent); - } - - assertThat(lcmFunction.apply(max, values)) - .isEqualTo(expected); - } - } - } - - @Test - public void testOneAndMax256() { - assertThat(UInt256s.cappedLCM(UInt256.MAX_VALUE, UInt256.ONE, UInt256.MAX_VALUE)) - .isEqualTo(UInt256.MAX_VALUE); - } - - @Test - public void testPrimeLCM256() { - testPrimeLCM( - UInt256[]::new, - UInt256::from, - UInt256::multiply, - UInt256.ONE, - UInt256.MAX_VALUE, - i -> new BigInteger(1, i.toByteArray()), - UInt256s::cappedLCM - ); - } - - @Test - public void testGeometricLCM256() { - testGeometricLCM( - UInt256[]::new, - UInt256::from, - UInt256::multiply, - UInt256::pow, - UInt256.MAX_VALUE, - i -> new BigInteger(1, i.toByteArray()), - UInt256s::cappedLCM - ); - } -} \ No newline at end of file + @Test + public void when_constructing_uint256_from_big_integer__values_compare_equal() { + for (int pow2 = 0; pow2 <= 255; pow2++) { + assertEquals(UInt256s.fromBigInteger(BigInteger.valueOf(2).pow(pow2)), UInt256.TWO.pow(pow2)); + } + } + + @Test + public void when_constructing_uint256_from_negative_big_integer__exception_is_thrown() { + assertThatThrownBy(() -> UInt256s.fromBigInteger(BigInteger.valueOf(-1L))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void when_constructing_uint256_from_too_large_biginteger__exception_is_thrown() { + BigInteger tooBig = BigInteger.valueOf(2).pow(256); + assertThatThrownBy(() -> UInt256s.fromBigInteger(tooBig)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void when_constructing_uint256_from_bigdecimal__values_compare_equal() { + for (int pow2 = 0; pow2 <= 255; pow2++) { + assertEquals(UInt256s.fromBigDecimal(BigDecimal.valueOf(2).pow(pow2)), UInt256.TWO.pow(pow2)); + } + } + + @Test + public void when_constructing_uint256_from_negative_bigdecimal__exception_is_thrown() { + assertThatThrownBy(() -> UInt256s.fromBigDecimal(BigDecimal.valueOf(-1))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void when_constructing_uint256_from_too_large_bigdecimal__exception_is_thrown() { + BigDecimal tooBig = BigDecimal.valueOf(2).pow(256); + assertThatThrownBy(() -> UInt256s.fromBigDecimal(tooBig)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void when_constructing_uint256_from_fractional_bigdecimal__exception_is_thrown() { + BigDecimal fractional = BigDecimal.valueOf(Math.PI); + assertThatThrownBy(() -> UInt256s.fromBigDecimal(fractional)) + .isInstanceOf(ArithmeticException.class); + } + + @Test + public void test_min_cases() { + assertEquals(UInt256.ZERO, UInt256s.min(UInt256.ONE, UInt256.ZERO)); + assertEquals(UInt256.ZERO, UInt256s.min(UInt256.ZERO, UInt256.ONE)); + + assertEquals(UInt256.TEN, UInt256s.min(UInt256.MAX_VALUE, UInt256.TEN)); + assertEquals(UInt256.TEN, UInt256s.min(UInt256.TEN, UInt256.MAX_VALUE)); + + assertEquals(UInt256.ONE, UInt256s.min(UInt256.ONE, UInt256.ONE)); + } + + @Test + public void test_max_cases() { + assertEquals(UInt256.ONE, UInt256s.max(UInt256.ONE, UInt256.ZERO)); + assertEquals(UInt256.ONE, UInt256s.max(UInt256.ZERO, UInt256.ONE)); + + assertEquals(UInt256.MAX_VALUE, UInt256s.max(UInt256.MAX_VALUE, UInt256.TEN)); + assertEquals(UInt256.MAX_VALUE, UInt256s.max(UInt256.TEN, UInt256.MAX_VALUE)); + + assertEquals(UInt256.ONE, UInt256s.max(UInt256.ONE, UInt256.ONE)); + } + + @Test + public void lcm_edgecases() { + // Null cap + assertThatThrownBy(() -> UInt256s.cappedLCM(null, UInt256.ONE, UInt256.TWO)) + .isInstanceOf(NullPointerException.class); + + // Empty varargs + assertThatThrownBy(() -> UInt256s.cappedLCM(UInt256.MAX_VALUE)) + .isInstanceOf(ArrayIndexOutOfBoundsException.class); + + // Exceeds cap + assertNull(UInt256s.cappedLCM(UInt256.ONE, UInt256.TWO, UInt256.THREE)); + + // Zero element + assertEquals(UInt256.ZERO, UInt256s.cappedLCM(UInt256.MAX_VALUE, UInt256.ONE, UInt256.ZERO)); + + // Overflow - lcm of two largest primes less than UInt256.MAX_VALUE + UInt256 largePrime1 = + UInt256s.fromBigInteger( + new BigInteger( + "115792089237316195423570985008687907853269984665640564039457584007913129639747")); + UInt256 largePrime2 = + UInt256s.fromBigInteger( + new BigInteger( + "115792089237316195423570985008687907853269984665640564039457584007913129639579")); + assertNull(UInt256s.cappedLCM(UInt256.MAX_VALUE, largePrime1, largePrime2)); + } + + private static void testPrimeLCM( + IntFunction arrayBuilder, + LongFunction builder, + BinaryOperator multiply, + T one, + T max, + Function toBI, + BiFunction lcmFunction) { + List primeList = + Arrays.asList( + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, + 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149); + IntFunction primeNumbers = size -> primeList.stream().mapToLong(i -> i).limit(size); + + BigInteger maxBI = toBI.apply(max); + for (int size = 1; size < primeList.size(); size++) { + T[] values = primeNumbers.apply(size).mapToObj(builder).toArray(arrayBuilder); + + BigInteger biExpected = + primeNumbers + .apply(size) + .mapToObj(BigInteger::valueOf) + .reduce(BigInteger.ONE, BigInteger::multiply); + + final T expected; + if (biExpected.compareTo(maxBI) > 0) { + expected = null; + } else { + expected = primeNumbers.apply(size).mapToObj(builder).reduce(one, multiply); + } + + assertThat(lcmFunction.apply(max, values)).isEqualTo(expected); + } + } + + private static void testGeometricLCM( + IntFunction arrayBuilder, + LongFunction builder, + BinaryOperator multiply, + BiFunction pow, + T max, + Function toBI, + BiFunction lcmFunction) { + + BigInteger maxBI = toBI.apply(max); + for (int exponent = 1; exponent < 12; exponent++) { + for (int base = 2; base < 1024; base++) { + final T baseNum = builder.apply(base); + final T[] values = + Stream.iterate(baseNum, n -> multiply.apply(n, baseNum)) + .limit(exponent) + .toArray(arrayBuilder); + + final BigInteger biExpected = BigInteger.valueOf(base).pow(exponent); + final T expected; + if (biExpected.compareTo(maxBI) > 0) { + expected = null; + } else { + expected = pow.apply(builder.apply(base), exponent); + } + + assertThat(lcmFunction.apply(max, values)).isEqualTo(expected); + } + } + } + + @Test + public void testOneAndMax256() { + assertThat(UInt256s.cappedLCM(UInt256.MAX_VALUE, UInt256.ONE, UInt256.MAX_VALUE)) + .isEqualTo(UInt256.MAX_VALUE); + } + + @Test + public void testPrimeLCM256() { + testPrimeLCM( + UInt256[]::new, + UInt256::from, + UInt256::multiply, + UInt256.ONE, + UInt256.MAX_VALUE, + i -> new BigInteger(1, i.toByteArray()), + UInt256s::cappedLCM); + } + + @Test + public void testGeometricLCM256() { + testGeometricLCM( + UInt256[]::new, + UInt256::from, + UInt256::multiply, + UInt256::pow, + UInt256.MAX_VALUE, + i -> new BigInteger(1, i.toByteArray()), + UInt256s::cappedLCM); + } +} diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt384Test.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt384Test.java index ab0489ceeb..d9e95fb0e4 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt384Test.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UInt384Test.java @@ -64,14 +64,6 @@ package com.radixdlt.utils; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.util.Arrays; - -import org.junit.Test; - -import com.google.common.math.BigIntegerMath; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -79,589 +71,574 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.common.math.BigIntegerMath; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Arrays; import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; -/** - * Basic unit tests for {@link UInt384}. - */ +/** Basic unit tests for {@link UInt384}. */ public class UInt384Test { - // Range of numbers to be tested at extremes for Integer and Long. - private static final int TEST_RANGE = 1_000_000; - - /** - * Exhaustively test construction for all non-negative {@code short} - * values, from {@code 0} up to and including {@link Short.MAX_VALUE}. - */ - @Test - public void when_constructing_int384_from_short_values__values_compare_equal() { - for (int i = 0; i <= Short.MAX_VALUE; ++i) { - short s = (short) i; - UInt384 int384 = UInt384.from(s); - assertEqualToLong(s, int384); - } - } - - /** - * Test construction for two ranges of {@code int} values from {@code 0} - * through to {@code TEST_RANGE}, and also from {@code Integer.MAX_VALUE} - * down to {@code Integer.MAX_VALUE - TEST_RANGE}. - */ - @Test - public void when_constructing_int384_from_int_values__values_compare_equal() { - // Here we will just assume that testing some values near the - // extremes of the range will suffice. - for (int i = 0; i <= TEST_RANGE; ++i) { - UInt384 int384 = UInt384.from(i); - assertEqualToLong(i, int384); - } - for (int i = 0; i <= TEST_RANGE; ++i) { - int ii = Integer.MAX_VALUE - i; - UInt384 int384 = UInt384.from(ii); - assertEqualToLong(ii, int384); - } - } - - /** - * Test construction for two ranges of {@code long} values from {@code 0} - * through to {@code TEST_RANGE}, and also from {@code Long.MAX_VALUE} - * down to {@code Long.MAX_VALUE - TEST_RANGE}. - */ - @Test - public void when_constructing_int384_from_long_values__accessors_compare_equal() { - // Here we will just assume that testing some values near the - // extremes of the range will suffice. - for (int i = 0; i < TEST_RANGE; ++i) { - long l = i; - UInt384 int384 = UInt384.from(l); - assertEqualToLong(l, int384); - } - for (int i = 0; i < TEST_RANGE; ++i) { - long l = Long.MAX_VALUE - i; - UInt384 int384 = UInt384.from(l); - assertEqualToLong(l, int384); - } - } - - @Test - public void when_performing_basic_addition__the_correct_values_are_returned() { - // Some basics - // 0 + 1 = 1 - assertEqualToLong(1L, UInt384.ZERO.add(UInt384.ONE)); - // 1 + 1 = 2 - assertEqualToLong(2L, UInt384.ONE.add(UInt384.ONE)); - // max + 1 = 0 (overflow) - assertEqualToLong(0L, UInt384.MAX_VALUE.add(UInt384.ONE)); - // 1 + max = 0 (overflow) - assertEqualToLong(0L, UInt384.ONE.add(UInt384.MAX_VALUE)); - - // The half-add methods too - // 0 + 1 = 1 - assertEqualToLong(1L, UInt384.ZERO.add(UInt256.ONE)); - // 1 + 1 = 2 - assertEqualToLong(2L, UInt384.ONE.add(UInt256.ONE)); - // max + 1 = 0 (overflow) - assertEqualToLong(0L, UInt384.MAX_VALUE.add(UInt256.ONE)); - } - - @Test - public void when_performing_addition_overflowing_between_words__the_correct_values_are_returned() { - // Test adding with carry. - UInt384 carry1 = UInt384.from(UInt128.ZERO, UInt256.MAX_VALUE).add(UInt384.ONE); - assertEquals(UInt128.ONE, carry1.getHigh()); - assertEquals(UInt256.ZERO, carry1.getLow()); - - // And also for the half-add method (actually 2/3 add in this case) - // Test adding with carry. - UInt384 carry2 = UInt384.from(UInt128.ZERO, UInt256.MAX_VALUE).add(UInt256.ONE); - assertEquals(UInt128.ONE, carry2.getHigh()); - assertEquals(UInt256.ZERO, carry2.getLow()); - } - - @Test - public void when_performing_basic_subtraction__the_correct_values_are_returned() { - // Some basics - // 1 - 1 = 0 - assertEqualToLong(0L, UInt384.ONE.subtract(UInt384.ONE)); - // 2 - 1 = 1 - assertEqualToLong(1L, UInt384.TWO.subtract(UInt384.ONE)); - // 0 - 1 = max (underflow) - assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.subtract(UInt384.ONE)); - // 0 - max = 1 (underflow) - assertEqualToLong(1L, UInt384.ZERO.subtract(UInt384.MAX_VALUE)); - - // Half subtract methods also - // 1 - 1 = 0 - assertEqualToLong(0L, UInt384.ONE.subtract(UInt256.ONE)); - // 2 - 1 = 1 - assertEqualToLong(1L, UInt384.TWO.subtract(UInt256.ONE)); - // 0 - 1 = max (underflow) - assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.subtract(UInt256.ONE)); - } - - @Test - public void when_performing_subtraction_underflowing_between_words__the_correct_value_is_returned() { - // Test subtraction with carry. - UInt384 carry1 = UInt384.from(UInt128.ONE, UInt256.ZERO).subtract(UInt384.ONE); - assertEquals(UInt128.ZERO, carry1.high); - assertEquals(UInt256.MAX_VALUE, carry1.low); - UInt384 carry2 = UInt384.ZERO.subtract(UInt384.ONE); // underflow - assertEquals(UInt128.MAX_VALUE, carry2.high); - assertEquals(UInt256.MAX_VALUE, carry2.low); - - // also with half-subtract methods - UInt384 carry3 = UInt384.from(UInt128.ONE, UInt256.ZERO).subtract(UInt256.ONE); - assertEquals(UInt128.ZERO, carry3.high); - assertEquals(UInt256.MAX_VALUE, carry3.low); - UInt384 carry4 = UInt384.ZERO.subtract(UInt256.ONE); // underflow - assertEquals(UInt128.MAX_VALUE, carry4.high); - assertEquals(UInt256.MAX_VALUE, carry4.low); - } - - @Test - public void when_incrementing_int384__the_correct_values_are_returned() { - assertEquals(UInt384.ONE, UInt384.ZERO.increment()); - assertEquals(UInt384.ZERO, UInt384.MAX_VALUE.increment()); // Internal and full overflow - } - - @Test - public void when_decrementing_int384__the_correct_values_are_returned() { - assertEquals(UInt384.ZERO, UInt384.ONE.decrement()); - assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.decrement()); // Internal and full overflow - } - - @Test - public void when_multiplying_two_values__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt384.ZERO)); - assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt384.ONE)); - assertEquals(UInt384.ZERO, UInt384.ONE.multiply(UInt384.ZERO)); - assertEquals(UInt384.ONE, UInt384.ONE.multiply(UInt384.ONE)); - - // Some values in the long range - assertEquals(UInt384.from(12345678L * 13L), UInt384.from(12345678L).multiply(UInt384.from(13L))); - } - - @Test - public void when_multiplying_uint256_values__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt256.ZERO)); - assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt256.ONE)); - assertEquals(UInt384.ZERO, UInt384.ONE.multiply(UInt256.ZERO)); - assertEquals(UInt384.ONE, UInt384.ONE.multiply(UInt256.ONE)); - - // Some values in the long range - assertEquals(UInt384.from(12345678L * 13L), UInt384.from(12345678L).multiply(UInt256.from(13L))); - } - - @Test - public void when_dividing_one_value_by_another__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt384.ZERO, UInt384.ZERO.divide(UInt384.ONE)); - assertEquals(UInt384.ONE, UInt384.ONE.divide(UInt384.ONE)); - - // Some values in the long range - assertEquals(UInt384.from(12345678L / 13L), UInt384.from(12345678L).divide(UInt384.from(13L))); - } - - @Test(expected = IllegalArgumentException.class) - public void when_dividing_by_zero__an_exception_is_thrown() { - UInt384.ONE.divide(UInt384.ZERO); - fail(); - } - - @Test - public void when_dividing_one_value_by_another_int256__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt384.ZERO, UInt384.ZERO.divide(UInt256.ONE)); - assertEquals(UInt384.ONE, UInt384.ONE.divide(UInt256.ONE)); - assertEquals(UInt384.MAX_VALUE, UInt384.MAX_VALUE.divide(UInt256.ONE)); - assertEquals(UInt384.ONE.shiftLeft(UInt384.SIZE - UInt256.SIZE), UInt384.MAX_VALUE.divide(UInt256.MAX_VALUE)); - - // Some values in the long range - assertEquals(UInt384.from(12345678L / 13L), UInt384.from(12345678L).divide(UInt256.from(13L))); - } - - @Test(expected = IllegalArgumentException.class) - public void when_dividing_by_zero_int256__an_exception_is_thrown() { - UInt384.ONE.divide(UInt256.ZERO); - fail(); - } - - @Test - public void when_computing_the_remainder_of_dividing_one_value_by_another__the_correct_value_is_returned() { - // Some basics - assertEquals(UInt384.ZERO, UInt384.ZERO.remainder(UInt384.ONE)); - assertEquals(UInt384.ZERO, UInt384.ONE.remainder(UInt384.ONE)); - assertEquals(UInt384.ONE, UInt384.ONE.remainder(UInt384.TWO)); - - // Some values in the long range - assertEquals(UInt384.from(12345678L % 13L), UInt384.from(12345678L).remainder(UInt384.from(13L))); - } - - @Test(expected = IllegalArgumentException.class) - public void when_computing_the_remainder_of_dividing_by_zero__an_exception_is_thrown() { - UInt384.ONE.remainder(UInt384.ZERO); - fail(); - } - - @Test - public void when_comparing_int384_values_using_compareTo__the_correct_value_is_returned() { - assertThat(UInt384.ZERO) - .isEqualByComparingTo(UInt384.ZERO) - .isLessThan(UInt384.ONE) - .isLessThan(UInt384.MAX_VALUE); - assertThat(UInt384.ONE).isGreaterThan(UInt384.ZERO); - assertThat(UInt384.MAX_VALUE).isGreaterThan(UInt384.ZERO); - - UInt384 i127 = UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT); - UInt384 i128 = i127.add(i127); - UInt384 i129 = i128.add(i128); - assertThat(i128).isGreaterThan(i127); // In case something has gone horribly wrong. - assertThat(i128.add(i127)).isGreaterThan(i128); - assertThat(i129).isGreaterThan(i128.add(i127)); - } - - @Test - public void when_comparing_int384_values_using_equals__the_correct_value_is_returned() { - assertNotEquals(UInt384.ZERO, null); // Nothing should be equal to null - assertEquals(UInt384.ZERO, UInt384.ZERO); // Same object check - UInt384 i127a = UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT); - UInt384 i127b = UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT); - assertEquals(i127a, i127b); - assertNotEquals(i127a, i127b.add(i127b)); - assertNotEquals(i127a, UInt384.ZERO); - } - - @Test - public void when_calculating_leading_zeros__the_correct_value_is_returned() { - assertEquals(UInt384.SIZE, UInt384.ZERO.numberOfLeadingZeros()); - assertEquals(UInt384.SIZE - 1, UInt384.ONE.numberOfLeadingZeros()); - assertEquals(0, UInt384.MAX_VALUE.numberOfLeadingZeros()); - assertEquals(UInt128.SIZE - 1, UInt384.from(UInt128.ONE, UInt256.ZERO).numberOfLeadingZeros()); - assertEquals(UInt128.SIZE, UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT).numberOfLeadingZeros()); - } - - @Test - public void when_binary_oring_two_values__the_correct_value_is_returned() { - // Use bit positions in both high and low word for tests - UInt384 b0 = UInt384.ONE; - UInt384 b128 = UInt384.from(UInt128.ONE, UInt256.ZERO); - // Basic sanity checks to make sure nothing is horribly wrong - assertThat(b128).isGreaterThan(b0); - assertThat(b0).isGreaterThan(UInt384.ZERO); - assertThat(b128).isGreaterThan(UInt384.ZERO); - - // Now for the real tests - assertEquals(b0, UInt384.ZERO.or(b0)); - assertEquals(b0, b0.or(b0)); - assertEquals(b128, UInt384.ZERO.or(b128)); - assertEquals(b128, b128.or(b128)); - assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.or(UInt384.MAX_VALUE)); - assertEquals(UInt384.MAX_VALUE, UInt384.MAX_VALUE.or(UInt384.MAX_VALUE)); - } - - @Test - public void when_binary_anding_two_values__the_correct_value_is_returned() { - // Use bit positions in both high and low word for tests - UInt384 b0 = UInt384.ONE; - UInt384 b128 = UInt384.from(UInt128.ONE, UInt256.ZERO); - assertEquals(b0, UInt384.MAX_VALUE.and(b0)); - assertEquals(b128, UInt384.MAX_VALUE.and(b128)); - assertEquals(UInt384.ZERO, UInt384.MAX_VALUE.and(UInt384.ZERO)); - } - - @Test - public void when_binary_xoring_two_values__the_correct_value_is_returned() { - // Use bit positions in both high and low word for tests - UInt384 b0 = UInt384.ONE; - UInt384 b128 = UInt384.from(UInt128.ONE, UInt256.ZERO); - assertEquals(b0, UInt384.ZERO.xor(b0)); - assertEquals(UInt384.ZERO, b0.xor(b0)); - assertEquals(b128, UInt384.ZERO.xor(b128)); - assertEquals(UInt384.ZERO, b128.xor(b128)); - assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.xor(UInt384.MAX_VALUE)); - assertEquals(UInt384.ZERO, UInt384.MAX_VALUE.xor(UInt384.MAX_VALUE)); - } - - @Test - public void when_creating_int256_from_byte_array__the_correct_value_is_created() { - byte[] m1 = { - -1 - }; - byte[] p1 = { - 1 - }; - byte[] bytesArray = new byte[UInt384.BYTES]; - Arrays.fill(bytesArray, (byte) 0); - bytesArray[UInt384.BYTES - 1] = 1; - UInt384 m1Bits128 = UInt384.from(m1); - UInt384 p1Bits128 = UInt384.from(p1); - UInt384 bytesArrayBits128 = UInt384.from(bytesArray); - - assertEquals(UInt384.from(255), m1Bits128); // Sign extension did not happen - assertEquals(UInt384.ONE, p1Bits128); // Zero fill happened correctly - assertEquals(UInt384.ONE, bytesArrayBits128); // Correct size array OK - } - - @Test - public void when_converting_int256_to_byte_array__the_correct_values_are_returned() { - UInt128 bp0 = UInt128.from(0x0001_0203_0405_0607L, 0x0809_0A0B_0C0D_0E0FL); - UInt256 bp1 = UInt256.from( - UInt128.from(0x1011_1213_1415_1617L, 0x1819_1A1B_1C1D_1E1FL), - UInt128.from(0x2021_2223_2425_2627L, 0x2829_2A2B_2C2D_2E2FL) - ); - UInt384 bitPattern = UInt384.from(bp0, bp1); - byte[] bytes2 = new byte[UInt384.BYTES * 3]; - Arrays.fill(bytes2, (byte) -1); - - // Make sure we got the value in big-endian order - byte[] bytes = bitPattern.toByteArray(); - for (int i = 0; i < UInt384.BYTES; ++i) { - assertEquals(i, bytes[i]); - } - - bitPattern.toByteArray(bytes2, UInt384.BYTES); - // Make sure we didn't overwrite bytes outside our range - for (int i = 0; i < UInt384.BYTES; ++i) { - assertEquals(-1, bytes2[i]); - assertEquals(-1, bytes2[i + UInt384.BYTES * 2]); - } - // Make sure we got the value in big-endian order - for (int i = 0; i < UInt384.BYTES; ++i) { - assertEquals(i, bytes2[UInt384.BYTES + i]); - } - } - - @Test - public void when_performing_binary_shifts__the_correct_value_is_returned() { - final UInt256 minusTwo = UInt256.ZERO.decrement().decrement(); - final UInt128 maxSigned = UInt128.HIGH_BIT.decrement(); - - // Basic cases, left shift - assertEquals(UInt384.ZERO, UInt384.ZERO.shiftLeft()); - // Zero extend on left - assertEquals(UInt384.from(UInt128.MAX_VALUE, minusTwo), UInt384.MAX_VALUE.shiftLeft()); - assertEquals(UInt384.from(2), UInt384.ONE.shiftLeft()); - // Make sure bit crosses word boundary correctly - assertEquals(UInt384.from(UInt128.ONE, UInt256.ZERO), UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT).shiftLeft()); - - // Basic cases, right shift - assertEquals(UInt384.ZERO, UInt384.ZERO.shiftRight()); - // Zeros inserted at right - assertEquals(UInt384.from(maxSigned, UInt256.MAX_VALUE), UInt384.MAX_VALUE.shiftRight()); - assertEquals(UInt384.ZERO, UInt384.ONE.shiftRight()); - assertEquals(UInt384.ONE, UInt384.from(2).shiftRight()); - // Make sure bit crosses word boundary correctly - assertEquals(UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT), UInt384.from(UInt128.ONE, UInt256.ZERO).shiftRight()); - } - - /** - * Test multi shiftLeft. - */ - @Test - public void when_performing_binary_left_shifts_by_n__the_correct_value_is_returned() { - for (int i = 0; i < UInt384.SIZE; ++i) { - UInt384 powNum = UInt384.TWO.pow(i); - UInt384 shiftNum = UInt384.ONE.shiftLeft(i); - assertEquals(powNum, shiftNum); - } - assertEquals(UInt384.ZERO, UInt384.ONE.shiftLeft(UInt384.SIZE)); - assertEquals(UInt384.ZERO, UInt384.ONE.shiftLeft(-UInt384.SIZE)); - assertEquals(UInt384.ONE, UInt384.TWO.shiftLeft(-1)); - } - - /** - * Test multi shiftRight. - */ - @Test - public void when_performing_binary_right_shifts_by_n__the_correct_value_is_returned() { - for (int i = 0; i < UInt384.SIZE; ++i) { - UInt384 powNum = UInt384.TWO.pow(UInt384.SIZE - (i + 1)); - UInt384 shiftNum = UInt384.HIGH_BIT.shiftRight(i); - assertEquals(powNum, shiftNum); - } - assertEquals(UInt384.ZERO, UInt384.HIGH_BIT.shiftRight(UInt384.SIZE)); - assertEquals(UInt384.ZERO, UInt384.HIGH_BIT.shiftRight(-UInt384.SIZE)); - assertEquals(UInt384.TWO, UInt384.ONE.shiftRight(-1)); - } - - /** - * Integer square root. - */ - @Test - public void when_performing_integer_square_root__the_correct_value_is_returned() { - long max = 1 << 53; // Precision of double - for (long n = 0; n < max; n += 997) { - long lsqrt = (long) Math.floor(Math.sqrt(n)); - UInt384 nn = UInt384.from(n); - UInt384 isqrt = nn.isqrt(); - assertEquals(UInt384.from(lsqrt), isqrt); - } - for (int n = 0; n < UInt256.SIZE; ++n) { - UInt384 num = UInt384.ONE.shiftLeft(n); - byte[] bytes = num.toByteArray(); - BigInteger nsqrt = BigIntegerMath.sqrt(new BigInteger(1, bytes), RoundingMode.FLOOR); - byte[] otherBytes = num.isqrt().toByteArray(); - BigInteger isqrt = new BigInteger(1, otherBytes); - assertEquals(nsqrt, isqrt); - } - } - - @Test - public void when_performing_bitwise_inversion__the_correct_value_is_returned() { - assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.invert()); - } - - @Test - public void when_using_predicates__the_correct_value_is_returned() { - // Basic tests for odd/even - assertTrue(UInt384.ONE.isOdd()); - assertFalse(UInt384.ONE.isEven()); - assertTrue(UInt384.MAX_VALUE.isOdd()); - assertFalse(UInt384.MAX_VALUE.isEven()); - UInt384 two = UInt384.ONE.add(UInt384.ONE); - assertFalse(two.isOdd()); - assertTrue(two.isEven()); - UInt384 minusTwo = UInt384.MAX_VALUE.add(UInt384.MAX_VALUE); - assertFalse(minusTwo.isOdd()); - assertTrue(minusTwo.isEven()); - - assertFalse(UInt384.ONE.isZero()); - assertFalse(UInt384.MAX_VALUE.isZero()); - assertTrue(UInt384.ZERO.isZero()); - } - - @Test - public void when_converting_int384_to_string__the_correct_value_is_returned() { - // Some basics - assertEquals("0", UInt384.ZERO.toString()); - assertEquals("1", UInt384.ONE.toString()); - assertEquals("10", UInt384.TEN.toString()); - - assertEquals("12345678", UInt384.from(12345678L).toString()); - UInt384 maxPositive = UInt384.from(UInt128.MAX_VALUE, UInt256.MAX_VALUE); - // Need to zero extend here to make positive - byte[] bytes = new byte[UInt384.BYTES + 1]; - bytes[0] = 0; - maxPositive.toByteArray(bytes, 1); - assertEquals(new BigInteger(bytes).toString(), maxPositive.toString()); - - int lastBit = UInt384.SIZE - 1; - assertFalse(UInt384.ZERO.isHighBitSet()); - assertFalse(UInt384.TWO.pow(lastBit - 1).isHighBitSet()); - assertFalse(UInt384.TWO.pow(lastBit).decrement().isHighBitSet()); - assertTrue(UInt384.MAX_VALUE.isHighBitSet()); - assertTrue(UInt384.ONE.invert().isHighBitSet()); - assertTrue(UInt384.TWO.pow(lastBit).isHighBitSet()); - assertTrue(UInt384.HIGH_BIT.isHighBitSet()); - } - - @Test - public void when_converting_string_to_int384__the_correct_value_is_returned() { - testRoundTrip("0"); - testRoundTrip("123456789"); - testRoundTrip("123456789123456789"); - testRoundTrip("123456789123456789123456789123456789"); - assertEquals( - UInt384.from(UInt128.MAX_VALUE, UInt256.MAX_VALUE), - UInt384.from(BigInteger.ONE.shiftLeft(384).subtract(BigInteger.ONE).toString()) - ); - } - - @Test - public void when_calculating_powers__the_correct_value_is_returned() { - assertEquals(UInt384.from(1L << 9), UInt384.TWO.pow(9)); - assertEquals(UInt384.from(10_000_000_000L), UInt384.TEN.pow(10)); - assertEquals(UInt384.ONE, UInt384.ZERO.pow(0)); // At least in the limit - assertEquals(UInt384.ONE, UInt384.ONE.pow(0)); - } - - @Test - public void equalsContract() { - EqualsVerifier.forClass(UInt384.class).verify(); - } - - /** - * Test div 0. - */ - @Test(expected = IllegalArgumentException.class) - public void testDiv0() { - UInt384.ONE.divide(UInt384.ZERO); - } - - /** - * Test rem 0. - */ - @Test(expected = IllegalArgumentException.class) - public void testRem0() { - UInt384.ONE.remainder(UInt384.ZERO); - } - - /** - * NumberFormatException on empty string. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionOnEmpty() { - UInt384.from(""); - } - - /** - * NumberFormatException if no actual number. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionIfNoNumber() { - UInt384.from("+"); - } - - /** - * NumberFormatException if invalid digit. - */ - @Test(expected = NumberFormatException.class) - public void numberFormatExceptionIfInvalidDigit() { - UInt384.from("+a"); - } - - /** - * IllegalArgumentException if byte array is empty. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionIfByteArrayEmpty() { - UInt384.from(new byte[0]); - } - - /** - * IllegalArgumentException on radix too big. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnRadixTooBig() { - UInt384.ONE.toString(Character.MAX_RADIX + 1); - } - - /** - * IllegalArgumentException on radix too small. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnRadixTooSmall() { - UInt384.ONE.toString(Character.MIN_RADIX - 1); - } - - /** - * IllegalArgumentException on negative exponent for pow. - */ - @Test(expected = IllegalArgumentException.class) - public void illegalArgumentExceptionOnNegativeExponent() { - UInt384.ONE.pow(-1); - } - - private static void testRoundTrip(String s) { - assertEquals(s, UInt384.from(s).toString()); - } - - private static void assertEqualToLong(long expectedValue, UInt384 testValue) { - assertEquals(UInt128.ZERO, testValue.getHigh()); - assertEquals(UInt128.ZERO, testValue.getLow().getHigh()); - assertEquals(0L, testValue.getLow().getHigh().getHigh()); - assertEquals(expectedValue, testValue.low.low.getLow()); - } + // Range of numbers to be tested at extremes for Integer and Long. + private static final int TEST_RANGE = 1_000_000; + + /** + * Exhaustively test construction for all non-negative {@code short} values, from {@code 0} up to + * and including {@link Short.MAX_VALUE}. + */ + @Test + public void when_constructing_int384_from_short_values__values_compare_equal() { + for (int i = 0; i <= Short.MAX_VALUE; ++i) { + short s = (short) i; + UInt384 int384 = UInt384.from(s); + assertEqualToLong(s, int384); + } + } + + /** + * Test construction for two ranges of {@code int} values from {@code 0} through to {@code + * TEST_RANGE}, and also from {@code Integer.MAX_VALUE} down to {@code Integer.MAX_VALUE - + * TEST_RANGE}. + */ + @Test + public void when_constructing_int384_from_int_values__values_compare_equal() { + // Here we will just assume that testing some values near the + // extremes of the range will suffice. + for (int i = 0; i <= TEST_RANGE; ++i) { + UInt384 int384 = UInt384.from(i); + assertEqualToLong(i, int384); + } + for (int i = 0; i <= TEST_RANGE; ++i) { + int ii = Integer.MAX_VALUE - i; + UInt384 int384 = UInt384.from(ii); + assertEqualToLong(ii, int384); + } + } + + /** + * Test construction for two ranges of {@code long} values from {@code 0} through to {@code + * TEST_RANGE}, and also from {@code Long.MAX_VALUE} down to {@code Long.MAX_VALUE - TEST_RANGE}. + */ + @Test + public void when_constructing_int384_from_long_values__accessors_compare_equal() { + // Here we will just assume that testing some values near the + // extremes of the range will suffice. + for (int i = 0; i < TEST_RANGE; ++i) { + long l = i; + UInt384 int384 = UInt384.from(l); + assertEqualToLong(l, int384); + } + for (int i = 0; i < TEST_RANGE; ++i) { + long l = Long.MAX_VALUE - i; + UInt384 int384 = UInt384.from(l); + assertEqualToLong(l, int384); + } + } + + @Test + public void when_performing_basic_addition__the_correct_values_are_returned() { + // Some basics + // 0 + 1 = 1 + assertEqualToLong(1L, UInt384.ZERO.add(UInt384.ONE)); + // 1 + 1 = 2 + assertEqualToLong(2L, UInt384.ONE.add(UInt384.ONE)); + // max + 1 = 0 (overflow) + assertEqualToLong(0L, UInt384.MAX_VALUE.add(UInt384.ONE)); + // 1 + max = 0 (overflow) + assertEqualToLong(0L, UInt384.ONE.add(UInt384.MAX_VALUE)); + + // The half-add methods too + // 0 + 1 = 1 + assertEqualToLong(1L, UInt384.ZERO.add(UInt256.ONE)); + // 1 + 1 = 2 + assertEqualToLong(2L, UInt384.ONE.add(UInt256.ONE)); + // max + 1 = 0 (overflow) + assertEqualToLong(0L, UInt384.MAX_VALUE.add(UInt256.ONE)); + } + + @Test + public void + when_performing_addition_overflowing_between_words__the_correct_values_are_returned() { + // Test adding with carry. + UInt384 carry1 = UInt384.from(UInt128.ZERO, UInt256.MAX_VALUE).add(UInt384.ONE); + assertEquals(UInt128.ONE, carry1.getHigh()); + assertEquals(UInt256.ZERO, carry1.getLow()); + + // And also for the half-add method (actually 2/3 add in this case) + // Test adding with carry. + UInt384 carry2 = UInt384.from(UInt128.ZERO, UInt256.MAX_VALUE).add(UInt256.ONE); + assertEquals(UInt128.ONE, carry2.getHigh()); + assertEquals(UInt256.ZERO, carry2.getLow()); + } + + @Test + public void when_performing_basic_subtraction__the_correct_values_are_returned() { + // Some basics + // 1 - 1 = 0 + assertEqualToLong(0L, UInt384.ONE.subtract(UInt384.ONE)); + // 2 - 1 = 1 + assertEqualToLong(1L, UInt384.TWO.subtract(UInt384.ONE)); + // 0 - 1 = max (underflow) + assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.subtract(UInt384.ONE)); + // 0 - max = 1 (underflow) + assertEqualToLong(1L, UInt384.ZERO.subtract(UInt384.MAX_VALUE)); + + // Half subtract methods also + // 1 - 1 = 0 + assertEqualToLong(0L, UInt384.ONE.subtract(UInt256.ONE)); + // 2 - 1 = 1 + assertEqualToLong(1L, UInt384.TWO.subtract(UInt256.ONE)); + // 0 - 1 = max (underflow) + assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.subtract(UInt256.ONE)); + } + + @Test + public void + when_performing_subtraction_underflowing_between_words__the_correct_value_is_returned() { + // Test subtraction with carry. + UInt384 carry1 = UInt384.from(UInt128.ONE, UInt256.ZERO).subtract(UInt384.ONE); + assertEquals(UInt128.ZERO, carry1.high); + assertEquals(UInt256.MAX_VALUE, carry1.low); + UInt384 carry2 = UInt384.ZERO.subtract(UInt384.ONE); // underflow + assertEquals(UInt128.MAX_VALUE, carry2.high); + assertEquals(UInt256.MAX_VALUE, carry2.low); + + // also with half-subtract methods + UInt384 carry3 = UInt384.from(UInt128.ONE, UInt256.ZERO).subtract(UInt256.ONE); + assertEquals(UInt128.ZERO, carry3.high); + assertEquals(UInt256.MAX_VALUE, carry3.low); + UInt384 carry4 = UInt384.ZERO.subtract(UInt256.ONE); // underflow + assertEquals(UInt128.MAX_VALUE, carry4.high); + assertEquals(UInt256.MAX_VALUE, carry4.low); + } + + @Test + public void when_incrementing_int384__the_correct_values_are_returned() { + assertEquals(UInt384.ONE, UInt384.ZERO.increment()); + assertEquals(UInt384.ZERO, UInt384.MAX_VALUE.increment()); // Internal and full overflow + } + + @Test + public void when_decrementing_int384__the_correct_values_are_returned() { + assertEquals(UInt384.ZERO, UInt384.ONE.decrement()); + assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.decrement()); // Internal and full overflow + } + + @Test + public void when_multiplying_two_values__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt384.ZERO)); + assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt384.ONE)); + assertEquals(UInt384.ZERO, UInt384.ONE.multiply(UInt384.ZERO)); + assertEquals(UInt384.ONE, UInt384.ONE.multiply(UInt384.ONE)); + + // Some values in the long range + assertEquals( + UInt384.from(12345678L * 13L), UInt384.from(12345678L).multiply(UInt384.from(13L))); + } + + @Test + public void when_multiplying_uint256_values__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt256.ZERO)); + assertEquals(UInt384.ZERO, UInt384.ZERO.multiply(UInt256.ONE)); + assertEquals(UInt384.ZERO, UInt384.ONE.multiply(UInt256.ZERO)); + assertEquals(UInt384.ONE, UInt384.ONE.multiply(UInt256.ONE)); + + // Some values in the long range + assertEquals( + UInt384.from(12345678L * 13L), UInt384.from(12345678L).multiply(UInt256.from(13L))); + } + + @Test + public void when_dividing_one_value_by_another__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt384.ZERO, UInt384.ZERO.divide(UInt384.ONE)); + assertEquals(UInt384.ONE, UInt384.ONE.divide(UInt384.ONE)); + + // Some values in the long range + assertEquals(UInt384.from(12345678L / 13L), UInt384.from(12345678L).divide(UInt384.from(13L))); + } + + @Test(expected = IllegalArgumentException.class) + public void when_dividing_by_zero__an_exception_is_thrown() { + UInt384.ONE.divide(UInt384.ZERO); + fail(); + } + + @Test + public void when_dividing_one_value_by_another_int256__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt384.ZERO, UInt384.ZERO.divide(UInt256.ONE)); + assertEquals(UInt384.ONE, UInt384.ONE.divide(UInt256.ONE)); + assertEquals(UInt384.MAX_VALUE, UInt384.MAX_VALUE.divide(UInt256.ONE)); + assertEquals( + UInt384.ONE.shiftLeft(UInt384.SIZE - UInt256.SIZE), + UInt384.MAX_VALUE.divide(UInt256.MAX_VALUE)); + + // Some values in the long range + assertEquals(UInt384.from(12345678L / 13L), UInt384.from(12345678L).divide(UInt256.from(13L))); + } + + @Test(expected = IllegalArgumentException.class) + public void when_dividing_by_zero_int256__an_exception_is_thrown() { + UInt384.ONE.divide(UInt256.ZERO); + fail(); + } + + @Test + public void + when_computing_the_remainder_of_dividing_one_value_by_another__the_correct_value_is_returned() { + // Some basics + assertEquals(UInt384.ZERO, UInt384.ZERO.remainder(UInt384.ONE)); + assertEquals(UInt384.ZERO, UInt384.ONE.remainder(UInt384.ONE)); + assertEquals(UInt384.ONE, UInt384.ONE.remainder(UInt384.TWO)); + + // Some values in the long range + assertEquals( + UInt384.from(12345678L % 13L), UInt384.from(12345678L).remainder(UInt384.from(13L))); + } + + @Test(expected = IllegalArgumentException.class) + public void when_computing_the_remainder_of_dividing_by_zero__an_exception_is_thrown() { + UInt384.ONE.remainder(UInt384.ZERO); + fail(); + } + + @Test + public void when_comparing_int384_values_using_compareTo__the_correct_value_is_returned() { + assertThat(UInt384.ZERO) + .isEqualByComparingTo(UInt384.ZERO) + .isLessThan(UInt384.ONE) + .isLessThan(UInt384.MAX_VALUE); + assertThat(UInt384.ONE).isGreaterThan(UInt384.ZERO); + assertThat(UInt384.MAX_VALUE).isGreaterThan(UInt384.ZERO); + + UInt384 i127 = UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT); + UInt384 i128 = i127.add(i127); + UInt384 i129 = i128.add(i128); + assertThat(i128).isGreaterThan(i127); // In case something has gone horribly wrong. + assertThat(i128.add(i127)).isGreaterThan(i128); + assertThat(i129).isGreaterThan(i128.add(i127)); + } + + @Test + public void when_comparing_int384_values_using_equals__the_correct_value_is_returned() { + assertNotEquals(UInt384.ZERO, null); // Nothing should be equal to null + assertEquals(UInt384.ZERO, UInt384.ZERO); // Same object check + UInt384 i127a = UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT); + UInt384 i127b = UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT); + assertEquals(i127a, i127b); + assertNotEquals(i127a, i127b.add(i127b)); + assertNotEquals(i127a, UInt384.ZERO); + } + + @Test + public void when_calculating_leading_zeros__the_correct_value_is_returned() { + assertEquals(UInt384.SIZE, UInt384.ZERO.numberOfLeadingZeros()); + assertEquals(UInt384.SIZE - 1, UInt384.ONE.numberOfLeadingZeros()); + assertEquals(0, UInt384.MAX_VALUE.numberOfLeadingZeros()); + assertEquals(UInt128.SIZE - 1, UInt384.from(UInt128.ONE, UInt256.ZERO).numberOfLeadingZeros()); + assertEquals(UInt128.SIZE, UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT).numberOfLeadingZeros()); + } + + @Test + public void when_binary_oring_two_values__the_correct_value_is_returned() { + // Use bit positions in both high and low word for tests + UInt384 b0 = UInt384.ONE; + UInt384 b128 = UInt384.from(UInt128.ONE, UInt256.ZERO); + // Basic sanity checks to make sure nothing is horribly wrong + assertThat(b128).isGreaterThan(b0); + assertThat(b0).isGreaterThan(UInt384.ZERO); + assertThat(b128).isGreaterThan(UInt384.ZERO); + + // Now for the real tests + assertEquals(b0, UInt384.ZERO.or(b0)); + assertEquals(b0, b0.or(b0)); + assertEquals(b128, UInt384.ZERO.or(b128)); + assertEquals(b128, b128.or(b128)); + assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.or(UInt384.MAX_VALUE)); + assertEquals(UInt384.MAX_VALUE, UInt384.MAX_VALUE.or(UInt384.MAX_VALUE)); + } + + @Test + public void when_binary_anding_two_values__the_correct_value_is_returned() { + // Use bit positions in both high and low word for tests + UInt384 b0 = UInt384.ONE; + UInt384 b128 = UInt384.from(UInt128.ONE, UInt256.ZERO); + assertEquals(b0, UInt384.MAX_VALUE.and(b0)); + assertEquals(b128, UInt384.MAX_VALUE.and(b128)); + assertEquals(UInt384.ZERO, UInt384.MAX_VALUE.and(UInt384.ZERO)); + } + + @Test + public void when_binary_xoring_two_values__the_correct_value_is_returned() { + // Use bit positions in both high and low word for tests + UInt384 b0 = UInt384.ONE; + UInt384 b128 = UInt384.from(UInt128.ONE, UInt256.ZERO); + assertEquals(b0, UInt384.ZERO.xor(b0)); + assertEquals(UInt384.ZERO, b0.xor(b0)); + assertEquals(b128, UInt384.ZERO.xor(b128)); + assertEquals(UInt384.ZERO, b128.xor(b128)); + assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.xor(UInt384.MAX_VALUE)); + assertEquals(UInt384.ZERO, UInt384.MAX_VALUE.xor(UInt384.MAX_VALUE)); + } + + @Test + public void when_creating_int256_from_byte_array__the_correct_value_is_created() { + byte[] m1 = {-1}; + byte[] p1 = {1}; + byte[] bytesArray = new byte[UInt384.BYTES]; + Arrays.fill(bytesArray, (byte) 0); + bytesArray[UInt384.BYTES - 1] = 1; + UInt384 m1Bits128 = UInt384.from(m1); + UInt384 p1Bits128 = UInt384.from(p1); + UInt384 bytesArrayBits128 = UInt384.from(bytesArray); + + assertEquals(UInt384.from(255), m1Bits128); // Sign extension did not happen + assertEquals(UInt384.ONE, p1Bits128); // Zero fill happened correctly + assertEquals(UInt384.ONE, bytesArrayBits128); // Correct size array OK + } + + @Test + public void when_converting_int256_to_byte_array__the_correct_values_are_returned() { + UInt128 bp0 = UInt128.from(0x0001_0203_0405_0607L, 0x0809_0A0B_0C0D_0E0FL); + UInt256 bp1 = + UInt256.from( + UInt128.from(0x1011_1213_1415_1617L, 0x1819_1A1B_1C1D_1E1FL), + UInt128.from(0x2021_2223_2425_2627L, 0x2829_2A2B_2C2D_2E2FL)); + UInt384 bitPattern = UInt384.from(bp0, bp1); + byte[] bytes2 = new byte[UInt384.BYTES * 3]; + Arrays.fill(bytes2, (byte) -1); + + // Make sure we got the value in big-endian order + byte[] bytes = bitPattern.toByteArray(); + for (int i = 0; i < UInt384.BYTES; ++i) { + assertEquals(i, bytes[i]); + } + + bitPattern.toByteArray(bytes2, UInt384.BYTES); + // Make sure we didn't overwrite bytes outside our range + for (int i = 0; i < UInt384.BYTES; ++i) { + assertEquals(-1, bytes2[i]); + assertEquals(-1, bytes2[i + UInt384.BYTES * 2]); + } + // Make sure we got the value in big-endian order + for (int i = 0; i < UInt384.BYTES; ++i) { + assertEquals(i, bytes2[UInt384.BYTES + i]); + } + } + + @Test + public void when_performing_binary_shifts__the_correct_value_is_returned() { + final UInt256 minusTwo = UInt256.ZERO.decrement().decrement(); + final UInt128 maxSigned = UInt128.HIGH_BIT.decrement(); + + // Basic cases, left shift + assertEquals(UInt384.ZERO, UInt384.ZERO.shiftLeft()); + // Zero extend on left + assertEquals(UInt384.from(UInt128.MAX_VALUE, minusTwo), UInt384.MAX_VALUE.shiftLeft()); + assertEquals(UInt384.from(2), UInt384.ONE.shiftLeft()); + // Make sure bit crosses word boundary correctly + assertEquals( + UInt384.from(UInt128.ONE, UInt256.ZERO), + UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT).shiftLeft()); + + // Basic cases, right shift + assertEquals(UInt384.ZERO, UInt384.ZERO.shiftRight()); + // Zeros inserted at right + assertEquals(UInt384.from(maxSigned, UInt256.MAX_VALUE), UInt384.MAX_VALUE.shiftRight()); + assertEquals(UInt384.ZERO, UInt384.ONE.shiftRight()); + assertEquals(UInt384.ONE, UInt384.from(2).shiftRight()); + // Make sure bit crosses word boundary correctly + assertEquals( + UInt384.from(UInt128.ZERO, UInt256.HIGH_BIT), + UInt384.from(UInt128.ONE, UInt256.ZERO).shiftRight()); + } + + /** Test multi shiftLeft. */ + @Test + public void when_performing_binary_left_shifts_by_n__the_correct_value_is_returned() { + for (int i = 0; i < UInt384.SIZE; ++i) { + UInt384 powNum = UInt384.TWO.pow(i); + UInt384 shiftNum = UInt384.ONE.shiftLeft(i); + assertEquals(powNum, shiftNum); + } + assertEquals(UInt384.ZERO, UInt384.ONE.shiftLeft(UInt384.SIZE)); + assertEquals(UInt384.ZERO, UInt384.ONE.shiftLeft(-UInt384.SIZE)); + assertEquals(UInt384.ONE, UInt384.TWO.shiftLeft(-1)); + } + + /** Test multi shiftRight. */ + @Test + public void when_performing_binary_right_shifts_by_n__the_correct_value_is_returned() { + for (int i = 0; i < UInt384.SIZE; ++i) { + UInt384 powNum = UInt384.TWO.pow(UInt384.SIZE - (i + 1)); + UInt384 shiftNum = UInt384.HIGH_BIT.shiftRight(i); + assertEquals(powNum, shiftNum); + } + assertEquals(UInt384.ZERO, UInt384.HIGH_BIT.shiftRight(UInt384.SIZE)); + assertEquals(UInt384.ZERO, UInt384.HIGH_BIT.shiftRight(-UInt384.SIZE)); + assertEquals(UInt384.TWO, UInt384.ONE.shiftRight(-1)); + } + + /** Integer square root. */ + @Test + public void when_performing_integer_square_root__the_correct_value_is_returned() { + long max = 1 << 53; // Precision of double + for (long n = 0; n < max; n += 997) { + long lsqrt = (long) Math.floor(Math.sqrt(n)); + UInt384 nn = UInt384.from(n); + UInt384 isqrt = nn.isqrt(); + assertEquals(UInt384.from(lsqrt), isqrt); + } + for (int n = 0; n < UInt256.SIZE; ++n) { + UInt384 num = UInt384.ONE.shiftLeft(n); + byte[] bytes = num.toByteArray(); + BigInteger nsqrt = BigIntegerMath.sqrt(new BigInteger(1, bytes), RoundingMode.FLOOR); + byte[] otherBytes = num.isqrt().toByteArray(); + BigInteger isqrt = new BigInteger(1, otherBytes); + assertEquals(nsqrt, isqrt); + } + } + + @Test + public void when_performing_bitwise_inversion__the_correct_value_is_returned() { + assertEquals(UInt384.MAX_VALUE, UInt384.ZERO.invert()); + } + + @Test + public void when_using_predicates__the_correct_value_is_returned() { + // Basic tests for odd/even + assertTrue(UInt384.ONE.isOdd()); + assertFalse(UInt384.ONE.isEven()); + assertTrue(UInt384.MAX_VALUE.isOdd()); + assertFalse(UInt384.MAX_VALUE.isEven()); + UInt384 two = UInt384.ONE.add(UInt384.ONE); + assertFalse(two.isOdd()); + assertTrue(two.isEven()); + UInt384 minusTwo = UInt384.MAX_VALUE.add(UInt384.MAX_VALUE); + assertFalse(minusTwo.isOdd()); + assertTrue(minusTwo.isEven()); + + assertFalse(UInt384.ONE.isZero()); + assertFalse(UInt384.MAX_VALUE.isZero()); + assertTrue(UInt384.ZERO.isZero()); + } + + @Test + public void when_converting_int384_to_string__the_correct_value_is_returned() { + // Some basics + assertEquals("0", UInt384.ZERO.toString()); + assertEquals("1", UInt384.ONE.toString()); + assertEquals("10", UInt384.TEN.toString()); + + assertEquals("12345678", UInt384.from(12345678L).toString()); + UInt384 maxPositive = UInt384.from(UInt128.MAX_VALUE, UInt256.MAX_VALUE); + // Need to zero extend here to make positive + byte[] bytes = new byte[UInt384.BYTES + 1]; + bytes[0] = 0; + maxPositive.toByteArray(bytes, 1); + assertEquals(new BigInteger(bytes).toString(), maxPositive.toString()); + + int lastBit = UInt384.SIZE - 1; + assertFalse(UInt384.ZERO.isHighBitSet()); + assertFalse(UInt384.TWO.pow(lastBit - 1).isHighBitSet()); + assertFalse(UInt384.TWO.pow(lastBit).decrement().isHighBitSet()); + assertTrue(UInt384.MAX_VALUE.isHighBitSet()); + assertTrue(UInt384.ONE.invert().isHighBitSet()); + assertTrue(UInt384.TWO.pow(lastBit).isHighBitSet()); + assertTrue(UInt384.HIGH_BIT.isHighBitSet()); + } + + @Test + public void when_converting_string_to_int384__the_correct_value_is_returned() { + testRoundTrip("0"); + testRoundTrip("123456789"); + testRoundTrip("123456789123456789"); + testRoundTrip("123456789123456789123456789123456789"); + assertEquals( + UInt384.from(UInt128.MAX_VALUE, UInt256.MAX_VALUE), + UInt384.from(BigInteger.ONE.shiftLeft(384).subtract(BigInteger.ONE).toString())); + } + + @Test + public void when_calculating_powers__the_correct_value_is_returned() { + assertEquals(UInt384.from(1L << 9), UInt384.TWO.pow(9)); + assertEquals(UInt384.from(10_000_000_000L), UInt384.TEN.pow(10)); + assertEquals(UInt384.ONE, UInt384.ZERO.pow(0)); // At least in the limit + assertEquals(UInt384.ONE, UInt384.ONE.pow(0)); + } + + @Test + public void equalsContract() { + EqualsVerifier.forClass(UInt384.class).verify(); + } + + /** Test div 0. */ + @Test(expected = IllegalArgumentException.class) + public void testDiv0() { + UInt384.ONE.divide(UInt384.ZERO); + } + + /** Test rem 0. */ + @Test(expected = IllegalArgumentException.class) + public void testRem0() { + UInt384.ONE.remainder(UInt384.ZERO); + } + + /** NumberFormatException on empty string. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionOnEmpty() { + UInt384.from(""); + } + + /** NumberFormatException if no actual number. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionIfNoNumber() { + UInt384.from("+"); + } + + /** NumberFormatException if invalid digit. */ + @Test(expected = NumberFormatException.class) + public void numberFormatExceptionIfInvalidDigit() { + UInt384.from("+a"); + } + + /** IllegalArgumentException if byte array is empty. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionIfByteArrayEmpty() { + UInt384.from(new byte[0]); + } + + /** IllegalArgumentException on radix too big. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnRadixTooBig() { + UInt384.ONE.toString(Character.MAX_RADIX + 1); + } + + /** IllegalArgumentException on radix too small. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnRadixTooSmall() { + UInt384.ONE.toString(Character.MIN_RADIX - 1); + } + + /** IllegalArgumentException on negative exponent for pow. */ + @Test(expected = IllegalArgumentException.class) + public void illegalArgumentExceptionOnNegativeExponent() { + UInt384.ONE.pow(-1); + } + + private static void testRoundTrip(String s) { + assertEquals(s, UInt384.from(s).toString()); + } + + private static void assertEqualToLong(long expectedValue, UInt384 testValue) { + assertEquals(UInt128.ZERO, testValue.getHigh()); + assertEquals(UInt128.ZERO, testValue.getLow().getHigh()); + assertEquals(0L, testValue.getLow().getHigh().getHigh()); + assertEquals(expectedValue, testValue.low.low.getLow()); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UIntUtilsTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UIntUtilsTest.java index 97e6e5f2dc..655aab1c21 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/UIntUtilsTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/UIntUtilsTest.java @@ -69,93 +69,91 @@ import org.junit.Test; -/** - * Basic unit tests for {@link UIntUtils}. - */ +/** Basic unit tests for {@link UIntUtils}. */ public class UIntUtilsTest { - @Test - public void when_adding_uint384_values__the_correct_result_is_returned() { - assertEquals(UInt384.TEN, UIntUtils.addWithOverflow(UInt384.FIVE, UInt384.FIVE)); - } - - @Test - public void when_adding_uint256_values__the_correct_result_is_returned() { - assertEquals(UInt384.TEN, UIntUtils.addWithOverflow(UInt384.FIVE, UInt256.FIVE)); - } - - @Test - public void when_adding_two_uint256_values__the_correct_result_is_returned() { - assertEquals(UInt256.TEN, UIntUtils.addWithOverflow(UInt256.FIVE, UInt256.FIVE)); - } - - @Test(expected = ArithmeticException.class) - public void when_adding_uint384_one_to_max_value__an_exception_is_thrown() { - UIntUtils.addWithOverflow(UInt384.MAX_VALUE, UInt384.ONE); - fail(); - } - - @Test(expected = ArithmeticException.class) - public void when_adding_uint256_one_to_max_value__an_exception_is_thrown() { - UIntUtils.addWithOverflow(UInt384.MAX_VALUE, UInt256.ONE); - fail(); - } - - @Test(expected = ArithmeticException.class) - public void when_adding_uint256_one_to_uint256_max_value__an_exception_is_thrown() { - UIntUtils.addWithOverflow(UInt256.MAX_VALUE, UInt256.ONE); - fail(); - } - - @Test - public void when_subtracting_uint384_values__the_correct_result_is_returned() { - assertEquals(UInt384.FIVE, UIntUtils.subtractWithUnderflow(UInt384.TEN, UInt384.FIVE)); - } - - @Test - public void when_subtracting_uint256_values__the_correct_result_is_returned() { - assertEquals(UInt384.FIVE, UIntUtils.subtractWithUnderflow(UInt384.TEN, UInt256.FIVE)); - } - - @Test - public void when_subtracting_two_uint256_values__the_correct_result_is_returned() { - assertEquals(UInt256.FIVE, UIntUtils.subtractWithUnderflow(UInt256.TEN, UInt256.FIVE)); - } - - @Test(expected = ArithmeticException.class) - public void when_subtracting_uint384_one_from_zero__an_exception_is_thrown() { - UIntUtils.subtractWithUnderflow(UInt384.ZERO, UInt384.ONE); - fail(); - } - - @Test(expected = ArithmeticException.class) - public void when_subtracting_uint256_one_from_zero__an_exception_is_thrown() { - UIntUtils.subtractWithUnderflow(UInt384.ZERO, UInt256.ONE); - fail(); - } - - @Test(expected = ArithmeticException.class) - public void when_subtracting_uint256_one_from_uint256_zero__an_exception_is_thrown() { - UIntUtils.subtractWithUnderflow(UInt256.ZERO, UInt256.ONE); - fail(); - } - - @Test - public void when_converting_uint128_to_double__the_correct_value_is_returned() { - // Some small values. Note that all the long sized values are the same code path. - // Note that 0.0 is the correct delta -> integers in this range are represented exactly. - assertEquals(0.0, UIntUtils.toDouble(UInt128.ZERO), 0.0); - assertEquals(1e9, UIntUtils.toDouble(UInt128.TEN.pow(9)), 0.0); - - // Check each bit works OK - for (int i = 0; i < UInt128.SIZE; ++i) { - UInt128 value = UInt128.TWO.pow(i); - double dvalue = Math.pow(2.0, i); - assertEquals(dvalue, UIntUtils.toDouble(value), 0.0); // Values are exact - } - - // Check for rounding overflow - the big number is UInt128.MAX_VALUE. - assertEquals(340282366920938463463374607431768211455.0, UIntUtils.toDouble(UInt128.MAX_VALUE), 0.0); - } - + @Test + public void when_adding_uint384_values__the_correct_result_is_returned() { + assertEquals(UInt384.TEN, UIntUtils.addWithOverflow(UInt384.FIVE, UInt384.FIVE)); + } + + @Test + public void when_adding_uint256_values__the_correct_result_is_returned() { + assertEquals(UInt384.TEN, UIntUtils.addWithOverflow(UInt384.FIVE, UInt256.FIVE)); + } + + @Test + public void when_adding_two_uint256_values__the_correct_result_is_returned() { + assertEquals(UInt256.TEN, UIntUtils.addWithOverflow(UInt256.FIVE, UInt256.FIVE)); + } + + @Test(expected = ArithmeticException.class) + public void when_adding_uint384_one_to_max_value__an_exception_is_thrown() { + UIntUtils.addWithOverflow(UInt384.MAX_VALUE, UInt384.ONE); + fail(); + } + + @Test(expected = ArithmeticException.class) + public void when_adding_uint256_one_to_max_value__an_exception_is_thrown() { + UIntUtils.addWithOverflow(UInt384.MAX_VALUE, UInt256.ONE); + fail(); + } + + @Test(expected = ArithmeticException.class) + public void when_adding_uint256_one_to_uint256_max_value__an_exception_is_thrown() { + UIntUtils.addWithOverflow(UInt256.MAX_VALUE, UInt256.ONE); + fail(); + } + + @Test + public void when_subtracting_uint384_values__the_correct_result_is_returned() { + assertEquals(UInt384.FIVE, UIntUtils.subtractWithUnderflow(UInt384.TEN, UInt384.FIVE)); + } + + @Test + public void when_subtracting_uint256_values__the_correct_result_is_returned() { + assertEquals(UInt384.FIVE, UIntUtils.subtractWithUnderflow(UInt384.TEN, UInt256.FIVE)); + } + + @Test + public void when_subtracting_two_uint256_values__the_correct_result_is_returned() { + assertEquals(UInt256.FIVE, UIntUtils.subtractWithUnderflow(UInt256.TEN, UInt256.FIVE)); + } + + @Test(expected = ArithmeticException.class) + public void when_subtracting_uint384_one_from_zero__an_exception_is_thrown() { + UIntUtils.subtractWithUnderflow(UInt384.ZERO, UInt384.ONE); + fail(); + } + + @Test(expected = ArithmeticException.class) + public void when_subtracting_uint256_one_from_zero__an_exception_is_thrown() { + UIntUtils.subtractWithUnderflow(UInt384.ZERO, UInt256.ONE); + fail(); + } + + @Test(expected = ArithmeticException.class) + public void when_subtracting_uint256_one_from_uint256_zero__an_exception_is_thrown() { + UIntUtils.subtractWithUnderflow(UInt256.ZERO, UInt256.ONE); + fail(); + } + + @Test + public void when_converting_uint128_to_double__the_correct_value_is_returned() { + // Some small values. Note that all the long sized values are the same code path. + // Note that 0.0 is the correct delta -> integers in this range are represented exactly. + assertEquals(0.0, UIntUtils.toDouble(UInt128.ZERO), 0.0); + assertEquals(1e9, UIntUtils.toDouble(UInt128.TEN.pow(9)), 0.0); + + // Check each bit works OK + for (int i = 0; i < UInt128.SIZE; ++i) { + UInt128 value = UInt128.TWO.pow(i); + double dvalue = Math.pow(2.0, i); + assertEquals(dvalue, UIntUtils.toDouble(value), 0.0); // Values are exact + } + + // Check for rounding overflow - the big number is UInt128.MAX_VALUE. + assertEquals( + 340282366920938463463374607431768211455.0, UIntUtils.toDouble(UInt128.MAX_VALUE), 0.0); + } } diff --git a/radixdlt-java-common/src/test/java/com/radixdlt/utils/functional/ResultTest.java b/radixdlt-java-common/src/test/java/com/radixdlt/utils/functional/ResultTest.java index ab0953c75a..546ef6f376 100644 --- a/radixdlt-java-common/src/test/java/com/radixdlt/utils/functional/ResultTest.java +++ b/radixdlt-java-common/src/test/java/com/radixdlt/utils/functional/ResultTest.java @@ -68,21 +68,18 @@ import org.junit.Test; public class ResultTest { - @Test - public void resultOkEquals() { - EqualsVerifier.forClass(Result.ResultOk.class) - .verify(); - } + @Test + public void resultOkEquals() { + EqualsVerifier.forClass(Result.ResultOk.class).verify(); + } - @Test - public void resultFailEquals() { - EqualsVerifier.forClass(Result.ResultFail.class) - .verify(); - } + @Test + public void resultFailEquals() { + EqualsVerifier.forClass(Result.ResultFail.class).verify(); + } - @Test - public void failureEquals() { - EqualsVerifier.forClass(Failure.class) - .verify(); - } + @Test + public void failureEquals() { + EqualsVerifier.forClass(Failure.class).verify(); + } } diff --git a/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/cloud/AWSSecrets.java b/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/cloud/AWSSecrets.java index 707fb07bed..7a2633d48e 100644 --- a/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/cloud/AWSSecrets.java +++ b/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/cloud/AWSSecrets.java @@ -1,11 +1,68 @@ -package com.radixdlt.cloud; +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; -import org.bouncycastle.jce.provider.BouncyCastleProvider; +package com.radixdlt.cloud; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.RadixKeyStore; @@ -14,7 +71,6 @@ import com.radixdlt.networks.Network; import com.radixdlt.utils.AWSSecretManager; import com.radixdlt.utils.AWSSecretsOutputOptions; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -30,249 +86,289 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.bouncycastle.jce.provider.BouncyCastleProvider; public class AWSSecrets { - private static final Boolean DEFAULT_ENABLE_AWS_SECRETS = false; - private static final Boolean DEFAULT_RECREATE_AWS_SECRETS = false; - private static final String DEFAULT_NETWORK_NAME = "testnet"; - private static final String FULLNODE_PREFIX = "fullnode"; - private static final String CORE_NODE_PREFIX = "node"; - public static final String KEYPAIR_NAME = "node"; - - private AWSSecrets() { - } - - public static void main(String[] args) { - var options = new Options(); - options.addOption("h", "help", false, "Show usage information (this message)"); - options.addOption("n", "node-number", true, "Number of full nodes"); - options.addOption("p", "node-name-prefix", true, "Text prefix with which node name is numbered"); - options.addOption("s", "secret-password-key", true, "Password to encrypt key"); - options.addOption("as", "enable-aws-secrets", true, "Store as AWS Secrets(default: " + DEFAULT_ENABLE_AWS_SECRETS + ")"); - options.addOption("rs", "recreate-aws-secrets", true, "Recreate AWS Secrets(default: " + DEFAULT_RECREATE_AWS_SECRETS + ")"); - options.addOption("k", "network-name", true, "Network name(default: " + DEFAULT_NETWORK_NAME + ")"); - options.addOption("l", "node-names", true, "List of node names"); - - var parser = new DefaultParser(); - try { - var cmd = parser.parse(options, args); - - if (!cmd.getArgList().isEmpty()) { - System.err.println("Extra arguments: " + cmd.getArgList().stream().collect(Collectors.joining(" "))); - usage(options); - return; - } - - if (cmd.hasOption('h')) { - usage(options); - return; - } - final int nodeCount = getOption(cmd, 'n') - .map(Integer::parseInt) - .orElseThrow(() -> new IllegalArgumentException("Must specify number of nodes")); - - var namePrefix = getOption(cmd, 'p').orElse(FULLNODE_PREFIX); - - - final var nodeNames = getOption(cmd, 'l') - .orElseThrow(() -> new IllegalArgumentException("Must specify the list of nodenames")); - - var listOfNodes = Optional.ofNullable(nodeNames) - .map(value -> Stream.of(value.split(",")) - .map(entry -> entry.replaceAll("[^\\w-]", "")) - .collect(Collectors.toList())) - .orElse(List.of()); - - if (nodeCount <= 0 && listOfNodes.size() <= 0) { - throw new IllegalArgumentException("There must be at least one node"); - } - - var networkName = getOption(cmd, 'k').orElse(DEFAULT_NETWORK_NAME); - var defaultKeyPassword = getOption(cmd, 's').orElse(""); - - boolean enableAwsSecrets = Boolean.parseBoolean(cmd.getOptionValue("as")); - boolean recreateAwsSecrets = Boolean.parseBoolean(cmd.getOptionValue("rs")); - - var awsSecretsOutputOptions = new AWSSecretsOutputOptions(enableAwsSecrets, recreateAwsSecrets, networkName); - - var nodes = nodeCount > 0 - ? IntStream.range(0, nodeCount) - .mapToObj(counter -> String.format("%s%s", namePrefix, counter)) - .collect(Collectors.toList()) - : listOfNodes; - - System.out.println("name prefix " + namePrefix); - generateAndStoreKey(networkName, namePrefix, defaultKeyPassword, awsSecretsOutputOptions, nodes); - if (namePrefix.equals(CORE_NODE_PREFIX)) { - System.out.println("Core node. Generate staking keys"); - generateAndStoreStakingKey(networkName, defaultKeyPassword, awsSecretsOutputOptions, nodes); - } - } catch ( - ParseException e) { - System.out.println(e); - } - } - - private static void generateAndStoreKey( - String networkName, - String namePrefix, - String defaultKeyPassword, - AWSSecretsOutputOptions awsSecretsOutputOptions, - List nodes - ) { - generateAndStoreKey(networkName, namePrefix, defaultKeyPassword, awsSecretsOutputOptions, nodes, Boolean.FALSE); - } - - private static void generateAndStoreStakingKey( - String networkName, - String defaultKeyPassword, - AWSSecretsOutputOptions awsSecretsOutputOptions, - List nodes - ) { - generateAndStoreKey(networkName, CORE_NODE_PREFIX, defaultKeyPassword, awsSecretsOutputOptions, nodes, Boolean.TRUE); - } - - private static void generateAndStoreKey( - String networkName, - String namePrefix, - String defaultKeyPassword, - AWSSecretsOutputOptions awsSecretsOutputOptions, - List nodes, - Boolean isStaker - ) { - Security.insertProviderAt(new BouncyCastleProvider(), 1); - - for (var nodeName : nodes) { - var keyStoreName = String.format("%s.ks", nodeName); - var keyStoreSecretName = String.format("%s.ks", nodeName); - var passwordName = "password"; - var network = findNetwork(networkName.toUpperCase()); - var publicKeyFileSecretName = String.format("%s/%s/public_key", networkName, nodeName); - - if (namePrefix.equals(CORE_NODE_PREFIX)) { - if (isStaker) { - keyStoreSecretName = "staker_key"; - passwordName = "staker_password"; - keyStoreName = String.format("%s_stake.ks", nodeName); - publicKeyFileSecretName = String.format("%s/%s/staker_public_key", networkName, nodeName); - } else { - keyStoreSecretName = "validator_key"; - passwordName = "validator_password"; - publicKeyFileSecretName = String.format("%s/%s/validator_public_key", networkName, nodeName); - } - } - - final var keyFileSecretName = String.format("%s/%s/%s", networkName, nodeName, keyStoreSecretName); - final var passwordSecretName = String.format("%s/%s/%s", networkName, nodeName, passwordName); - final var password = generatePassword(defaultKeyPassword); - - try { - var keyFilePath = Paths.get(keyStoreName); - var keystoreFile = new File(keyFilePath.toString()); - var keyPair = ECKeyPair.generateNew(); - - RadixKeyStore.fromFile(keystoreFile, password.toCharArray(), !keystoreFile.canWrite()) - .writeKeyPair(KEYPAIR_NAME, keyPair); - - var keyFileAwsSecret = new HashMap(); - var publicKeyFileAwsSecret = new HashMap(); - - putKeyBytes(keyFilePath, keyFileAwsSecret); - printAddresses(network, keyPair, publicKeyFileAwsSecret); - - var keyPasswordAwsSecret = new HashMap(); - keyPasswordAwsSecret.put("key", password); - - writeBinaryAWSSecret(keyFileAwsSecret, keyFileSecretName, awsSecretsOutputOptions, false, true); - writeBinaryAWSSecret(keyPasswordAwsSecret, passwordSecretName, awsSecretsOutputOptions, false, false); - writeBinaryAWSSecret(publicKeyFileAwsSecret, publicKeyFileSecretName, awsSecretsOutputOptions, false, false); - } catch (Exception e) { - System.out.println(e); - } - } - } - - private static void printAddresses(Network network, ECKeyPair keyPair, HashMap publicKeyFileAwsSecret) { - var pubKey = keyPair.getPublicKey(); - var nodeAddressing = NodeAddressing.bech32(network.getNodeHrp()); - var validatorAddressing = ValidatorAddressing.bech32(network.getValidatorHrp()); - - publicKeyFileAwsSecret.put("bech32", nodeAddressing.of(pubKey)); - publicKeyFileAwsSecret.put("hex", pubKey.toHex()); - publicKeyFileAwsSecret.put("validator_address", validatorAddressing.of(pubKey)); - - System.out.println(nodeAddressing.of(pubKey)); - System.out.println(pubKey.toHex()); - } - - private static void putKeyBytes(Path keyFilePath, HashMap keyFileAwsSecret) { - try { - var data = Files.readAllBytes(keyFilePath); - keyFileAwsSecret.put("key", data); - } catch (IOException e) { - throw new IllegalStateException("While reading validator keys", e); - } - } - - private static String generatePassword(String password) { - if (password == null || password.isEmpty()) { - //anphanumeric and special charactrers - int asciiOrigin = 48; //0 - int asciiBound = 122; //z - int passwordLength = 8; - SecureRandom random = new SecureRandom(); - return random.ints(asciiOrigin, asciiBound + 1) - .filter(i -> Character.isAlphabetic(i) || Character.isDigit(i)) - .limit(passwordLength) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, - StringBuilder::append - ) - .toString(); - } else { - return password; - } - } - - private static void usage(Options options) { - new HelpFormatter().printHelp(AWSSecrets.class.getSimpleName(), options, true); - } - - private static Optional getOption(CommandLine cmd, char opt) { - return Optional.ofNullable(cmd.getOptionValue(opt)); - } - - private static void writeBinaryAWSSecret( - Map awsSecret, String secretName, AWSSecretsOutputOptions awsSecretsOutputOptions, - boolean compress, boolean binarySecret - ) { - if (!awsSecretsOutputOptions.getEnableAwsSecrets()) { - System.out.println("Secret " + secretName + " not stored in AWS"); - return; - } - if (AWSSecretManager.awsSecretExists(secretName) && !canBeUpdated(awsSecretsOutputOptions)) { - System.out.println("Secret " + secretName + " cannot be updated"); - return; - } - if (AWSSecretManager.awsSecretExists(secretName)) { - AWSSecretManager.updateAWSSecret(awsSecret, secretName, awsSecretsOutputOptions, compress, binarySecret); - } else { - AWSSecretManager.createAWSSecret(awsSecret, secretName, awsSecretsOutputOptions, compress, binarySecret); - } - } - - private static boolean canBeUpdated(final AWSSecretsOutputOptions awsSecretsOutputOptions) { - return awsSecretsOutputOptions.getRecreateAwsSecrets() - && (!awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("betanet") - || !awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("mainnet")); - } - - private static Network findNetwork(String networkName) { - var network = Network.ofName(networkName); - - if (network.isEmpty()) { - System.out.println("Network " + networkName + " is not supported. Available networks: " + Arrays.toString(Network.values())); - } - - return network.get(); - } + private static final Boolean DEFAULT_ENABLE_AWS_SECRETS = false; + private static final Boolean DEFAULT_RECREATE_AWS_SECRETS = false; + private static final String DEFAULT_NETWORK_NAME = "testnet"; + private static final String FULLNODE_PREFIX = "fullnode"; + private static final String CORE_NODE_PREFIX = "node"; + public static final String KEYPAIR_NAME = "node"; + + private AWSSecrets() {} + + public static void main(String[] args) { + var options = new Options(); + options.addOption("h", "help", false, "Show usage information (this message)"); + options.addOption("n", "node-number", true, "Number of full nodes"); + options.addOption( + "p", "node-name-prefix", true, "Text prefix with which node name is numbered"); + options.addOption("s", "secret-password-key", true, "Password to encrypt key"); + options.addOption( + "as", + "enable-aws-secrets", + true, + "Store as AWS Secrets(default: " + DEFAULT_ENABLE_AWS_SECRETS + ")"); + options.addOption( + "rs", + "recreate-aws-secrets", + true, + "Recreate AWS Secrets(default: " + DEFAULT_RECREATE_AWS_SECRETS + ")"); + options.addOption( + "k", "network-name", true, "Network name(default: " + DEFAULT_NETWORK_NAME + ")"); + options.addOption("l", "node-names", true, "List of node names"); + + var parser = new DefaultParser(); + try { + var cmd = parser.parse(options, args); + + if (!cmd.getArgList().isEmpty()) { + System.err.println( + "Extra arguments: " + cmd.getArgList().stream().collect(Collectors.joining(" "))); + usage(options); + return; + } + + if (cmd.hasOption('h')) { + usage(options); + return; + } + final int nodeCount = + getOption(cmd, 'n') + .map(Integer::parseInt) + .orElseThrow(() -> new IllegalArgumentException("Must specify number of nodes")); + + var namePrefix = getOption(cmd, 'p').orElse(FULLNODE_PREFIX); + + final var nodeNames = + getOption(cmd, 'l') + .orElseThrow( + () -> new IllegalArgumentException("Must specify the list of nodenames")); + + var listOfNodes = + Optional.ofNullable(nodeNames) + .map( + value -> + Stream.of(value.split(",")) + .map(entry -> entry.replaceAll("[^\\w-]", "")) + .collect(Collectors.toList())) + .orElse(List.of()); + + if (nodeCount <= 0 && listOfNodes.size() <= 0) { + throw new IllegalArgumentException("There must be at least one node"); + } + + var networkName = getOption(cmd, 'k').orElse(DEFAULT_NETWORK_NAME); + var defaultKeyPassword = getOption(cmd, 's').orElse(""); + + boolean enableAwsSecrets = Boolean.parseBoolean(cmd.getOptionValue("as")); + boolean recreateAwsSecrets = Boolean.parseBoolean(cmd.getOptionValue("rs")); + + var awsSecretsOutputOptions = + new AWSSecretsOutputOptions(enableAwsSecrets, recreateAwsSecrets, networkName); + + var nodes = + nodeCount > 0 + ? IntStream.range(0, nodeCount) + .mapToObj(counter -> String.format("%s%s", namePrefix, counter)) + .collect(Collectors.toList()) + : listOfNodes; + + System.out.println("name prefix " + namePrefix); + generateAndStoreKey( + networkName, namePrefix, defaultKeyPassword, awsSecretsOutputOptions, nodes); + if (namePrefix.equals(CORE_NODE_PREFIX)) { + System.out.println("Core node. Generate staking keys"); + generateAndStoreStakingKey(networkName, defaultKeyPassword, awsSecretsOutputOptions, nodes); + } + } catch (ParseException e) { + System.out.println(e); + } + } + + private static void generateAndStoreKey( + String networkName, + String namePrefix, + String defaultKeyPassword, + AWSSecretsOutputOptions awsSecretsOutputOptions, + List nodes) { + generateAndStoreKey( + networkName, namePrefix, defaultKeyPassword, awsSecretsOutputOptions, nodes, Boolean.FALSE); + } + + private static void generateAndStoreStakingKey( + String networkName, + String defaultKeyPassword, + AWSSecretsOutputOptions awsSecretsOutputOptions, + List nodes) { + generateAndStoreKey( + networkName, + CORE_NODE_PREFIX, + defaultKeyPassword, + awsSecretsOutputOptions, + nodes, + Boolean.TRUE); + } + + private static void generateAndStoreKey( + String networkName, + String namePrefix, + String defaultKeyPassword, + AWSSecretsOutputOptions awsSecretsOutputOptions, + List nodes, + Boolean isStaker) { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + + for (var nodeName : nodes) { + var keyStoreName = String.format("%s.ks", nodeName); + var keyStoreSecretName = String.format("%s.ks", nodeName); + var passwordName = "password"; + var network = findNetwork(networkName.toUpperCase()); + var publicKeyFileSecretName = String.format("%s/%s/public_key", networkName, nodeName); + + if (namePrefix.equals(CORE_NODE_PREFIX)) { + if (isStaker) { + keyStoreSecretName = "staker_key"; + passwordName = "staker_password"; + keyStoreName = String.format("%s_stake.ks", nodeName); + publicKeyFileSecretName = String.format("%s/%s/staker_public_key", networkName, nodeName); + } else { + keyStoreSecretName = "validator_key"; + passwordName = "validator_password"; + publicKeyFileSecretName = + String.format("%s/%s/validator_public_key", networkName, nodeName); + } + } + + final var keyFileSecretName = + String.format("%s/%s/%s", networkName, nodeName, keyStoreSecretName); + final var passwordSecretName = String.format("%s/%s/%s", networkName, nodeName, passwordName); + final var password = generatePassword(defaultKeyPassword); + + try { + var keyFilePath = Paths.get(keyStoreName); + var keystoreFile = new File(keyFilePath.toString()); + var keyPair = ECKeyPair.generateNew(); + + RadixKeyStore.fromFile(keystoreFile, password.toCharArray(), !keystoreFile.canWrite()) + .writeKeyPair(KEYPAIR_NAME, keyPair); + + var keyFileAwsSecret = new HashMap(); + var publicKeyFileAwsSecret = new HashMap(); + + putKeyBytes(keyFilePath, keyFileAwsSecret); + printAddresses(network, keyPair, publicKeyFileAwsSecret); + + var keyPasswordAwsSecret = new HashMap(); + keyPasswordAwsSecret.put("key", password); + + writeBinaryAWSSecret( + keyFileAwsSecret, keyFileSecretName, awsSecretsOutputOptions, false, true); + writeBinaryAWSSecret( + keyPasswordAwsSecret, passwordSecretName, awsSecretsOutputOptions, false, false); + writeBinaryAWSSecret( + publicKeyFileAwsSecret, publicKeyFileSecretName, awsSecretsOutputOptions, false, false); + } catch (Exception e) { + System.out.println(e); + } + } + } + + private static void printAddresses( + Network network, ECKeyPair keyPair, HashMap publicKeyFileAwsSecret) { + var pubKey = keyPair.getPublicKey(); + var nodeAddressing = NodeAddressing.bech32(network.getNodeHrp()); + var validatorAddressing = ValidatorAddressing.bech32(network.getValidatorHrp()); + + publicKeyFileAwsSecret.put("bech32", nodeAddressing.of(pubKey)); + publicKeyFileAwsSecret.put("hex", pubKey.toHex()); + publicKeyFileAwsSecret.put("validator_address", validatorAddressing.of(pubKey)); + + System.out.println(nodeAddressing.of(pubKey)); + System.out.println(pubKey.toHex()); + } + + private static void putKeyBytes(Path keyFilePath, HashMap keyFileAwsSecret) { + try { + var data = Files.readAllBytes(keyFilePath); + keyFileAwsSecret.put("key", data); + } catch (IOException e) { + throw new IllegalStateException("While reading validator keys", e); + } + } + + private static String generatePassword(String password) { + if (password == null || password.isEmpty()) { + // anphanumeric and special charactrers + int asciiOrigin = 48; // 0 + int asciiBound = 122; // z + int passwordLength = 8; + SecureRandom random = new SecureRandom(); + return random + .ints(asciiOrigin, asciiBound + 1) + .filter(i -> Character.isAlphabetic(i) || Character.isDigit(i)) + .limit(passwordLength) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } else { + return password; + } + } + + private static void usage(Options options) { + new HelpFormatter().printHelp(AWSSecrets.class.getSimpleName(), options, true); + } + + private static Optional getOption(CommandLine cmd, char opt) { + return Optional.ofNullable(cmd.getOptionValue(opt)); + } + + private static void writeBinaryAWSSecret( + Map awsSecret, + String secretName, + AWSSecretsOutputOptions awsSecretsOutputOptions, + boolean compress, + boolean binarySecret) { + if (!awsSecretsOutputOptions.getEnableAwsSecrets()) { + System.out.println("Secret " + secretName + " not stored in AWS"); + return; + } + if (AWSSecretManager.awsSecretExists(secretName) && !canBeUpdated(awsSecretsOutputOptions)) { + System.out.println("Secret " + secretName + " cannot be updated"); + return; + } + if (AWSSecretManager.awsSecretExists(secretName)) { + AWSSecretManager.updateAWSSecret( + awsSecret, secretName, awsSecretsOutputOptions, compress, binarySecret); + } else { + AWSSecretManager.createAWSSecret( + awsSecret, secretName, awsSecretsOutputOptions, compress, binarySecret); + } + } + + private static boolean canBeUpdated(final AWSSecretsOutputOptions awsSecretsOutputOptions) { + return awsSecretsOutputOptions.getRecreateAwsSecrets() + && (!awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("betanet") + || !awsSecretsOutputOptions.getNetworkName().equalsIgnoreCase("mainnet")); + } + + private static Network findNetwork(String networkName) { + var network = Network.ofName(networkName); + + if (network.isEmpty()) { + System.out.println( + "Network " + + networkName + + " is not supported. Available networks: " + + Arrays.toString(Network.values())); + } + + return network.get(); + } } diff --git a/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/identity/LocalRadixIdentity.java b/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/identity/LocalRadixIdentity.java index b25050e357..7179eb2ffe 100644 --- a/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/identity/LocalRadixIdentity.java +++ b/radixdlt-java/radixdlt-cli/src/main/java/com/radixdlt/identity/LocalRadixIdentity.java @@ -68,13 +68,13 @@ import com.radixdlt.crypto.ECPublicKey; public class LocalRadixIdentity { - private final ECKeyPair myKey; + private final ECKeyPair myKey; - LocalRadixIdentity(ECKeyPair myKey) { - this.myKey = myKey; - } + LocalRadixIdentity(ECKeyPair myKey) { + this.myKey = myKey; + } - public ECPublicKey getPublicKey() { - return myKey.getPublicKey(); - } + public ECPublicKey getPublicKey() { + return myKey.getPublicKey(); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/AccountAddress.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/AccountAddress.java index 11aa4931b8..4200e02f31 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/AccountAddress.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/AccountAddress.java @@ -67,53 +67,52 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.networks.Addressing; - import java.util.Objects; public class AccountAddress { - private final REAddr address; + private final REAddr address; - private AccountAddress(REAddr address) { - this.address = address; - } + private AccountAddress(REAddr address) { + this.address = address; + } - public static AccountAddress create(REAddr address) { - return new AccountAddress(address); - } + public static AccountAddress create(REAddr address) { + return new AccountAddress(address); + } - public static AccountAddress create(ECPublicKey publicKey) { - return create(REAddr.ofPubKeyAccount(publicKey)); - } + public static AccountAddress create(ECPublicKey publicKey) { + return create(REAddr.ofPubKeyAccount(publicKey)); + } - public REAddr getAddress() { - return address; - } + public REAddr getAddress() { + return address; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof AccountAddress)) { - return false; - } + if (!(o instanceof AccountAddress)) { + return false; + } - var that = (AccountAddress) o; - return address.equals(that.address); - } + var that = (AccountAddress) o; + return address.equals(that.address); + } - @Override - public int hashCode() { - return Objects.hash(address); - } + @Override + public int hashCode() { + return Objects.hash(address); + } - @Override - public String toString() { - return "{" + address + '}'; - } + @Override + public String toString() { + return "{" + address + '}'; + } - public String toString(int networkId) { - return Addressing.ofNetworkId(networkId).forAccounts().of(address); - } + public String toString(int networkId) { + return Addressing.ofNetworkId(networkId).forAccounts().of(address); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ActionType.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ActionType.java index 5a70f253e5..41c0946882 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ActionType.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ActionType.java @@ -66,51 +66,50 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; - import java.util.Arrays; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; public enum ActionType { - MSG("Message"), - TRANSFER("TokenTransfer"), - STAKE("StakeTokens"), - UNSTAKE("UnstakeTokens"), - BURN("BurnTokens"), - MINT("MintTokens"), - REGISTER_VALIDATOR("RegisterValidator"), - UNREGISTER_VALIDATOR("UnregisterValidator"), - UPDATE_VALIDATOR_METADATA("UpdateValidatorMetadata"), - UPDATE_VALIDATOR_FEE("UpdateValidatorFee"), - UPDATE_VALIDATOR_OWNER("UpdateValidatorOwnerAddress"), - UPDATE_VALIDATOR_DELEGATION_FLAG("UpdateAllowDelegationFlag"), - CREATE_FIXED("CreateFixedSupplyToken"), - CREATE_MUTABLE("CreateMutableSupplyToken"), - UNKNOWN("Other"); + MSG("Message"), + TRANSFER("TokenTransfer"), + STAKE("StakeTokens"), + UNSTAKE("UnstakeTokens"), + BURN("BurnTokens"), + MINT("MintTokens"), + REGISTER_VALIDATOR("RegisterValidator"), + UNREGISTER_VALIDATOR("UnregisterValidator"), + UPDATE_VALIDATOR_METADATA("UpdateValidatorMetadata"), + UPDATE_VALIDATOR_FEE("UpdateValidatorFee"), + UPDATE_VALIDATOR_OWNER("UpdateValidatorOwnerAddress"), + UPDATE_VALIDATOR_DELEGATION_FLAG("UpdateAllowDelegationFlag"), + CREATE_FIXED("CreateFixedSupplyToken"), + CREATE_MUTABLE("CreateMutableSupplyToken"), + UNKNOWN("Other"); - private final String text; + private final String text; - private static final Map TO_ACTION_TYPE = Arrays.stream(values()) - .collect(Collectors.toMap(ActionType::toJson, Function.identity())); + private static final Map TO_ACTION_TYPE = + Arrays.stream(values()).collect(Collectors.toMap(ActionType::toJson, Function.identity())); - ActionType(String text) { - this.text = text; - } + ActionType(String text) { + this.text = text; + } - @JsonValue - public String toJson() { - return text; - } + @JsonValue + public String toJson() { + return text; + } - @JsonCreator - public static ActionType create(String action) { - var result = TO_ACTION_TYPE.get(action); + @JsonCreator + public static ActionType create(String action) { + var result = TO_ACTION_TYPE.get(action); - if (result == null) { - throw new IllegalArgumentException("Unable to parse ActionType from : " + action); - } + if (result == null) { + throw new IllegalArgumentException("Unable to parse ActionType from : " + action); + } - return result; - } + return result; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/EventType.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/EventType.java index 33dae150df..5dfc5f6671 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/EventType.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/EventType.java @@ -66,37 +66,36 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; - import java.util.Arrays; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; public enum EventType { - TOKEN_CREATED("token_created"); + TOKEN_CREATED("token_created"); - private final String text; + private final String text; - private static final Map TO_EVENT_TYPE = Arrays.stream(values()) - .collect(Collectors.toMap(EventType::toJson, Function.identity())); + private static final Map TO_EVENT_TYPE = + Arrays.stream(values()).collect(Collectors.toMap(EventType::toJson, Function.identity())); - EventType(String text) { - this.text = text; - } + EventType(String text) { + this.text = text; + } - @JsonValue - public String toJson() { - return text; - } + @JsonValue + public String toJson() { + return text; + } - @JsonCreator - public static EventType create(String action) { - var result = TO_EVENT_TYPE.get(action); + @JsonCreator + public static EventType create(String action) { + var result = TO_EVENT_TYPE.get(action); - if (result == null) { - throw new IllegalArgumentException("Unable to parse ActionType from : " + action); - } + if (result == null) { + throw new IllegalArgumentException("Unable to parse ActionType from : " + action); + } - return result; - } + return result; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NavigationCursor.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NavigationCursor.java index 1340aba850..4e21c00cd6 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NavigationCursor.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NavigationCursor.java @@ -64,46 +64,45 @@ package com.radixdlt.client.lib.api; -import com.fasterxml.jackson.annotation.JsonCreator; +import static java.util.Objects.requireNonNull; +import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Objects; -import static java.util.Objects.requireNonNull; - public class NavigationCursor { - private final String content; + private final String content; - private NavigationCursor(String content) { - this.content = content; - } + private NavigationCursor(String content) { + this.content = content; + } - @JsonCreator - public static NavigationCursor create(String content) { - requireNonNull(content); + @JsonCreator + public static NavigationCursor create(String content) { + requireNonNull(content); - return new NavigationCursor(content); - } + return new NavigationCursor(content); + } - public String value() { - return content; - } + public String value() { + return content; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return o instanceof NavigationCursor && content.equals(((NavigationCursor) o).content); - } + return o instanceof NavigationCursor && content.equals(((NavigationCursor) o).content); + } - @Override - public int hashCode() { - return Objects.hash(content); - } + @Override + public int hashCode() { + return Objects.hash(content); + } - @Override - public String toString() { - return "NavigationCursor('" + content + "')"; - } + @Override + public String toString() { + return "NavigationCursor('" + content + "')"; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NodeAddress.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NodeAddress.java index f06d0593b7..ae85ac38c1 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NodeAddress.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/NodeAddress.java @@ -66,49 +66,48 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.networks.Addressing; - import java.util.Objects; public class NodeAddress { - private final ECPublicKey address; + private final ECPublicKey address; - private NodeAddress(ECPublicKey address) { - this.address = address; - } + private NodeAddress(ECPublicKey address) { + this.address = address; + } - public static NodeAddress of(ECPublicKey publicKey) { - return new NodeAddress(publicKey); - } + public static NodeAddress of(ECPublicKey publicKey) { + return new NodeAddress(publicKey); + } - public ECPublicKey getAddress() { - return address; - } + public ECPublicKey getAddress() { + return address; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NodeAddress)) { - return false; - } + if (!(o instanceof NodeAddress)) { + return false; + } - var that = (NodeAddress) o; - return address.equals(that.address); - } + var that = (NodeAddress) o; + return address.equals(that.address); + } - @Override - public int hashCode() { - return Objects.hash(address); - } + @Override + public int hashCode() { + return Objects.hash(address); + } - @Override - public String toString() { - return "{" + address.toHex() + '}'; - } + @Override + public String toString() { + return "{" + address.toHex() + '}'; + } - public String toString(int networkId) { - return Addressing.ofNetworkId(networkId).forNodes().of(address); - } + public String toString(int networkId) { + return Addressing.ofNetworkId(networkId).forNodes().of(address); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TransactionRequest.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TransactionRequest.java index f3f37f9f65..2b08c136cd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TransactionRequest.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TransactionRequest.java @@ -75,157 +75,178 @@ import com.radixdlt.client.lib.api.action.TransferAction; import com.radixdlt.client.lib.api.action.UnregisterValidatorAction; import com.radixdlt.client.lib.api.action.UnstakeAction; -import com.radixdlt.client.lib.api.action.UpdateValidatorMetadataAction; import com.radixdlt.client.lib.api.action.UpdateValidatorAllowDelegationFlagAction; import com.radixdlt.client.lib.api.action.UpdateValidatorFeeAction; +import com.radixdlt.client.lib.api.action.UpdateValidatorMetadataAction; import com.radixdlt.client.lib.api.action.UpdateValidatorOwnerAction; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.utils.UInt256; - import java.util.ArrayList; import java.util.List; import java.util.Optional; public class TransactionRequest { - private final List actions; - private final String message; - private final AccountAddress feePayer; - private final Boolean disableResourceAllocationAndDestroy; - - private TransactionRequest( - String message, List actions, AccountAddress feePayer, Boolean disableResourceAllocationAndDestroy - ) { - this.message = message; - this.actions = actions; - this.feePayer = feePayer; - this.disableResourceAllocationAndDestroy = disableResourceAllocationAndDestroy; - } - - public static TransactionRequestBuilder createBuilder(AccountAddress feePayer) { - return new TransactionRequestBuilder(feePayer); - } - - @JsonProperty("message") - public String getMessage() { - return message; - } - - @JsonProperty("actions") - public List getActions() { - return actions; - } - - @JsonProperty("feePayer") - public AccountAddress getFeePayer() { - return feePayer; - } - - @JsonProperty("disableResourceAllocationAndDestroy") - public Boolean disableResourceAllocationAndDestroy() { - return disableResourceAllocationAndDestroy; - } - - public static final class TransactionRequestBuilder { - private final List actions = new ArrayList<>(); - private final AccountAddress feePayer; - private String message; - private Boolean disableResourceAllocationAndDestroy; - - private TransactionRequestBuilder(AccountAddress feePayer) { - this.feePayer = feePayer; - } - - public TransactionRequestBuilder transfer(AccountAddress from, AccountAddress to, UInt256 amount, String rri) { - actions.add(new TransferAction(from, to, amount, rri)); - return this; - } - - public TransactionRequestBuilder stake(AccountAddress from, ValidatorAddress validator, UInt256 amount) { - actions.add(new StakeAction(from, validator, amount)); - return this; - } - - public TransactionRequestBuilder unstake(AccountAddress to, ValidatorAddress validator, UInt256 amount) { - actions.add(new UnstakeAction(to, validator, amount)); - return this; - } - - public TransactionRequestBuilder burn(AccountAddress from, UInt256 amount, String rri) { - actions.add(new BurnAction(from, amount, rri)); - return this; - } - - public TransactionRequestBuilder mint(AccountAddress from, UInt256 amount, String rri) { - actions.add(new MintAction(from, amount, rri)); - return this; - } - - public TransactionRequestBuilder registerValidator(ValidatorAddress validator, Optional name, Optional url) { - actions.add(new RegisterValidatorAction(validator, name.orElse(null), url.orElse(null))); - return this; - } - - public TransactionRequestBuilder unregisterValidator(ValidatorAddress validator, Optional name, Optional url) { - actions.add(new UnregisterValidatorAction(validator, name.orElse(null), url.orElse(null))); - return this; - } - - public TransactionRequestBuilder updateValidatorMetadata(ValidatorAddress validator, Optional name, Optional url) { - actions.add(new UpdateValidatorMetadataAction(validator, name.orElse(null), url.orElse(null))); - return this; - } - - public TransactionRequestBuilder updateValidatorAllowDelegationFlag(ValidatorAddress validator, boolean allowDelegation) { - actions.add(new UpdateValidatorAllowDelegationFlagAction(validator, allowDelegation)); - return this; - } - - public TransactionRequestBuilder updateValidatorFee(ValidatorAddress validator, double validatorFee) { - actions.add(new UpdateValidatorFeeAction(validator, validatorFee)); - return this; - } - - public TransactionRequestBuilder updateValidatorOwner(ValidatorAddress validator, AccountAddress owner) { - actions.add(new UpdateValidatorOwnerAction(validator, owner)); - return this; - } - - public TransactionRequestBuilder createFixed( - AccountAddress to, ECPublicKey publicKeyOfSigner, String symbol, - String name, String description, String iconUrl, String tokenUrl, UInt256 supply - ) { - actions.add(new CreateFixedTokenAction( - to, publicKeyOfSigner, symbol, name, - description, iconUrl, tokenUrl, supply - )); - return this; - } - - public TransactionRequestBuilder createMutable( - ECPublicKey publicKeyOfSigner, String symbol, String name, - Optional description, Optional iconUrl, Optional tokenUrl - ) { - actions.add(new CreateMutableTokenAction( - publicKeyOfSigner, symbol, name, - description.orElse(null), - iconUrl.orElse(null), - tokenUrl.orElse(null) - )); - return this; - } - - public TransactionRequestBuilder message(String message) { - this.message = message; - return this; - } - - public TransactionRequestBuilder disableResourceAllocationAndDestroy() { - this.disableResourceAllocationAndDestroy = true; - return this; - } - - public TransactionRequest build() { - return new TransactionRequest(message, actions, feePayer, disableResourceAllocationAndDestroy); - } - } + private final List actions; + private final String message; + private final AccountAddress feePayer; + private final Boolean disableResourceAllocationAndDestroy; + + private TransactionRequest( + String message, + List actions, + AccountAddress feePayer, + Boolean disableResourceAllocationAndDestroy) { + this.message = message; + this.actions = actions; + this.feePayer = feePayer; + this.disableResourceAllocationAndDestroy = disableResourceAllocationAndDestroy; + } + + public static TransactionRequestBuilder createBuilder(AccountAddress feePayer) { + return new TransactionRequestBuilder(feePayer); + } + + @JsonProperty("message") + public String getMessage() { + return message; + } + + @JsonProperty("actions") + public List getActions() { + return actions; + } + + @JsonProperty("feePayer") + public AccountAddress getFeePayer() { + return feePayer; + } + + @JsonProperty("disableResourceAllocationAndDestroy") + public Boolean disableResourceAllocationAndDestroy() { + return disableResourceAllocationAndDestroy; + } + + public static final class TransactionRequestBuilder { + private final List actions = new ArrayList<>(); + private final AccountAddress feePayer; + private String message; + private Boolean disableResourceAllocationAndDestroy; + + private TransactionRequestBuilder(AccountAddress feePayer) { + this.feePayer = feePayer; + } + + public TransactionRequestBuilder transfer( + AccountAddress from, AccountAddress to, UInt256 amount, String rri) { + actions.add(new TransferAction(from, to, amount, rri)); + return this; + } + + public TransactionRequestBuilder stake( + AccountAddress from, ValidatorAddress validator, UInt256 amount) { + actions.add(new StakeAction(from, validator, amount)); + return this; + } + + public TransactionRequestBuilder unstake( + AccountAddress to, ValidatorAddress validator, UInt256 amount) { + actions.add(new UnstakeAction(to, validator, amount)); + return this; + } + + public TransactionRequestBuilder burn(AccountAddress from, UInt256 amount, String rri) { + actions.add(new BurnAction(from, amount, rri)); + return this; + } + + public TransactionRequestBuilder mint(AccountAddress from, UInt256 amount, String rri) { + actions.add(new MintAction(from, amount, rri)); + return this; + } + + public TransactionRequestBuilder registerValidator( + ValidatorAddress validator, Optional name, Optional url) { + actions.add(new RegisterValidatorAction(validator, name.orElse(null), url.orElse(null))); + return this; + } + + public TransactionRequestBuilder unregisterValidator( + ValidatorAddress validator, Optional name, Optional url) { + actions.add(new UnregisterValidatorAction(validator, name.orElse(null), url.orElse(null))); + return this; + } + + public TransactionRequestBuilder updateValidatorMetadata( + ValidatorAddress validator, Optional name, Optional url) { + actions.add( + new UpdateValidatorMetadataAction(validator, name.orElse(null), url.orElse(null))); + return this; + } + + public TransactionRequestBuilder updateValidatorAllowDelegationFlag( + ValidatorAddress validator, boolean allowDelegation) { + actions.add(new UpdateValidatorAllowDelegationFlagAction(validator, allowDelegation)); + return this; + } + + public TransactionRequestBuilder updateValidatorFee( + ValidatorAddress validator, double validatorFee) { + actions.add(new UpdateValidatorFeeAction(validator, validatorFee)); + return this; + } + + public TransactionRequestBuilder updateValidatorOwner( + ValidatorAddress validator, AccountAddress owner) { + actions.add(new UpdateValidatorOwnerAction(validator, owner)); + return this; + } + + public TransactionRequestBuilder createFixed( + AccountAddress to, + ECPublicKey publicKeyOfSigner, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + UInt256 supply) { + actions.add( + new CreateFixedTokenAction( + to, publicKeyOfSigner, symbol, name, description, iconUrl, tokenUrl, supply)); + return this; + } + + public TransactionRequestBuilder createMutable( + ECPublicKey publicKeyOfSigner, + String symbol, + String name, + Optional description, + Optional iconUrl, + Optional tokenUrl) { + actions.add( + new CreateMutableTokenAction( + publicKeyOfSigner, + symbol, + name, + description.orElse(null), + iconUrl.orElse(null), + tokenUrl.orElse(null))); + return this; + } + + public TransactionRequestBuilder message(String message) { + this.message = message; + return this; + } + + public TransactionRequestBuilder disableResourceAllocationAndDestroy() { + this.disableResourceAllocationAndDestroy = true; + return this; + } + + public TransactionRequest build() { + return new TransactionRequest( + message, actions, feePayer, disableResourceAllocationAndDestroy); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TxTimestamp.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TxTimestamp.java index ba7f18f8f0..f1f566cde0 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TxTimestamp.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/TxTimestamp.java @@ -64,56 +64,55 @@ package com.radixdlt.client.lib.api; -import com.fasterxml.jackson.annotation.JsonCreator; +import static java.util.Objects.requireNonNull; +import com.fasterxml.jackson.annotation.JsonCreator; import java.time.Instant; import java.util.Objects; -import static java.util.Objects.requireNonNull; - public class TxTimestamp { - private final Instant instant; + private final Instant instant; - private TxTimestamp(Instant instant) { - this.instant = instant; - } + private TxTimestamp(Instant instant) { + this.instant = instant; + } - public static TxTimestamp create(Instant instant) { - requireNonNull(instant); + public static TxTimestamp create(Instant instant) { + requireNonNull(instant); - return new TxTimestamp(instant); - } + return new TxTimestamp(instant); + } - @JsonCreator - public static TxTimestamp create(String input) { - return create(Instant.parse(input)); - } + @JsonCreator + public static TxTimestamp create(String input) { + return create(Instant.parse(input)); + } - public Instant getInstant() { - return instant; - } + public Instant getInstant() { + return instant; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof TxTimestamp)) { - return false; - } + if (!(o instanceof TxTimestamp)) { + return false; + } - var that = (TxTimestamp) o; - return instant.equals(that.instant); - } + var that = (TxTimestamp) o; + return instant.equals(that.instant); + } - @Override - public int hashCode() { - return Objects.hash(instant); - } + @Override + public int hashCode() { + return Objects.hash(instant); + } - @Override - public String toString() { - return "TxTimestamp(" + instant + ")"; - } + @Override + public String toString() { + return "TxTimestamp(" + instant + ")"; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ValidatorAddress.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ValidatorAddress.java index cabe97911c..58de724739 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ValidatorAddress.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/ValidatorAddress.java @@ -66,49 +66,48 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.networks.Addressing; - import java.util.Objects; public class ValidatorAddress { - private final ECPublicKey address; + private final ECPublicKey address; - private ValidatorAddress(ECPublicKey address) { - this.address = address; - } + private ValidatorAddress(ECPublicKey address) { + this.address = address; + } - public static ValidatorAddress of(ECPublicKey publicKey) { - return new ValidatorAddress(publicKey); - } + public static ValidatorAddress of(ECPublicKey publicKey) { + return new ValidatorAddress(publicKey); + } - public ECPublicKey getAddress() { - return address; - } + public ECPublicKey getAddress() { + return address; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ValidatorAddress)) { - return false; - } + if (!(o instanceof ValidatorAddress)) { + return false; + } - var that = (ValidatorAddress) o; - return address.equals(that.address); - } + var that = (ValidatorAddress) o; + return address.equals(that.address); + } - @Override - public int hashCode() { - return Objects.hash(address); - } + @Override + public int hashCode() { + return Objects.hash(address); + } - @Override - public String toString() { - return "{" + address.toHex() + '}'; - } + @Override + public String toString() { + return "{" + address.toHex() + '}'; + } - public String toString(int networkId) { - return Addressing.ofNetworkId(networkId).forValidators().of(address); - } + public String toString(int networkId) { + return Addressing.ofNetworkId(networkId).forValidators().of(address); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/Action.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/Action.java index de00164303..2cdb841a21 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/Action.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/Action.java @@ -64,5 +64,4 @@ package com.radixdlt.client.lib.api.action; -public interface Action { -} +public interface Action {} diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/BurnAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/BurnAction.java index faf12e27fc..7f3acab218 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/BurnAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/BurnAction.java @@ -71,14 +71,14 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class BurnAction implements Action { - private final ActionType type = ActionType.BURN; - private final AccountAddress from; - private final UInt256 amount; - private final String rri; + private final ActionType type = ActionType.BURN; + private final AccountAddress from; + private final UInt256 amount; + private final String rri; - public BurnAction(AccountAddress from, UInt256 amount, String rri) { - this.from = from; - this.amount = amount; - this.rri = rri; - } + public BurnAction(AccountAddress from, UInt256 amount, String rri) { + this.from = from; + this.amount = amount; + this.rri = rri; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateFixedTokenAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateFixedTokenAction.java index 0ebe3a7d99..0917feb7c6 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateFixedTokenAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateFixedTokenAction.java @@ -72,33 +72,32 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class CreateFixedTokenAction implements Action { - private final ActionType type = ActionType.CREATE_FIXED; - private final AccountAddress to; - private final ECPublicKey publicKeyOfSigner; - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenUrl; - private final UInt256 supply; + private final ActionType type = ActionType.CREATE_FIXED; + private final AccountAddress to; + private final ECPublicKey publicKeyOfSigner; + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenUrl; + private final UInt256 supply; - public CreateFixedTokenAction( - AccountAddress to, - ECPublicKey publicKeyOfSigner, - String symbol, - String name, - String description, - String iconUrl, - String tokenUrl, - UInt256 supply - ) { - this.to = to; - this.publicKeyOfSigner = publicKeyOfSigner; - this.symbol = symbol; - this.name = name; - this.description = description; - this.iconUrl = iconUrl; - this.tokenUrl = tokenUrl; - this.supply = supply; - } + public CreateFixedTokenAction( + AccountAddress to, + ECPublicKey publicKeyOfSigner, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + UInt256 supply) { + this.to = to; + this.publicKeyOfSigner = publicKeyOfSigner; + this.symbol = symbol; + this.name = name; + this.description = description; + this.iconUrl = iconUrl; + this.tokenUrl = tokenUrl; + this.supply = supply; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateMutableTokenAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateMutableTokenAction.java index f7258abfe9..8a2b0c859b 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateMutableTokenAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/CreateMutableTokenAction.java @@ -70,23 +70,26 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class CreateMutableTokenAction implements Action { - private final ActionType type = ActionType.CREATE_MUTABLE; - private final ECPublicKey publicKeyOfSigner; - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenUrl; + private final ActionType type = ActionType.CREATE_MUTABLE; + private final ECPublicKey publicKeyOfSigner; + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenUrl; - public CreateMutableTokenAction( - ECPublicKey publicKeyOfSigner, String symbol, String name, - String description, String iconUrl, String tokenUrl - ) { - this.publicKeyOfSigner = publicKeyOfSigner; - this.symbol = symbol; - this.name = name; - this.description = description; - this.iconUrl = iconUrl; - this.tokenUrl = tokenUrl; - } + public CreateMutableTokenAction( + ECPublicKey publicKeyOfSigner, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl) { + this.publicKeyOfSigner = publicKeyOfSigner; + this.symbol = symbol; + this.name = name; + this.description = description; + this.iconUrl = iconUrl; + this.tokenUrl = tokenUrl; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/MintAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/MintAction.java index 11c76aa4d7..d57eee6ce0 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/MintAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/MintAction.java @@ -71,14 +71,14 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class MintAction implements Action { - private final ActionType type = ActionType.MINT; - private final AccountAddress to; - private final UInt256 amount; - private final String rri; + private final ActionType type = ActionType.MINT; + private final AccountAddress to; + private final UInt256 amount; + private final String rri; - public MintAction(AccountAddress to, UInt256 amount, String rri) { - this.to = to; - this.amount = amount; - this.rri = rri; - } + public MintAction(AccountAddress to, UInt256 amount, String rri) { + this.to = to; + this.amount = amount; + this.rri = rri; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/RegisterValidatorAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/RegisterValidatorAction.java index 0eee5132a9..2df0d00f0e 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/RegisterValidatorAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/RegisterValidatorAction.java @@ -70,14 +70,14 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class RegisterValidatorAction implements Action { - private final ActionType type = ActionType.REGISTER_VALIDATOR; - private final ValidatorAddress validator; - private final String name; - private final String url; + private final ActionType type = ActionType.REGISTER_VALIDATOR; + private final ValidatorAddress validator; + private final String name; + private final String url; - public RegisterValidatorAction(ValidatorAddress validator, String name, String url) { - this.validator = validator; - this.name = name; - this.url = url; - } + public RegisterValidatorAction(ValidatorAddress validator, String name, String url) { + this.validator = validator; + this.name = name; + this.url = url; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/StakeAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/StakeAction.java index 4056ada211..f61ba90845 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/StakeAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/StakeAction.java @@ -71,48 +71,50 @@ import com.radixdlt.client.lib.api.ActionType; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class StakeAction implements Action { - private final ActionType type = ActionType.STAKE; - private final AccountAddress from; - private final ValidatorAddress validator; - private final UInt256 amount; + private final ActionType type = ActionType.STAKE; + private final AccountAddress from; + private final ValidatorAddress validator; + private final UInt256 amount; - @JsonCreator - public StakeAction( - @JsonProperty(value = "from", required = true) AccountAddress from, - @JsonProperty(value = "validator", required = true) ValidatorAddress validator, - @JsonProperty(value = "amount", required = true) UInt256 amount - ) { - this.from = from; - this.validator = validator; - this.amount = amount; - } + @JsonCreator + public StakeAction( + @JsonProperty(value = "from", required = true) AccountAddress from, + @JsonProperty(value = "validator", required = true) ValidatorAddress validator, + @JsonProperty(value = "amount", required = true) UInt256 amount) { + this.from = from; + this.validator = validator; + this.amount = amount; + } - public String toJSON(int networkId) { - return String.format("{\"from\":\"%s\",\"validator\":\"%s\",\"amount\":\"%s\",\"type\":\"StakeTokens\"}", - from.toString(networkId), validator.toString(networkId), amount); - } + public String toJSON(int networkId) { + return String.format( + "{\"from\":\"%s\",\"validator\":\"%s\",\"amount\":\"%s\",\"type\":\"StakeTokens\"}", + from.toString(networkId), validator.toString(networkId), amount); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof StakeAction)) { - return false; - } + if (!(o instanceof StakeAction)) { + return false; + } - var that = (StakeAction) o; - return type == that.type && from.equals(that.from) && validator.equals(that.validator) && amount.equals(that.amount); - } + var that = (StakeAction) o; + return type == that.type + && from.equals(that.from) + && validator.equals(that.validator) + && amount.equals(that.amount); + } - @Override - public int hashCode() { - return Objects.hash(type, from, validator, amount); - } + @Override + public int hashCode() { + return Objects.hash(type, from, validator, amount); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/TransferAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/TransferAction.java index 2d3567a5c7..24289c240c 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/TransferAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/TransferAction.java @@ -71,16 +71,16 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class TransferAction implements Action { - private final ActionType type = ActionType.TRANSFER; - private final AccountAddress from; - private final AccountAddress to; - private final UInt256 amount; - private final String rri; + private final ActionType type = ActionType.TRANSFER; + private final AccountAddress from; + private final AccountAddress to; + private final UInt256 amount; + private final String rri; - public TransferAction(AccountAddress from, AccountAddress to, UInt256 amount, String rri) { - this.from = from; - this.to = to; - this.amount = amount; - this.rri = rri; - } + public TransferAction(AccountAddress from, AccountAddress to, UInt256 amount, String rri) { + this.from = from; + this.to = to; + this.amount = amount; + this.rri = rri; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnregisterValidatorAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnregisterValidatorAction.java index 933f1f3e6c..82d6dee385 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnregisterValidatorAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnregisterValidatorAction.java @@ -70,14 +70,14 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class UnregisterValidatorAction implements Action { - private final ActionType type = ActionType.UNREGISTER_VALIDATOR; - private final ValidatorAddress delegate; - private final String name; - private final String url; + private final ActionType type = ActionType.UNREGISTER_VALIDATOR; + private final ValidatorAddress delegate; + private final String name; + private final String url; - public UnregisterValidatorAction(ValidatorAddress delegate, String name, String url) { - this.delegate = delegate; - this.name = name; - this.url = url; - } + public UnregisterValidatorAction(ValidatorAddress delegate, String name, String url) { + this.delegate = delegate; + this.name = name; + this.url = url; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnstakeAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnstakeAction.java index a082e061d1..3cbaedd903 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnstakeAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UnstakeAction.java @@ -72,14 +72,14 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class UnstakeAction implements Action { - private final ActionType type = ActionType.UNSTAKE; - private final AccountAddress to; - private final ValidatorAddress validator; - private final UInt256 amount; + private final ActionType type = ActionType.UNSTAKE; + private final AccountAddress to; + private final ValidatorAddress validator; + private final UInt256 amount; - public UnstakeAction(AccountAddress to, ValidatorAddress validator, UInt256 amount) { - this.to = to; - this.validator = validator; - this.amount = amount; - } + public UnstakeAction(AccountAddress to, ValidatorAddress validator, UInt256 amount) { + this.to = to; + this.validator = validator; + this.amount = amount; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorAllowDelegationFlagAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorAllowDelegationFlagAction.java index db2ca4c396..acc546bf88 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorAllowDelegationFlagAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorAllowDelegationFlagAction.java @@ -70,12 +70,13 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class UpdateValidatorAllowDelegationFlagAction implements Action { - private final ActionType type = ActionType.UPDATE_VALIDATOR_DELEGATION_FLAG; - private final ValidatorAddress validator; - private final boolean allowDelegation; + private final ActionType type = ActionType.UPDATE_VALIDATOR_DELEGATION_FLAG; + private final ValidatorAddress validator; + private final boolean allowDelegation; - public UpdateValidatorAllowDelegationFlagAction(ValidatorAddress validator, boolean allowDelegation) { - this.validator = validator; - this.allowDelegation = allowDelegation; - } + public UpdateValidatorAllowDelegationFlagAction( + ValidatorAddress validator, boolean allowDelegation) { + this.validator = validator; + this.allowDelegation = allowDelegation; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorFeeAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorFeeAction.java index d7f59f1a49..8a2c52a9cd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorFeeAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorFeeAction.java @@ -70,12 +70,12 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class UpdateValidatorFeeAction implements Action { - private final ActionType type = ActionType.UPDATE_VALIDATOR_FEE; - private final ValidatorAddress validator; - private final double validatorFee; + private final ActionType type = ActionType.UPDATE_VALIDATOR_FEE; + private final ValidatorAddress validator; + private final double validatorFee; - public UpdateValidatorFeeAction(ValidatorAddress validator, double validatorFee) { - this.validator = validator; - this.validatorFee = validatorFee; - } + public UpdateValidatorFeeAction(ValidatorAddress validator, double validatorFee) { + this.validator = validator; + this.validatorFee = validatorFee; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorMetadataAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorMetadataAction.java index f9ae435cb4..fd1b6bb640 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorMetadataAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorMetadataAction.java @@ -70,14 +70,14 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class UpdateValidatorMetadataAction implements Action { - private final ActionType type = ActionType.UPDATE_VALIDATOR_METADATA; - private final ValidatorAddress delegate; - private final String name; - private final String url; + private final ActionType type = ActionType.UPDATE_VALIDATOR_METADATA; + private final ValidatorAddress delegate; + private final String name; + private final String url; - public UpdateValidatorMetadataAction(ValidatorAddress delegate, String name, String url) { - this.delegate = delegate; - this.name = name; - this.url = url; - } + public UpdateValidatorMetadataAction(ValidatorAddress delegate, String name, String url) { + this.delegate = delegate; + this.name = name; + this.url = url; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorOwnerAction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorOwnerAction.java index ea11141afa..e0d54c5a87 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorOwnerAction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/action/UpdateValidatorOwnerAction.java @@ -71,12 +71,12 @@ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class UpdateValidatorOwnerAction implements Action { - private final ActionType type = ActionType.UPDATE_VALIDATOR_OWNER; - private final ValidatorAddress validator; - private final AccountAddress owner; + private final ActionType type = ActionType.UPDATE_VALIDATOR_OWNER; + private final ValidatorAddress validator; + private final AccountAddress owner; - public UpdateValidatorOwnerAction(ValidatorAddress validator, AccountAddress owner) { - this.validator = validator; - this.owner = owner; - } + public UpdateValidatorOwnerAction(ValidatorAddress validator, AccountAddress owner) { + this.validator = validator; + this.owner = owner; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/AsyncRadixApi.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/AsyncRadixApi.java index 2f5a554087..66a4134521 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/AsyncRadixApi.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/AsyncRadixApi.java @@ -64,16 +64,15 @@ package com.radixdlt.client.lib.api.async; -import com.radixdlt.client.lib.dto.TransactionDTO; -import com.radixdlt.client.lib.dto.TransactionHistory; -import org.bouncycastle.util.encoders.Hex; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.*; +import static com.radixdlt.errors.ClientErrors.MISSING_BASE_URL; +import static java.util.Optional.ofNullable; import com.fasterxml.jackson.core.type.TypeReference; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.NavigationCursor; import com.radixdlt.client.lib.api.TransactionRequest; import com.radixdlt.client.lib.api.ValidatorAddress; - import com.radixdlt.client.lib.api.rpc.BasicAuth; import com.radixdlt.client.lib.api.rpc.JsonRpcRequest; import com.radixdlt.client.lib.api.rpc.JsonRpcResponse; @@ -104,6 +103,8 @@ import com.radixdlt.client.lib.dto.SyncData; import com.radixdlt.client.lib.dto.TokenBalances; import com.radixdlt.client.lib.dto.TokenInfo; +import com.radixdlt.client.lib.dto.TransactionDTO; +import com.radixdlt.client.lib.dto.TransactionHistory; import com.radixdlt.client.lib.dto.TransactionStatusDTO; import com.radixdlt.client.lib.dto.TransactionsDTO; import com.radixdlt.client.lib.dto.TxBlobDTO; @@ -115,391 +116,413 @@ import com.radixdlt.networks.Addressing; import com.radixdlt.utils.functional.Promise; import com.radixdlt.utils.functional.Result; - import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.OptionalLong; - -import static com.radixdlt.client.lib.api.rpc.RpcMethod.*; -import static com.radixdlt.errors.ClientErrors.MISSING_BASE_URL; - -import static java.util.Optional.ofNullable; +import org.bouncycastle.util.encoders.Hex; public class AsyncRadixApi extends RadixApiBase implements RadixApi { - private final Network network = new Network() { - @Override - public Promise id() { - return call(request(NETWORK_ID), new TypeReference<>() {}); - } - - @Override - public Promise throughput() { - return call(request(NETWORK_THROUGHPUT), new TypeReference<>() {}); - } - - @Override - public Promise demand() { - return call(request(NETWORK_DEMAND), new TypeReference<>() {}); - } - - @Override - public Promise configuration() { - return call(request(NETWORK_CONFIG), new TypeReference<>() {}); - } - - @Override - public Promise data() { - return call(request(NETWORK_DATA), new TypeReference<>() {}); - } - - @Override - public Promise> peers() { - return call(request(NETWORK_PEERS), new TypeReference<>() {}); - } - - @Override - public Promise> addressBook() { - return call(request(NETWORK_ADDRESS_BOOK), new TypeReference<>() {}); - } - }; - - private final Token token = new Token() { - @Override - public Promise describeNative() { - return call(request(TOKEN_NATIVE), new TypeReference<>() {}); - } - - @Override - public Promise describe(String rri) { - return call(request(TOKEN_INFO, rri), new TypeReference<>() {}); - } - }; - - private final Transaction transaction = new Transaction() { - @Override - public Promise build(TransactionRequest request) { - return call( - request( - CONSTRUCTION_BUILD, request.getActions(), request.getFeePayer(), - request.getMessage(), request.disableResourceAllocationAndDestroy() - ), - new TypeReference<>() {} - ); - } - - @Override - public Promise finalize(FinalizedTransaction request, boolean immediateSubmit) { - return call( - request( - CONSTRUCTION_FINALIZE, - Hex.toHexString(request.getRawBlob()), request.getSignature(), request.getPublicKey(), Boolean.toString(immediateSubmit) - ), - new TypeReference<>() {} - ); - } - - @Override - public Promise submit(TxBlobDTO request) { - return call( - request(CONSTRUCTION_SUBMIT, Hex.toHexString(request.getBlob()), request.getTxId()), - new TypeReference<>() {} - ); - } - - @Override - public Promise lookup(AID txId) { - return call(request(TRANSACTION_LOOKUP, txId.toString()), new TypeReference<>() {}); - } - - @Override - public Promise status(AID txId) { - return call(request(TRANSACTION_STATUS, txId.toString()), new TypeReference<>() {}); - } - - @Override - public Promise list(long limit, OptionalLong offset) { - var request = request(TRANSACTION_LIST, limit); - offset.ifPresent(request::addParameters); - return call(request, new TypeReference<>() {}); - } - }; - - private final SingleAccount account = new SingleAccount() { - @Override - public Promise balances(AccountAddress address) { - return call(request(ACCOUNT_BALANCES, address.toString(networkId())), new TypeReference<>() {}); - } - - @Override - public Promise history( - AccountAddress address, int size, OptionalLong nextOffset - ) { - var request = request(ACCOUNT_HISTORY, address.toString(networkId()), size); - nextOffset.ifPresent(request::addParameters); - - return call(request, new TypeReference<>() {}); - } - - @Override - public Promise> stakes(AccountAddress address) { - return call(request(ACCOUNT_STAKES, address.toString(networkId())), new TypeReference<>() {}); - } - - @Override - public Promise> unstakes(AccountAddress address) { - return call(request(ACCOUNT_UNSTAKES, address.toString(networkId())), new TypeReference<>() {}); - } - }; - - private final Validator validator = new Validator() { - @Override - public Promise list(int size, Optional cursor) { - var request = request(VALIDATORS_LIST, size); - cursor.ifPresent(cursorValue -> request.addParameters(cursorValue.value())); - - return call(request, new TypeReference<>() {}); - } - - @Override - public Promise lookup(ValidatorAddress validatorAddress) { - return call(request(VALIDATORS_LOOKUP, validatorAddress.toString(networkId())), new TypeReference<>() {}); - } - }; - - private final Local local = new Local() { - @Override - public Promise accountInfo() { - return call(request(ACCOUNT_INFO), new TypeReference<>() {}); - } - - @Override - public Promise submitTxSingleStep(TransactionRequest request) { - return call( - request(ACCOUNT_SUBMIT_SINGLE_STEP, request.getActions(), request.getMessage()), - new TypeReference<>() {} - ); - } - - @Override - public Promise validatorInfo() { - return call(request(VALIDATION_NODE_INFO), new TypeReference<>() {}); - } - - @Override - public Promise currentEpoch() { - return call(request(VALIDATION_CURRENT_EPOCH), new TypeReference<>() {}); - } - }; - - private final Api api = new Api() { - @Override - public Promise configuration() { - return call(request(API_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Promise data() { - return call(request(API_DATA), new TypeReference<>() {}); - } - }; - - private final Consensus consensus = new Consensus() { - @Override - public Promise configuration() { - return call(request(BFT_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Promise data() { - return call(request(BFT_DATA), new TypeReference<>() {}); - } - }; - - private final Mempool mempool = new Mempool() { - @Override - public Promise configuration() { - return call(request(MEMPOOL_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Promise data() { - return call(request(MEMPOOL_DATA), new TypeReference<>() {}); - } - }; - - private final RadixEngine radixEngine = new RadixEngine() { - @Override - public Promise> configuration() { - return call(request(RADIX_ENGINE_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Promise data() { - return call(request(RADIX_ENGINE_DATA), new TypeReference<>() {}); - } - }; - - private final Sync sync = new Sync() { - @Override - public Promise configuration() { - return call(request(SYNC_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Promise data() { - return call(request(SYNC_DATA), new TypeReference<>() {}); - } - }; - - private final Ledger ledger = new Ledger() { - @Override - public Promise latest() { - return call(request(LEDGER_PROOF), new TypeReference<>() {}); - } - - @Override - public Promise epoch() { - return call(request(LEDGER_EPOCH_PROOF), new TypeReference<>() {}); - } - - @Override - public Promise checkpoints() { - return call(request(LEDGER_CHECKPOINTS), new TypeReference<>() {}); - } - }; - - @Override - public Network network() { - return network; - } - - @Override - public Transaction transaction() { - return transaction; - } - - @Override - public Token token() { - return token; - } - - @Override - public Local local() { - return local; - } - - @Override - public SingleAccount account() { - return account; - } - - @Override - public Validator validator() { - return validator; - } - - @Override - public Api api() { - return api; - } - - @Override - public Consensus consensus() { - return consensus; - } - - @Override - public Mempool mempool() { - return mempool; - } - - @Override - public RadixEngine radixEngine() { - return radixEngine; - } - - @Override - public Sync sync() { - return sync; - } - - @Override - public Ledger ledger() { - return ledger; - } - - @Override - public AsyncRadixApi withTrace() { - enableTrace(); - return this; - } - - @Override - public Addressing addressing() { - return networkAddressing(); - } - - @Override - public AsyncRadixApi withTimeout(Duration timeout) { - setTimeout(timeout); - return this; - } - - private AsyncRadixApi( - String baseUrl, - int primaryPort, - int secondaryPort, - HttpClient client, - Optional authentication - ) { - super(baseUrl, primaryPort, secondaryPort, client, authentication); - } - - static Promise connect( - String url, - int primaryPort, - int secondaryPort, - Optional authentication - ) { - return buildHttpClient().fold(Promise::failure, client -> connect(url, primaryPort, secondaryPort, client, authentication)); - } - - static Promise connect( - String url, - int primaryPort, - int secondaryPort, - HttpClient client, - Optional authentication - ) { - return ofNullable(url) - .map(baseUrl -> Result.ok(new AsyncRadixApi(baseUrl, primaryPort, secondaryPort, client, authentication))) - .orElseGet(MISSING_BASE_URL::result) - .flatMap(asyncRadixApi -> asyncRadixApi.network().id().join() - .onSuccess(networkId -> asyncRadixApi.configureSerialization(networkId.getNetworkId())) - .map(__ -> asyncRadixApi)) - .fold(Promise::failure, Promise::ok); - } - - private Promise call(JsonRpcRequest request, TypeReference> typeReference) { - return serialize(request) - .onSuccess(this::trace) - .map(value -> buildRequest(request, value)) - .map(httpRequest -> client().sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())) - .map(future -> Promise.promise(promise -> future.thenAccept(body -> bodyHandler(body, promise, typeReference)))) - .fold(Promise::failure, promise -> promise); - } - - private void bodyHandler( - HttpResponse body, - Promise promise, - TypeReference> reference - ) { - promise.resolve(deserialize(trace(body.body()), reference) - .flatMap(response -> response.rawError() == null - ? Result.ok(response.rawResult()) - : Result.fail(response.rawError().toFailure()))); - } + private final Network network = + new Network() { + @Override + public Promise id() { + return call(request(NETWORK_ID), new TypeReference<>() {}); + } + + @Override + public Promise throughput() { + return call(request(NETWORK_THROUGHPUT), new TypeReference<>() {}); + } + + @Override + public Promise demand() { + return call(request(NETWORK_DEMAND), new TypeReference<>() {}); + } + + @Override + public Promise configuration() { + return call(request(NETWORK_CONFIG), new TypeReference<>() {}); + } + + @Override + public Promise data() { + return call(request(NETWORK_DATA), new TypeReference<>() {}); + } + + @Override + public Promise> peers() { + return call(request(NETWORK_PEERS), new TypeReference<>() {}); + } + + @Override + public Promise> addressBook() { + return call(request(NETWORK_ADDRESS_BOOK), new TypeReference<>() {}); + } + }; + + private final Token token = + new Token() { + @Override + public Promise describeNative() { + return call(request(TOKEN_NATIVE), new TypeReference<>() {}); + } + + @Override + public Promise describe(String rri) { + return call(request(TOKEN_INFO, rri), new TypeReference<>() {}); + } + }; + + private final Transaction transaction = + new Transaction() { + @Override + public Promise build(TransactionRequest request) { + return call( + request( + CONSTRUCTION_BUILD, + request.getActions(), + request.getFeePayer(), + request.getMessage(), + request.disableResourceAllocationAndDestroy()), + new TypeReference<>() {}); + } + + @Override + public Promise finalize(FinalizedTransaction request, boolean immediateSubmit) { + return call( + request( + CONSTRUCTION_FINALIZE, + Hex.toHexString(request.getRawBlob()), + request.getSignature(), + request.getPublicKey(), + Boolean.toString(immediateSubmit)), + new TypeReference<>() {}); + } + + @Override + public Promise submit(TxBlobDTO request) { + return call( + request(CONSTRUCTION_SUBMIT, Hex.toHexString(request.getBlob()), request.getTxId()), + new TypeReference<>() {}); + } + + @Override + public Promise lookup(AID txId) { + return call(request(TRANSACTION_LOOKUP, txId.toString()), new TypeReference<>() {}); + } + + @Override + public Promise status(AID txId) { + return call(request(TRANSACTION_STATUS, txId.toString()), new TypeReference<>() {}); + } + + @Override + public Promise list(long limit, OptionalLong offset) { + var request = request(TRANSACTION_LIST, limit); + offset.ifPresent(request::addParameters); + return call(request, new TypeReference<>() {}); + } + }; + + private final SingleAccount account = + new SingleAccount() { + @Override + public Promise balances(AccountAddress address) { + return call( + request(ACCOUNT_BALANCES, address.toString(networkId())), new TypeReference<>() {}); + } + + @Override + public Promise history( + AccountAddress address, int size, OptionalLong nextOffset) { + var request = request(ACCOUNT_HISTORY, address.toString(networkId()), size); + nextOffset.ifPresent(request::addParameters); + + return call(request, new TypeReference<>() {}); + } + + @Override + public Promise> stakes(AccountAddress address) { + return call( + request(ACCOUNT_STAKES, address.toString(networkId())), new TypeReference<>() {}); + } + + @Override + public Promise> unstakes(AccountAddress address) { + return call( + request(ACCOUNT_UNSTAKES, address.toString(networkId())), new TypeReference<>() {}); + } + }; + + private final Validator validator = + new Validator() { + @Override + public Promise list(int size, Optional cursor) { + var request = request(VALIDATORS_LIST, size); + cursor.ifPresent(cursorValue -> request.addParameters(cursorValue.value())); + + return call(request, new TypeReference<>() {}); + } + + @Override + public Promise lookup(ValidatorAddress validatorAddress) { + return call( + request(VALIDATORS_LOOKUP, validatorAddress.toString(networkId())), + new TypeReference<>() {}); + } + }; + + private final Local local = + new Local() { + @Override + public Promise accountInfo() { + return call(request(ACCOUNT_INFO), new TypeReference<>() {}); + } + + @Override + public Promise submitTxSingleStep(TransactionRequest request) { + return call( + request(ACCOUNT_SUBMIT_SINGLE_STEP, request.getActions(), request.getMessage()), + new TypeReference<>() {}); + } + + @Override + public Promise validatorInfo() { + return call(request(VALIDATION_NODE_INFO), new TypeReference<>() {}); + } + + @Override + public Promise currentEpoch() { + return call(request(VALIDATION_CURRENT_EPOCH), new TypeReference<>() {}); + } + }; + + private final Api api = + new Api() { + @Override + public Promise configuration() { + return call(request(API_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Promise data() { + return call(request(API_DATA), new TypeReference<>() {}); + } + }; + + private final Consensus consensus = + new Consensus() { + @Override + public Promise configuration() { + return call(request(BFT_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Promise data() { + return call(request(BFT_DATA), new TypeReference<>() {}); + } + }; + + private final Mempool mempool = + new Mempool() { + @Override + public Promise configuration() { + return call(request(MEMPOOL_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Promise data() { + return call(request(MEMPOOL_DATA), new TypeReference<>() {}); + } + }; + + private final RadixEngine radixEngine = + new RadixEngine() { + @Override + public Promise> configuration() { + return call(request(RADIX_ENGINE_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Promise data() { + return call(request(RADIX_ENGINE_DATA), new TypeReference<>() {}); + } + }; + + private final Sync sync = + new Sync() { + @Override + public Promise configuration() { + return call(request(SYNC_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Promise data() { + return call(request(SYNC_DATA), new TypeReference<>() {}); + } + }; + + private final Ledger ledger = + new Ledger() { + @Override + public Promise latest() { + return call(request(LEDGER_PROOF), new TypeReference<>() {}); + } + + @Override + public Promise epoch() { + return call(request(LEDGER_EPOCH_PROOF), new TypeReference<>() {}); + } + + @Override + public Promise checkpoints() { + return call(request(LEDGER_CHECKPOINTS), new TypeReference<>() {}); + } + }; + + @Override + public Network network() { + return network; + } + + @Override + public Transaction transaction() { + return transaction; + } + + @Override + public Token token() { + return token; + } + + @Override + public Local local() { + return local; + } + + @Override + public SingleAccount account() { + return account; + } + + @Override + public Validator validator() { + return validator; + } + + @Override + public Api api() { + return api; + } + + @Override + public Consensus consensus() { + return consensus; + } + + @Override + public Mempool mempool() { + return mempool; + } + + @Override + public RadixEngine radixEngine() { + return radixEngine; + } + + @Override + public Sync sync() { + return sync; + } + + @Override + public Ledger ledger() { + return ledger; + } + + @Override + public AsyncRadixApi withTrace() { + enableTrace(); + return this; + } + + @Override + public Addressing addressing() { + return networkAddressing(); + } + + @Override + public AsyncRadixApi withTimeout(Duration timeout) { + setTimeout(timeout); + return this; + } + + private AsyncRadixApi( + String baseUrl, + int primaryPort, + int secondaryPort, + HttpClient client, + Optional authentication) { + super(baseUrl, primaryPort, secondaryPort, client, authentication); + } + + static Promise connect( + String url, int primaryPort, int secondaryPort, Optional authentication) { + return buildHttpClient() + .fold( + Promise::failure, + client -> connect(url, primaryPort, secondaryPort, client, authentication)); + } + + static Promise connect( + String url, + int primaryPort, + int secondaryPort, + HttpClient client, + Optional authentication) { + return ofNullable(url) + .map( + baseUrl -> + Result.ok( + new AsyncRadixApi(baseUrl, primaryPort, secondaryPort, client, authentication))) + .orElseGet(MISSING_BASE_URL::result) + .flatMap( + asyncRadixApi -> + asyncRadixApi + .network() + .id() + .join() + .onSuccess( + networkId -> asyncRadixApi.configureSerialization(networkId.getNetworkId())) + .map(__ -> asyncRadixApi)) + .fold(Promise::failure, Promise::ok); + } + + private Promise call( + JsonRpcRequest request, TypeReference> typeReference) { + return serialize(request) + .onSuccess(this::trace) + .map(value -> buildRequest(request, value)) + .map(httpRequest -> client().sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())) + .map( + future -> + Promise.promise( + promise -> + future.thenAccept(body -> bodyHandler(body, promise, typeReference)))) + .fold(Promise::failure, promise -> promise); + } + + private void bodyHandler( + HttpResponse body, Promise promise, TypeReference> reference) { + promise.resolve( + deserialize(trace(body.body()), reference) + .flatMap( + response -> + response.rawError() == null + ? Result.ok(response.rawResult()) + : Result.fail(response.rawError().toFailure()))); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/RadixApi.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/RadixApi.java index f4c1ed03ba..9ce0643b71 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/RadixApi.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/async/RadixApi.java @@ -108,24 +108,32 @@ import com.radixdlt.identifiers.AID; import com.radixdlt.networks.Addressing; import com.radixdlt.utils.functional.Promise; - import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.OptionalLong; /** + * + * *

Asynchronous Radix JSON RPC client.

+ * + *

The Radix Web API consists of several endpoints which are assigned to two large groups. Each + * group served by dedicated embedded HTTP server hence full configuration of the client requires + * base URL and two ports. + * + *

Each endpoint can be individually enabled or disabled, so even if client is successfully + * connected, it does not mean that all API's are available. This should be kept in mind while using + * client with particular node. + * *

- * The Radix Web API consists of several endpoints which are assigned to two large groups. Each group served by - * dedicated embedded HTTP server hence full configuration of the client requires base URL and two ports. - *

- * Each endpoint can be individually enabled or disabled, so even if client is successfully connected, it does not - * mean that all API's are available. This should be kept in mind while using client with particular node. - *

+ * *

Client API structure

+ * * API is split into following groups: + * *

+ * * * * @@ -146,395 +154,322 @@ *
apiGroups
*/ public interface RadixApi { - int DEFAULT_PRIMARY_PORT = 8080; - int DEFAULT_SECONDARY_PORT = 3333; - - /** - * Create client and connect to specified node. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * - * @return {@link Promise} which will be resolved with built client or with error info. - */ - static Promise connect(String baseUrl) { - return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT); - } - - /** - * Create client and connect to specified node. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param authentication Login/password for basic authentication - * - * @return {@link Promise} which will be resolved with built client or with error info. - */ - static Promise connect(String baseUrl, BasicAuth authentication) { - return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT, authentication); - } - - /** - * Create client and connect to specified node at specified primary and secondary ports. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param primaryPort primary API port - * @param secondaryPort secondary API port - * - * @return {@link Promise} which will be resolved with built client or with error info. - */ - static Promise connect(String baseUrl, int primaryPort, int secondaryPort) { - return AsyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.empty()); - } - - /** - * Create client and connect to specified node at specified primary and secondary ports. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param primaryPort primary API port - * @param secondaryPort secondary API port - * @param authentication Login/password for basic authentication - * - * @return {@link Promise} which will be resolved with built client or with error info. - */ - static Promise connect(String baseUrl, int primaryPort, int secondaryPort, BasicAuth authentication) { - return AsyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.of(authentication)); - } - - /** - * Enable tracing in client. - */ - RadixApi withTrace(); - - /** - * Get {@link Addressing} instance corresponding to connected network - */ - Addressing addressing(); - - /** - * Configure timeout for asynchronous operations. - * - * @param timeout - operation timeout - */ - AsyncRadixApi withTimeout(Duration timeout); - - /** - * Network API's - */ - interface Network { - /** - * Get network ID. - */ - Promise id(); - - /** - * Get current network throughput in transactions per second. - */ - Promise throughput(); - - /** - * Get current network demand in transactions per second. - */ - Promise demand(); - - /** - * Get current network configuration. - */ - Promise configuration(); - - /** - * Get network metrics. - */ - Promise data(); - - /** - * Get network peers. - */ - Promise> peers(); - - /** - * Get current address book. - */ - Promise> addressBook(); - } - - Network network(); - - /** - * Transaction API's. - *

- * Radix API uses three step transaction submission: - *

    - *
  1. Build - transaction blob is assembled from the high level action description
  2. - *
  3. Finalize - transaction is prepared, validated, transaction ID is calculated and returned
  4. - *
  5. Submit - transaction is actually submitted to mempool
  6. - *
- * This process is designed for the case of very unreliable communication, to prevent double submission and - * other potential issues. If this is less of an issue in particular use case, it is possible to omit last - * step and submit transaction during finalization step. To achieve this, set {@code immediateSubmit} flag - * in {@link #finalize(FinalizedTransaction, boolean)} to {@code true}. - */ - interface Transaction { - /** - * Build transaction for a given transaction request. - * - * @param request transaction request - */ - Promise build(TransactionRequest request); - - /** - * Finalize transaction. - * - * @param request transaction request (can be built from {@link BuiltTransaction} by invoking {@link BuiltTransaction#toFinalized(ECKeyPair)} - * method) - * @param immediateSubmit if set to {@code true} then transaction will be immediately submitted to mempool - */ - Promise finalize(FinalizedTransaction request, boolean immediateSubmit); - - /** - * Submit transaction. - * - * @param request transaction request - */ - Promise submit(TxBlobDTO request); - - /** - * Lookup transaction. - * - * @param txId the ID of the transaction to look up - */ - Promise lookup(AID txId); - - /** - * Get transaction status. - * - * @param txId the ID of the transaction to get status for - */ - Promise status(AID txId); - - /** - * Get paginated list of indexed transactions. - * - * @param limit number of transactions to return - * @param offset starting offset - */ - Promise list(long limit, OptionalLong offset); - } - - Transaction transaction(); - - /** - * Token-related API's - */ - interface Token { - /** - * Get description of the native token. - */ - Promise describeNative(); - - /** - * Get description of the token with a given RRI. - */ - Promise describe(String rri); - } - - Token token(); - - /** - * API's which deal with information local to node to which client is connected. - *

- * WARNING: These API's may expose or use security-sensitive information. Use with care. - */ - interface Local { - /** - * Get local node account information. - */ - Promise accountInfo(); - - /** - * Submit transaction is single step, using local node private key to sign the transaction. - * - * @param request high level action description - */ - Promise submitTxSingleStep(TransactionRequest request); - - /** - * Get information about local node as a validator. - */ - Promise validatorInfo(); - - /** - * Get information about current epoch validator set. - */ - Promise currentEpoch(); - } - - Local local(); - - /** - * Single account address API's - */ - interface SingleAccount { - /** - * Get account balances. - * - * @param address account address for which information is requested - */ - Promise balances(AccountAddress address); - - /** - * Get transaction history. - * - * @param address account address for which information is requested - * @param size batch size - * @param offset offset to start retrieval at - */ - Promise history(AccountAddress address, int size, OptionalLong offset); - - /** - * Get stakes made from given account. - * - * @param address account address for which information is requested - */ - Promise> stakes(AccountAddress address); - - /** - * Get pending (not yet transferred back) unstakes. - * - * @param address account address for which information is requested - */ - Promise> unstakes(AccountAddress address); - } - - SingleAccount account(); - - /** - * General validator information API's - */ - interface Validator { - /** - * Get paginated list of all validators known to the network. - *

- * To get full list, pass empty cursor for first request and then just pass cursor received in the response - * back to API until you get empty cursor again. - * - * @param size batch size - * @param cursor pagination cursor - */ - Promise list(int size, Optional cursor); - - /** - * Lookup validator by address. - * - * @param validatorAddress validator address - */ - Promise lookup(ValidatorAddress validatorAddress); - } - - Validator validator(); - - /** - * Node API configuration and metrics. - */ - interface Api { - /** - * Get API configuration. - */ - Promise configuration(); - - /** - * Get API metrics. - */ - Promise data(); - } - - Api api(); - - /** - * Consensus configuration and metrics. - */ - interface Consensus { - /** - * Get consensus configuration. - */ - Promise configuration(); - - /** - * Get consensus metrics. - */ - Promise data(); - } - - Consensus consensus(); - - /** - * Mempool configuration and metrics. - */ - interface Mempool { - /** - * Get mempool configuration. - */ - Promise configuration(); - - /** - * Get mempool metrics. - */ - Promise data(); - } - - Mempool mempool(); - - /** - * RadixEngine configuration and metrics. - */ - interface RadixEngine { - /** - * Get Radix Engine configuration. - */ - Promise> configuration(); - - /** - * Get Radix Engine metrics. - */ - Promise data(); - } - - RadixEngine radixEngine(); - - /** - * Inter-node synchronization configuration and metrics. - */ - interface Sync { - /** - * Get synchronization configuration. - */ - Promise configuration(); - - /** - * Get synchronization metrics. - */ - Promise data(); - } - - Sync sync(); - - /** - * Ledger API's. - */ - interface Ledger { - /** - * Get latest proof. - */ - Promise latest(); - - /** - * Get latest epoch proof. - */ - Promise epoch(); - - /** - * Get checkpoint configuration. - */ - Promise checkpoints(); - } - - Ledger ledger(); + int DEFAULT_PRIMARY_PORT = 8080; + int DEFAULT_SECONDARY_PORT = 3333; + + /** + * Create client and connect to specified node. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @return {@link Promise} which will be resolved with built client or with error info. + */ + static Promise connect(String baseUrl) { + return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT); + } + + /** + * Create client and connect to specified node. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param authentication Login/password for basic authentication + * @return {@link Promise} which will be resolved with built client or with error info. + */ + static Promise connect(String baseUrl, BasicAuth authentication) { + return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT, authentication); + } + + /** + * Create client and connect to specified node at specified primary and secondary ports. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param primaryPort primary API port + * @param secondaryPort secondary API port + * @return {@link Promise} which will be resolved with built client or with error info. + */ + static Promise connect(String baseUrl, int primaryPort, int secondaryPort) { + return AsyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.empty()); + } + + /** + * Create client and connect to specified node at specified primary and secondary ports. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param primaryPort primary API port + * @param secondaryPort secondary API port + * @param authentication Login/password for basic authentication + * @return {@link Promise} which will be resolved with built client or with error info. + */ + static Promise connect( + String baseUrl, int primaryPort, int secondaryPort, BasicAuth authentication) { + return AsyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.of(authentication)); + } + + /** Enable tracing in client. */ + RadixApi withTrace(); + + /** Get {@link Addressing} instance corresponding to connected network */ + Addressing addressing(); + + /** + * Configure timeout for asynchronous operations. + * + * @param timeout - operation timeout + */ + AsyncRadixApi withTimeout(Duration timeout); + + /** Network API's */ + interface Network { + /** Get network ID. */ + Promise id(); + + /** Get current network throughput in transactions per second. */ + Promise throughput(); + + /** Get current network demand in transactions per second. */ + Promise demand(); + + /** Get current network configuration. */ + Promise configuration(); + + /** Get network metrics. */ + Promise data(); + + /** Get network peers. */ + Promise> peers(); + + /** Get current address book. */ + Promise> addressBook(); + } + + Network network(); + + /** + * Transaction API's. + * + *

Radix API uses three step transaction submission: + * + *

    + *
  1. Build - transaction blob is assembled from the high level action description + *
  2. Finalize - transaction is prepared, validated, transaction ID is calculated and returned + *
  3. Submit - transaction is actually submitted to mempool + *
+ * + * This process is designed for the case of very unreliable communication, to prevent double + * submission and other potential issues. If this is less of an issue in particular use case, it + * is possible to omit last step and submit transaction during finalization step. To achieve this, + * set {@code immediateSubmit} flag in {@link #finalize(FinalizedTransaction, boolean)} to {@code + * true}. + */ + interface Transaction { + /** + * Build transaction for a given transaction request. + * + * @param request transaction request + */ + Promise build(TransactionRequest request); + + /** + * Finalize transaction. + * + * @param request transaction request (can be built from {@link BuiltTransaction} by invoking + * {@link BuiltTransaction#toFinalized(ECKeyPair)} method) + * @param immediateSubmit if set to {@code true} then transaction will be immediately submitted + * to mempool + */ + Promise finalize(FinalizedTransaction request, boolean immediateSubmit); + + /** + * Submit transaction. + * + * @param request transaction request + */ + Promise submit(TxBlobDTO request); + + /** + * Lookup transaction. + * + * @param txId the ID of the transaction to look up + */ + Promise lookup(AID txId); + + /** + * Get transaction status. + * + * @param txId the ID of the transaction to get status for + */ + Promise status(AID txId); + + /** + * Get paginated list of indexed transactions. + * + * @param limit number of transactions to return + * @param offset starting offset + */ + Promise list(long limit, OptionalLong offset); + } + + Transaction transaction(); + + /** Token-related API's */ + interface Token { + /** Get description of the native token. */ + Promise describeNative(); + + /** Get description of the token with a given RRI. */ + Promise describe(String rri); + } + + Token token(); + + /** + * API's which deal with information local to node to which client is connected. + * + *

WARNING: These API's may expose or use security-sensitive information. Use with care. + */ + interface Local { + /** Get local node account information. */ + Promise accountInfo(); + + /** + * Submit transaction is single step, using local node private key to sign the transaction. + * + * @param request high level action description + */ + Promise submitTxSingleStep(TransactionRequest request); + + /** Get information about local node as a validator. */ + Promise validatorInfo(); + + /** Get information about current epoch validator set. */ + Promise currentEpoch(); + } + + Local local(); + + /** Single account address API's */ + interface SingleAccount { + /** + * Get account balances. + * + * @param address account address for which information is requested + */ + Promise balances(AccountAddress address); + + /** + * Get transaction history. + * + * @param address account address for which information is requested + * @param size batch size + * @param offset offset to start retrieval at + */ + Promise history(AccountAddress address, int size, OptionalLong offset); + + /** + * Get stakes made from given account. + * + * @param address account address for which information is requested + */ + Promise> stakes(AccountAddress address); + + /** + * Get pending (not yet transferred back) unstakes. + * + * @param address account address for which information is requested + */ + Promise> unstakes(AccountAddress address); + } + + SingleAccount account(); + + /** General validator information API's */ + interface Validator { + /** + * Get paginated list of all validators known to the network. + * + *

To get full list, pass empty cursor for first request and then just pass cursor received + * in the response back to API until you get empty cursor again. + * + * @param size batch size + * @param cursor pagination cursor + */ + Promise list(int size, Optional cursor); + + /** + * Lookup validator by address. + * + * @param validatorAddress validator address + */ + Promise lookup(ValidatorAddress validatorAddress); + } + + Validator validator(); + + /** Node API configuration and metrics. */ + interface Api { + /** Get API configuration. */ + Promise configuration(); + + /** Get API metrics. */ + Promise data(); + } + + Api api(); + + /** Consensus configuration and metrics. */ + interface Consensus { + /** Get consensus configuration. */ + Promise configuration(); + + /** Get consensus metrics. */ + Promise data(); + } + + Consensus consensus(); + + /** Mempool configuration and metrics. */ + interface Mempool { + /** Get mempool configuration. */ + Promise configuration(); + + /** Get mempool metrics. */ + Promise data(); + } + + Mempool mempool(); + + /** RadixEngine configuration and metrics. */ + interface RadixEngine { + /** Get Radix Engine configuration. */ + Promise> configuration(); + + /** Get Radix Engine metrics. */ + Promise data(); + } + + RadixEngine radixEngine(); + + /** Inter-node synchronization configuration and metrics. */ + interface Sync { + /** Get synchronization configuration. */ + Promise configuration(); + + /** Get synchronization metrics. */ + Promise data(); + } + + Sync sync(); + + /** Ledger API's. */ + interface Ledger { + /** Get latest proof. */ + Promise latest(); + + /** Get latest epoch proof. */ + Promise epoch(); + + /** Get checkpoint configuration. */ + Promise checkpoints(); + } + + Ledger ledger(); } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/BasicAuth.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/BasicAuth.java index 15eb31d451..6c0b9ef2a2 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/BasicAuth.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/BasicAuth.java @@ -69,39 +69,39 @@ import java.util.Objects; public final class BasicAuth { - private final String login; - private final String password; + private final String login; + private final String password; - private BasicAuth(String login, String password) { - this.login = login; - this.password = password; - } + private BasicAuth(String login, String password) { + this.login = login; + this.password = password; + } - public static BasicAuth with(String login, String password) { - return new BasicAuth(login, password); - } + public static BasicAuth with(String login, String password) { + return new BasicAuth(login, password); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof BasicAuth)) { - return false; - } + if (!(o instanceof BasicAuth)) { + return false; + } - var authData = (BasicAuth) o; - return login.equals(authData.login) && password.equals(authData.password); - } + var authData = (BasicAuth) o; + return login.equals(authData.login) && password.equals(authData.password); + } - @Override - public int hashCode() { - return Objects.hash(login, password); - } + @Override + public int hashCode() { + return Objects.hash(login, password); + } - public String asHeader() { - var auth = (login + ':' + password).getBytes(StandardCharsets.ISO_8859_1); - return "Basic " + new String(Base64.getEncoder().encode(auth)); - } + public String asHeader() { + var auth = (login + ':' + password).getBytes(StandardCharsets.ISO_8859_1); + return "Basic " + new String(Base64.getEncoder().encode(auth)); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/EndPoint.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/EndPoint.java index e17f037206..edef95caf7 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/EndPoint.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/EndPoint.java @@ -1,34 +1,97 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.client.lib.api.rpc; import static com.radixdlt.client.lib.api.rpc.PortSelector.PRIMARY; import static com.radixdlt.client.lib.api.rpc.PortSelector.SECONDARY; public enum EndPoint { - ACCOUNTS("/account", PRIMARY), - TOKENS("/token", PRIMARY), - CONSTRUCTION("/construction", PRIMARY), - TRANSACTIONS("/transaction", PRIMARY), - NETWORK("/network", PRIMARY), - - TRANSACTIONS_NODE("/transactions", SECONDARY), - SYSTEM_NODE("/system", SECONDARY), - ACCOUNT_NODE("/account", SECONDARY), - VALIDATION_NODE("/validator", SECONDARY); - - private final String path; - private final PortSelector portSelector; - - EndPoint(String path, PortSelector portSelector) { - this.path = path; - this.portSelector = portSelector; - } - - public String path() { - return path; - } - - public PortSelector portSelector() { - return portSelector; - } -} + ACCOUNTS("/account", PRIMARY), + TOKENS("/token", PRIMARY), + CONSTRUCTION("/construction", PRIMARY), + TRANSACTIONS("/transaction", PRIMARY), + NETWORK("/network", PRIMARY), + + TRANSACTIONS_NODE("/transactions", SECONDARY), + SYSTEM_NODE("/system", SECONDARY), + ACCOUNT_NODE("/account", SECONDARY), + VALIDATION_NODE("/validator", SECONDARY); + + private final String path; + private final PortSelector portSelector; + EndPoint(String path, PortSelector portSelector) { + this.path = path; + this.portSelector = portSelector; + } + + public String path() { + return path; + } + + public PortSelector portSelector() { + return portSelector; + } +} diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/ErrorInfo.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/ErrorInfo.java index 5321edbd05..29fc32a8b6 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/ErrorInfo.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/ErrorInfo.java @@ -67,72 +67,67 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.functional.Failure; - import java.util.Objects; import java.util.Optional; public class ErrorInfo { - private final int code; - private final String message; - private final Object data; + private final int code; + private final String message; + private final Object data; - private ErrorInfo(int code, String message, Object data) { - this.code = code; - this.message = message; - this.data = data; - } + private ErrorInfo(int code, String message, Object data) { + this.code = code; + this.message = message; + this.data = data; + } - @JsonCreator - public static ErrorInfo create( - @JsonProperty("code") int code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data - ) { - return new ErrorInfo(code, message, data); - } + @JsonCreator + public static ErrorInfo create( + @JsonProperty("code") int code, + @JsonProperty("message") String message, + @JsonProperty("data") Object data) { + return new ErrorInfo(code, message, data); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ErrorInfo)) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ErrorInfo)) { + return false; + } - var errorInfo = (ErrorInfo) o; - return code == errorInfo.code && Objects.equals(message, errorInfo.message); - } + var errorInfo = (ErrorInfo) o; + return code == errorInfo.code && Objects.equals(message, errorInfo.message); + } - @Override - public int hashCode() { - return Objects.hash(code, message); - } + @Override + public int hashCode() { + return Objects.hash(code, message); + } - @Override - public String toString() { - return "{" + code + ", '" + message + "'}"; - } + @Override + public String toString() { + return "{" + code + ", '" + message + "'}"; + } - public Failure toFailure() { - var text = (message == null && data == null) - ? "" - : message == null - ? data.toString() - : message; + public Failure toFailure() { + var text = + (message == null && data == null) ? "" : message == null ? data.toString() : message; - return Failure.failure(code, text); - } + return Failure.failure(code, text); + } - public int getCode() { - return code; - } + public int getCode() { + return code; + } - public Optional getMessage() { - return Optional.ofNullable(message); - } + public Optional getMessage() { + return Optional.ofNullable(message); + } - public Optional getData() { - return Optional.ofNullable(data); - } + public Optional getData() { + return Optional.ofNullable(data); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcRequest.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcRequest.java index 2f18f6befc..47c018f3e7 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcRequest.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcRequest.java @@ -65,7 +65,6 @@ package com.radixdlt.client.lib.api.rpc; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -73,66 +72,65 @@ import java.util.stream.Stream; public class JsonRpcRequest { - private static final String VERSION = "2.0"; - - private final String version; - private final String id; - private final RpcMethod method; - private final List parameters = new ArrayList<>(); - - private JsonRpcRequest(String version, String id, RpcMethod method, List parameters) { - this.version = version; - this.id = id; - this.method = method; - this.parameters.addAll(parameters); - } - - public static JsonRpcRequest create(RpcMethod method, Long id, Object... parameters) { - var list = Stream.of(parameters) - .filter(JsonRpcRequest::isNotEmpty) - .collect(Collectors.toList()); - - return new JsonRpcRequest(VERSION, id.toString(), method, list); - } - - private static boolean isNotEmpty(Object obj) { - if (obj == null) { - return false; - } - - if (obj instanceof Optional) { - return ((Optional) obj).isPresent(); - } - - return true; - } - - @JsonProperty("jsonrpc") - public String getVersion() { - return version; - } - - @JsonProperty("id") - public String getId() { - return id; - } - - @JsonProperty("params") - public List getParameters() { - return parameters; - } - - @JsonProperty("method") - public String getMethod() { - return method.method(); - } - - public JsonRpcRequest addParameters(Object... params) { - parameters.addAll(List.of(params)); - return this; - } - - public RpcMethod rpcDetails() { - return method; - } + private static final String VERSION = "2.0"; + + private final String version; + private final String id; + private final RpcMethod method; + private final List parameters = new ArrayList<>(); + + private JsonRpcRequest(String version, String id, RpcMethod method, List parameters) { + this.version = version; + this.id = id; + this.method = method; + this.parameters.addAll(parameters); + } + + public static JsonRpcRequest create(RpcMethod method, Long id, Object... parameters) { + var list = + Stream.of(parameters).filter(JsonRpcRequest::isNotEmpty).collect(Collectors.toList()); + + return new JsonRpcRequest(VERSION, id.toString(), method, list); + } + + private static boolean isNotEmpty(Object obj) { + if (obj == null) { + return false; + } + + if (obj instanceof Optional) { + return ((Optional) obj).isPresent(); + } + + return true; + } + + @JsonProperty("jsonrpc") + public String getVersion() { + return version; + } + + @JsonProperty("id") + public String getId() { + return id; + } + + @JsonProperty("params") + public List getParameters() { + return parameters; + } + + @JsonProperty("method") + public String getMethod() { + return method.method(); + } + + public JsonRpcRequest addParameters(Object... params) { + parameters.addAll(List.of(params)); + return this; + } + + public RpcMethod rpcDetails() { + return method; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcResponse.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcResponse.java index 44f1805e97..dccb6601d7 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcResponse.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/JsonRpcResponse.java @@ -65,80 +65,81 @@ package com.radixdlt.client.lib.api.rpc; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; import java.util.Optional; public class JsonRpcResponse { - @JsonProperty(value = "jsonrpc", required = true) - private final String version; - @JsonProperty(value = "id", required = true) - private final String id; - @JsonProperty("result") - private final T result; - @JsonProperty("error") - private final ErrorInfo error; - - public JsonRpcResponse( - @JsonProperty("jsonrpc") String version, - @JsonProperty("id") String id, - @JsonProperty("result") T result, - @JsonProperty("error") ErrorInfo error - ) { - this.version = version; - this.id = id; - this.result = result; - this.error = error; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof JsonRpcResponse)) { - return false; - } - - var that = (JsonRpcResponse) o; - return version.equals(that.version) - && id.equals(that.id) - && Objects.equals(result, that.result) - && Objects.equals(error, that.error); - } - - @Override - public int hashCode() { - return Objects.hash(version, id, result, error); - } - - @Override - public String toString() { - return "JsonRpcResponse(" + version + ", " + id + ", " + result + ", " + error + ')'; - } - - public Optional error() { - return Optional.ofNullable(error); - } - - public Optional result() { - return Optional.ofNullable(result); - } - - public T rawResult() { - return result; - } - - public ErrorInfo rawError() { - return error; - } - - public String getVersion() { - return version; - } - - public String getId() { - return id; - } + @JsonProperty(value = "jsonrpc", required = true) + private final String version; + + @JsonProperty(value = "id", required = true) + private final String id; + + @JsonProperty("result") + private final T result; + + @JsonProperty("error") + private final ErrorInfo error; + + public JsonRpcResponse( + @JsonProperty("jsonrpc") String version, + @JsonProperty("id") String id, + @JsonProperty("result") T result, + @JsonProperty("error") ErrorInfo error) { + this.version = version; + this.id = id; + this.result = result; + this.error = error; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof JsonRpcResponse)) { + return false; + } + + var that = (JsonRpcResponse) o; + return version.equals(that.version) + && id.equals(that.id) + && Objects.equals(result, that.result) + && Objects.equals(error, that.error); + } + + @Override + public int hashCode() { + return Objects.hash(version, id, result, error); + } + + @Override + public String toString() { + return "JsonRpcResponse(" + version + ", " + id + ", " + result + ", " + error + ')'; + } + + public Optional error() { + return Optional.ofNullable(error); + } + + public Optional result() { + return Optional.ofNullable(result); + } + + public T rawResult() { + return result; + } + + public ErrorInfo rawError() { + return error; + } + + public String getVersion() { + return version; + } + + public String getId() { + return id; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/PortSelector.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/PortSelector.java index d769a786e0..ecf964a202 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/PortSelector.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/PortSelector.java @@ -65,6 +65,6 @@ package com.radixdlt.client.lib.api.rpc; public enum PortSelector { - PRIMARY, - SECONDARY; + PRIMARY, + SECONDARY; } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RadixApiBase.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RadixApiBase.java index 17f52b0aee..fce5bb8e39 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RadixApiBase.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RadixApiBase.java @@ -64,8 +64,12 @@ package com.radixdlt.client.lib.api.rpc; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import static com.radixdlt.errors.ClientErrors.SSL_ALGORITHM_ERROR; +import static com.radixdlt.errors.ClientErrors.SSL_GENERAL_ERROR; +import static com.radixdlt.errors.ClientErrors.SSL_KEY_ERROR; +import static com.radixdlt.errors.ClientErrors.UNABLE_TO_DESERIALIZE; +import static com.radixdlt.errors.ClientErrors.UNABLE_TO_SERIALIZE; +import static com.radixdlt.networks.Network.LOCALNET; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.type.TypeReference; @@ -86,7 +90,6 @@ import com.radixdlt.networks.Addressing; import com.radixdlt.utils.functional.Failure; import com.radixdlt.utils.functional.Result; - import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -98,183 +101,174 @@ import java.time.Duration; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; - import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; - -import static com.radixdlt.errors.ClientErrors.SSL_ALGORITHM_ERROR; -import static com.radixdlt.errors.ClientErrors.SSL_GENERAL_ERROR; -import static com.radixdlt.errors.ClientErrors.SSL_KEY_ERROR; -import static com.radixdlt.errors.ClientErrors.UNABLE_TO_DESERIALIZE; -import static com.radixdlt.errors.ClientErrors.UNABLE_TO_SERIALIZE; -import static com.radixdlt.networks.Network.LOCALNET; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public abstract class RadixApiBase { - private static final Logger log = LogManager.getLogger(); - - private static final String AUTH_HEADER = "Authorization"; - private static final String CONTENT_TYPE = "Content-Type"; - private static final String APPLICATION_JSON = "application/json"; - private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(30); - private static final ObjectMapper DEFAULT_OBJECT_MAPPER = createDefaultMapper(); - - private final AtomicLong idCounter = new AtomicLong(); - private final String baseUrl; - private final int primaryPort; - private final int secondaryPort; - private final HttpClient client; - private final Optional authHeader; - - private Duration timeout = DEFAULT_TIMEOUT; - private boolean doTrace = false; - private ObjectMapper objectMapper; - private int networkId = LOCALNET.getId(); - private Addressing networkAddressing; - - protected RadixApiBase( - String baseUrl, - int primaryPort, - int secondaryPort, - HttpClient client, - Optional authentication - ) { - this.baseUrl = sanitize(baseUrl); - this.primaryPort = primaryPort; - this.secondaryPort = secondaryPort; - this.client = client; - this.authHeader = authentication.map(BasicAuth::asHeader); - } - - private static String sanitize(String baseUrl) { - return baseUrl.endsWith("/") - ? baseUrl.substring(0, baseUrl.length() - 1) - : baseUrl; - } - - protected void enableTrace() { - doTrace = true; - } - - protected void setTimeout(Duration timeout) { - this.timeout = timeout; - } - - protected JsonRpcRequest request(RpcMethod rpcMethod, Object... parameters) { - return JsonRpcRequest.create(rpcMethod, idCounter.incrementAndGet(), parameters); - } - - protected HttpRequest buildRequest(JsonRpcRequest request, String value) { - var requestBuilder = HttpRequest.newBuilder() - .uri(buildUrl(request.rpcDetails())) - .timeout(timeout) - .header(CONTENT_TYPE, APPLICATION_JSON); - - authHeader.ifPresent(header -> requestBuilder.header(AUTH_HEADER, header)); - - return requestBuilder - .POST(BodyPublishers.ofString(value)) - .build(); - } - - protected T trace(T value) { - if (doTrace) { - log.debug(value.toString()); - } - - return value; - } - - protected int networkId() { - return networkId; - } - - protected Addressing networkAddressing() { - return networkAddressing; - } - - protected HttpClient client() { - return client; - } - - protected Result serialize(JsonRpcRequest request) { - return Result.wrap(UNABLE_TO_SERIALIZE, () -> objectMapper().writeValueAsString(request)); - } - - protected Result> deserialize(String body, TypeReference> typeReference) { - return Result.wrap(UNABLE_TO_DESERIALIZE, () -> objectMapper().readValue(body, typeReference)); - } - - protected static Result buildHttpClient() { - var props = System.getProperties(); - props.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true"); - - var trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { } - } - }; - - return Result.wrap( - RadixApiBase::decodeSslExceptions, - () -> { - var sc = SSLContext.getInstance("SSL"); - sc.init(null, trustAllCerts, new SecureRandom()); - return sc; - } - ).map(sc -> HttpClient.newBuilder() - .connectTimeout(DEFAULT_TIMEOUT) - .sslContext(sc) - .build()); - } - - protected void configureSerialization(int networkId) { - this.networkId = networkId; - this.networkAddressing = Addressing.ofNetworkId(networkId); - var module = new SimpleModule() - .addSerializer(ValidatorAddress.class, new ValidatorAddressSerializer(networkAddressing)) - .addSerializer(AccountAddress.class, new AccountAddressSerializer(networkAddressing)) - .addSerializer(NodeAddress.class, new NodeAddressSerializer(networkAddressing)) - .addSerializer(ECPublicKey.class, new ECPublicKeySerializer()) - .addDeserializer(AccountAddress.class, new AccountAddressDeserializer(networkAddressing)) - .addDeserializer(ValidatorAddress.class, new ValidatorAddressDeserializer(networkAddressing)) - .addDeserializer(NodeAddress.class, new NodeAddressDeserializer(networkAddressing)) - .addDeserializer(ECPublicKey.class, new ECPublicKeyDeserializer()); - objectMapper = createDefaultMapper().registerModule(module); - } - - private URI buildUrl(RpcMethod rpcMethod) { - var endPoint = rpcMethod.endPoint(); - var port = endPoint.portSelector() == PortSelector.PRIMARY - ? primaryPort - : secondaryPort; - - return URI.create(baseUrl + ":" + port + endPoint.path()); - } - - private static Failure decodeSslExceptions(Throwable throwable) { - if (throwable instanceof NoSuchAlgorithmException) { - return SSL_KEY_ERROR.with(throwable.getMessage()); - } - - if (throwable instanceof KeyException) { - return SSL_ALGORITHM_ERROR.with(throwable.getMessage()); - } - - return SSL_GENERAL_ERROR.with(throwable.getMessage()); - } - - private ObjectMapper objectMapper() { - return objectMapper == null ? DEFAULT_OBJECT_MAPPER : objectMapper; - } - - private static ObjectMapper createDefaultMapper() { - return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_ABSENT); - } + private static final Logger log = LogManager.getLogger(); + + private static final String AUTH_HEADER = "Authorization"; + private static final String CONTENT_TYPE = "Content-Type"; + private static final String APPLICATION_JSON = "application/json"; + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(30); + private static final ObjectMapper DEFAULT_OBJECT_MAPPER = createDefaultMapper(); + + private final AtomicLong idCounter = new AtomicLong(); + private final String baseUrl; + private final int primaryPort; + private final int secondaryPort; + private final HttpClient client; + private final Optional authHeader; + + private Duration timeout = DEFAULT_TIMEOUT; + private boolean doTrace = false; + private ObjectMapper objectMapper; + private int networkId = LOCALNET.getId(); + private Addressing networkAddressing; + + protected RadixApiBase( + String baseUrl, + int primaryPort, + int secondaryPort, + HttpClient client, + Optional authentication) { + this.baseUrl = sanitize(baseUrl); + this.primaryPort = primaryPort; + this.secondaryPort = secondaryPort; + this.client = client; + this.authHeader = authentication.map(BasicAuth::asHeader); + } + + private static String sanitize(String baseUrl) { + return baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + } + + protected void enableTrace() { + doTrace = true; + } + + protected void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + protected JsonRpcRequest request(RpcMethod rpcMethod, Object... parameters) { + return JsonRpcRequest.create(rpcMethod, idCounter.incrementAndGet(), parameters); + } + + protected HttpRequest buildRequest(JsonRpcRequest request, String value) { + var requestBuilder = + HttpRequest.newBuilder() + .uri(buildUrl(request.rpcDetails())) + .timeout(timeout) + .header(CONTENT_TYPE, APPLICATION_JSON); + + authHeader.ifPresent(header -> requestBuilder.header(AUTH_HEADER, header)); + + return requestBuilder.POST(BodyPublishers.ofString(value)).build(); + } + + protected T trace(T value) { + if (doTrace) { + log.debug(value.toString()); + } + + return value; + } + + protected int networkId() { + return networkId; + } + + protected Addressing networkAddressing() { + return networkAddressing; + } + + protected HttpClient client() { + return client; + } + + protected Result serialize(JsonRpcRequest request) { + return Result.wrap(UNABLE_TO_SERIALIZE, () -> objectMapper().writeValueAsString(request)); + } + + protected Result> deserialize( + String body, TypeReference> typeReference) { + return Result.wrap(UNABLE_TO_DESERIALIZE, () -> objectMapper().readValue(body, typeReference)); + } + + protected static Result buildHttpClient() { + var props = System.getProperties(); + props.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true"); + + var trustAllCerts = + new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + } + }; + + return Result.wrap( + RadixApiBase::decodeSslExceptions, + () -> { + var sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new SecureRandom()); + return sc; + }) + .map(sc -> HttpClient.newBuilder().connectTimeout(DEFAULT_TIMEOUT).sslContext(sc).build()); + } + + protected void configureSerialization(int networkId) { + this.networkId = networkId; + this.networkAddressing = Addressing.ofNetworkId(networkId); + var module = + new SimpleModule() + .addSerializer( + ValidatorAddress.class, new ValidatorAddressSerializer(networkAddressing)) + .addSerializer(AccountAddress.class, new AccountAddressSerializer(networkAddressing)) + .addSerializer(NodeAddress.class, new NodeAddressSerializer(networkAddressing)) + .addSerializer(ECPublicKey.class, new ECPublicKeySerializer()) + .addDeserializer( + AccountAddress.class, new AccountAddressDeserializer(networkAddressing)) + .addDeserializer( + ValidatorAddress.class, new ValidatorAddressDeserializer(networkAddressing)) + .addDeserializer(NodeAddress.class, new NodeAddressDeserializer(networkAddressing)) + .addDeserializer(ECPublicKey.class, new ECPublicKeyDeserializer()); + objectMapper = createDefaultMapper().registerModule(module); + } + + private URI buildUrl(RpcMethod rpcMethod) { + var endPoint = rpcMethod.endPoint(); + var port = endPoint.portSelector() == PortSelector.PRIMARY ? primaryPort : secondaryPort; + + return URI.create(baseUrl + ":" + port + endPoint.path()); + } + + private static Failure decodeSslExceptions(Throwable throwable) { + if (throwable instanceof NoSuchAlgorithmException) { + return SSL_KEY_ERROR.with(throwable.getMessage()); + } + + if (throwable instanceof KeyException) { + return SSL_ALGORITHM_ERROR.with(throwable.getMessage()); + } + + return SSL_GENERAL_ERROR.with(throwable.getMessage()); + } + + private ObjectMapper objectMapper() { + return objectMapper == null ? DEFAULT_OBJECT_MAPPER : objectMapper; + } + + private static ObjectMapper createDefaultMapper() { + return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_ABSENT); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RpcMethod.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RpcMethod.java index ed48f1d147..c3040e4e5b 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RpcMethod.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/rpc/RpcMethod.java @@ -1,75 +1,139 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.client.lib.api.rpc; import static com.radixdlt.client.lib.api.rpc.EndPoint.*; public enum RpcMethod { - TOKEN_NATIVE("get_native_token", TOKENS), - TOKEN_INFO("get_info", TOKENS), + TOKEN_NATIVE("get_native_token", TOKENS), + TOKEN_INFO("get_info", TOKENS), - ACCOUNT_BALANCES("get_balances", ACCOUNTS), - ACCOUNT_HISTORY("get_transaction_history", ACCOUNTS), - ACCOUNT_STAKES("get_stake_positions", ACCOUNTS), - ACCOUNT_UNSTAKES("get_unstake_positions", ACCOUNTS), + ACCOUNT_BALANCES("get_balances", ACCOUNTS), + ACCOUNT_HISTORY("get_transaction_history", ACCOUNTS), + ACCOUNT_STAKES("get_stake_positions", ACCOUNTS), + ACCOUNT_UNSTAKES("get_unstake_positions", ACCOUNTS), - TRANSACTION_LOOKUP("lookup_transaction", TRANSACTIONS), - TRANSACTION_STATUS("get_transaction_status", TRANSACTIONS), + TRANSACTION_LOOKUP("lookup_transaction", TRANSACTIONS), + TRANSACTION_STATUS("get_transaction_status", TRANSACTIONS), - NETWORK_ID("get_id", NETWORK), - NETWORK_THROUGHPUT("get_throughput", NETWORK), - NETWORK_DEMAND("get_demand", NETWORK), + NETWORK_ID("get_id", NETWORK), + NETWORK_THROUGHPUT("get_throughput", NETWORK), + NETWORK_DEMAND("get_demand", NETWORK), - VALIDATORS_LIST("get_next_epoch_set", ACCOUNTS), - VALIDATORS_LOOKUP("lookup_validator", ACCOUNTS), + VALIDATORS_LIST("get_next_epoch_set", ACCOUNTS), + VALIDATORS_LOOKUP("lookup_validator", ACCOUNTS), - CONSTRUCTION_BUILD("build_transaction", CONSTRUCTION), - CONSTRUCTION_FINALIZE("finalize_transaction", CONSTRUCTION), - CONSTRUCTION_SUBMIT("submit_transaction", CONSTRUCTION), + CONSTRUCTION_BUILD("build_transaction", CONSTRUCTION), + CONSTRUCTION_FINALIZE("finalize_transaction", CONSTRUCTION), + CONSTRUCTION_SUBMIT("submit_transaction", CONSTRUCTION), - NETWORK_CONFIG("networking.get_configuration", SYSTEM_NODE), - NETWORK_PEERS("networking.get_peers", SYSTEM_NODE), - NETWORK_DATA("networking.get_data", SYSTEM_NODE), - NETWORK_ADDRESS_BOOK("networking.get_address_book", SYSTEM_NODE), + NETWORK_CONFIG("networking.get_configuration", SYSTEM_NODE), + NETWORK_PEERS("networking.get_peers", SYSTEM_NODE), + NETWORK_DATA("networking.get_data", SYSTEM_NODE), + NETWORK_ADDRESS_BOOK("networking.get_address_book", SYSTEM_NODE), - TRANSACTION_LIST("get_transactions", TRANSACTIONS_NODE), + TRANSACTION_LIST("get_transactions", TRANSACTIONS_NODE), - API_CONFIGURATION("api.get_configuration", SYSTEM_NODE), - API_DATA("api.get_data", SYSTEM_NODE), + API_CONFIGURATION("api.get_configuration", SYSTEM_NODE), + API_DATA("api.get_data", SYSTEM_NODE), - BFT_CONFIGURATION("bft.get_configuration", SYSTEM_NODE), - BFT_DATA("bft.get_data", SYSTEM_NODE), + BFT_CONFIGURATION("bft.get_configuration", SYSTEM_NODE), + BFT_DATA("bft.get_data", SYSTEM_NODE), - MEMPOOL_CONFIGURATION("mempool.get_configuration", SYSTEM_NODE), - MEMPOOL_DATA("mempool.get_data", SYSTEM_NODE), + MEMPOOL_CONFIGURATION("mempool.get_configuration", SYSTEM_NODE), + MEMPOOL_DATA("mempool.get_data", SYSTEM_NODE), - LEDGER_PROOF("ledger.get_latest_proof", SYSTEM_NODE), - LEDGER_EPOCH_PROOF("ledger.get_latest_epoch_proof", SYSTEM_NODE), - LEDGER_CHECKPOINTS("checkpoints.get_checkpoints", SYSTEM_NODE), + LEDGER_PROOF("ledger.get_latest_proof", SYSTEM_NODE), + LEDGER_EPOCH_PROOF("ledger.get_latest_epoch_proof", SYSTEM_NODE), + LEDGER_CHECKPOINTS("checkpoints.get_checkpoints", SYSTEM_NODE), - RADIX_ENGINE_CONFIGURATION("radix_engine.get_configuration", SYSTEM_NODE), - RADIX_ENGINE_DATA("radix_engine.get_data", SYSTEM_NODE), + RADIX_ENGINE_CONFIGURATION("radix_engine.get_configuration", SYSTEM_NODE), + RADIX_ENGINE_DATA("radix_engine.get_data", SYSTEM_NODE), - SYNC_CONFIGURATION("sync.get_configuration", SYSTEM_NODE), - SYNC_DATA("sync.get_data", SYSTEM_NODE), + SYNC_CONFIGURATION("sync.get_configuration", SYSTEM_NODE), + SYNC_DATA("sync.get_data", SYSTEM_NODE), - VALIDATION_NODE_INFO("get_node_info", VALIDATION_NODE), - VALIDATION_CURRENT_EPOCH("get_current_epoch_data", VALIDATION_NODE), + VALIDATION_NODE_INFO("get_node_info", VALIDATION_NODE), + VALIDATION_CURRENT_EPOCH("get_current_epoch_data", VALIDATION_NODE), - ACCOUNT_INFO("get_info", ACCOUNT_NODE), - ACCOUNT_SUBMIT_SINGLE_STEP("submit_transaction_single_step", ACCOUNT_NODE); + ACCOUNT_INFO("get_info", ACCOUNT_NODE), + ACCOUNT_SUBMIT_SINGLE_STEP("submit_transaction_single_step", ACCOUNT_NODE); - private final String method; - private final EndPoint endPoint; + private final String method; + private final EndPoint endPoint; - RpcMethod(String method, EndPoint endPoint) { - this.method = method; - this.endPoint = endPoint; - } + RpcMethod(String method, EndPoint endPoint) { + this.method = method; + this.endPoint = endPoint; + } - public String method() { - return method; - } + public String method() { + return method; + } - public EndPoint endPoint() { - return endPoint; - } + public EndPoint endPoint() { + return endPoint; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/ImperativeRadixApi.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/ImperativeRadixApi.java index 23bca88601..4e82d7efcb 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/ImperativeRadixApi.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/ImperativeRadixApi.java @@ -105,26 +105,35 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.identifiers.AID; import com.radixdlt.utils.functional.Result; - import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.OptionalLong; /** + * + * *

Imperative version of synchronous Radix JSON RPC client.

- * This version of the API converts functional style synchronous API's into more - * traditional "throw if error"-style Java API. - *

- * The Radix Web API consists of several endpoints which are assigned to two large groups. Each group served by - * dedicated embedded HTTP server hence full configuration of the client requires base URL and two ports. - *

- * Each endpoint can be individually enabled or disabled, so even if client is successfully connected, it does not - * mean that all API's are available. This should be kept in mind while using client with particular hode. + * + * This version of the API converts functional style synchronous API's into more traditional "throw + * if error"-style Java API. + * + *

The Radix Web API consists of several endpoints which are assigned to two large groups. Each + * group served by dedicated embedded HTTP server hence full configuration of the client requires + * base URL and two ports. + * + *

Each endpoint can be individually enabled or disabled, so even if client is successfully + * connected, it does not mean that all API's are available. This should be kept in mind while using + * client with particular hode. + * *

+ * *

Client API structure

+ * * API is split into following groups: + * *

+ * * * * @@ -145,639 +154,572 @@ *
apiGroups
*/ public interface ImperativeRadixApi { - /** - * Create client and connect to specified node. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * - * @return created client - */ - - static ImperativeRadixApi connect(String baseUrl) { - return toImperative(unwrap(RadixApi.connect(baseUrl))); - } - - /** - * Create client and connect to specified node. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param authentication Login/password for basic authentication - * - * @return created client - */ - static ImperativeRadixApi connect(String baseUrl, BasicAuth authentication) { - return toImperative(unwrap(RadixApi.connect(baseUrl, authentication))); - } - - /** - * Create client and connect to specified node at specified primary and secondary ports. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param primaryPort primary API port - * @param secondaryPort secondary API port - * - * @return created client - */ - static ImperativeRadixApi connect(String baseUrl, int primaryPort, int secondaryPort) { - return toImperative(unwrap(RadixApi.connect(baseUrl, primaryPort, secondaryPort))); - } - - /** - * Create client and connect to specified node at specified primary and secondary ports. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param primaryPort primary API port - * @param secondaryPort secondary API port - * @param authentication Login/password for basic authentication - * - * @return created client - */ - static ImperativeRadixApi connect(String baseUrl, int primaryPort, int secondaryPort, BasicAuth authentication) { - return toImperative(unwrap(RadixApi.connect(baseUrl, primaryPort, secondaryPort, authentication))); - } - - /** - * Enable tracing in client. - */ - ImperativeRadixApi withTrace(); - - /** - * Configure timeout for network operations. - * - * @param timeout - operation timeout - */ - ImperativeRadixApi withTimeout(Duration timeout); - - /** - * Network API's - */ - interface Network { - /** - * Get network ID. - */ - NetworkId id(); - - /** - * Get current network throughput in transactions per second. - */ - NetworkStats throughput(); - - /** - * Get current network demand in transactions per second. - */ - NetworkStats demand(); - - /** - * Get current network configuration. - */ - NetworkConfiguration configuration(); - - /** - * Get network metrics. - */ - NetworkData data(); - - /** - * Get network peers. - */ - List peers(); - } - - Network network(); - - /** - * Transaction API's. - *

- * Radix API uses three step transaction submission: - *

    - *
  1. Build - transaction blob is assembled from the high level action description
  2. - *
  3. Finalize - transaction is prepared, validated, transaction ID is calculated and returned
  4. - *
  5. Submit - transaction is actually submitted to mempool
  6. - *
- * This process is designed for the case of very unreliable communication, to prevent double submission and - * other potential issues. If this is less of an issue in particular use case, it is possible to omit last - * step and submit transaction during finalization step. To achieve this, set {@code immediateSubmit} flag - * in {@link #finalize(FinalizedTransaction, boolean)} to {@code true}. - */ - interface Transaction { - /** - * Build transaction for a given transaction request. - * - * @param request transaction request - */ - BuiltTransaction build(TransactionRequest request); - - /** - * Finalize transaction. - * - * @param request transaction request (can be built from {@link BuiltTransaction} by invoking {@link BuiltTransaction#toFinalized(ECKeyPair)} - * method) - * @param immediateSubmit if set to {@code true} then transaction will be immediately submitted to mempool - */ - TxBlobDTO finalize(FinalizedTransaction request, boolean immediateSubmit); - - /** - * Submit transaction. - * - * @param request transaction request - */ - TxDTO submit(TxBlobDTO request); - - /** - * Lookup transaction. - * - * @param txId the ID of the transaction to look up - */ - TransactionDTO lookup(AID txId); - - /** - * Get transaction status. - * - * @param txId the ID of the transaction to get status for - */ - TransactionStatusDTO status(AID txId); - } - - Transaction transaction(); - - /** - * Token-related API's - */ - interface Token { - /** - * Get description of the native token. - */ - TokenInfo describeNative(); - - /** - * Get description of the token with a given RRI. - */ - TokenInfo describe(String rri); - } - - Token token(); - - /** - * API's which deal with information local to node to which client is connected. - *

- * WARNING: These API's may expose or use security-sensitive information. Use with care. - */ - interface Local { - /** - * Get local node account information. - */ - LocalAccount accountInfo(); - - /** - * Submit transaction is single step, using local node private key to sign the transaction. - * - * @param request high level action description - */ - TxDTO submitTxSingleStep(TransactionRequest request); - - /** - * Get information about local node as a validator. - */ - LocalValidatorInfo validatorInfo(); - - /** - * Get information about current epoch validator set. - */ - EpochData currentEpoch(); - } - - Local local(); - - /** - * Single account address API's - */ - interface SingleAccount { - /** - * Get account balances. - * - * @param address account address for which information is requested - */ - TokenBalances balances(AccountAddress address); - - /** - * Get transaction history. - * - * @param address account address for which information is requested - * @param size batch size - * @param offset offset to start retrieval at - */ - TransactionHistory history(AccountAddress address, int size, OptionalLong offset); - - /** - * Get stakes made from given account. - * - * @param address account address for which information is requested - */ - List stakes(AccountAddress address); - - /** - * Get pending (not yet transferred back) unstakes. - * - * @param address account address for which information is requested - */ - List unstakes(AccountAddress address); - } - - SingleAccount account(); - - /** - * General validator information API's - */ - interface Validator { - /** - * Get paginated list of all validators known to the network. - *

- * To get full list, pass empty cursor for first request and then just pass cursor received in the response - * back to API until you get empty cursor again. - * - * @param size batch size - * @param cursor pagination cursor - */ - ValidatorsResponse list(int size, Optional cursor); - - /** - * Lookup validator by address. - * - * @param validatorAddress validator address - */ - ValidatorDTO lookup(ValidatorAddress validatorAddress); - } - - Validator validator(); - - /** - * Node API configuration and metrics. - */ - interface Api { - /** - * Get API configuration. - */ - ApiConfiguration configuration(); - - /** - * Get API metrics. - */ - ApiData data(); - } - - Api api(); - - /** - * Consensus configuration and metrics. - */ - interface Consensus { - /** - * Get consensus configuration. - */ - ConsensusConfiguration configuration(); - - /** - * Get consensus metrics. - */ - ConsensusData data(); - } - - Consensus consensus(); - - /** - * Mempool configuration and metrics. - */ - interface Mempool { - /** - * Get mempool configuration. - */ - MempoolConfiguration configuration(); - - /** - * Get mempool metrics. - */ - MempoolData data(); - } - - Mempool mempool(); - - /** - * RadixEngine configuration and metrics. - */ - interface RadixEngine { - /** - * Get Radix Engine configuration. - */ - List configuration(); - - /** - * Get Radix Engine metrics. - */ - RadixEngineData data(); - } - - RadixEngine radixEngine(); - - /** - * Inter-node synchronization configuration and metrics. - */ - interface Sync { - /** - * Get synchronization configuration. - */ - SyncConfiguration configuration(); - - /** - * Get synchronization metrics. - */ - SyncData data(); - } - - Sync sync(); - - /** - * Ledger API's. - */ - interface Ledger { - /** - * Get latest proof. - */ - Proof latest(); - - /** - * Get latest epoch proof. - */ - Proof epoch(); - - /** - * Get checkpoint configuration. - */ - Checkpoint checkpoints(); - } - - Ledger ledger(); - - static T unwrap(Result value) { - return value.fold(failure -> { - throw new RadixApiException(failure); - }, content -> content); - } - - // CHECKSTYLE:OFF checkstyle:MethodLength - static ImperativeRadixApi toImperative(RadixApi api) { - return new ImperativeRadixApi() { - @Override - public ImperativeRadixApi withTrace() { - api.withTrace(); - return this; - } - - @Override - public ImperativeRadixApi withTimeout(Duration timeout) { - api.withTimeout(timeout); - return this; - } - - @Override - public Network network() { - return new Network() { - @Override - public NetworkId id() { - return unwrap(api.network().id()); - } - - @Override - public NetworkStats throughput() { - return unwrap(api.network().throughput()); - } - - @Override - public NetworkStats demand() { - return unwrap(api.network().demand()); - } - - @Override - public NetworkConfiguration configuration() { - return unwrap(api.network().configuration()); - } - - @Override - public NetworkData data() { - return unwrap(api.network().data()); - } - - @Override - public List peers() { - return unwrap(api.network().peers()); - } - }; - } - - @Override - public Transaction transaction() { - return new Transaction() { - @Override - public BuiltTransaction build(TransactionRequest request) { - return unwrap(api.transaction().build(request)); - } - - @Override - public TxBlobDTO finalize(FinalizedTransaction request, boolean immediateSubmit) { - return unwrap(api.transaction().finalize(request, immediateSubmit)); - } - - @Override - public TxDTO submit(TxBlobDTO request) { - return unwrap(api.transaction().submit(request)); - } - - @Override - public TransactionDTO lookup(AID txId) { - return unwrap(api.transaction().lookup(txId)); - } - - @Override - public TransactionStatusDTO status(AID txId) { - return unwrap(api.transaction().status(txId)); - } - }; - } - - @Override - public Token token() { - return new Token() { - @Override - public TokenInfo describeNative() { - return unwrap(api.token().describeNative()); - } - - @Override - public TokenInfo describe(String rri) { - return unwrap(api.token().describe(rri)); - } - }; - } - - @Override - public Local local() { - return new Local() { - @Override - public LocalAccount accountInfo() { - return unwrap(api.local().accountInfo()); - } - - @Override - public TxDTO submitTxSingleStep(TransactionRequest request) { - return unwrap(api.local().submitTxSingleStep(request)); - } - - @Override - public LocalValidatorInfo validatorInfo() { - return unwrap(api.local().validatorInfo()); - } - - @Override - public EpochData currentEpoch() { - return unwrap(api.local().currentEpoch()); - } - }; - } - - @Override - public SingleAccount account() { - return new SingleAccount() { - @Override - public TokenBalances balances(AccountAddress address) { - return unwrap(api.account().balances(address)); - } - - @Override - public TransactionHistory history(AccountAddress address, int size, OptionalLong offset) { - return unwrap(api.account().history(address, size, offset)); - } - - @Override - public List stakes(AccountAddress address) { - return unwrap(api.account().stakes(address)); - } - - @Override - public List unstakes(AccountAddress address) { - return unwrap(api.account().unstakes(address)); - } - }; - } - - @Override - public Validator validator() { - return new Validator() { - @Override - public ValidatorsResponse list(int size, Optional cursor) { - return unwrap(api.validator().list(size, cursor)); - } - - @Override - public ValidatorDTO lookup(ValidatorAddress validatorAddress) { - return unwrap(api.validator().lookup(validatorAddress)); - } - }; - } - - @Override - public Api api() { - return new Api() { - @Override - public ApiConfiguration configuration() { - return unwrap(api.api().configuration()); - } - - @Override - public ApiData data() { - return unwrap(api.api().data()); - } - }; - } - - @Override - public Consensus consensus() { - return new Consensus() { - @Override - public ConsensusConfiguration configuration() { - return unwrap(api.consensus().configuration()); - } - - @Override - public ConsensusData data() { - return unwrap(api.consensus().data()); - } - }; - } - - @Override - public Mempool mempool() { - return new Mempool() { - @Override - public MempoolConfiguration configuration() { - return unwrap(api.mempool().configuration()); - } - - @Override - public MempoolData data() { - return unwrap(api.mempool().data()); - } - }; - } - - @Override - public RadixEngine radixEngine() { - return new RadixEngine() { - @Override - public List configuration() { - return unwrap(api.radixEngine().configuration()); - } - - @Override - public RadixEngineData data() { - return unwrap(api.radixEngine().data()); - } - }; - } - - @Override - public Sync sync() { - return new Sync() { - @Override - public SyncConfiguration configuration() { - return unwrap(api.sync().configuration()); - } - - @Override - public SyncData data() { - return unwrap(api.sync().data()); - } - }; - } - - @Override - public Ledger ledger() { - return new Ledger() { - @Override - public Proof latest() { - return unwrap(api.ledger().latest()); - } - - @Override - public Proof epoch() { - return unwrap(api.ledger().epoch()); - } - - @Override - public Checkpoint checkpoints() { - return unwrap(api.ledger().checkpoints()); - } - }; - } - }; - } - // CHECKSTYLE:ON + /** + * Create client and connect to specified node. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @return created client + */ + static ImperativeRadixApi connect(String baseUrl) { + return toImperative(unwrap(RadixApi.connect(baseUrl))); + } + + /** + * Create client and connect to specified node. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param authentication Login/password for basic authentication + * @return created client + */ + static ImperativeRadixApi connect(String baseUrl, BasicAuth authentication) { + return toImperative(unwrap(RadixApi.connect(baseUrl, authentication))); + } + + /** + * Create client and connect to specified node at specified primary and secondary ports. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param primaryPort primary API port + * @param secondaryPort secondary API port + * @return created client + */ + static ImperativeRadixApi connect(String baseUrl, int primaryPort, int secondaryPort) { + return toImperative(unwrap(RadixApi.connect(baseUrl, primaryPort, secondaryPort))); + } + + /** + * Create client and connect to specified node at specified primary and secondary ports. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param primaryPort primary API port + * @param secondaryPort secondary API port + * @param authentication Login/password for basic authentication + * @return created client + */ + static ImperativeRadixApi connect( + String baseUrl, int primaryPort, int secondaryPort, BasicAuth authentication) { + return toImperative( + unwrap(RadixApi.connect(baseUrl, primaryPort, secondaryPort, authentication))); + } + + /** Enable tracing in client. */ + ImperativeRadixApi withTrace(); + + /** + * Configure timeout for network operations. + * + * @param timeout - operation timeout + */ + ImperativeRadixApi withTimeout(Duration timeout); + + /** Network API's */ + interface Network { + /** Get network ID. */ + NetworkId id(); + + /** Get current network throughput in transactions per second. */ + NetworkStats throughput(); + + /** Get current network demand in transactions per second. */ + NetworkStats demand(); + + /** Get current network configuration. */ + NetworkConfiguration configuration(); + + /** Get network metrics. */ + NetworkData data(); + + /** Get network peers. */ + List peers(); + } + + Network network(); + + /** + * Transaction API's. + * + *

Radix API uses three step transaction submission: + * + *

    + *
  1. Build - transaction blob is assembled from the high level action description + *
  2. Finalize - transaction is prepared, validated, transaction ID is calculated and returned + *
  3. Submit - transaction is actually submitted to mempool + *
+ * + * This process is designed for the case of very unreliable communication, to prevent double + * submission and other potential issues. If this is less of an issue in particular use case, it + * is possible to omit last step and submit transaction during finalization step. To achieve this, + * set {@code immediateSubmit} flag in {@link #finalize(FinalizedTransaction, boolean)} to {@code + * true}. + */ + interface Transaction { + /** + * Build transaction for a given transaction request. + * + * @param request transaction request + */ + BuiltTransaction build(TransactionRequest request); + + /** + * Finalize transaction. + * + * @param request transaction request (can be built from {@link BuiltTransaction} by invoking + * {@link BuiltTransaction#toFinalized(ECKeyPair)} method) + * @param immediateSubmit if set to {@code true} then transaction will be immediately submitted + * to mempool + */ + TxBlobDTO finalize(FinalizedTransaction request, boolean immediateSubmit); + + /** + * Submit transaction. + * + * @param request transaction request + */ + TxDTO submit(TxBlobDTO request); + + /** + * Lookup transaction. + * + * @param txId the ID of the transaction to look up + */ + TransactionDTO lookup(AID txId); + + /** + * Get transaction status. + * + * @param txId the ID of the transaction to get status for + */ + TransactionStatusDTO status(AID txId); + } + + Transaction transaction(); + + /** Token-related API's */ + interface Token { + /** Get description of the native token. */ + TokenInfo describeNative(); + + /** Get description of the token with a given RRI. */ + TokenInfo describe(String rri); + } + + Token token(); + + /** + * API's which deal with information local to node to which client is connected. + * + *

WARNING: These API's may expose or use security-sensitive information. Use with care. + */ + interface Local { + /** Get local node account information. */ + LocalAccount accountInfo(); + + /** + * Submit transaction is single step, using local node private key to sign the transaction. + * + * @param request high level action description + */ + TxDTO submitTxSingleStep(TransactionRequest request); + + /** Get information about local node as a validator. */ + LocalValidatorInfo validatorInfo(); + + /** Get information about current epoch validator set. */ + EpochData currentEpoch(); + } + + Local local(); + + /** Single account address API's */ + interface SingleAccount { + /** + * Get account balances. + * + * @param address account address for which information is requested + */ + TokenBalances balances(AccountAddress address); + + /** + * Get transaction history. + * + * @param address account address for which information is requested + * @param size batch size + * @param offset offset to start retrieval at + */ + TransactionHistory history(AccountAddress address, int size, OptionalLong offset); + + /** + * Get stakes made from given account. + * + * @param address account address for which information is requested + */ + List stakes(AccountAddress address); + + /** + * Get pending (not yet transferred back) unstakes. + * + * @param address account address for which information is requested + */ + List unstakes(AccountAddress address); + } + + SingleAccount account(); + + /** General validator information API's */ + interface Validator { + /** + * Get paginated list of all validators known to the network. + * + *

To get full list, pass empty cursor for first request and then just pass cursor received + * in the response back to API until you get empty cursor again. + * + * @param size batch size + * @param cursor pagination cursor + */ + ValidatorsResponse list(int size, Optional cursor); + + /** + * Lookup validator by address. + * + * @param validatorAddress validator address + */ + ValidatorDTO lookup(ValidatorAddress validatorAddress); + } + + Validator validator(); + + /** Node API configuration and metrics. */ + interface Api { + /** Get API configuration. */ + ApiConfiguration configuration(); + + /** Get API metrics. */ + ApiData data(); + } + + Api api(); + + /** Consensus configuration and metrics. */ + interface Consensus { + /** Get consensus configuration. */ + ConsensusConfiguration configuration(); + + /** Get consensus metrics. */ + ConsensusData data(); + } + + Consensus consensus(); + + /** Mempool configuration and metrics. */ + interface Mempool { + /** Get mempool configuration. */ + MempoolConfiguration configuration(); + + /** Get mempool metrics. */ + MempoolData data(); + } + + Mempool mempool(); + + /** RadixEngine configuration and metrics. */ + interface RadixEngine { + /** Get Radix Engine configuration. */ + List configuration(); + + /** Get Radix Engine metrics. */ + RadixEngineData data(); + } + + RadixEngine radixEngine(); + + /** Inter-node synchronization configuration and metrics. */ + interface Sync { + /** Get synchronization configuration. */ + SyncConfiguration configuration(); + + /** Get synchronization metrics. */ + SyncData data(); + } + + Sync sync(); + + /** Ledger API's. */ + interface Ledger { + /** Get latest proof. */ + Proof latest(); + + /** Get latest epoch proof. */ + Proof epoch(); + + /** Get checkpoint configuration. */ + Checkpoint checkpoints(); + } + + Ledger ledger(); + + static T unwrap(Result value) { + return value.fold( + failure -> { + throw new RadixApiException(failure); + }, + content -> content); + } + + // CHECKSTYLE:OFF checkstyle:MethodLength + static ImperativeRadixApi toImperative(RadixApi api) { + return new ImperativeRadixApi() { + @Override + public ImperativeRadixApi withTrace() { + api.withTrace(); + return this; + } + + @Override + public ImperativeRadixApi withTimeout(Duration timeout) { + api.withTimeout(timeout); + return this; + } + + @Override + public Network network() { + return new Network() { + @Override + public NetworkId id() { + return unwrap(api.network().id()); + } + + @Override + public NetworkStats throughput() { + return unwrap(api.network().throughput()); + } + + @Override + public NetworkStats demand() { + return unwrap(api.network().demand()); + } + + @Override + public NetworkConfiguration configuration() { + return unwrap(api.network().configuration()); + } + + @Override + public NetworkData data() { + return unwrap(api.network().data()); + } + + @Override + public List peers() { + return unwrap(api.network().peers()); + } + }; + } + + @Override + public Transaction transaction() { + return new Transaction() { + @Override + public BuiltTransaction build(TransactionRequest request) { + return unwrap(api.transaction().build(request)); + } + + @Override + public TxBlobDTO finalize(FinalizedTransaction request, boolean immediateSubmit) { + return unwrap(api.transaction().finalize(request, immediateSubmit)); + } + + @Override + public TxDTO submit(TxBlobDTO request) { + return unwrap(api.transaction().submit(request)); + } + + @Override + public TransactionDTO lookup(AID txId) { + return unwrap(api.transaction().lookup(txId)); + } + + @Override + public TransactionStatusDTO status(AID txId) { + return unwrap(api.transaction().status(txId)); + } + }; + } + + @Override + public Token token() { + return new Token() { + @Override + public TokenInfo describeNative() { + return unwrap(api.token().describeNative()); + } + + @Override + public TokenInfo describe(String rri) { + return unwrap(api.token().describe(rri)); + } + }; + } + + @Override + public Local local() { + return new Local() { + @Override + public LocalAccount accountInfo() { + return unwrap(api.local().accountInfo()); + } + + @Override + public TxDTO submitTxSingleStep(TransactionRequest request) { + return unwrap(api.local().submitTxSingleStep(request)); + } + + @Override + public LocalValidatorInfo validatorInfo() { + return unwrap(api.local().validatorInfo()); + } + + @Override + public EpochData currentEpoch() { + return unwrap(api.local().currentEpoch()); + } + }; + } + + @Override + public SingleAccount account() { + return new SingleAccount() { + @Override + public TokenBalances balances(AccountAddress address) { + return unwrap(api.account().balances(address)); + } + + @Override + public TransactionHistory history(AccountAddress address, int size, OptionalLong offset) { + return unwrap(api.account().history(address, size, offset)); + } + + @Override + public List stakes(AccountAddress address) { + return unwrap(api.account().stakes(address)); + } + + @Override + public List unstakes(AccountAddress address) { + return unwrap(api.account().unstakes(address)); + } + }; + } + + @Override + public Validator validator() { + return new Validator() { + @Override + public ValidatorsResponse list(int size, Optional cursor) { + return unwrap(api.validator().list(size, cursor)); + } + + @Override + public ValidatorDTO lookup(ValidatorAddress validatorAddress) { + return unwrap(api.validator().lookup(validatorAddress)); + } + }; + } + + @Override + public Api api() { + return new Api() { + @Override + public ApiConfiguration configuration() { + return unwrap(api.api().configuration()); + } + + @Override + public ApiData data() { + return unwrap(api.api().data()); + } + }; + } + + @Override + public Consensus consensus() { + return new Consensus() { + @Override + public ConsensusConfiguration configuration() { + return unwrap(api.consensus().configuration()); + } + + @Override + public ConsensusData data() { + return unwrap(api.consensus().data()); + } + }; + } + + @Override + public Mempool mempool() { + return new Mempool() { + @Override + public MempoolConfiguration configuration() { + return unwrap(api.mempool().configuration()); + } + + @Override + public MempoolData data() { + return unwrap(api.mempool().data()); + } + }; + } + + @Override + public RadixEngine radixEngine() { + return new RadixEngine() { + @Override + public List configuration() { + return unwrap(api.radixEngine().configuration()); + } + + @Override + public RadixEngineData data() { + return unwrap(api.radixEngine().data()); + } + }; + } + + @Override + public Sync sync() { + return new Sync() { + @Override + public SyncConfiguration configuration() { + return unwrap(api.sync().configuration()); + } + + @Override + public SyncData data() { + return unwrap(api.sync().data()); + } + }; + } + + @Override + public Ledger ledger() { + return new Ledger() { + @Override + public Proof latest() { + return unwrap(api.ledger().latest()); + } + + @Override + public Proof epoch() { + return unwrap(api.ledger().epoch()); + } + + @Override + public Checkpoint checkpoints() { + return unwrap(api.ledger().checkpoints()); + } + }; + } + }; + } + // CHECKSTYLE:ON } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApi.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApi.java index 5ef7a42cff..78e6de7bd9 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApi.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApi.java @@ -108,24 +108,32 @@ import com.radixdlt.identifiers.AID; import com.radixdlt.networks.Addressing; import com.radixdlt.utils.functional.Result; - import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.OptionalLong; /** + * + * *

Synchronous Radix JSON RPC client.

+ * + *

The Radix Web API consists of several endpoints which are assigned to two large groups. Each + * group served by dedicated embedded HTTP server hence full configuration of the client requires + * base URL and two ports. + * + *

Each endpoint can be individually enabled or disabled, so even if client is successfully + * connected, it does not mean that all API's are available. This should be kept in mind while using + * client with particular node. + * *

- * The Radix Web API consists of several endpoints which are assigned to two large groups. Each group served by - * dedicated embedded HTTP server hence full configuration of the client requires base URL and two ports. - *

- * Each endpoint can be individually enabled or disabled, so even if client is successfully connected, it does not - * mean that all API's are available. This should be kept in mind while using client with particular node. - *

+ * *

Client API structure

+ * * API is split into following groups: + * *

+ * * * * @@ -146,395 +154,322 @@ *
apiGroups
*/ public interface RadixApi { - int DEFAULT_PRIMARY_PORT = 8080; - int DEFAULT_SECONDARY_PORT = 3333; - - /** - * Create client and connect to specified node. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * - * @return {@link Result} which will contain built client or error info. - */ - static Result connect(String baseUrl) { - return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT); - } - - /** - * Create client and connect to specified node. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param authentication Login/password for basic authentication - * - * @return {@link Result} which will contain built client or error info. - */ - static Result connect(String baseUrl, BasicAuth authentication) { - return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT, authentication); - } - - /** - * Create client and connect to specified node at specified primary and secondary ports. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param primaryPort primary API port - * @param secondaryPort secondary API port - * - * @return {@link Result} which will contain built client or error info. - */ - static Result connect(String baseUrl, int primaryPort, int secondaryPort) { - return SyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.empty()); - } - - /** - * Create client and connect to specified node at specified primary and secondary ports. - * - * @param baseUrl base URL to connect. Note that it should not include path part of the URL. - * @param primaryPort primary API port - * @param secondaryPort secondary API port - * @param authentication Login/password for basic authentication - * - * @return {@link Result} which will contain built client or error info. - */ - static Result connect(String baseUrl, int primaryPort, int secondaryPort, BasicAuth authentication) { - return SyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.of(authentication)); - } - - /** - * Enable tracing in client. - */ - RadixApi withTrace(); - - /** - * Get {@link Addressing} instance corresponding to connected network - */ - Addressing addressing(); - - /** - * Configure timeout for network operations. - * - * @param timeout - operation timeout - */ - RadixApi withTimeout(Duration timeout); - - /** - * Network API's - */ - interface Network { - /** - * Get network ID. - */ - Result id(); - - /** - * Get current network throughput in transactions per second. - */ - Result throughput(); - - /** - * Get current network demand in transactions per second. - */ - Result demand(); - - /** - * Get current network configuration. - */ - Result configuration(); - - /** - * Get network metrics. - */ - Result data(); - - /** - * Get network peers. - */ - Result> peers(); - - /** - * Get current address book. - */ - Result> addressBook(); - } - - Network network(); - - /** - * Transaction API's. - *

- * Radix API uses three step transaction submission: - *

    - *
  1. Build - transaction blob is assembled from the high level action description
  2. - *
  3. Finalize - transaction is prepared, validated, transaction ID is calculated and returned
  4. - *
  5. Submit - transaction is actually submitted to mempool
  6. - *
- * This process is designed for the case of very unreliable communication, to prevent double submission and - * other potential issues. If this is less of an issue in particular use case, it is possible to omit last - * step and submit transaction during finalization step. To achieve this, set {@code immediateSubmit} flag - * in {@link #finalize(FinalizedTransaction, boolean)} to {@code true}. - */ - interface Transaction { - /** - * Build transaction for a given transaction request. - * - * @param request transaction request - */ - Result build(TransactionRequest request); - - /** - * Finalize transaction. - * - * @param request transaction request (can be built from {@link BuiltTransaction} by invoking {@link BuiltTransaction#toFinalized(ECKeyPair)} - * method) - * @param immediateSubmit if set to {@code true} then transaction will be immediately submitted to mempool - */ - Result finalize(FinalizedTransaction request, boolean immediateSubmit); - - /** - * Submit transaction. - * - * @param request transaction request - */ - Result submit(TxBlobDTO request); - - /** - * Lookup transaction. - * - * @param txId the ID of the transaction to look up - */ - Result lookup(AID txId); - - /** - * Get transaction status. - * - * @param txId the ID of the transaction to get status for - */ - Result status(AID txId); - - /** - * Get paginated list of indexed transactions. - * - * @param limit number of transactions to return - * @param offset starting offset - */ - Result list(long limit, OptionalLong offset); - } - - Transaction transaction(); - - /** - * Token-related API's - */ - interface Token { - /** - * Get description of the native token. - */ - Result describeNative(); - - /** - * Get description of the token with a given RRI. - */ - Result describe(String rri); - } - - Token token(); - - /** - * API's which deal with information local to node to which client is connected. - *

- * WARNING: These API's may expose or use security-sensitive information. Use with care. - */ - interface Local { - /** - * Get local node account information. - */ - Result accountInfo(); - - /** - * Submit transaction is single step, using local node private key to sign the transaction. - * - * @param request high level action description - */ - Result submitTxSingleStep(TransactionRequest request); - - /** - * Get information about local node as a validator. - */ - Result validatorInfo(); - - /** - * Get information about current epoch validator set. - */ - Result currentEpoch(); - } - - Local local(); - - /** - * Single account address API's - */ - interface SingleAccount { - /** - * Get account balances. - * - * @param address account address for which information is requested - */ - Result balances(AccountAddress address); - - /** - * Get transaction history. - * - * @param address account address for which information is requested - * @param limit batch size - * @param offset offset to start retrieval at - */ - Result history(AccountAddress address, int limit, OptionalLong offset); - - /** - * Get stakes made from given account. - * - * @param address account address for which information is requested - */ - Result> stakes(AccountAddress address); - - /** - * Get pending (not yet transferred back) unstakes. - * - * @param address account address for which information is requested - */ - Result> unstakes(AccountAddress address); - } - - SingleAccount account(); - - /** - * General validator information API's - */ - interface Validator { - /** - * Get paginated list of all validators known to the network. - *

- * To get full list, pass empty cursor for first request and then just pass cursor received in the response - * back to API until you get empty cursor again. - * - * @param size batch size - * @param cursor pagination cursor - */ - Result list(int size, Optional cursor); - - /** - * Lookup validator by address. - * - * @param validatorAddress validator address - */ - Result lookup(ValidatorAddress validatorAddress); - } - - Validator validator(); - - /** - * Node API configuration and metrics. - */ - interface Api { - /** - * Get API configuration. - */ - Result configuration(); - - /** - * Get API metrics. - */ - Result data(); - } - - Api api(); - - /** - * Consensus configuration and metrics. - */ - interface Consensus { - /** - * Get consensus configuration. - */ - Result configuration(); - - /** - * Get consensus metrics. - */ - Result data(); - } - - Consensus consensus(); - - /** - * Mempool configuration and metrics. - */ - interface Mempool { - /** - * Get mempool configuration. - */ - Result configuration(); - - /** - * Get mempool metrics. - */ - Result data(); - } - - Mempool mempool(); - - /** - * RadixEngine configuration and metrics. - */ - interface RadixEngine { - /** - * Get Radix Engine configuration. - */ - Result> configuration(); - - /** - * Get Radix Engine metrics. - */ - Result data(); - } - - RadixEngine radixEngine(); - - /** - * Inter-node synchronization configuration and metrics. - */ - interface Sync { - /** - * Get synchronization configuration. - */ - Result configuration(); - - /** - * Get synchronization metrics. - */ - Result data(); - } - - Sync sync(); - - /** - * Ledger API's. - */ - interface Ledger { - /** - * Get latest proof. - */ - Result latest(); - - /** - * Get latest epoch proof. - */ - Result epoch(); - - /** - * Get checkpoint configuration. - */ - Result checkpoints(); - } - - Ledger ledger(); + int DEFAULT_PRIMARY_PORT = 8080; + int DEFAULT_SECONDARY_PORT = 3333; + + /** + * Create client and connect to specified node. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @return {@link Result} which will contain built client or error info. + */ + static Result connect(String baseUrl) { + return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT); + } + + /** + * Create client and connect to specified node. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param authentication Login/password for basic authentication + * @return {@link Result} which will contain built client or error info. + */ + static Result connect(String baseUrl, BasicAuth authentication) { + return connect(baseUrl, DEFAULT_PRIMARY_PORT, DEFAULT_SECONDARY_PORT, authentication); + } + + /** + * Create client and connect to specified node at specified primary and secondary ports. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param primaryPort primary API port + * @param secondaryPort secondary API port + * @return {@link Result} which will contain built client or error info. + */ + static Result connect(String baseUrl, int primaryPort, int secondaryPort) { + return SyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.empty()); + } + + /** + * Create client and connect to specified node at specified primary and secondary ports. + * + * @param baseUrl base URL to connect. Note that it should not include path part of the URL. + * @param primaryPort primary API port + * @param secondaryPort secondary API port + * @param authentication Login/password for basic authentication + * @return {@link Result} which will contain built client or error info. + */ + static Result connect( + String baseUrl, int primaryPort, int secondaryPort, BasicAuth authentication) { + return SyncRadixApi.connect(baseUrl, primaryPort, secondaryPort, Optional.of(authentication)); + } + + /** Enable tracing in client. */ + RadixApi withTrace(); + + /** Get {@link Addressing} instance corresponding to connected network */ + Addressing addressing(); + + /** + * Configure timeout for network operations. + * + * @param timeout - operation timeout + */ + RadixApi withTimeout(Duration timeout); + + /** Network API's */ + interface Network { + /** Get network ID. */ + Result id(); + + /** Get current network throughput in transactions per second. */ + Result throughput(); + + /** Get current network demand in transactions per second. */ + Result demand(); + + /** Get current network configuration. */ + Result configuration(); + + /** Get network metrics. */ + Result data(); + + /** Get network peers. */ + Result> peers(); + + /** Get current address book. */ + Result> addressBook(); + } + + Network network(); + + /** + * Transaction API's. + * + *

Radix API uses three step transaction submission: + * + *

    + *
  1. Build - transaction blob is assembled from the high level action description + *
  2. Finalize - transaction is prepared, validated, transaction ID is calculated and returned + *
  3. Submit - transaction is actually submitted to mempool + *
+ * + * This process is designed for the case of very unreliable communication, to prevent double + * submission and other potential issues. If this is less of an issue in particular use case, it + * is possible to omit last step and submit transaction during finalization step. To achieve this, + * set {@code immediateSubmit} flag in {@link #finalize(FinalizedTransaction, boolean)} to {@code + * true}. + */ + interface Transaction { + /** + * Build transaction for a given transaction request. + * + * @param request transaction request + */ + Result build(TransactionRequest request); + + /** + * Finalize transaction. + * + * @param request transaction request (can be built from {@link BuiltTransaction} by invoking + * {@link BuiltTransaction#toFinalized(ECKeyPair)} method) + * @param immediateSubmit if set to {@code true} then transaction will be immediately submitted + * to mempool + */ + Result finalize(FinalizedTransaction request, boolean immediateSubmit); + + /** + * Submit transaction. + * + * @param request transaction request + */ + Result submit(TxBlobDTO request); + + /** + * Lookup transaction. + * + * @param txId the ID of the transaction to look up + */ + Result lookup(AID txId); + + /** + * Get transaction status. + * + * @param txId the ID of the transaction to get status for + */ + Result status(AID txId); + + /** + * Get paginated list of indexed transactions. + * + * @param limit number of transactions to return + * @param offset starting offset + */ + Result list(long limit, OptionalLong offset); + } + + Transaction transaction(); + + /** Token-related API's */ + interface Token { + /** Get description of the native token. */ + Result describeNative(); + + /** Get description of the token with a given RRI. */ + Result describe(String rri); + } + + Token token(); + + /** + * API's which deal with information local to node to which client is connected. + * + *

WARNING: These API's may expose or use security-sensitive information. Use with care. + */ + interface Local { + /** Get local node account information. */ + Result accountInfo(); + + /** + * Submit transaction is single step, using local node private key to sign the transaction. + * + * @param request high level action description + */ + Result submitTxSingleStep(TransactionRequest request); + + /** Get information about local node as a validator. */ + Result validatorInfo(); + + /** Get information about current epoch validator set. */ + Result currentEpoch(); + } + + Local local(); + + /** Single account address API's */ + interface SingleAccount { + /** + * Get account balances. + * + * @param address account address for which information is requested + */ + Result balances(AccountAddress address); + + /** + * Get transaction history. + * + * @param address account address for which information is requested + * @param limit batch size + * @param offset offset to start retrieval at + */ + Result history(AccountAddress address, int limit, OptionalLong offset); + + /** + * Get stakes made from given account. + * + * @param address account address for which information is requested + */ + Result> stakes(AccountAddress address); + + /** + * Get pending (not yet transferred back) unstakes. + * + * @param address account address for which information is requested + */ + Result> unstakes(AccountAddress address); + } + + SingleAccount account(); + + /** General validator information API's */ + interface Validator { + /** + * Get paginated list of all validators known to the network. + * + *

To get full list, pass empty cursor for first request and then just pass cursor received + * in the response back to API until you get empty cursor again. + * + * @param size batch size + * @param cursor pagination cursor + */ + Result list(int size, Optional cursor); + + /** + * Lookup validator by address. + * + * @param validatorAddress validator address + */ + Result lookup(ValidatorAddress validatorAddress); + } + + Validator validator(); + + /** Node API configuration and metrics. */ + interface Api { + /** Get API configuration. */ + Result configuration(); + + /** Get API metrics. */ + Result data(); + } + + Api api(); + + /** Consensus configuration and metrics. */ + interface Consensus { + /** Get consensus configuration. */ + Result configuration(); + + /** Get consensus metrics. */ + Result data(); + } + + Consensus consensus(); + + /** Mempool configuration and metrics. */ + interface Mempool { + /** Get mempool configuration. */ + Result configuration(); + + /** Get mempool metrics. */ + Result data(); + } + + Mempool mempool(); + + /** RadixEngine configuration and metrics. */ + interface RadixEngine { + /** Get Radix Engine configuration. */ + Result> configuration(); + + /** Get Radix Engine metrics. */ + Result data(); + } + + RadixEngine radixEngine(); + + /** Inter-node synchronization configuration and metrics. */ + interface Sync { + /** Get synchronization configuration. */ + Result configuration(); + + /** Get synchronization metrics. */ + Result data(); + } + + Sync sync(); + + /** Ledger API's. */ + interface Ledger { + /** Get latest proof. */ + Result latest(); + + /** Get latest epoch proof. */ + Result epoch(); + + /** Get checkpoint configuration. */ + Result checkpoints(); + } + + Ledger ledger(); } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApiException.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApiException.java index 643a5f0b19..a465cfe641 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApiException.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/RadixApiException.java @@ -67,7 +67,7 @@ import com.radixdlt.utils.functional.Failure; public class RadixApiException extends RuntimeException { - public RadixApiException(Failure failure) { - super(failure.message()); - } + public RadixApiException(Failure failure) { + super(failure.message()); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/SyncRadixApi.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/SyncRadixApi.java index 7772c2baeb..47d2930859 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/SyncRadixApi.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/sync/SyncRadixApi.java @@ -64,7 +64,49 @@ package com.radixdlt.client.lib.api.sync; -import org.bouncycastle.util.encoders.Hex; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_BALANCES; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_HISTORY; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_INFO; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_STAKES; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_SUBMIT_SINGLE_STEP; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_UNSTAKES; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.API_CONFIGURATION; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.API_DATA; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.BFT_CONFIGURATION; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.BFT_DATA; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.CONSTRUCTION_BUILD; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.CONSTRUCTION_FINALIZE; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.CONSTRUCTION_SUBMIT; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.LEDGER_CHECKPOINTS; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.LEDGER_EPOCH_PROOF; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.LEDGER_PROOF; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.MEMPOOL_CONFIGURATION; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.MEMPOOL_DATA; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_ADDRESS_BOOK; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_CONFIG; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_DATA; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_DEMAND; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_ID; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_PEERS; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_THROUGHPUT; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.RADIX_ENGINE_CONFIGURATION; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.RADIX_ENGINE_DATA; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.SYNC_CONFIGURATION; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.SYNC_DATA; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.TOKEN_INFO; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.TOKEN_NATIVE; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.TRANSACTION_LIST; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.TRANSACTION_LOOKUP; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.TRANSACTION_STATUS; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATION_CURRENT_EPOCH; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATION_NODE_INFO; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATORS_LIST; +import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATORS_LOOKUP; +import static com.radixdlt.errors.ClientErrors.INTERRUPTED_OPERATION; +import static com.radixdlt.errors.ClientErrors.IO_ERROR; +import static com.radixdlt.errors.ClientErrors.MISSING_BASE_URL; +import static com.radixdlt.errors.InternalErrors.UNKNOWN; +import static java.util.Optional.ofNullable; import com.fasterxml.jackson.core.type.TypeReference; import com.radixdlt.client.lib.api.AccountAddress; @@ -114,7 +156,6 @@ import com.radixdlt.networks.Addressing; import com.radixdlt.utils.functional.Failure; import com.radixdlt.utils.functional.Result; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; @@ -122,433 +163,413 @@ import java.util.List; import java.util.Optional; import java.util.OptionalLong; - -import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_BALANCES; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_HISTORY; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_INFO; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_STAKES; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_SUBMIT_SINGLE_STEP; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.ACCOUNT_UNSTAKES; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.API_CONFIGURATION; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.API_DATA; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.BFT_CONFIGURATION; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.BFT_DATA; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.CONSTRUCTION_BUILD; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.CONSTRUCTION_FINALIZE; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.CONSTRUCTION_SUBMIT; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.LEDGER_CHECKPOINTS; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.LEDGER_EPOCH_PROOF; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.LEDGER_PROOF; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.MEMPOOL_CONFIGURATION; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.MEMPOOL_DATA; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_ADDRESS_BOOK; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_CONFIG; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_DATA; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_DEMAND; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_ID; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_PEERS; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.NETWORK_THROUGHPUT; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.RADIX_ENGINE_CONFIGURATION; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.RADIX_ENGINE_DATA; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.SYNC_CONFIGURATION; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.SYNC_DATA; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.TOKEN_INFO; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.TOKEN_NATIVE; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.TRANSACTION_LIST; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.TRANSACTION_LOOKUP; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.TRANSACTION_STATUS; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATION_CURRENT_EPOCH; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATION_NODE_INFO; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATORS_LIST; -import static com.radixdlt.client.lib.api.rpc.RpcMethod.VALIDATORS_LOOKUP; -import static com.radixdlt.errors.ClientErrors.INTERRUPTED_OPERATION; -import static com.radixdlt.errors.ClientErrors.IO_ERROR; -import static com.radixdlt.errors.ClientErrors.MISSING_BASE_URL; -import static com.radixdlt.errors.InternalErrors.UNKNOWN; - -import static java.util.Optional.ofNullable; +import org.bouncycastle.util.encoders.Hex; public class SyncRadixApi extends RadixApiBase implements RadixApi { - private final Network network = new Network() { - @Override - public Result id() { - return call(request(NETWORK_ID), new TypeReference<>() {}); - } - - @Override - public Result throughput() { - return call(request(NETWORK_THROUGHPUT), new TypeReference<>() {}); - } - - @Override - public Result demand() { - return call(request(NETWORK_DEMAND), new TypeReference<>() {}); - } - - @Override - public Result configuration() { - return call(request(NETWORK_CONFIG), new TypeReference<>() {}); - } - - @Override - public Result data() { - return call(request(NETWORK_DATA), new TypeReference<>() {}); - } - - @Override - public Result> peers() { - return call(request(NETWORK_PEERS), new TypeReference<>() {}); - } - - @Override - public Result> addressBook() { - return call(request(NETWORK_ADDRESS_BOOK), new TypeReference<>() {}); - } - }; - - private final Token token = new Token() { - @Override - public Result describeNative() { - return call(request(TOKEN_NATIVE), new TypeReference<>() {}); - } - - @Override - public Result describe(String rri) { - return call(request(TOKEN_INFO, rri), new TypeReference<>() {}); - } - }; - - private final Transaction transaction = new Transaction() { - @Override - public Result build(TransactionRequest request) { - return call( - request( - CONSTRUCTION_BUILD, request.getActions(), request.getFeePayer(), - request.getMessage(), request.disableResourceAllocationAndDestroy() - ), - new TypeReference<>() {} - ); - } - - @Override - public Result finalize(FinalizedTransaction request, boolean immediateSubmit) { - return call( - request( - CONSTRUCTION_FINALIZE, - Hex.toHexString(request.getRawBlob()), request.getSignature(), request.getPublicKey(), Boolean.toString(immediateSubmit) - ), - new TypeReference<>() {} - ); - } - - @Override - public Result submit(TxBlobDTO request) { - return call( - request(CONSTRUCTION_SUBMIT, Hex.toHexString(request.getBlob()), request.getTxId()), - new TypeReference<>() {} - ); - } - - @Override - public Result lookup(AID txId) { - return call(request(TRANSACTION_LOOKUP, txId.toString()), new TypeReference<>() {}); - } - - @Override - public Result status(AID txId) { - return call(request(TRANSACTION_STATUS, txId.toString()), new TypeReference<>() {}); - } - - @Override - public Result list(long limit, OptionalLong offset) { - var request = request(TRANSACTION_LIST, limit); - offset.ifPresent(request::addParameters); - return call(request, new TypeReference<>() {}); - } - }; - - private final SingleAccount account = new SingleAccount() { - @Override - public Result balances(AccountAddress address) { - return call(request(ACCOUNT_BALANCES, address.toString(networkId())), new TypeReference<>() {}); - } - - @Override - public Result history( - AccountAddress address, int size, OptionalLong nextOffset - ) { - var request = request(ACCOUNT_HISTORY, address.toString(networkId()), size); - nextOffset.ifPresent(request::addParameters); - - return call(request, new TypeReference<>() {}); - } - - @Override - public Result> stakes(AccountAddress address) { - return call(request(ACCOUNT_STAKES, address.toString(networkId())), new TypeReference<>() {}); - } - - @Override - public Result> unstakes(AccountAddress address) { - return call(request(ACCOUNT_UNSTAKES, address.toString(networkId())), new TypeReference<>() {}); - } - }; - - private final Validator validator = new Validator() { - @Override - public Result list(int size, Optional cursor) { - var request = request(VALIDATORS_LIST, size); - cursor.ifPresent(cursorValue -> request.addParameters(cursorValue.value())); - - return call(request, new TypeReference<>() {}); - } - - @Override - public Result lookup(ValidatorAddress validatorAddress) { - return call(request(VALIDATORS_LOOKUP, validatorAddress.toString(networkId())), new TypeReference<>() {}); - } - }; - - private final Local local = new Local() { - @Override - public Result accountInfo() { - return call(request(ACCOUNT_INFO), new TypeReference<>() {}); - } - - @Override - public Result submitTxSingleStep(TransactionRequest request) { - return call( - request(ACCOUNT_SUBMIT_SINGLE_STEP, request.getActions(), request.getMessage()), - new TypeReference<>() {} - ); - } - - @Override - public Result validatorInfo() { - return call(request(VALIDATION_NODE_INFO), new TypeReference<>() {}); - } - - @Override - public Result currentEpoch() { - return call(request(VALIDATION_CURRENT_EPOCH), new TypeReference<>() {}); - } - }; - - private final Api api = new Api() { - @Override - public Result configuration() { - return call(request(API_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Result data() { - return call(request(API_DATA), new TypeReference<>() {}); - } - }; - - private final Consensus consensus = new Consensus() { - @Override - public Result configuration() { - return call(request(BFT_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Result data() { - return call(request(BFT_DATA), new TypeReference<>() {}); - } - }; - - private final Mempool mempool = new Mempool() { - @Override - public Result configuration() { - return call(request(MEMPOOL_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Result data() { - return call(request(MEMPOOL_DATA), new TypeReference<>() {}); - } - }; - - private final RadixEngine radixEngine = new RadixEngine() { - @Override - public Result> configuration() { - return call(request(RADIX_ENGINE_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Result data() { - return call(request(RADIX_ENGINE_DATA), new TypeReference<>() {}); - } - }; - - private final Sync sync = new Sync() { - @Override - public Result configuration() { - return call(request(SYNC_CONFIGURATION), new TypeReference<>() {}); - } - - @Override - public Result data() { - return call(request(SYNC_DATA), new TypeReference<>() {}); - } - }; - - private final Ledger ledger = new Ledger() { - @Override - public Result latest() { - return call(request(LEDGER_PROOF), new TypeReference<>() {}); - } - - @Override - public Result epoch() { - return call(request(LEDGER_EPOCH_PROOF), new TypeReference<>() {}); - } - - @Override - public Result checkpoints() { - return call(request(LEDGER_CHECKPOINTS), new TypeReference<>() {}); - } - }; - - @Override - public Network network() { - return network; - } - - @Override - public Transaction transaction() { - return transaction; - } - - @Override - public Token token() { - return token; - } - - @Override - public Local local() { - return local; - } - - @Override - public SingleAccount account() { - return account; - } - - @Override - public Validator validator() { - return validator; - } - - @Override - public Api api() { - return api; - } - - @Override - public Consensus consensus() { - return consensus; - } - - @Override - public Mempool mempool() { - return mempool; - } - - @Override - public RadixEngine radixEngine() { - return radixEngine; - } - - @Override - public Sync sync() { - return sync; - } - - @Override - public Ledger ledger() { - return ledger; - } - - @Override - public SyncRadixApi withTrace() { - enableTrace(); - return this; - } - - @Override - public Addressing addressing() { - return networkAddressing(); - } - - @Override - public SyncRadixApi withTimeout(Duration timeout) { - setTimeout(timeout); - return this; - } - - private SyncRadixApi( - String baseUrl, - int primaryPort, - int secondaryPort, - HttpClient client, - Optional authentication - ) { - super(baseUrl, primaryPort, secondaryPort, client, authentication); - } - - static Result connect( - String url, - int primaryPort, - int secondaryPort, - Optional authentication - ) { - return buildHttpClient().flatMap(client -> connect(url, primaryPort, secondaryPort, client, authentication)); - } - - static Result connect( - String url, - int primaryPort, - int secondaryPort, - HttpClient client, - Optional authentication - ) { - return ofNullable(url) - .map(baseUrl -> Result.ok(new SyncRadixApi(baseUrl, primaryPort, secondaryPort, client, authentication))) - .orElseGet(MISSING_BASE_URL::result) - .flatMap(syncRadixApi -> syncRadixApi.network().id() - .onSuccess(networkId -> syncRadixApi.configureSerialization(networkId.getNetworkId())) - .map(__ -> syncRadixApi)); - } - - private Result call(JsonRpcRequest request, TypeReference> typeReference) { - return serialize(request) - .onSuccess(this::trace) - .map(value -> buildRequest(request, value)) - .flatMap(httpRequest -> Result.wrap(this::errorMapper, () -> client().send(httpRequest, HttpResponse.BodyHandlers.ofString()))) - .flatMap(body -> bodyHandler(body, typeReference)); - } - - private Failure errorMapper(Throwable throwable) { - if (throwable instanceof IOException) { - return IO_ERROR.with(throwable.getMessage()); - } - - if (throwable instanceof InterruptedException) { - return INTERRUPTED_OPERATION.with(throwable.getMessage()); - } - - return UNKNOWN.with(throwable.getClass().getName(), throwable.getMessage()); - } - - private Result bodyHandler( - HttpResponse body, - TypeReference> reference - ) { - return deserialize(trace(body.body()), reference) - .flatMap(response -> response.rawError() == null - ? Result.ok(response.rawResult()) - : Result.fail(response.rawError().toFailure())); - } + private final Network network = + new Network() { + @Override + public Result id() { + return call(request(NETWORK_ID), new TypeReference<>() {}); + } + + @Override + public Result throughput() { + return call(request(NETWORK_THROUGHPUT), new TypeReference<>() {}); + } + + @Override + public Result demand() { + return call(request(NETWORK_DEMAND), new TypeReference<>() {}); + } + + @Override + public Result configuration() { + return call(request(NETWORK_CONFIG), new TypeReference<>() {}); + } + + @Override + public Result data() { + return call(request(NETWORK_DATA), new TypeReference<>() {}); + } + + @Override + public Result> peers() { + return call(request(NETWORK_PEERS), new TypeReference<>() {}); + } + + @Override + public Result> addressBook() { + return call(request(NETWORK_ADDRESS_BOOK), new TypeReference<>() {}); + } + }; + + private final Token token = + new Token() { + @Override + public Result describeNative() { + return call(request(TOKEN_NATIVE), new TypeReference<>() {}); + } + + @Override + public Result describe(String rri) { + return call(request(TOKEN_INFO, rri), new TypeReference<>() {}); + } + }; + + private final Transaction transaction = + new Transaction() { + @Override + public Result build(TransactionRequest request) { + return call( + request( + CONSTRUCTION_BUILD, + request.getActions(), + request.getFeePayer(), + request.getMessage(), + request.disableResourceAllocationAndDestroy()), + new TypeReference<>() {}); + } + + @Override + public Result finalize(FinalizedTransaction request, boolean immediateSubmit) { + return call( + request( + CONSTRUCTION_FINALIZE, + Hex.toHexString(request.getRawBlob()), + request.getSignature(), + request.getPublicKey(), + Boolean.toString(immediateSubmit)), + new TypeReference<>() {}); + } + + @Override + public Result submit(TxBlobDTO request) { + return call( + request(CONSTRUCTION_SUBMIT, Hex.toHexString(request.getBlob()), request.getTxId()), + new TypeReference<>() {}); + } + + @Override + public Result lookup(AID txId) { + return call(request(TRANSACTION_LOOKUP, txId.toString()), new TypeReference<>() {}); + } + + @Override + public Result status(AID txId) { + return call(request(TRANSACTION_STATUS, txId.toString()), new TypeReference<>() {}); + } + + @Override + public Result list(long limit, OptionalLong offset) { + var request = request(TRANSACTION_LIST, limit); + offset.ifPresent(request::addParameters); + return call(request, new TypeReference<>() {}); + } + }; + + private final SingleAccount account = + new SingleAccount() { + @Override + public Result balances(AccountAddress address) { + return call( + request(ACCOUNT_BALANCES, address.toString(networkId())), new TypeReference<>() {}); + } + + @Override + public Result history( + AccountAddress address, int size, OptionalLong nextOffset) { + var request = request(ACCOUNT_HISTORY, address.toString(networkId()), size); + nextOffset.ifPresent(request::addParameters); + + return call(request, new TypeReference<>() {}); + } + + @Override + public Result> stakes(AccountAddress address) { + return call( + request(ACCOUNT_STAKES, address.toString(networkId())), new TypeReference<>() {}); + } + + @Override + public Result> unstakes(AccountAddress address) { + return call( + request(ACCOUNT_UNSTAKES, address.toString(networkId())), new TypeReference<>() {}); + } + }; + + private final Validator validator = + new Validator() { + @Override + public Result list(int size, Optional cursor) { + var request = request(VALIDATORS_LIST, size); + cursor.ifPresent(cursorValue -> request.addParameters(cursorValue.value())); + + return call(request, new TypeReference<>() {}); + } + + @Override + public Result lookup(ValidatorAddress validatorAddress) { + return call( + request(VALIDATORS_LOOKUP, validatorAddress.toString(networkId())), + new TypeReference<>() {}); + } + }; + + private final Local local = + new Local() { + @Override + public Result accountInfo() { + return call(request(ACCOUNT_INFO), new TypeReference<>() {}); + } + + @Override + public Result submitTxSingleStep(TransactionRequest request) { + return call( + request(ACCOUNT_SUBMIT_SINGLE_STEP, request.getActions(), request.getMessage()), + new TypeReference<>() {}); + } + + @Override + public Result validatorInfo() { + return call(request(VALIDATION_NODE_INFO), new TypeReference<>() {}); + } + + @Override + public Result currentEpoch() { + return call(request(VALIDATION_CURRENT_EPOCH), new TypeReference<>() {}); + } + }; + + private final Api api = + new Api() { + @Override + public Result configuration() { + return call(request(API_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Result data() { + return call(request(API_DATA), new TypeReference<>() {}); + } + }; + + private final Consensus consensus = + new Consensus() { + @Override + public Result configuration() { + return call(request(BFT_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Result data() { + return call(request(BFT_DATA), new TypeReference<>() {}); + } + }; + + private final Mempool mempool = + new Mempool() { + @Override + public Result configuration() { + return call(request(MEMPOOL_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Result data() { + return call(request(MEMPOOL_DATA), new TypeReference<>() {}); + } + }; + + private final RadixEngine radixEngine = + new RadixEngine() { + @Override + public Result> configuration() { + return call(request(RADIX_ENGINE_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Result data() { + return call(request(RADIX_ENGINE_DATA), new TypeReference<>() {}); + } + }; + + private final Sync sync = + new Sync() { + @Override + public Result configuration() { + return call(request(SYNC_CONFIGURATION), new TypeReference<>() {}); + } + + @Override + public Result data() { + return call(request(SYNC_DATA), new TypeReference<>() {}); + } + }; + + private final Ledger ledger = + new Ledger() { + @Override + public Result latest() { + return call(request(LEDGER_PROOF), new TypeReference<>() {}); + } + + @Override + public Result epoch() { + return call(request(LEDGER_EPOCH_PROOF), new TypeReference<>() {}); + } + + @Override + public Result checkpoints() { + return call(request(LEDGER_CHECKPOINTS), new TypeReference<>() {}); + } + }; + + @Override + public Network network() { + return network; + } + + @Override + public Transaction transaction() { + return transaction; + } + + @Override + public Token token() { + return token; + } + + @Override + public Local local() { + return local; + } + + @Override + public SingleAccount account() { + return account; + } + + @Override + public Validator validator() { + return validator; + } + + @Override + public Api api() { + return api; + } + + @Override + public Consensus consensus() { + return consensus; + } + + @Override + public Mempool mempool() { + return mempool; + } + + @Override + public RadixEngine radixEngine() { + return radixEngine; + } + + @Override + public Sync sync() { + return sync; + } + + @Override + public Ledger ledger() { + return ledger; + } + + @Override + public SyncRadixApi withTrace() { + enableTrace(); + return this; + } + + @Override + public Addressing addressing() { + return networkAddressing(); + } + + @Override + public SyncRadixApi withTimeout(Duration timeout) { + setTimeout(timeout); + return this; + } + + private SyncRadixApi( + String baseUrl, + int primaryPort, + int secondaryPort, + HttpClient client, + Optional authentication) { + super(baseUrl, primaryPort, secondaryPort, client, authentication); + } + + static Result connect( + String url, int primaryPort, int secondaryPort, Optional authentication) { + return buildHttpClient() + .flatMap(client -> connect(url, primaryPort, secondaryPort, client, authentication)); + } + + static Result connect( + String url, + int primaryPort, + int secondaryPort, + HttpClient client, + Optional authentication) { + return ofNullable(url) + .map( + baseUrl -> + Result.ok( + new SyncRadixApi(baseUrl, primaryPort, secondaryPort, client, authentication))) + .orElseGet(MISSING_BASE_URL::result) + .flatMap( + syncRadixApi -> + syncRadixApi + .network() + .id() + .onSuccess( + networkId -> syncRadixApi.configureSerialization(networkId.getNetworkId())) + .map(__ -> syncRadixApi)); + } + + private Result call( + JsonRpcRequest request, TypeReference> typeReference) { + return serialize(request) + .onSuccess(this::trace) + .map(value -> buildRequest(request, value)) + .flatMap( + httpRequest -> + Result.wrap( + this::errorMapper, + () -> client().send(httpRequest, HttpResponse.BodyHandlers.ofString()))) + .flatMap(body -> bodyHandler(body, typeReference)); + } + + private Failure errorMapper(Throwable throwable) { + if (throwable instanceof IOException) { + return IO_ERROR.with(throwable.getMessage()); + } + + if (throwable instanceof InterruptedException) { + return INTERRUPTED_OPERATION.with(throwable.getMessage()); + } + + return UNKNOWN.with(throwable.getClass().getName(), throwable.getMessage()); + } + + private Result bodyHandler( + HttpResponse body, TypeReference> reference) { + return deserialize(trace(body.body()), reference) + .flatMap( + response -> + response.rawError() == null + ? Result.ok(response.rawResult()) + : Result.fail(response.rawError().toFailure())); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/token/Amount.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/token/Amount.java index b383c66881..70e274887e 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/token/Amount.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/api/token/Amount.java @@ -67,27 +67,27 @@ import com.radixdlt.utils.UInt256; public interface Amount { - int SUB_UNITS_POW_10 = 18; + int SUB_UNITS_POW_10 = 18; - static Amount amount(long value) { - return () -> UInt256.from(value); - } + static Amount amount(long value) { + return () -> UInt256.from(value); + } - default UInt256 tokens() { - return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10)); - } + default UInt256 tokens() { + return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10)); + } - default UInt256 millis() { - return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10 - 3)); - } + default UInt256 millis() { + return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10 - 3)); + } - default UInt256 micros() { - return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10 - 6)); - } + default UInt256 micros() { + return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10 - 6)); + } - default UInt256 nanos() { - return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10 - 9)); - } + default UInt256 nanos() { + return subunits().multiply(UInt256.TEN.pow(SUB_UNITS_POW_10 - 9)); + } - UInt256 subunits(); + UInt256 subunits(); } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AccountBalance.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AccountBalance.java index 489590d1d3..d4391e85dd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AccountBalance.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AccountBalance.java @@ -66,58 +66,59 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; public final class AccountBalance { - private final List stakes; - private final List tokens; - private final List preparedStakes; + private final List stakes; + private final List tokens; + private final List preparedStakes; - private AccountBalance(List stakes, List tokens, List preparedStakes) { - this.stakes = stakes; - this.tokens = tokens; - this.preparedStakes = preparedStakes; - } + private AccountBalance( + List stakes, List tokens, List preparedStakes) { + this.stakes = stakes; + this.tokens = tokens; + this.preparedStakes = preparedStakes; + } - @JsonCreator - public static AccountBalance create( - @JsonProperty(value = "stakes", required = true) List stakes, - @JsonProperty(value = "preparedStakes", required = true) List preparedStakes, - @JsonProperty(value = "tokens", required = true) List tokens - ) { - return new AccountBalance(stakes, tokens, preparedStakes); - } + @JsonCreator + public static AccountBalance create( + @JsonProperty(value = "stakes", required = true) List stakes, + @JsonProperty(value = "preparedStakes", required = true) List preparedStakes, + @JsonProperty(value = "tokens", required = true) List tokens) { + return new AccountBalance(stakes, tokens, preparedStakes); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof AccountBalance)) { - return false; - } + if (!(o instanceof AccountBalance)) { + return false; + } - var that = (AccountBalance) o; - return stakes.equals(that.stakes) && tokens.equals(that.tokens) && preparedStakes.equals(that.preparedStakes); - } + var that = (AccountBalance) o; + return stakes.equals(that.stakes) + && tokens.equals(that.tokens) + && preparedStakes.equals(that.preparedStakes); + } - @Override - public int hashCode() { - return Objects.hash(stakes, tokens, preparedStakes); - } + @Override + public int hashCode() { + return Objects.hash(stakes, tokens, preparedStakes); + } - public List getStakes() { - return stakes; - } + public List getStakes() { + return stakes; + } - public List getPreparedStakes() { - return preparedStakes; - } + public List getPreparedStakes() { + return preparedStakes; + } - public List getTokens() { - return tokens; - } + public List getTokens() { + return tokens; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Action.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Action.java index 9ba5932d2c..62a1014958 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Action.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Action.java @@ -71,114 +71,122 @@ import com.radixdlt.client.lib.api.ActionType; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; import java.util.Optional; public final class Action { - @JsonProperty("type") - private final ActionType type; - - @JsonProperty("from") - private final AccountAddress from; - - @JsonProperty("to") - private final AccountAddress to; - - @JsonProperty("validator") - private final ValidatorAddress validator; - - @JsonProperty("amount") - private final UInt256 amount; - - @JsonProperty("rri") - private final String rri; - - private Action( - ActionType type, AccountAddress from, AccountAddress to, ValidatorAddress validator, UInt256 amount, String rri - ) { - this.type = type; - this.from = from; - this.to = to; - this.validator = validator; - this.amount = amount; - this.rri = rri; - } - - @JsonCreator - public static Action create( - @JsonProperty("type") ActionType type, - @JsonProperty("from") AccountAddress from, - @JsonProperty("to") AccountAddress to, - @JsonProperty("validator") ValidatorAddress validator, - @JsonProperty("amount") UInt256 amount, - @JsonProperty("rri") String rri - ) { - return new Action(type, from, to, validator, amount, rri); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Action)) { - return false; - } - - var actionDTO = (Action) o; - return type == actionDTO.type - && Objects.equals(from, actionDTO.from) - && Objects.equals(to, actionDTO.to) - && Objects.equals(validator, actionDTO.validator) - && Objects.equals(amount, actionDTO.amount) - && Objects.equals(rri, actionDTO.rri); - } - - @Override - public int hashCode() { - return Objects.hash(type, from, to, validator, amount, rri); - } - - @Override - public String toString() { - return "Action(" - + "type=" + type - + ", from=" + from - + ", to=" + to - + ", validator=" + validator - + ", amount=" + amount - + ", rri=" + rri - + ')'; - } - - @JsonIgnore - public ActionType getType() { - return type; - } - - @JsonIgnore - public Optional getFrom() { - return Optional.ofNullable(from); - } - - @JsonIgnore - public Optional getTo() { - return Optional.ofNullable(to); - } - - @JsonIgnore - public Optional getValidator() { - return Optional.ofNullable(validator); - } - - @JsonIgnore - public Optional getAmount() { - return Optional.ofNullable(amount); - } - - @JsonIgnore - public Optional getRri() { - return Optional.ofNullable(rri); - } + @JsonProperty("type") + private final ActionType type; + + @JsonProperty("from") + private final AccountAddress from; + + @JsonProperty("to") + private final AccountAddress to; + + @JsonProperty("validator") + private final ValidatorAddress validator; + + @JsonProperty("amount") + private final UInt256 amount; + + @JsonProperty("rri") + private final String rri; + + private Action( + ActionType type, + AccountAddress from, + AccountAddress to, + ValidatorAddress validator, + UInt256 amount, + String rri) { + this.type = type; + this.from = from; + this.to = to; + this.validator = validator; + this.amount = amount; + this.rri = rri; + } + + @JsonCreator + public static Action create( + @JsonProperty("type") ActionType type, + @JsonProperty("from") AccountAddress from, + @JsonProperty("to") AccountAddress to, + @JsonProperty("validator") ValidatorAddress validator, + @JsonProperty("amount") UInt256 amount, + @JsonProperty("rri") String rri) { + return new Action(type, from, to, validator, amount, rri); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Action)) { + return false; + } + + var actionDTO = (Action) o; + return type == actionDTO.type + && Objects.equals(from, actionDTO.from) + && Objects.equals(to, actionDTO.to) + && Objects.equals(validator, actionDTO.validator) + && Objects.equals(amount, actionDTO.amount) + && Objects.equals(rri, actionDTO.rri); + } + + @Override + public int hashCode() { + return Objects.hash(type, from, to, validator, amount, rri); + } + + @Override + public String toString() { + return "Action(" + + "type=" + + type + + ", from=" + + from + + ", to=" + + to + + ", validator=" + + validator + + ", amount=" + + amount + + ", rri=" + + rri + + ')'; + } + + @JsonIgnore + public ActionType getType() { + return type; + } + + @JsonIgnore + public Optional getFrom() { + return Optional.ofNullable(from); + } + + @JsonIgnore + public Optional getTo() { + return Optional.ofNullable(to); + } + + @JsonIgnore + public Optional getValidator() { + return Optional.ofNullable(validator); + } + + @JsonIgnore + public Optional getAmount() { + return Optional.ofNullable(amount); + } + + @JsonIgnore + public Optional getRri() { + return Optional.ofNullable(rri); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AddressBookEntry.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AddressBookEntry.java index 5f0dae33d2..f96287bf0d 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AddressBookEntry.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/AddressBookEntry.java @@ -67,69 +67,70 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.NodeAddress; - import java.util.List; import java.util.Objects; public final class AddressBookEntry { - private final NodeAddress address; - private final boolean banned; - private final List knownAddresses; + private final NodeAddress address; + private final boolean banned; + private final List knownAddresses; - private AddressBookEntry(NodeAddress address, boolean banned, List knownAddresses) { - this.address = address; - this.banned = banned; - this.knownAddresses = knownAddresses; - } + private AddressBookEntry(NodeAddress address, boolean banned, List knownAddresses) { + this.address = address; + this.banned = banned; + this.knownAddresses = knownAddresses; + } - @JsonCreator - public static AddressBookEntry create( - @JsonProperty(value = "address", required = true) NodeAddress address, - @JsonProperty(value = "banned", required = true) boolean banned, - @JsonProperty(value = "knownAddresses", required = true) List knownAddresses - ) { - return new AddressBookEntry(address, banned, knownAddresses); - } + @JsonCreator + public static AddressBookEntry create( + @JsonProperty(value = "address", required = true) NodeAddress address, + @JsonProperty(value = "banned", required = true) boolean banned, + @JsonProperty(value = "knownAddresses", required = true) List knownAddresses) { + return new AddressBookEntry(address, banned, knownAddresses); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof AddressBookEntry)) { - return false; - } + if (!(o instanceof AddressBookEntry)) { + return false; + } - var that = (AddressBookEntry) o; - return banned == that.banned - && address.equals(that.address) - && knownAddresses.equals(that.knownAddresses); - } + var that = (AddressBookEntry) o; + return banned == that.banned + && address.equals(that.address) + && knownAddresses.equals(that.knownAddresses); + } - @Override - public int hashCode() { - return Objects.hash(address, banned, knownAddresses); - } + @Override + public int hashCode() { + return Objects.hash(address, banned, knownAddresses); + } - @Override - public String toString() { - return "{" - + "address=" + address - + ", banned=" + banned - + ", addresses=" + knownAddresses - + '}'; - } + @Override + public String toString() { + return "{" + + "address=" + + address + + ", banned=" + + banned + + ", addresses=" + + knownAddresses + + '}'; + } - public NodeAddress getAddress() { - return address; - } + public NodeAddress getAddress() { + return address; + } - public boolean isBanned() { - return banned; - } + public boolean isBanned() { + return banned; + } - public List getKnownAddresses() { - return knownAddresses; - } + public List getKnownAddresses() { + return knownAddresses; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiConfiguration.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiConfiguration.java index b89c9e3548..02b91f1058 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiConfiguration.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiConfiguration.java @@ -66,44 +66,42 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; public final class ApiConfiguration { - private final List endpoints; + private final List endpoints; - private ApiConfiguration(List endpoints) { - this.endpoints = endpoints; - } + private ApiConfiguration(List endpoints) { + this.endpoints = endpoints; + } - @JsonCreator - public static ApiConfiguration create( - @JsonProperty(value = "endpoints", required = true) List endpoints - ) { - return new ApiConfiguration(endpoints); - } + @JsonCreator + public static ApiConfiguration create( + @JsonProperty(value = "endpoints", required = true) List endpoints) { + return new ApiConfiguration(endpoints); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ApiConfiguration)) { - return false; - } + if (!(o instanceof ApiConfiguration)) { + return false; + } - var that = (ApiConfiguration) o; - return endpoints.equals(that.endpoints); - } + var that = (ApiConfiguration) o; + return endpoints.equals(that.endpoints); + } - @Override - public int hashCode() { - return Objects.hash(endpoints); - } + @Override + public int hashCode() { + return Objects.hash(endpoints); + } - public List getEndpoints() { - return endpoints; - } + public List getEndpoints() { + return endpoints; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiData.java index f7fc23172f..ef2ec37029 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiData.java @@ -66,50 +66,48 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ApiData { - private final ApiDataElapsed elapsed; - private final ApiDataCount count; + private final ApiDataElapsed elapsed; + private final ApiDataCount count; - private ApiData(ApiDataElapsed elapsed, ApiDataCount count) { - this.elapsed = elapsed; - this.count = count; - } + private ApiData(ApiDataElapsed elapsed, ApiDataCount count) { + this.elapsed = elapsed; + this.count = count; + } - @JsonCreator - public static ApiData create( - @JsonProperty(value = "elapsed", required = true) ApiDataElapsed elapsed, - @JsonProperty(value = "count", required = true) ApiDataCount count - ) { - return new ApiData(elapsed, count); - } + @JsonCreator + public static ApiData create( + @JsonProperty(value = "elapsed", required = true) ApiDataElapsed elapsed, + @JsonProperty(value = "count", required = true) ApiDataCount count) { + return new ApiData(elapsed, count); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ApiData)) { - return false; - } + if (!(o instanceof ApiData)) { + return false; + } - var that = (ApiData) o; - return elapsed.equals(that.elapsed) && count.equals(that.count); - } + var that = (ApiData) o; + return elapsed.equals(that.elapsed) && count.equals(that.count); + } - @Override - public int hashCode() { - return Objects.hash(elapsed, count); - } + @Override + public int hashCode() { + return Objects.hash(elapsed, count); + } - public ApiDataElapsed getElapsed() { - return elapsed; - } + public ApiDataElapsed getElapsed() { + return elapsed; + } - public ApiDataCount getCount() { - return count; - } + public ApiDataCount getCount() { + return count; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataCount.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataCount.java index 8bbf700c8a..b81b94d04e 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataCount.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataCount.java @@ -66,47 +66,46 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ApiDataCount { - private final ApiDbCount apiDb; + private final ApiDbCount apiDb; - private ApiDataCount(ApiDbCount apiDb) { - this.apiDb = apiDb; - } + private ApiDataCount(ApiDbCount apiDb) { + this.apiDb = apiDb; + } - @JsonCreator - public static ApiDataCount create(@JsonProperty(value = "apidb", required = true) ApiDbCount apiDb) { - return new ApiDataCount(apiDb); - } + @JsonCreator + public static ApiDataCount create( + @JsonProperty(value = "apidb", required = true) ApiDbCount apiDb) { + return new ApiDataCount(apiDb); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ApiDataCount)) { - return false; - } + if (!(o instanceof ApiDataCount)) { + return false; + } - var that = (ApiDataCount) o; - return apiDb.equals(that.apiDb); - } + var that = (ApiDataCount) o; + return apiDb.equals(that.apiDb); + } - @Override - public int hashCode() { - return Objects.hash(apiDb); - } + @Override + public int hashCode() { + return Objects.hash(apiDb); + } - @Override - public String toString() { - return "{apiDb:" + apiDb + '}'; - } + @Override + public String toString() { + return "{apiDb:" + apiDb + '}'; + } - public ApiDbCount getApiDb() { - return apiDb; - } + public ApiDbCount getApiDb() { + return apiDb; + } } - diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataElapsed.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataElapsed.java index 9130b69152..941d11406b 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataElapsed.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDataElapsed.java @@ -66,46 +66,46 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ApiDataElapsed { - private final ApiDbElapsed apiDb; + private final ApiDbElapsed apiDb; - private ApiDataElapsed(ApiDbElapsed apiDb) { - this.apiDb = apiDb; - } + private ApiDataElapsed(ApiDbElapsed apiDb) { + this.apiDb = apiDb; + } - @JsonCreator - public static ApiDataElapsed create(@JsonProperty(value = "apidb", required = true) ApiDbElapsed apiDb) { - return new ApiDataElapsed(apiDb); - } + @JsonCreator + public static ApiDataElapsed create( + @JsonProperty(value = "apidb", required = true) ApiDbElapsed apiDb) { + return new ApiDataElapsed(apiDb); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ApiDataElapsed)) { - return false; - } + if (!(o instanceof ApiDataElapsed)) { + return false; + } - var that = (ApiDataElapsed) o; - return apiDb.equals(that.apiDb); - } + var that = (ApiDataElapsed) o; + return apiDb.equals(that.apiDb); + } - @Override - public int hashCode() { - return Objects.hash(apiDb); - } + @Override + public int hashCode() { + return Objects.hash(apiDb); + } - @Override - public String toString() { - return "{apiDb:" + apiDb + '}'; - } + @Override + public String toString() { + return "{apiDb:" + apiDb + '}'; + } - public ApiDbElapsed getApiDb() { - return apiDb; - } + public ApiDbElapsed getApiDb() { + return apiDb; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbCount.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbCount.java index 55b1f5032f..385b5a230c 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbCount.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbCount.java @@ -66,90 +66,93 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ApiDbCount { - private final Count flush; - private final Size queue; - private final ReadWriteStats balance; - private final ReadWriteStats transaction; - private final ReadWriteStats token; - - private ApiDbCount( - Count flush, - Size queue, - ReadWriteStats balance, - ReadWriteStats transaction, - ReadWriteStats token - ) { - this.flush = flush; - this.queue = queue; - this.balance = balance; - this.transaction = transaction; - this.token = token; - } - - @JsonCreator - public static ApiDbCount create( - @JsonProperty(value = "flush", required = true) Count flush, - @JsonProperty(value = "queue", required = true) Size queue, - @JsonProperty(value = "balance", required = true) ReadWriteStats balance, - @JsonProperty(value = "transaction", required = true) ReadWriteStats transaction, - @JsonProperty(value = "token", required = true) ReadWriteStats token - ) { - return new ApiDbCount(flush, queue, balance, transaction, token); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof ApiDbCount)) { - return false; - } - - var that = (ApiDbCount) o; - return flush.equals(that.flush) - && queue.equals(that.queue) - && balance.equals(that.balance) - && transaction.equals(that.transaction) - && token.equals(that.token); - } - - @Override - public int hashCode() { - return Objects.hash(flush, queue, balance, transaction, token); - } - - @Override - public String toString() { - return "{flush:" + flush - + ", queue:" + queue - + ", balance:" + balance - + ", transaction:" + transaction - + ", token:" + token + '}'; - } - - public Count getFlush() { - return flush; - } - - public Size getQueue() { - return queue; - } - - public ReadWriteStats getBalance() { - return balance; - } - - public ReadWriteStats getTransaction() { - return transaction; - } - - public ReadWriteStats getToken() { - return token; - } + private final Count flush; + private final Size queue; + private final ReadWriteStats balance; + private final ReadWriteStats transaction; + private final ReadWriteStats token; + + private ApiDbCount( + Count flush, + Size queue, + ReadWriteStats balance, + ReadWriteStats transaction, + ReadWriteStats token) { + this.flush = flush; + this.queue = queue; + this.balance = balance; + this.transaction = transaction; + this.token = token; + } + + @JsonCreator + public static ApiDbCount create( + @JsonProperty(value = "flush", required = true) Count flush, + @JsonProperty(value = "queue", required = true) Size queue, + @JsonProperty(value = "balance", required = true) ReadWriteStats balance, + @JsonProperty(value = "transaction", required = true) ReadWriteStats transaction, + @JsonProperty(value = "token", required = true) ReadWriteStats token) { + return new ApiDbCount(flush, queue, balance, transaction, token); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ApiDbCount)) { + return false; + } + + var that = (ApiDbCount) o; + return flush.equals(that.flush) + && queue.equals(that.queue) + && balance.equals(that.balance) + && transaction.equals(that.transaction) + && token.equals(that.token); + } + + @Override + public int hashCode() { + return Objects.hash(flush, queue, balance, transaction, token); + } + + @Override + public String toString() { + return "{flush:" + + flush + + ", queue:" + + queue + + ", balance:" + + balance + + ", transaction:" + + transaction + + ", token:" + + token + + '}'; + } + + public Count getFlush() { + return flush; + } + + public Size getQueue() { + return queue; + } + + public ReadWriteStats getBalance() { + return balance; + } + + public ReadWriteStats getTransaction() { + return transaction; + } + + public ReadWriteStats getToken() { + return token; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbElapsed.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbElapsed.java index 0e50a3cb48..98f9c2b7a2 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbElapsed.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ApiDbElapsed.java @@ -66,69 +66,78 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ApiDbElapsed { - private final ReadWrite balance; - private final ReadWrite transaction; - private final ReadWrite token; - private final TimeDTO flush; + private final ReadWrite balance; + private final ReadWrite transaction; + private final ReadWrite token; + private final TimeDTO flush; - private ApiDbElapsed(ReadWrite balance, ReadWrite transaction, ReadWrite token, TimeDTO flush) { - this.balance = balance; - this.transaction = transaction; - this.token = token; - this.flush = flush; - } + private ApiDbElapsed(ReadWrite balance, ReadWrite transaction, ReadWrite token, TimeDTO flush) { + this.balance = balance; + this.transaction = transaction; + this.token = token; + this.flush = flush; + } - @JsonCreator - public static ApiDbElapsed create( - @JsonProperty(value = "balance", required = true) ReadWrite balance, - @JsonProperty(value = "transaction", required = true) ReadWrite transaction, - @JsonProperty(value = "token", required = true) ReadWrite token, - @JsonProperty(value = "flush", required = true) TimeDTO flush - ) { - return new ApiDbElapsed(balance, transaction, token, flush); - } + @JsonCreator + public static ApiDbElapsed create( + @JsonProperty(value = "balance", required = true) ReadWrite balance, + @JsonProperty(value = "transaction", required = true) ReadWrite transaction, + @JsonProperty(value = "token", required = true) ReadWrite token, + @JsonProperty(value = "flush", required = true) TimeDTO flush) { + return new ApiDbElapsed(balance, transaction, token, flush); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ApiDbElapsed)) { - return false; - } + if (!(o instanceof ApiDbElapsed)) { + return false; + } - var that = (ApiDbElapsed) o; - return balance.equals(that.balance) && transaction.equals(that.transaction) && token.equals(that.token) && flush.equals(that.flush); - } + var that = (ApiDbElapsed) o; + return balance.equals(that.balance) + && transaction.equals(that.transaction) + && token.equals(that.token) + && flush.equals(that.flush); + } - @Override - public int hashCode() { - return Objects.hash(balance, transaction, token, flush); - } + @Override + public int hashCode() { + return Objects.hash(balance, transaction, token, flush); + } - @Override - public String toString() { - return "{balance:" + balance + ", transaction:" + transaction + ", token:" + token + ", flush:" + flush + '}'; - } + @Override + public String toString() { + return "{balance:" + + balance + + ", transaction:" + + transaction + + ", token:" + + token + + ", flush:" + + flush + + '}'; + } - public ReadWrite getBalance() { - return balance; - } + public ReadWrite getBalance() { + return balance; + } - public ReadWrite getTransaction() { - return transaction; - } + public ReadWrite getTransaction() { + return transaction; + } - public ReadWrite getToken() { - return token; - } + public ReadWrite getToken() { + return token; + } - public TimeDTO getFlush() { - return flush; - } + public TimeDTO getFlush() { + return flush; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Balance.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Balance.java index 08584266a0..707d367f14 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Balance.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Balance.java @@ -64,62 +64,60 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.UInt256; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class Balance { - private final String rri; - private final UInt256 amount; + private final String rri; + private final UInt256 amount; - private Balance(String rri, UInt256 amount) { - this.rri = rri; - this.amount = amount; - } + private Balance(String rri, UInt256 amount) { + this.rri = rri; + this.amount = amount; + } - @JsonCreator - public static Balance create( - @JsonProperty(value = "rri", required = true) String rri, - @JsonProperty(value = "amount", required = true) UInt256 amount - ) { - requireNonNull(rri); - requireNonNull(amount); + @JsonCreator + public static Balance create( + @JsonProperty(value = "rri", required = true) String rri, + @JsonProperty(value = "amount", required = true) UInt256 amount) { + requireNonNull(rri); + requireNonNull(amount); - return new Balance(rri, amount); - } + return new Balance(rri, amount); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Balance)) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Balance)) { + return false; + } - var that = (Balance) o; - return rri.equals(that.rri) && amount.equals(that.amount); - } + var that = (Balance) o; + return rri.equals(that.rri) && amount.equals(that.amount); + } - @Override - public int hashCode() { - return Objects.hash(rri, amount); - } + @Override + public int hashCode() { + return Objects.hash(rri, amount); + } - @Override - public String toString() { - return "Balance(" + "rri=" + rri + ", amount=" + amount + ')'; - } + @Override + public String toString() { + return "Balance(" + "rri=" + rri + ", amount=" + amount + ')'; + } - public String getRri() { - return rri; - } + public String getRri() { + return rri; + } - public UInt256 getAmount() { - return amount; - } + public UInt256 getAmount() { + return amount; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BalanceStakes.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BalanceStakes.java index b3c362441d..fbab320df1 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BalanceStakes.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BalanceStakes.java @@ -64,63 +64,61 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.UInt256; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class BalanceStakes { - private final String delegate; - private final UInt256 amount; + private final String delegate; + private final UInt256 amount; - private BalanceStakes(String delegate, UInt256 amount) { - this.delegate = delegate; - this.amount = amount; - } + private BalanceStakes(String delegate, UInt256 amount) { + this.delegate = delegate; + this.amount = amount; + } - @JsonCreator - public static BalanceStakes create( - @JsonProperty(value = "delegate", required = true) String validator, - @JsonProperty(value = "amount", required = true) UInt256 amount - ) { - requireNonNull(validator); - requireNonNull(amount); + @JsonCreator + public static BalanceStakes create( + @JsonProperty(value = "delegate", required = true) String validator, + @JsonProperty(value = "amount", required = true) UInt256 amount) { + requireNonNull(validator); + requireNonNull(amount); - return new BalanceStakes(validator, amount); - } + return new BalanceStakes(validator, amount); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof BalanceStakes)) { - return false; - } + if (!(o instanceof BalanceStakes)) { + return false; + } - var that = (BalanceStakes) o; - return delegate.equals(that.delegate) && amount.equals(that.amount); - } + var that = (BalanceStakes) o; + return delegate.equals(that.delegate) && amount.equals(that.amount); + } - @Override - public int hashCode() { - return Objects.hash(delegate, amount); - } + @Override + public int hashCode() { + return Objects.hash(delegate, amount); + } - @Override - public String toString() { - return "StakePositionsDTO(" + "validator=" + delegate + ", amount=" + amount + ')'; - } + @Override + public String toString() { + return "StakePositionsDTO(" + "validator=" + delegate + ", amount=" + amount + ')'; + } - public String getDelegate() { - return delegate; - } + public String getDelegate() { + return delegate; + } - public UInt256 getAmount() { - return amount; - } + public UInt256 getAmount() { + return amount; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BuiltTransaction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BuiltTransaction.java index 1a337baacf..5c6391b097 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BuiltTransaction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/BuiltTransaction.java @@ -64,69 +64,66 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.utils.UInt256; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class BuiltTransaction { - private final TxBlob transaction; - private final UInt256 fee; - - private BuiltTransaction(TxBlob transaction, UInt256 fee) { - this.transaction = transaction; - this.fee = fee; - } - - @JsonCreator - public static BuiltTransaction create( - @JsonProperty(value = "transaction", required = true) TxBlob transaction, - @JsonProperty(value = "fee", required = true) UInt256 fee - ) { - requireNonNull(transaction); - requireNonNull(fee); - - return new BuiltTransaction(transaction, fee); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof BuiltTransaction)) { - return false; - } - - var that = (BuiltTransaction) o; - return transaction.equals(that.transaction) && fee.equals(that.fee); - } - - @Override - public int hashCode() { - return Objects.hash(transaction, fee); - } - - @Override - public String toString() { - return "BuiltTransactionDTO(transaction=" + transaction - + ", fee=" + fee + ')'; - } - - public TxBlob getTransaction() { - return transaction; - } - - public UInt256 getFee() { - return fee; - } - - public FinalizedTransaction toFinalized(ECKeyPair keyPair) { - return FinalizedTransaction.create(this, keyPair); - } + private final TxBlob transaction; + private final UInt256 fee; + + private BuiltTransaction(TxBlob transaction, UInt256 fee) { + this.transaction = transaction; + this.fee = fee; + } + + @JsonCreator + public static BuiltTransaction create( + @JsonProperty(value = "transaction", required = true) TxBlob transaction, + @JsonProperty(value = "fee", required = true) UInt256 fee) { + requireNonNull(transaction); + requireNonNull(fee); + + return new BuiltTransaction(transaction, fee); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof BuiltTransaction)) { + return false; + } + + var that = (BuiltTransaction) o; + return transaction.equals(that.transaction) && fee.equals(that.fee); + } + + @Override + public int hashCode() { + return Objects.hash(transaction, fee); + } + + @Override + public String toString() { + return "BuiltTransactionDTO(transaction=" + transaction + ", fee=" + fee + ')'; + } + + public TxBlob getTransaction() { + return transaction; + } + + public UInt256 getFee() { + return fee; + } + + public FinalizedTransaction toFinalized(ECKeyPair keyPair) { + return FinalizedTransaction.create(this, keyPair); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ChannelType.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ChannelType.java index 4f3998ffa0..c5c4bfbda0 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ChannelType.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ChannelType.java @@ -65,19 +65,21 @@ package com.radixdlt.client.lib.dto; import com.fasterxml.jackson.annotation.JsonCreator; - import java.util.Locale; public enum ChannelType { - IN, - OUT; + IN, + OUT; - @JsonCreator - public static ChannelType create(String source) { - switch (source.toUpperCase(Locale.US)) { - case "IN": return IN; - case "OUT": return OUT; - default: throw new IllegalArgumentException("Unknown channel type " + source); - } - } + @JsonCreator + public static ChannelType create(String source) { + switch (source.toUpperCase(Locale.US)) { + case "IN": + return IN; + case "OUT": + return OUT; + default: + throw new IllegalArgumentException("Unknown channel type " + source); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Checkpoint.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Checkpoint.java index 93fedb99be..22ad4e82cd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Checkpoint.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Checkpoint.java @@ -66,56 +66,54 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; public final class Checkpoint { - private final List txn; - private final Proof proof; + private final List txn; + private final Proof proof; - private Checkpoint(List txn, Proof proof) { - this.txn = txn; - this.proof = proof; - } + private Checkpoint(List txn, Proof proof) { + this.txn = txn; + this.proof = proof; + } - @JsonCreator - public static Checkpoint create( - @JsonProperty(value = "txn", required = true) List txn, - @JsonProperty(value = "proof", required = true) Proof proof - ) { - return new Checkpoint(txn, proof); - } + @JsonCreator + public static Checkpoint create( + @JsonProperty(value = "txn", required = true) List txn, + @JsonProperty(value = "proof", required = true) Proof proof) { + return new Checkpoint(txn, proof); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof Checkpoint)) { - return false; - } + if (!(o instanceof Checkpoint)) { + return false; + } - var that = (Checkpoint) o; - return txn.equals(that.txn) && proof.equals(that.proof); - } + var that = (Checkpoint) o; + return txn.equals(that.txn) && proof.equals(that.proof); + } - @Override - public int hashCode() { - return Objects.hash(txn, proof); - } + @Override + public int hashCode() { + return Objects.hash(txn, proof); + } - @Override - public String toString() { - return "{txn:" + txn + ", proof:" + proof + '}'; - } + @Override + public String toString() { + return "{txn:" + txn + ", proof:" + proof + '}'; + } - public List getTxn() { - return txn; - } + public List getTxn() { + return txn; + } - public Proof getProof() { - return proof; - } + public Proof getProof() { + return proof; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusConfiguration.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusConfiguration.java index e44116afcf..2cf3eed386 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusConfiguration.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusConfiguration.java @@ -66,55 +66,57 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ConsensusConfiguration { - private final long pacemakerTimeout; - private final long bftSyncPatienceMs; + private final long pacemakerTimeout; + private final long bftSyncPatienceMs; - private ConsensusConfiguration(long pacemakerTimeout, long bftSyncPatienceMs) { - this.pacemakerTimeout = pacemakerTimeout; - this.bftSyncPatienceMs = bftSyncPatienceMs; - } + private ConsensusConfiguration(long pacemakerTimeout, long bftSyncPatienceMs) { + this.pacemakerTimeout = pacemakerTimeout; + this.bftSyncPatienceMs = bftSyncPatienceMs; + } - @JsonCreator - public static ConsensusConfiguration create( - @JsonProperty(value = "pacemakerTimeout", required = true) long pacemakerTimeout, - @JsonProperty(value = "bftSyncPatienceMs", required = true) long bftSyncPatienceMs - ) { - return new ConsensusConfiguration(pacemakerTimeout, bftSyncPatienceMs); - } + @JsonCreator + public static ConsensusConfiguration create( + @JsonProperty(value = "pacemakerTimeout", required = true) long pacemakerTimeout, + @JsonProperty(value = "bftSyncPatienceMs", required = true) long bftSyncPatienceMs) { + return new ConsensusConfiguration(pacemakerTimeout, bftSyncPatienceMs); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ConsensusConfiguration)) { - return false; - } + if (!(o instanceof ConsensusConfiguration)) { + return false; + } - var that = (ConsensusConfiguration) o; - return pacemakerTimeout == that.pacemakerTimeout && bftSyncPatienceMs == that.bftSyncPatienceMs; - } + var that = (ConsensusConfiguration) o; + return pacemakerTimeout == that.pacemakerTimeout && bftSyncPatienceMs == that.bftSyncPatienceMs; + } - @Override - public int hashCode() { - return Objects.hash(pacemakerTimeout, bftSyncPatienceMs); - } + @Override + public int hashCode() { + return Objects.hash(pacemakerTimeout, bftSyncPatienceMs); + } - @Override - public String toString() { - return "{pacemakerTimeout:" + pacemakerTimeout + ", bftSyncPatienceMs:" + bftSyncPatienceMs + '}'; - } + @Override + public String toString() { + return "{pacemakerTimeout:" + + pacemakerTimeout + + ", bftSyncPatienceMs:" + + bftSyncPatienceMs + + '}'; + } - public long getPacemakerTimeout() { - return pacemakerTimeout; - } + public long getPacemakerTimeout() { + return pacemakerTimeout; + } - public long getBftSyncPatienceMs() { - return bftSyncPatienceMs; - } + public long getBftSyncPatienceMs() { + return bftSyncPatienceMs; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusData.java index 1cadac39b0..052f0cc61f 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusData.java @@ -66,210 +66,220 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ConsensusData { - private final long stateVersion; - private final long voteQuorums; - private final long rejected; - private final long vertexStoreRebuilds; - private final long vertexStoreForks; - private final long timeout; - private final long vertexStoreSize; - private final long processed; - private final long consensusEvents; - private final long indirectParent; - private final long proposalsMade; - private final long timedOutViews; - private final long timeoutQuorums; - private final ConsensusDataSync sync; + private final long stateVersion; + private final long voteQuorums; + private final long rejected; + private final long vertexStoreRebuilds; + private final long vertexStoreForks; + private final long timeout; + private final long vertexStoreSize; + private final long processed; + private final long consensusEvents; + private final long indirectParent; + private final long proposalsMade; + private final long timedOutViews; + private final long timeoutQuorums; + private final ConsensusDataSync sync; - private ConsensusData( - long stateVersion, - long voteQuorums, - long rejected, - long vertexStoreRebuilds, - long vertexStoreForks, - long timeout, - long vertexStoreSize, - long processed, - long consensusEvents, - long indirectParent, - long proposalsMade, - long timedOutViews, - long timeoutQuorums, - ConsensusDataSync sync - ) { - this.stateVersion = stateVersion; - this.voteQuorums = voteQuorums; - this.rejected = rejected; - this.vertexStoreRebuilds = vertexStoreRebuilds; - this.vertexStoreForks = vertexStoreForks; - this.timeout = timeout; - this.vertexStoreSize = vertexStoreSize; - this.processed = processed; - this.consensusEvents = consensusEvents; - this.indirectParent = indirectParent; - this.proposalsMade = proposalsMade; - this.timedOutViews = timedOutViews; - this.timeoutQuorums = timeoutQuorums; - this.sync = sync; - } + private ConsensusData( + long stateVersion, + long voteQuorums, + long rejected, + long vertexStoreRebuilds, + long vertexStoreForks, + long timeout, + long vertexStoreSize, + long processed, + long consensusEvents, + long indirectParent, + long proposalsMade, + long timedOutViews, + long timeoutQuorums, + ConsensusDataSync sync) { + this.stateVersion = stateVersion; + this.voteQuorums = voteQuorums; + this.rejected = rejected; + this.vertexStoreRebuilds = vertexStoreRebuilds; + this.vertexStoreForks = vertexStoreForks; + this.timeout = timeout; + this.vertexStoreSize = vertexStoreSize; + this.processed = processed; + this.consensusEvents = consensusEvents; + this.indirectParent = indirectParent; + this.proposalsMade = proposalsMade; + this.timedOutViews = timedOutViews; + this.timeoutQuorums = timeoutQuorums; + this.sync = sync; + } - @JsonCreator - public static ConsensusData create( - @JsonProperty(value = "stateVersion", required = true) long stateVersion, - @JsonProperty(value = "voteQuorums", required = true) long voteQuorums, - @JsonProperty(value = "rejected", required = true) long rejected, - @JsonProperty(value = "vertexStoreRebuilds", required = true) long vertexStoreRebuilds, - @JsonProperty(value = "vertexStoreForks", required = true) long vertexStoreForks, - @JsonProperty(value = "timeout", required = true) long timeout, - @JsonProperty(value = "vertexStoreSize", required = true) long vertexStoreSize, - @JsonProperty(value = "processed", required = true) long processed, - @JsonProperty(value = "consensusEvents", required = true) long consensusEvents, - @JsonProperty(value = "indirectParent", required = true) long indirectParent, - @JsonProperty(value = "proposalsMade", required = true) long proposalsMade, - @JsonProperty(value = "timedOutViews", required = true) long timedOutViews, - @JsonProperty(value = "timeoutQuorums", required = true) long timeoutQuorums, - @JsonProperty(value = "sync", required = true) ConsensusDataSync sync - ) { - return new ConsensusData( - stateVersion, - voteQuorums, - rejected, - vertexStoreRebuilds, - vertexStoreForks, - timeout, - vertexStoreSize, - processed, - consensusEvents, - indirectParent, - proposalsMade, - timedOutViews, - timeoutQuorums, - sync - ); - } + @JsonCreator + public static ConsensusData create( + @JsonProperty(value = "stateVersion", required = true) long stateVersion, + @JsonProperty(value = "voteQuorums", required = true) long voteQuorums, + @JsonProperty(value = "rejected", required = true) long rejected, + @JsonProperty(value = "vertexStoreRebuilds", required = true) long vertexStoreRebuilds, + @JsonProperty(value = "vertexStoreForks", required = true) long vertexStoreForks, + @JsonProperty(value = "timeout", required = true) long timeout, + @JsonProperty(value = "vertexStoreSize", required = true) long vertexStoreSize, + @JsonProperty(value = "processed", required = true) long processed, + @JsonProperty(value = "consensusEvents", required = true) long consensusEvents, + @JsonProperty(value = "indirectParent", required = true) long indirectParent, + @JsonProperty(value = "proposalsMade", required = true) long proposalsMade, + @JsonProperty(value = "timedOutViews", required = true) long timedOutViews, + @JsonProperty(value = "timeoutQuorums", required = true) long timeoutQuorums, + @JsonProperty(value = "sync", required = true) ConsensusDataSync sync) { + return new ConsensusData( + stateVersion, + voteQuorums, + rejected, + vertexStoreRebuilds, + vertexStoreForks, + timeout, + vertexStoreSize, + processed, + consensusEvents, + indirectParent, + proposalsMade, + timedOutViews, + timeoutQuorums, + sync); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ConsensusData)) { - return false; - } + if (!(o instanceof ConsensusData)) { + return false; + } - var that = (ConsensusData) o; - return stateVersion == that.stateVersion - && voteQuorums == that.voteQuorums - && rejected == that.rejected - && vertexStoreRebuilds == that.vertexStoreRebuilds - && vertexStoreForks == that.vertexStoreForks - && timeout == that.timeout - && vertexStoreSize == that.vertexStoreSize - && processed == that.processed - && consensusEvents == that.consensusEvents - && indirectParent == that.indirectParent - && proposalsMade == that.proposalsMade - && timedOutViews == that.timedOutViews - && timeoutQuorums == that.timeoutQuorums - && sync.equals(that.sync); - } + var that = (ConsensusData) o; + return stateVersion == that.stateVersion + && voteQuorums == that.voteQuorums + && rejected == that.rejected + && vertexStoreRebuilds == that.vertexStoreRebuilds + && vertexStoreForks == that.vertexStoreForks + && timeout == that.timeout + && vertexStoreSize == that.vertexStoreSize + && processed == that.processed + && consensusEvents == that.consensusEvents + && indirectParent == that.indirectParent + && proposalsMade == that.proposalsMade + && timedOutViews == that.timedOutViews + && timeoutQuorums == that.timeoutQuorums + && sync.equals(that.sync); + } - @Override - public int hashCode() { - return Objects.hash( - stateVersion, - voteQuorums, - rejected, - vertexStoreRebuilds, - vertexStoreForks, - timeout, - vertexStoreSize, - processed, - consensusEvents, - indirectParent, - proposalsMade, - timedOutViews, - timeoutQuorums, - sync - ); - } + @Override + public int hashCode() { + return Objects.hash( + stateVersion, + voteQuorums, + rejected, + vertexStoreRebuilds, + vertexStoreForks, + timeout, + vertexStoreSize, + processed, + consensusEvents, + indirectParent, + proposalsMade, + timedOutViews, + timeoutQuorums, + sync); + } - @Override - public String toString() { - return "{stateVersion:" + stateVersion - + ", voteQuorums:" + voteQuorums - + ", rejected:" + rejected - + ", vertexStoreRebuilds:" + vertexStoreRebuilds - + ", vertexStoreForks:" + vertexStoreForks - + ", timeout:" + timeout - + ", vertexStoreSize:" + vertexStoreSize - + ", processed:" + processed - + ", consensusEvents:" + consensusEvents - + ", indirectParent:" + indirectParent - + ", proposalsMade:" + proposalsMade - + ", timedOutViews:" + timedOutViews - + ", timeoutQuorums:" + timeoutQuorums - + ", sync:" + sync + '}'; - } + @Override + public String toString() { + return "{stateVersion:" + + stateVersion + + ", voteQuorums:" + + voteQuorums + + ", rejected:" + + rejected + + ", vertexStoreRebuilds:" + + vertexStoreRebuilds + + ", vertexStoreForks:" + + vertexStoreForks + + ", timeout:" + + timeout + + ", vertexStoreSize:" + + vertexStoreSize + + ", processed:" + + processed + + ", consensusEvents:" + + consensusEvents + + ", indirectParent:" + + indirectParent + + ", proposalsMade:" + + proposalsMade + + ", timedOutViews:" + + timedOutViews + + ", timeoutQuorums:" + + timeoutQuorums + + ", sync:" + + sync + + '}'; + } - public long getStateVersion() { - return stateVersion; - } + public long getStateVersion() { + return stateVersion; + } - public long getVoteQuorums() { - return voteQuorums; - } + public long getVoteQuorums() { + return voteQuorums; + } - public long getRejected() { - return rejected; - } + public long getRejected() { + return rejected; + } - public long getVertexStoreRebuilds() { - return vertexStoreRebuilds; - } + public long getVertexStoreRebuilds() { + return vertexStoreRebuilds; + } - public long getVertexStoreForks() { - return vertexStoreForks; - } + public long getVertexStoreForks() { + return vertexStoreForks; + } - public long getTimeout() { - return timeout; - } + public long getTimeout() { + return timeout; + } - public long getVertexStoreSize() { - return vertexStoreSize; - } + public long getVertexStoreSize() { + return vertexStoreSize; + } - public long getProcessed() { - return processed; - } + public long getProcessed() { + return processed; + } - public long getConsensusEvents() { - return consensusEvents; - } + public long getConsensusEvents() { + return consensusEvents; + } - public long getIndirectParent() { - return indirectParent; - } + public long getIndirectParent() { + return indirectParent; + } - public long getProposalsMade() { - return proposalsMade; - } + public long getProposalsMade() { + return proposalsMade; + } - public long getTimedOutViews() { - return timedOutViews; - } + public long getTimedOutViews() { + return timedOutViews; + } - public long getTimeoutQuorums() { - return timeoutQuorums; - } + public long getTimeoutQuorums() { + return timeoutQuorums; + } - public ConsensusDataSync getSync() { - return sync; - } + public ConsensusDataSync getSync() { + return sync; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusDataSync.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusDataSync.java index c81363c693..d24fcc4386 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusDataSync.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ConsensusDataSync.java @@ -66,55 +66,53 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ConsensusDataSync { - private final long requestTimeouts; - private final long requestsSent; + private final long requestTimeouts; + private final long requestsSent; - private ConsensusDataSync(long requestTimeouts, long requestsSent) { - this.requestTimeouts = requestTimeouts; - this.requestsSent = requestsSent; - } + private ConsensusDataSync(long requestTimeouts, long requestsSent) { + this.requestTimeouts = requestTimeouts; + this.requestsSent = requestsSent; + } - @JsonCreator - public static ConsensusDataSync create( - @JsonProperty("request_timeouts") long requestTimeouts, - @JsonProperty("requests_sent") long requestsSent - ) { - return new ConsensusDataSync(requestTimeouts, requestsSent); - } + @JsonCreator + public static ConsensusDataSync create( + @JsonProperty("request_timeouts") long requestTimeouts, + @JsonProperty("requests_sent") long requestsSent) { + return new ConsensusDataSync(requestTimeouts, requestsSent); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ConsensusDataSync)) { - return false; - } + if (!(o instanceof ConsensusDataSync)) { + return false; + } - var that = (ConsensusDataSync) o; - return requestTimeouts == that.requestTimeouts && requestsSent == that.requestsSent; - } + var that = (ConsensusDataSync) o; + return requestTimeouts == that.requestTimeouts && requestsSent == that.requestsSent; + } - @Override - public int hashCode() { - return Objects.hash(requestTimeouts, requestsSent); - } + @Override + public int hashCode() { + return Objects.hash(requestTimeouts, requestsSent); + } - @Override - public String toString() { - return "{requestTimeouts:" + requestTimeouts + ", requestsSent:" + requestsSent + '}'; - } + @Override + public String toString() { + return "{requestTimeouts:" + requestTimeouts + ", requestsSent:" + requestsSent + '}'; + } - public long getRequestTimeouts() { - return requestTimeouts; - } + public long getRequestTimeouts() { + return requestTimeouts; + } - public long getRequestsSent() { - return requestsSent; - } + public long getRequestsSent() { + return requestsSent; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Count.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Count.java index fb4f15d87c..b1b699ccd0 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Count.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Count.java @@ -66,46 +66,45 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class Count { - private final long count; + private final long count; - private Count(long count) { - this.count = count; - } + private Count(long count) { + this.count = count; + } - @JsonCreator - public static Count create(@JsonProperty(value = "count", required = true) long count) { - return new Count(count); - } + @JsonCreator + public static Count create(@JsonProperty(value = "count", required = true) long count) { + return new Count(count); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof Count)) { - return false; - } + if (!(o instanceof Count)) { + return false; + } - var countDTO = (Count) o; - return count == countDTO.count; - } + var countDTO = (Count) o; + return count == countDTO.count; + } - @Override - public int hashCode() { - return Objects.hash(count); - } + @Override + public int hashCode() { + return Objects.hash(count); + } - @Override - public String toString() { - return "{count:" + count + '}'; - } + @Override + public String toString() { + return "{count:" + count + '}'; + } - public long getCount() { - return count; - } + public long getCount() { + return count; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/CurrentEpochInfo.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/CurrentEpochInfo.java index 5731c7a0fa..8579284425 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/CurrentEpochInfo.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/CurrentEpochInfo.java @@ -68,125 +68,144 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Objects; public final class CurrentEpochInfo { - private final AccountAddress owner; - private final double uptimePercentage; - private final double validatorFee; - private final long proposalsMissed; - private final long proposalsCompleted; - private final List stakes; - private final boolean registered; - private final UInt256 totalStake; - - private CurrentEpochInfo( - AccountAddress owner, - double uptimePercentage, - double validatorFee, - long proposalsMissed, - long proposalsCompleted, - List stakes, - boolean registered, - UInt256 totalStake - ) { - this.owner = owner; - this.uptimePercentage = uptimePercentage; - this.validatorFee = validatorFee; - this.proposalsMissed = proposalsMissed; - this.proposalsCompleted = proposalsCompleted; - this.stakes = stakes; - this.registered = registered; - this.totalStake = totalStake; - } - - @JsonCreator - public static CurrentEpochInfo create( - @JsonProperty(value = "owner", required = true) AccountAddress owner, - @JsonProperty(value = "uptimePercentage", required = true) double uptimePercentage, - @JsonProperty(value = "validatorFee", required = true) double validatorFee, - @JsonProperty(value = "proposalsMissed", required = true) long proposalsMissed, - @JsonProperty(value = "proposalsCompleted", required = true) long proposalsCompleted, - @JsonProperty(value = "stakes", required = true) List stakes, - @JsonProperty(value = "registered", required = true) boolean registered, - @JsonProperty(value = "totalStake", required = true) UInt256 totalStake - ) { - return new CurrentEpochInfo( - owner, uptimePercentage, validatorFee, proposalsMissed, proposalsCompleted, stakes, registered, totalStake - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof CurrentEpochInfo)) { - return false; - } - - var that = (CurrentEpochInfo) o; - return Double.compare(that.uptimePercentage, uptimePercentage) == 0 - && Double.compare(that.validatorFee, validatorFee) == 0 - && proposalsMissed == that.proposalsMissed - && proposalsCompleted == that.proposalsCompleted - && registered == that.registered - && owner.equals(that.owner) - && stakes.equals(that.stakes) - && totalStake.equals(that.totalStake); - } - - @Override - public int hashCode() { - return Objects.hash(owner, uptimePercentage, validatorFee, proposalsMissed, proposalsCompleted, stakes, registered, totalStake); - } - - @Override - public String toString() { - return "{" - + "owner=" + owner - + ", uptimePercentage=" + uptimePercentage - + ", validatorFee=" + validatorFee - + ", proposalsMissed=" + proposalsMissed - + ", proposalsCompleted=" + proposalsCompleted - + ", stakes=" + stakes - + ", registered=" + registered - + ", totalStake=" + totalStake - + '}'; - } - - public AccountAddress getOwner() { - return owner; - } - - public double getUptimePercentage() { - return uptimePercentage; - } - - public double getValidatorFee() { - return validatorFee; - } - - public long getProposalsMissed() { - return proposalsMissed; - } - - public long getProposalsCompleted() { - return proposalsCompleted; - } - - public List getStakes() { - return stakes; - } - - public boolean isRegistered() { - return registered; - } - - public UInt256 getTotalStake() { - return totalStake; - } + private final AccountAddress owner; + private final double uptimePercentage; + private final double validatorFee; + private final long proposalsMissed; + private final long proposalsCompleted; + private final List stakes; + private final boolean registered; + private final UInt256 totalStake; + + private CurrentEpochInfo( + AccountAddress owner, + double uptimePercentage, + double validatorFee, + long proposalsMissed, + long proposalsCompleted, + List stakes, + boolean registered, + UInt256 totalStake) { + this.owner = owner; + this.uptimePercentage = uptimePercentage; + this.validatorFee = validatorFee; + this.proposalsMissed = proposalsMissed; + this.proposalsCompleted = proposalsCompleted; + this.stakes = stakes; + this.registered = registered; + this.totalStake = totalStake; + } + + @JsonCreator + public static CurrentEpochInfo create( + @JsonProperty(value = "owner", required = true) AccountAddress owner, + @JsonProperty(value = "uptimePercentage", required = true) double uptimePercentage, + @JsonProperty(value = "validatorFee", required = true) double validatorFee, + @JsonProperty(value = "proposalsMissed", required = true) long proposalsMissed, + @JsonProperty(value = "proposalsCompleted", required = true) long proposalsCompleted, + @JsonProperty(value = "stakes", required = true) List stakes, + @JsonProperty(value = "registered", required = true) boolean registered, + @JsonProperty(value = "totalStake", required = true) UInt256 totalStake) { + return new CurrentEpochInfo( + owner, + uptimePercentage, + validatorFee, + proposalsMissed, + proposalsCompleted, + stakes, + registered, + totalStake); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof CurrentEpochInfo)) { + return false; + } + + var that = (CurrentEpochInfo) o; + return Double.compare(that.uptimePercentage, uptimePercentage) == 0 + && Double.compare(that.validatorFee, validatorFee) == 0 + && proposalsMissed == that.proposalsMissed + && proposalsCompleted == that.proposalsCompleted + && registered == that.registered + && owner.equals(that.owner) + && stakes.equals(that.stakes) + && totalStake.equals(that.totalStake); + } + + @Override + public int hashCode() { + return Objects.hash( + owner, + uptimePercentage, + validatorFee, + proposalsMissed, + proposalsCompleted, + stakes, + registered, + totalStake); + } + + @Override + public String toString() { + return "{" + + "owner=" + + owner + + ", uptimePercentage=" + + uptimePercentage + + ", validatorFee=" + + validatorFee + + ", proposalsMissed=" + + proposalsMissed + + ", proposalsCompleted=" + + proposalsCompleted + + ", stakes=" + + stakes + + ", registered=" + + registered + + ", totalStake=" + + totalStake + + '}'; + } + + public AccountAddress getOwner() { + return owner; + } + + public double getUptimePercentage() { + return uptimePercentage; + } + + public double getValidatorFee() { + return validatorFee; + } + + public long getProposalsMissed() { + return proposalsMissed; + } + + public long getProposalsCompleted() { + return proposalsCompleted; + } + + public List getStakes() { + return stakes; + } + + public boolean isRegistered() { + return registered; + } + + public UInt256 getTotalStake() { + return totalStake; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/DelegatedStake.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/DelegatedStake.java index 8d89b1d98a..94bd136b22 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/DelegatedStake.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/DelegatedStake.java @@ -68,55 +68,53 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class DelegatedStake { - private final UInt256 amount; - private final AccountAddress delegator; + private final UInt256 amount; + private final AccountAddress delegator; - private DelegatedStake(UInt256 amount, AccountAddress delegator) { - this.amount = amount; - this.delegator = delegator; - } + private DelegatedStake(UInt256 amount, AccountAddress delegator) { + this.amount = amount; + this.delegator = delegator; + } - @JsonCreator - public static DelegatedStake create( - @JsonProperty(value = "amount", required = true) UInt256 amount, - @JsonProperty(value = "delegator", required = true) AccountAddress delegator - ) { - return new DelegatedStake(amount, delegator); - } + @JsonCreator + public static DelegatedStake create( + @JsonProperty(value = "amount", required = true) UInt256 amount, + @JsonProperty(value = "delegator", required = true) AccountAddress delegator) { + return new DelegatedStake(amount, delegator); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof DelegatedStake)) { - return false; - } + if (!(o instanceof DelegatedStake)) { + return false; + } - var that = (DelegatedStake) o; - return amount.equals(that.amount) && delegator.equals(that.delegator); - } + var that = (DelegatedStake) o; + return amount.equals(that.amount) && delegator.equals(that.delegator); + } - @Override - public int hashCode() { - return Objects.hash(amount, delegator); - } + @Override + public int hashCode() { + return Objects.hash(amount, delegator); + } - @Override - public String toString() { - return "{amount:" + amount + ", delegator=" + delegator + '}'; - } + @Override + public String toString() { + return "{amount:" + amount + ", delegator=" + delegator + '}'; + } - public UInt256 getAmount() { - return amount; - } + public UInt256 getAmount() { + return amount; + } - public AccountAddress getDelegator() { - return delegator; - } + public AccountAddress getDelegator() { + return delegator; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochData.java index e8585e7520..a56438ee6c 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochData.java @@ -66,48 +66,46 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; public final class EpochData { - private final List validators; + private final List validators; - private EpochData(List validators) { - this.validators = validators; - } + private EpochData(List validators) { + this.validators = validators; + } - @JsonCreator - public static EpochData create(@JsonProperty("validators") List validators) { - return new EpochData(validators); - } + @JsonCreator + public static EpochData create(@JsonProperty("validators") List validators) { + return new EpochData(validators); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof EpochData)) { - return false; - } + if (!(o instanceof EpochData)) { + return false; + } - var epochData = (EpochData) o; - return validators.equals(epochData.validators); - } + var epochData = (EpochData) o; + return validators.equals(epochData.validators); + } - @Override - public int hashCode() { - return Objects.hash(validators); - } + @Override + public int hashCode() { + return Objects.hash(validators); + } - @Override - public String toString() { - return "{validators:" + validators + '}'; - } + @Override + public String toString() { + return "{validators:" + validators + '}'; + } - public List getValidators() { - return validators; - } + public List getValidators() { + return validators; + } } - diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochInfo.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochInfo.java index a8dc5273b5..d6b29b8ab2 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochInfo.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochInfo.java @@ -66,58 +66,53 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class EpochInfo { - private final CurrentEpochInfo current; - private final Updates updates; + private final CurrentEpochInfo current; + private final Updates updates; - private EpochInfo(CurrentEpochInfo current, Updates updates) { - this.current = current; - this.updates = updates; - } + private EpochInfo(CurrentEpochInfo current, Updates updates) { + this.current = current; + this.updates = updates; + } - @JsonCreator - public static EpochInfo create( - @JsonProperty(value = "current", required = true) CurrentEpochInfo current, - @JsonProperty(value = "updates", required = true) Updates updates - ) { - return new EpochInfo(current, updates); - } + @JsonCreator + public static EpochInfo create( + @JsonProperty(value = "current", required = true) CurrentEpochInfo current, + @JsonProperty(value = "updates", required = true) Updates updates) { + return new EpochInfo(current, updates); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof EpochInfo)) { - return false; - } + if (!(o instanceof EpochInfo)) { + return false; + } - var epochInfo = (EpochInfo) o; - return current.equals(epochInfo.current) && updates.equals(epochInfo.updates); - } + var epochInfo = (EpochInfo) o; + return current.equals(epochInfo.current) && updates.equals(epochInfo.updates); + } - @Override - public int hashCode() { - return Objects.hash(current, updates); - } + @Override + public int hashCode() { + return Objects.hash(current, updates); + } - @Override - public String toString() { - return "{" - + "current=" + current - + ", updates=" + updates - + '}'; - } + @Override + public String toString() { + return "{" + "current=" + current + ", updates=" + updates + '}'; + } - public CurrentEpochInfo getCurrent() { - return current; - } + public CurrentEpochInfo getCurrent() { + return current; + } - public Updates getUpdates() { - return updates; - } + public Updates getUpdates() { + return updates; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochValidatorData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochValidatorData.java index beaf0d6c1f..81a815ac8d 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochValidatorData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/EpochValidatorData.java @@ -68,92 +68,96 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class EpochValidatorData { - private final UInt256 totalDelegatedStake; - private final double uptimePercentage; - private final long proposalsMissed; - private final long proposalsCompleted; - private final ValidatorAddress address; - - private EpochValidatorData( - UInt256 totalDelegatedStake, - double uptimePercentage, - long proposalsMissed, - long proposalsCompleted, - ValidatorAddress address - ) { - this.totalDelegatedStake = totalDelegatedStake; - this.uptimePercentage = uptimePercentage; - this.proposalsMissed = proposalsMissed; - this.proposalsCompleted = proposalsCompleted; - this.address = address; - } - - @JsonCreator - public static EpochValidatorData create( - @JsonProperty(value = "totalDelegatedStake", required = true) UInt256 totalDelegatedStake, - @JsonProperty(value = "uptimePercentage", required = true) double uptimePercentage, - @JsonProperty(value = "proposalsMissed", required = true) long proposalsMissed, - @JsonProperty(value = "proposalsCompleted", required = true) long proposalsCompleted, - @JsonProperty(value = "address", required = true) ValidatorAddress address - ) { - return new EpochValidatorData(totalDelegatedStake, uptimePercentage, proposalsMissed, proposalsCompleted, address); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof EpochValidatorData)) { - return false; - } - - var that = (EpochValidatorData) o; - return Double.compare(that.uptimePercentage, uptimePercentage) == 0 - && proposalsMissed == that.proposalsMissed - && proposalsCompleted == that.proposalsCompleted - && totalDelegatedStake.equals(that.totalDelegatedStake) - && address.equals(that.address); - } - - @Override - public int hashCode() { - return Objects.hash(totalDelegatedStake, uptimePercentage, proposalsMissed, proposalsCompleted, address); - } - - @Override - public String toString() { - return "{" - + "totalDelegatedStake=" + totalDelegatedStake - + ", uptimePercentage=" + uptimePercentage - + ", proposalsMissed=" + proposalsMissed - + ", proposalsCompleted=" + proposalsCompleted - + ", address=" + address - + '}'; - } - - public UInt256 getTotalDelegatedStake() { - return totalDelegatedStake; - } - - public double getUptimePercentage() { - return uptimePercentage; - } - - public long getProposalsMissed() { - return proposalsMissed; - } - - public long getProposalsCompleted() { - return proposalsCompleted; - } - - public ValidatorAddress getAddress() { - return address; - } + private final UInt256 totalDelegatedStake; + private final double uptimePercentage; + private final long proposalsMissed; + private final long proposalsCompleted; + private final ValidatorAddress address; + + private EpochValidatorData( + UInt256 totalDelegatedStake, + double uptimePercentage, + long proposalsMissed, + long proposalsCompleted, + ValidatorAddress address) { + this.totalDelegatedStake = totalDelegatedStake; + this.uptimePercentage = uptimePercentage; + this.proposalsMissed = proposalsMissed; + this.proposalsCompleted = proposalsCompleted; + this.address = address; + } + + @JsonCreator + public static EpochValidatorData create( + @JsonProperty(value = "totalDelegatedStake", required = true) UInt256 totalDelegatedStake, + @JsonProperty(value = "uptimePercentage", required = true) double uptimePercentage, + @JsonProperty(value = "proposalsMissed", required = true) long proposalsMissed, + @JsonProperty(value = "proposalsCompleted", required = true) long proposalsCompleted, + @JsonProperty(value = "address", required = true) ValidatorAddress address) { + return new EpochValidatorData( + totalDelegatedStake, uptimePercentage, proposalsMissed, proposalsCompleted, address); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof EpochValidatorData)) { + return false; + } + + var that = (EpochValidatorData) o; + return Double.compare(that.uptimePercentage, uptimePercentage) == 0 + && proposalsMissed == that.proposalsMissed + && proposalsCompleted == that.proposalsCompleted + && totalDelegatedStake.equals(that.totalDelegatedStake) + && address.equals(that.address); + } + + @Override + public int hashCode() { + return Objects.hash( + totalDelegatedStake, uptimePercentage, proposalsMissed, proposalsCompleted, address); + } + + @Override + public String toString() { + return "{" + + "totalDelegatedStake=" + + totalDelegatedStake + + ", uptimePercentage=" + + uptimePercentage + + ", proposalsMissed=" + + proposalsMissed + + ", proposalsCompleted=" + + proposalsCompleted + + ", address=" + + address + + '}'; + } + + public UInt256 getTotalDelegatedStake() { + return totalDelegatedStake; + } + + public double getUptimePercentage() { + return uptimePercentage; + } + + public long getProposalsMissed() { + return proposalsMissed; + } + + public long getProposalsCompleted() { + return proposalsCompleted; + } + + public ValidatorAddress getAddress() { + return address; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Event.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Event.java index 99e6cb8872..b19033439c 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Event.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Event.java @@ -68,65 +68,57 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.EventType; - import java.util.Objects; import java.util.Optional; public final class Event { - @JsonProperty("type") - private final EventType type; + @JsonProperty("type") + private final EventType type; - @JsonProperty("rri") - private final String rri; + @JsonProperty("rri") + private final String rri; - private Event( - EventType type, String rri) { - this.type = type; - this.rri = rri; - } + private Event(EventType type, String rri) { + this.type = type; + this.rri = rri; + } - @JsonCreator - public static Event create( - @JsonProperty("type") EventType type, - @JsonProperty("rri") String rri - ) { - return new Event(type, rri); - } + @JsonCreator + public static Event create( + @JsonProperty("type") EventType type, @JsonProperty("rri") String rri) { + return new Event(type, rri); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Event)) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Event)) { + return false; + } - var eventDTO = (Event) o; - return type == eventDTO.type - && Objects.equals(rri, eventDTO.rri); - } + var eventDTO = (Event) o; + return type == eventDTO.type && Objects.equals(rri, eventDTO.rri); + } - @Override - public int hashCode() { - return Objects.hash(type, rri); - } + @Override + public int hashCode() { + return Objects.hash(type, rri); + } - @Override - public String toString() { - return "Event(" - + "type=" + type - + ", rri=" + rri - + ')'; - } + @Override + public String toString() { + return "Event(" + "type=" + type + ", rri=" + rri + ')'; + } - @JsonIgnore - public EventType getType() { - return type; - } + @JsonIgnore + public EventType getType() { + return type; + } - @JsonIgnore - public Optional getRri() { - return Optional.ofNullable(rri); - } + @JsonIgnore + public Optional getRri() { + return Optional.ofNullable(rri); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FeeTable.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FeeTable.java index 5667816268..1d16488249 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FeeTable.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FeeTable.java @@ -67,58 +67,54 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class FeeTable { - private final PerUpSubstateFee perUpSubstateFee; - private final UInt256 perByteFee; + private final PerUpSubstateFee perUpSubstateFee; + private final UInt256 perByteFee; - private FeeTable(PerUpSubstateFee perUpSubstateFee, UInt256 perByteFee) { - this.perUpSubstateFee = perUpSubstateFee; - this.perByteFee = perByteFee; - } + private FeeTable(PerUpSubstateFee perUpSubstateFee, UInt256 perByteFee) { + this.perUpSubstateFee = perUpSubstateFee; + this.perByteFee = perByteFee; + } - @JsonCreator - public static FeeTable create( - @JsonProperty(value = "perUpSubstateFee", required = true) PerUpSubstateFee perUpSubstateFee, - @JsonProperty(value = "perByteFee", required = true) UInt256 perByteFee - ) { - return new FeeTable(perUpSubstateFee, perByteFee); - } + @JsonCreator + public static FeeTable create( + @JsonProperty(value = "perUpSubstateFee", required = true) PerUpSubstateFee perUpSubstateFee, + @JsonProperty(value = "perByteFee", required = true) UInt256 perByteFee) { + return new FeeTable(perUpSubstateFee, perByteFee); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof FeeTable)) { - return false; - } + if (!(o instanceof FeeTable)) { + return false; + } - var feeTable = (FeeTable) o; - return perUpSubstateFee.equals(feeTable.perUpSubstateFee) && perByteFee.equals(feeTable.perByteFee); - } + var feeTable = (FeeTable) o; + return perUpSubstateFee.equals(feeTable.perUpSubstateFee) + && perByteFee.equals(feeTable.perByteFee); + } - @Override - public int hashCode() { - return Objects.hash(perUpSubstateFee, perByteFee); - } + @Override + public int hashCode() { + return Objects.hash(perUpSubstateFee, perByteFee); + } - @Override - public String toString() { - return "{" - + "perUpSubstateFee=" + perUpSubstateFee - + ", perByteFee=" + perByteFee - + '}'; - } + @Override + public String toString() { + return "{" + "perUpSubstateFee=" + perUpSubstateFee + ", perByteFee=" + perByteFee + '}'; + } - public PerUpSubstateFee getPerUpSubstateFee() { - return perUpSubstateFee; - } + public PerUpSubstateFee getPerUpSubstateFee() { + return perUpSubstateFee; + } - public UInt256 getPerByteFee() { - return perByteFee; - } + public UInt256 getPerByteFee() { + return perByteFee; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FinalizedTransaction.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FinalizedTransaction.java index e84293f600..dc5af4e39a 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FinalizedTransaction.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/FinalizedTransaction.java @@ -64,11 +64,7 @@ package com.radixdlt.client.lib.dto; -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.ASN1OutputStream; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.util.encoders.Hex; +import static java.util.Objects.requireNonNull; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; @@ -76,141 +72,147 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.AID; - import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Objects; import java.util.Optional; - -import static java.util.Objects.requireNonNull; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1OutputStream; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.util.encoders.Hex; public final class FinalizedTransaction { - private final byte[] blob; - private final ECDSASignature signature; - private final ECPublicKey publicKey; - private final AID txId; - - private FinalizedTransaction(byte[] blob, ECDSASignature signature, ECPublicKey publicKey, AID txId) { - this.blob = blob; - this.signature = signature; - this.publicKey = publicKey; - this.txId = txId; - } - - public static FinalizedTransaction create(byte[] blob, ECDSASignature signature, ECPublicKey publicKey, AID txId) { - requireNonNull(blob); - requireNonNull(signature); - requireNonNull(publicKey); - - return new FinalizedTransaction(blob, signature, publicKey, txId); - } - - public static FinalizedTransaction create(BuiltTransaction tx, ECKeyPair keyPair) { - requireNonNull(tx); - requireNonNull(keyPair); - - var signature = keyPair.sign(tx.getTransaction().getHashToSign()); - - return create(tx.getTransaction().getBlob(), signature, keyPair.getPublicKey(), null); - } - - public FinalizedTransaction withTxId(AID txId) { - return create(blob, signature, publicKey, txId); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof FinalizedTransaction)) { - return false; - } - - var that = (FinalizedTransaction) o; - return Arrays.equals(blob, that.blob) - && signature.equals(that.signature) - && publicKey.equals(that.publicKey) - && Objects.equals(txId, that.txId); - } - - @Override - public int hashCode() { - int result = Objects.hash(signature, publicKey, txId); - result = 31 * result + Arrays.hashCode(blob); - return result; - } - - @Override - public String toString() { - return "{" - + "blob=" + Arrays.toString(blob) - + ", signature=" + signature - + ", publicKey=" + publicKey - + ", txId=" + txId - + '}'; - } - - @JsonProperty("blob") - public Blob getBlob() { - return new Blob(blob); - } - - @JsonIgnore - public byte[] getRawBlob() { - return blob; - } - - @JsonProperty("signatureDER") - public String getSignature() { - return encodeToDer(signature); - } - - @JsonProperty("publicKeyOfSigner") - public String getPublicKey() { - return publicKey.toHex(); - } - - @JsonProperty("txID") - public String getTxId() { - return Optional.ofNullable(txId) - .map(AID::toString) - .orElse(null); - } - - private static String encodeToDer(ECDSASignature signature) { - try { - ASN1EncodableVector vector = new ASN1EncodableVector(); - vector.add(new ASN1Integer(signature.getR())); - vector.add(new ASN1Integer(signature.getS())); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ASN1OutputStream asnOS = ASN1OutputStream.create(baos); - - asnOS.writeObject(new DERSequence(vector)); - asnOS.flush(); - - return Hex.toHexString(baos.toByteArray()); - } catch (Exception e) { - throw new IllegalArgumentException("Unable to encode to DER signature: " + signature); - } - } - - public Optional rawTxId() { - return Optional.ofNullable(txId); - } - - private static class Blob { - private final byte[] blob; - - private Blob(byte[] blob) { - this.blob = blob; - } - - @JsonProperty("blob") - public String toJson() { - return Hex.toHexString(blob); - } - } + private final byte[] blob; + private final ECDSASignature signature; + private final ECPublicKey publicKey; + private final AID txId; + + private FinalizedTransaction( + byte[] blob, ECDSASignature signature, ECPublicKey publicKey, AID txId) { + this.blob = blob; + this.signature = signature; + this.publicKey = publicKey; + this.txId = txId; + } + + public static FinalizedTransaction create( + byte[] blob, ECDSASignature signature, ECPublicKey publicKey, AID txId) { + requireNonNull(blob); + requireNonNull(signature); + requireNonNull(publicKey); + + return new FinalizedTransaction(blob, signature, publicKey, txId); + } + + public static FinalizedTransaction create(BuiltTransaction tx, ECKeyPair keyPair) { + requireNonNull(tx); + requireNonNull(keyPair); + + var signature = keyPair.sign(tx.getTransaction().getHashToSign()); + + return create(tx.getTransaction().getBlob(), signature, keyPair.getPublicKey(), null); + } + + public FinalizedTransaction withTxId(AID txId) { + return create(blob, signature, publicKey, txId); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof FinalizedTransaction)) { + return false; + } + + var that = (FinalizedTransaction) o; + return Arrays.equals(blob, that.blob) + && signature.equals(that.signature) + && publicKey.equals(that.publicKey) + && Objects.equals(txId, that.txId); + } + + @Override + public int hashCode() { + int result = Objects.hash(signature, publicKey, txId); + result = 31 * result + Arrays.hashCode(blob); + return result; + } + + @Override + public String toString() { + return "{" + + "blob=" + + Arrays.toString(blob) + + ", signature=" + + signature + + ", publicKey=" + + publicKey + + ", txId=" + + txId + + '}'; + } + + @JsonProperty("blob") + public Blob getBlob() { + return new Blob(blob); + } + + @JsonIgnore + public byte[] getRawBlob() { + return blob; + } + + @JsonProperty("signatureDER") + public String getSignature() { + return encodeToDer(signature); + } + + @JsonProperty("publicKeyOfSigner") + public String getPublicKey() { + return publicKey.toHex(); + } + + @JsonProperty("txID") + public String getTxId() { + return Optional.ofNullable(txId).map(AID::toString).orElse(null); + } + + private static String encodeToDer(ECDSASignature signature) { + try { + ASN1EncodableVector vector = new ASN1EncodableVector(); + vector.add(new ASN1Integer(signature.getR())); + vector.add(new ASN1Integer(signature.getS())); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ASN1OutputStream asnOS = ASN1OutputStream.create(baos); + + asnOS.writeObject(new DERSequence(vector)); + asnOS.flush(); + + return Hex.toHexString(baos.toByteArray()); + } catch (Exception e) { + throw new IllegalArgumentException("Unable to encode to DER signature: " + signature); + } + } + + public Optional rawTxId() { + return Optional.ofNullable(txId); + } + + private static class Blob { + private final byte[] blob; + + private Blob(byte[] blob) { + this.blob = blob; + } + + @JsonProperty("blob") + public String toJson() { + return Hex.toHexString(blob); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetails.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetails.java index f05d9de06e..c7bad36b16 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetails.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetails.java @@ -66,77 +66,81 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ForkDetails { - private final String name; - private final long epoch; - private final String version; - private final ForkDetailsConfiguration config; + private final String name; + private final long epoch; + private final String version; + private final ForkDetailsConfiguration config; - private ForkDetails(String name, long epoch, String version, ForkDetailsConfiguration config) { - this.name = name; - this.version = version; - this.epoch = epoch; - this.config = config; - } + private ForkDetails(String name, long epoch, String version, ForkDetailsConfiguration config) { + this.name = name; + this.version = version; + this.epoch = epoch; + this.config = config; + } - @JsonCreator - public static ForkDetails create( - @JsonProperty(value = "name", required = true) String name, - @JsonProperty(value = "epoch", required = true) long epoch, - @JsonProperty(value = "version", required = true) String version, - @JsonProperty(value = "config", required = true) ForkDetailsConfiguration config - ) { - return new ForkDetails(name, epoch, version, config); - } + @JsonCreator + public static ForkDetails create( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty(value = "epoch", required = true) long epoch, + @JsonProperty(value = "version", required = true) String version, + @JsonProperty(value = "config", required = true) ForkDetailsConfiguration config) { + return new ForkDetails(name, epoch, version, config); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ForkDetails)) { - return false; - } + if (!(o instanceof ForkDetails)) { + return false; + } - var that = (ForkDetails) o; - return epoch == that.epoch - && name.equals(that.name) - && version.equals(that.version) - && config.equals(that.config); - } + var that = (ForkDetails) o; + return epoch == that.epoch + && name.equals(that.name) + && version.equals(that.version) + && config.equals(that.config); + } - @Override - public int hashCode() { - return Objects.hash(name, epoch, version, config); - } + @Override + public int hashCode() { + return Objects.hash(name, epoch, version, config); + } - @Override - public String toString() { - return "{" - + "name='" + name + '\'' - + ", epoch=" + epoch - + ", version='" + version + '\'' - + ", config=" + config - + '}'; - } + @Override + public String toString() { + return "{" + + "name='" + + name + + '\'' + + ", epoch=" + + epoch + + ", version='" + + version + + '\'' + + ", config=" + + config + + '}'; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getVersion() { - return version; - } + public String getVersion() { + return version; + } - public long getEpoch() { - return epoch; - } + public long getEpoch() { + return epoch; + } - public ForkDetailsConfiguration getConfig() { - return config; - } + public ForkDetailsConfiguration getConfig() { + return config; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetailsConfiguration.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetailsConfiguration.java index d3a070e6fa..1c45ecbfdf 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetailsConfiguration.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ForkDetailsConfiguration.java @@ -67,169 +67,187 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.Objects; public final class ForkDetailsConfiguration { - private final long maxValidators; - private final long maxTransactionsPerRound; - private final long maxRoundsPerEpoch; - private final long minimumCompletedProposalsPercentage; - private final long unstakingDelayEpochLength; - private final FeeTable feeTable; - private final long validatorFeeIncreaseDebouncerEpochLength; - private final UInt256 minimumStake; - private final List reservedSymbols; - private final long maxTransactionSize; - private final UInt256 rewardsPerProposal; - - private ForkDetailsConfiguration( - long maxValidators, - long maxTransactionsPerRound, - long maxRoundsPerEpoch, - long minimumCompletedProposalsPercentage, - long unstakingDelayEpochLength, - FeeTable feeTable, - long validatorFeeIncreaseDebouncerEpochLength, - UInt256 minimumStake, - List reservedSymbols, - long maxTransactionSize, - UInt256 rewardsPerProposal - ) { - this.maxValidators = maxValidators; - this.maxTransactionsPerRound = maxTransactionsPerRound; - this.maxRoundsPerEpoch = maxRoundsPerEpoch; - this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; - this.unstakingDelayEpochLength = unstakingDelayEpochLength; - this.feeTable = feeTable; - this.validatorFeeIncreaseDebouncerEpochLength = validatorFeeIncreaseDebouncerEpochLength; - this.minimumStake = minimumStake; - this.reservedSymbols = reservedSymbols; - this.maxTransactionSize = maxTransactionSize; - this.rewardsPerProposal = rewardsPerProposal; - } - - @JsonCreator - public static ForkDetailsConfiguration create( - @JsonProperty(value = "maxValidators", required = true) long maxValidators, - @JsonProperty(value = "maxTransactionsPerRound", required = true) long maxTransactionsPerRound, - @JsonProperty(value = "maxRoundsPerEpoch", required = true) long maxRoundsPerEpoch, - @JsonProperty(value = "minimumCompletedProposalsPercentage", required = true) long minimumCompletedProposalsPercentage, - @JsonProperty(value = "unstakingDelayEpochLength", required = true) long unstakingDelayEpochLength, - @JsonProperty(value = "feeTable", required = true) FeeTable feeTable, - @JsonProperty(value = "validatorFeeIncreaseDebouncerEpochLength", required = true) long validatorFeeIncreaseDebouncerEpochLength, - @JsonProperty(value = "minimumStake", required = true) UInt256 minimumStake, - @JsonProperty(value = "reservedSymbols", required = true) List reservedSymbols, - @JsonProperty(value = "maxTransactionSize", required = true) long maxTransactionSize, - @JsonProperty(value = "rewardsPerProposal", required = true) UInt256 rewardsPerProposal - ) { - return new ForkDetailsConfiguration( - maxValidators, maxTransactionsPerRound, maxRoundsPerEpoch, minimumCompletedProposalsPercentage, - unstakingDelayEpochLength, feeTable, validatorFeeIncreaseDebouncerEpochLength, minimumStake, - reservedSymbols, maxTransactionSize, rewardsPerProposal - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof ForkDetailsConfiguration)) { - return false; - } - - var that = (ForkDetailsConfiguration) o; - return maxValidators == that.maxValidators - && maxTransactionsPerRound == that.maxTransactionsPerRound - && maxRoundsPerEpoch == that.maxRoundsPerEpoch - && minimumCompletedProposalsPercentage == that.minimumCompletedProposalsPercentage - && unstakingDelayEpochLength == that.unstakingDelayEpochLength - && validatorFeeIncreaseDebouncerEpochLength == that.validatorFeeIncreaseDebouncerEpochLength - && maxTransactionSize == that.maxTransactionSize - && feeTable.equals(that.feeTable) - && minimumStake.equals(that.minimumStake) - && reservedSymbols.equals(that.reservedSymbols) - && rewardsPerProposal.equals(that.rewardsPerProposal); - } - - @Override - public int hashCode() { - return Objects.hash( - maxValidators, - maxTransactionsPerRound, - maxRoundsPerEpoch, - minimumCompletedProposalsPercentage, - unstakingDelayEpochLength, - feeTable, - validatorFeeIncreaseDebouncerEpochLength, - minimumStake, - reservedSymbols, - maxTransactionSize, - rewardsPerProposal - ); - } - - @Override - public String toString() { - return "{" - + "maxValidators=" + maxValidators - + ", maxTransactionsPerRound=" + maxTransactionsPerRound - + ", maxRoundsPerEpoch=" + maxRoundsPerEpoch - + ", minimumCompletedProposalsPercentage=" + minimumCompletedProposalsPercentage - + ", unstakingDelayEpochLength=" + unstakingDelayEpochLength - + ", feeTable=" + feeTable - + ", validatorFeeIncreaseDebouncerEpochLength=" + validatorFeeIncreaseDebouncerEpochLength - + ", minimumStake=" + minimumStake - + ", reservedSymbols=" + reservedSymbols - + ", maxTransactionSize=" + maxTransactionSize - + ", rewardsPerProposal=" + rewardsPerProposal - + '}'; - } - - public long getMaxValidators() { - return maxValidators; - } - - public long getMaxTransactionsPerRound() { - return maxTransactionsPerRound; - } - - public long getMaxRoundsPerEpoch() { - return maxRoundsPerEpoch; - } - - public long getMinimumCompletedProposalsPercentage() { - return minimumCompletedProposalsPercentage; - } - - public long getUnstakingDelayEpochLength() { - return unstakingDelayEpochLength; - } - - public FeeTable getFeeTable() { - return feeTable; - } - - public long getValidatorFeeIncreaseDebouncerEpochLength() { - return validatorFeeIncreaseDebouncerEpochLength; - } - - public UInt256 getMinimumStake() { - return minimumStake; - } - - public List getReservedSymbols() { - return reservedSymbols; - } - - public long getMaxTransactionSize() { - return maxTransactionSize; - } - - public UInt256 getRewardsPerProposal() { - return rewardsPerProposal; - } + private final long maxValidators; + private final long maxTransactionsPerRound; + private final long maxRoundsPerEpoch; + private final long minimumCompletedProposalsPercentage; + private final long unstakingDelayEpochLength; + private final FeeTable feeTable; + private final long validatorFeeIncreaseDebouncerEpochLength; + private final UInt256 minimumStake; + private final List reservedSymbols; + private final long maxTransactionSize; + private final UInt256 rewardsPerProposal; + + private ForkDetailsConfiguration( + long maxValidators, + long maxTransactionsPerRound, + long maxRoundsPerEpoch, + long minimumCompletedProposalsPercentage, + long unstakingDelayEpochLength, + FeeTable feeTable, + long validatorFeeIncreaseDebouncerEpochLength, + UInt256 minimumStake, + List reservedSymbols, + long maxTransactionSize, + UInt256 rewardsPerProposal) { + this.maxValidators = maxValidators; + this.maxTransactionsPerRound = maxTransactionsPerRound; + this.maxRoundsPerEpoch = maxRoundsPerEpoch; + this.minimumCompletedProposalsPercentage = minimumCompletedProposalsPercentage; + this.unstakingDelayEpochLength = unstakingDelayEpochLength; + this.feeTable = feeTable; + this.validatorFeeIncreaseDebouncerEpochLength = validatorFeeIncreaseDebouncerEpochLength; + this.minimumStake = minimumStake; + this.reservedSymbols = reservedSymbols; + this.maxTransactionSize = maxTransactionSize; + this.rewardsPerProposal = rewardsPerProposal; + } + + @JsonCreator + public static ForkDetailsConfiguration create( + @JsonProperty(value = "maxValidators", required = true) long maxValidators, + @JsonProperty(value = "maxTransactionsPerRound", required = true) + long maxTransactionsPerRound, + @JsonProperty(value = "maxRoundsPerEpoch", required = true) long maxRoundsPerEpoch, + @JsonProperty(value = "minimumCompletedProposalsPercentage", required = true) + long minimumCompletedProposalsPercentage, + @JsonProperty(value = "unstakingDelayEpochLength", required = true) + long unstakingDelayEpochLength, + @JsonProperty(value = "feeTable", required = true) FeeTable feeTable, + @JsonProperty(value = "validatorFeeIncreaseDebouncerEpochLength", required = true) + long validatorFeeIncreaseDebouncerEpochLength, + @JsonProperty(value = "minimumStake", required = true) UInt256 minimumStake, + @JsonProperty(value = "reservedSymbols", required = true) List reservedSymbols, + @JsonProperty(value = "maxTransactionSize", required = true) long maxTransactionSize, + @JsonProperty(value = "rewardsPerProposal", required = true) UInt256 rewardsPerProposal) { + return new ForkDetailsConfiguration( + maxValidators, + maxTransactionsPerRound, + maxRoundsPerEpoch, + minimumCompletedProposalsPercentage, + unstakingDelayEpochLength, + feeTable, + validatorFeeIncreaseDebouncerEpochLength, + minimumStake, + reservedSymbols, + maxTransactionSize, + rewardsPerProposal); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ForkDetailsConfiguration)) { + return false; + } + + var that = (ForkDetailsConfiguration) o; + return maxValidators == that.maxValidators + && maxTransactionsPerRound == that.maxTransactionsPerRound + && maxRoundsPerEpoch == that.maxRoundsPerEpoch + && minimumCompletedProposalsPercentage == that.minimumCompletedProposalsPercentage + && unstakingDelayEpochLength == that.unstakingDelayEpochLength + && validatorFeeIncreaseDebouncerEpochLength == that.validatorFeeIncreaseDebouncerEpochLength + && maxTransactionSize == that.maxTransactionSize + && feeTable.equals(that.feeTable) + && minimumStake.equals(that.minimumStake) + && reservedSymbols.equals(that.reservedSymbols) + && rewardsPerProposal.equals(that.rewardsPerProposal); + } + + @Override + public int hashCode() { + return Objects.hash( + maxValidators, + maxTransactionsPerRound, + maxRoundsPerEpoch, + minimumCompletedProposalsPercentage, + unstakingDelayEpochLength, + feeTable, + validatorFeeIncreaseDebouncerEpochLength, + minimumStake, + reservedSymbols, + maxTransactionSize, + rewardsPerProposal); + } + + @Override + public String toString() { + return "{" + + "maxValidators=" + + maxValidators + + ", maxTransactionsPerRound=" + + maxTransactionsPerRound + + ", maxRoundsPerEpoch=" + + maxRoundsPerEpoch + + ", minimumCompletedProposalsPercentage=" + + minimumCompletedProposalsPercentage + + ", unstakingDelayEpochLength=" + + unstakingDelayEpochLength + + ", feeTable=" + + feeTable + + ", validatorFeeIncreaseDebouncerEpochLength=" + + validatorFeeIncreaseDebouncerEpochLength + + ", minimumStake=" + + minimumStake + + ", reservedSymbols=" + + reservedSymbols + + ", maxTransactionSize=" + + maxTransactionSize + + ", rewardsPerProposal=" + + rewardsPerProposal + + '}'; + } + + public long getMaxValidators() { + return maxValidators; + } + + public long getMaxTransactionsPerRound() { + return maxTransactionsPerRound; + } + + public long getMaxRoundsPerEpoch() { + return maxRoundsPerEpoch; + } + + public long getMinimumCompletedProposalsPercentage() { + return minimumCompletedProposalsPercentage; + } + + public long getUnstakingDelayEpochLength() { + return unstakingDelayEpochLength; + } + + public FeeTable getFeeTable() { + return feeTable; + } + + public long getValidatorFeeIncreaseDebouncerEpochLength() { + return validatorFeeIncreaseDebouncerEpochLength; + } + + public UInt256 getMinimumStake() { + return minimumStake; + } + + public List getReservedSymbols() { + return reservedSymbols; + } + + public long getMaxTransactionSize() { + return maxTransactionSize; + } + + public UInt256 getRewardsPerProposal() { + return rewardsPerProposal; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/KnownAddress.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/KnownAddress.java index 4e335b374c..2f7dd08629 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/KnownAddress.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/KnownAddress.java @@ -66,72 +66,70 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class KnownAddress { - private final String uri; - private final boolean blacklisted; - private final String latestConnectionStatus; + private final String uri; + private final boolean blacklisted; + private final String latestConnectionStatus; - private KnownAddress(String uri, boolean blacklisted, String latestConnectionStatus) { - this.uri = uri; - this.blacklisted = blacklisted; - this.latestConnectionStatus = latestConnectionStatus; - } + private KnownAddress(String uri, boolean blacklisted, String latestConnectionStatus) { + this.uri = uri; + this.blacklisted = blacklisted; + this.latestConnectionStatus = latestConnectionStatus; + } - @JsonCreator - public static KnownAddress create( - @JsonProperty(value = "uri", required = true) String uri, - @JsonProperty(value = "blacklisted", required = true) Boolean blacklisted, - @JsonProperty("latestConnectionStatus") String latestConnectionStatus - ) { - return new KnownAddress( - uri, - blacklisted != null && blacklisted, - latestConnectionStatus - ); - } + @JsonCreator + public static KnownAddress create( + @JsonProperty(value = "uri", required = true) String uri, + @JsonProperty(value = "blacklisted", required = true) Boolean blacklisted, + @JsonProperty("latestConnectionStatus") String latestConnectionStatus) { + return new KnownAddress(uri, blacklisted != null && blacklisted, latestConnectionStatus); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof KnownAddress)) { - return false; - } + if (!(o instanceof KnownAddress)) { + return false; + } - var that = (KnownAddress) o; - return blacklisted == that.blacklisted - && uri.equals(that.uri) - && latestConnectionStatus.equals(that.latestConnectionStatus); - } + var that = (KnownAddress) o; + return blacklisted == that.blacklisted + && uri.equals(that.uri) + && latestConnectionStatus.equals(that.latestConnectionStatus); + } - @Override - public int hashCode() { - return Objects.hash(uri, blacklisted, latestConnectionStatus); - } + @Override + public int hashCode() { + return Objects.hash(uri, blacklisted, latestConnectionStatus); + } - @Override - public String toString() { - return "{" - + "uri='" + uri + '\'' - + ", blacklisted=" + blacklisted - + ", latestConnectionStatus=" + latestConnectionStatus - + '}'; - } + @Override + public String toString() { + return "{" + + "uri='" + + uri + + '\'' + + ", blacklisted=" + + blacklisted + + ", latestConnectionStatus=" + + latestConnectionStatus + + '}'; + } - public String getUri() { - return uri; - } + public String getUri() { + return uri; + } - public boolean isBlacklisted() { - return blacklisted; - } + public boolean isBlacklisted() { + return blacklisted; + } - public String getLastSuccessfulConnection() { - return latestConnectionStatus; - } + public String getLastSuccessfulConnection() { + return latestConnectionStatus; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalAccount.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalAccount.java index b3e5623298..88e5537ebb 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalAccount.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalAccount.java @@ -67,53 +67,51 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.AccountAddress; - import java.util.Objects; public final class LocalAccount { - private final AccountAddress address; - private final AccountBalance balance; + private final AccountAddress address; + private final AccountBalance balance; - private LocalAccount(AccountAddress address, AccountBalance balance) { - this.address = address; - this.balance = balance; - } + private LocalAccount(AccountAddress address, AccountBalance balance) { + this.address = address; + this.balance = balance; + } - @JsonCreator - public static LocalAccount create( - @JsonProperty(value = "address", required = true) AccountAddress address, - @JsonProperty(value = "balance", required = true) AccountBalance balance - ) { - return new LocalAccount(address, balance); - } + @JsonCreator + public static LocalAccount create( + @JsonProperty(value = "address", required = true) AccountAddress address, + @JsonProperty(value = "balance", required = true) AccountBalance balance) { + return new LocalAccount(address, balance); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof LocalAccount)) { - return false; - } - LocalAccount that = (LocalAccount) o; - return address.equals(that.address) && balance.equals(that.balance); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof LocalAccount)) { + return false; + } + LocalAccount that = (LocalAccount) o; + return address.equals(that.address) && balance.equals(that.balance); + } - @Override - public int hashCode() { - return Objects.hash(address, balance); - } + @Override + public int hashCode() { + return Objects.hash(address, balance); + } - @Override - public String toString() { - return "{address:" + address + ", balance:" + balance + '}'; - } + @Override + public String toString() { + return "{address:" + address + ", balance:" + balance + '}'; + } - public AccountAddress getAddress() { - return address; - } + public AccountAddress getAddress() { + return address; + } - public AccountBalance getBalance() { - return balance; - } + public AccountBalance getBalance() { + return balance; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalValidatorInfo.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalValidatorInfo.java index 978f1360ad..c5a8bd90ff 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalValidatorInfo.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/LocalValidatorInfo.java @@ -67,88 +67,96 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.ValidatorAddress; - import java.util.Objects; public final class LocalValidatorInfo { - private final ValidatorAddress address; - private final String name; - private final String url; - private final boolean allowDelegation; - private final EpochInfo epochInfo; - - private LocalValidatorInfo( - ValidatorAddress address, String name, String url, boolean allowDelegation, EpochInfo epochInfo - ) { - this.address = address; - this.name = name; - this.url = url; - this.allowDelegation = allowDelegation; - this.epochInfo = epochInfo; - } - - @JsonCreator - public static LocalValidatorInfo create( - @JsonProperty(value = "address", required = true) ValidatorAddress address, - @JsonProperty(value = "name", required = true) String name, - @JsonProperty(value = "url", required = true) String url, - @JsonProperty(value = "allowDelegation", required = true) boolean allowDelegation, - @JsonProperty(value = "epochInfo", required = true) EpochInfo epochInfo - ) { - return new LocalValidatorInfo(address, name, url, allowDelegation, epochInfo); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof LocalValidatorInfo)) { - return false; - } - - var that = (LocalValidatorInfo) o; - return allowDelegation == that.allowDelegation - && address.equals(that.address) - && name.equals(that.name) - && url.equals(that.url) - && epochInfo.equals(that.epochInfo); - } - - @Override - public int hashCode() { - return Objects.hash(address, name, url, allowDelegation, epochInfo); - } - - @Override - public String toString() { - return "{" - + "address=" + address - + ", name='" + name + '\'' - + ", url='" + url + '\'' - + ", allowDelegation=" + allowDelegation - + ", epochInfo=" + epochInfo - + '}'; - } - - public ValidatorAddress getAddress() { - return address; - } - - public String getName() { - return name; - } - - public String getUrl() { - return url; - } - - public boolean isAllowDelegation() { - return allowDelegation; - } - - public EpochInfo getEpochInfo() { - return epochInfo; - } + private final ValidatorAddress address; + private final String name; + private final String url; + private final boolean allowDelegation; + private final EpochInfo epochInfo; + + private LocalValidatorInfo( + ValidatorAddress address, + String name, + String url, + boolean allowDelegation, + EpochInfo epochInfo) { + this.address = address; + this.name = name; + this.url = url; + this.allowDelegation = allowDelegation; + this.epochInfo = epochInfo; + } + + @JsonCreator + public static LocalValidatorInfo create( + @JsonProperty(value = "address", required = true) ValidatorAddress address, + @JsonProperty(value = "name", required = true) String name, + @JsonProperty(value = "url", required = true) String url, + @JsonProperty(value = "allowDelegation", required = true) boolean allowDelegation, + @JsonProperty(value = "epochInfo", required = true) EpochInfo epochInfo) { + return new LocalValidatorInfo(address, name, url, allowDelegation, epochInfo); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof LocalValidatorInfo)) { + return false; + } + + var that = (LocalValidatorInfo) o; + return allowDelegation == that.allowDelegation + && address.equals(that.address) + && name.equals(that.name) + && url.equals(that.url) + && epochInfo.equals(that.epochInfo); + } + + @Override + public int hashCode() { + return Objects.hash(address, name, url, allowDelegation, epochInfo); + } + + @Override + public String toString() { + return "{" + + "address=" + + address + + ", name='" + + name + + '\'' + + ", url='" + + url + + '\'' + + ", allowDelegation=" + + allowDelegation + + ", epochInfo=" + + epochInfo + + '}'; + } + + public ValidatorAddress getAddress() { + return address; + } + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + + public boolean isAllowDelegation() { + return allowDelegation; + } + + public EpochInfo getEpochInfo() { + return epochInfo; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolConfiguration.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolConfiguration.java index c78c6c9c8b..b062131719 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolConfiguration.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolConfiguration.java @@ -66,55 +66,53 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class MempoolConfiguration { - private final long throttleMs; - private final long maxSize; + private final long throttleMs; + private final long maxSize; - private MempoolConfiguration(long throttleMs, long maxSize) { - this.throttleMs = throttleMs; - this.maxSize = maxSize; - } + private MempoolConfiguration(long throttleMs, long maxSize) { + this.throttleMs = throttleMs; + this.maxSize = maxSize; + } - @JsonCreator - public static MempoolConfiguration create( - @JsonProperty(value = "throttleMs", required = true) long throttleMs, - @JsonProperty(value = "maxSize", required = true) long maxSize - ) { - return new MempoolConfiguration(throttleMs, maxSize); - } + @JsonCreator + public static MempoolConfiguration create( + @JsonProperty(value = "throttleMs", required = true) long throttleMs, + @JsonProperty(value = "maxSize", required = true) long maxSize) { + return new MempoolConfiguration(throttleMs, maxSize); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof MempoolConfiguration)) { - return false; - } + if (!(o instanceof MempoolConfiguration)) { + return false; + } - var that = (MempoolConfiguration) o; - return throttleMs == that.throttleMs && maxSize == that.maxSize; - } + var that = (MempoolConfiguration) o; + return throttleMs == that.throttleMs && maxSize == that.maxSize; + } - @Override - public int hashCode() { - return Objects.hash(throttleMs, maxSize); - } + @Override + public int hashCode() { + return Objects.hash(throttleMs, maxSize); + } - @Override - public String toString() { - return "{throttleMs:" + throttleMs + ", maxSize:" + maxSize + '}'; - } + @Override + public String toString() { + return "{throttleMs:" + throttleMs + ", maxSize:" + maxSize + '}'; + } - public long getThrottleMs() { - return throttleMs; - } + public long getThrottleMs() { + return throttleMs; + } - public long getMaxSize() { - return maxSize; - } + public long getMaxSize() { + return maxSize; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolData.java index fef891f695..0418c5ce25 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolData.java @@ -66,99 +66,105 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class MempoolData { - private final long maxcount; - private final long relayerSentCount; - private final long count; - private final long addSuccess; - private final long proposedTransaction; - private final MempoolDataErrors errors; - - private MempoolData( - long maxcount, - long relayerSentCount, - long count, - long addSuccess, - long proposedTransaction, - MempoolDataErrors errors - ) { - this.maxcount = maxcount; - this.relayerSentCount = relayerSentCount; - this.count = count; - this.addSuccess = addSuccess; - this.proposedTransaction = proposedTransaction; - this.errors = errors; - } - - @JsonCreator - public static MempoolData create( - @JsonProperty(value = "maxcount", required = true) long maxcount, - @JsonProperty(value = "relayerSentCount", required = true) long relayerSentCount, - @JsonProperty(value = "count", required = true) long count, - @JsonProperty(value = "addSuccess", required = true) long addSuccess, - @JsonProperty(value = "proposedTransaction", required = true) long proposedTransaction, - @JsonProperty(value = "errors", required = true) MempoolDataErrors errors - ) { - return new MempoolData(maxcount, relayerSentCount, count, addSuccess, proposedTransaction, errors); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof MempoolData)) { - return false; - } - MempoolData that = (MempoolData) o; - return maxcount == that.maxcount - && relayerSentCount == that.relayerSentCount - && count == that.count - && addSuccess == that.addSuccess - && proposedTransaction == that.proposedTransaction - && errors.equals(that.errors); - } - - @Override - public int hashCode() { - return Objects.hash(maxcount, relayerSentCount, count, addSuccess, proposedTransaction, errors); - } - - @Override - public String toString() { - return "{" + "maxcount:" + maxcount - + ", relayerSentCount:" + relayerSentCount - + ", count:" + count - + ", addSuccess:" + addSuccess - + ", proposedTransaction:" + proposedTransaction - + ", errors:" + errors + '}'; - } - - public long getMaxcount() { - return maxcount; - } - - public long getRelayerSentCount() { - return relayerSentCount; - } - - public long getCount() { - return count; - } - - public long getAddSuccess() { - return addSuccess; - } - - public long getProposedTransaction() { - return proposedTransaction; - } - - public MempoolDataErrors getErrors() { - return errors; - } + private final long maxcount; + private final long relayerSentCount; + private final long count; + private final long addSuccess; + private final long proposedTransaction; + private final MempoolDataErrors errors; + + private MempoolData( + long maxcount, + long relayerSentCount, + long count, + long addSuccess, + long proposedTransaction, + MempoolDataErrors errors) { + this.maxcount = maxcount; + this.relayerSentCount = relayerSentCount; + this.count = count; + this.addSuccess = addSuccess; + this.proposedTransaction = proposedTransaction; + this.errors = errors; + } + + @JsonCreator + public static MempoolData create( + @JsonProperty(value = "maxcount", required = true) long maxcount, + @JsonProperty(value = "relayerSentCount", required = true) long relayerSentCount, + @JsonProperty(value = "count", required = true) long count, + @JsonProperty(value = "addSuccess", required = true) long addSuccess, + @JsonProperty(value = "proposedTransaction", required = true) long proposedTransaction, + @JsonProperty(value = "errors", required = true) MempoolDataErrors errors) { + return new MempoolData( + maxcount, relayerSentCount, count, addSuccess, proposedTransaction, errors); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof MempoolData)) { + return false; + } + MempoolData that = (MempoolData) o; + return maxcount == that.maxcount + && relayerSentCount == that.relayerSentCount + && count == that.count + && addSuccess == that.addSuccess + && proposedTransaction == that.proposedTransaction + && errors.equals(that.errors); + } + + @Override + public int hashCode() { + return Objects.hash(maxcount, relayerSentCount, count, addSuccess, proposedTransaction, errors); + } + + @Override + public String toString() { + return "{" + + "maxcount:" + + maxcount + + ", relayerSentCount:" + + relayerSentCount + + ", count:" + + count + + ", addSuccess:" + + addSuccess + + ", proposedTransaction:" + + proposedTransaction + + ", errors:" + + errors + + '}'; + } + + public long getMaxcount() { + return maxcount; + } + + public long getRelayerSentCount() { + return relayerSentCount; + } + + public long getCount() { + return count; + } + + public long getAddSuccess() { + return addSuccess; + } + + public long getProposedTransaction() { + return proposedTransaction; + } + + public MempoolDataErrors getErrors() { + return errors; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolDataErrors.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolDataErrors.java index fb8a971e41..64aac72884 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolDataErrors.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/MempoolDataErrors.java @@ -66,62 +66,60 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class MempoolDataErrors { - private final long other; - private final long hook; - private final long conflict; + private final long other; + private final long hook; + private final long conflict; - private MempoolDataErrors(long other, long hook, long conflict) { - this.other = other; - this.hook = hook; - this.conflict = conflict; - } + private MempoolDataErrors(long other, long hook, long conflict) { + this.other = other; + this.hook = hook; + this.conflict = conflict; + } - @JsonCreator - public static MempoolDataErrors create( - @JsonProperty(value = "other", required = true) long other, - @JsonProperty(value = "hook", required = true) long hook, - @JsonProperty(value = "conflict", required = true) long conflict - ) { - return new MempoolDataErrors(other, hook, conflict); - } + @JsonCreator + public static MempoolDataErrors create( + @JsonProperty(value = "other", required = true) long other, + @JsonProperty(value = "hook", required = true) long hook, + @JsonProperty(value = "conflict", required = true) long conflict) { + return new MempoolDataErrors(other, hook, conflict); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof MempoolDataErrors)) { - return false; - } + if (!(o instanceof MempoolDataErrors)) { + return false; + } - var that = (MempoolDataErrors) o; - return other == that.other && hook == that.hook && conflict == that.conflict; - } + var that = (MempoolDataErrors) o; + return other == that.other && hook == that.hook && conflict == that.conflict; + } - @Override - public int hashCode() { - return Objects.hash(other, hook, conflict); - } + @Override + public int hashCode() { + return Objects.hash(other, hook, conflict); + } - @Override - public String toString() { - return "{other:" + other + ", hook:" + hook + ", conflict:" + conflict + '}'; - } + @Override + public String toString() { + return "{other:" + other + ", hook:" + hook + ", conflict:" + conflict + '}'; + } - public long getOther() { - return other; - } + public long getOther() { + return other; + } - public long getHook() { - return hook; - } + public long getHook() { + return hook; + } - public long getConflict() { - return conflict; - } + public long getConflict() { + return conflict; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkChannel.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkChannel.java index 5c743264eb..f05b070f16 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkChannel.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkChannel.java @@ -66,79 +66,82 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; import java.util.Optional; public final class NetworkChannel { - private final long localPort; - private final String ip; - private final ChannelType type; - private final String uri; - - private NetworkChannel(long localPort, String ip, ChannelType type, String uri) { - this.localPort = localPort; - this.ip = ip; - this.type = type; - this.uri = uri; - } - - @JsonCreator - public static NetworkChannel create( - @JsonProperty(value = "localPort", required = true) long localPort, - @JsonProperty(value = "ip", required = true) String ip, - @JsonProperty(value = "type", required = true) ChannelType type, - @JsonProperty("uri") String uri - ) { - return new NetworkChannel(localPort, ip, type, uri); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof NetworkChannel)) { - return false; - } - - var that = (NetworkChannel) o; - return localPort == that.localPort - && ip.equals(that.ip) - && type == that.type - && Objects.equals(uri, that.uri); - } - - @Override - public int hashCode() { - return Objects.hash(localPort, ip, type, uri); - } - - @Override - public String toString() { - return "{" - + "localPort=" + localPort - + ", ip='" + ip + '\'' - + ", type=" + type - + ", uri='" + (uri == null ? "none" : uri) + '\'' - + '}'; - } - - - public long getLocalPort() { - return localPort; - } - - public String getIp() { - return ip; - } - - public ChannelType getType() { - return type; - } - - public Optional getUri() { - return Optional.ofNullable(uri); - } + private final long localPort; + private final String ip; + private final ChannelType type; + private final String uri; + + private NetworkChannel(long localPort, String ip, ChannelType type, String uri) { + this.localPort = localPort; + this.ip = ip; + this.type = type; + this.uri = uri; + } + + @JsonCreator + public static NetworkChannel create( + @JsonProperty(value = "localPort", required = true) long localPort, + @JsonProperty(value = "ip", required = true) String ip, + @JsonProperty(value = "type", required = true) ChannelType type, + @JsonProperty("uri") String uri) { + return new NetworkChannel(localPort, ip, type, uri); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof NetworkChannel)) { + return false; + } + + var that = (NetworkChannel) o; + return localPort == that.localPort + && ip.equals(that.ip) + && type == that.type + && Objects.equals(uri, that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(localPort, ip, type, uri); + } + + @Override + public String toString() { + return "{" + + "localPort=" + + localPort + + ", ip='" + + ip + + '\'' + + ", type=" + + type + + ", uri='" + + (uri == null ? "none" : uri) + + '\'' + + '}'; + } + + public long getLocalPort() { + return localPort; + } + + public String getIp() { + return ip; + } + + public ChannelType getType() { + return type; + } + + public Optional getUri() { + return Optional.ofNullable(uri); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkConfiguration.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkConfiguration.java index 9133967896..a094ce8069 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkConfiguration.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkConfiguration.java @@ -67,193 +67,211 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.NodeAddress; - import java.util.List; import java.util.Objects; public final class NetworkConfiguration { - private final long defaultPort; - private final long maxInboundChannels; - private final long broadcastPort; - private final String listenAddress; - private final long channelBufferSize; - private final long peerConnectionTimeout; - private final long pingTimeout; - private final long listenPort; - private final long discoveryInterval; - private final long maxOutboundChannels; - private final long peerLivenessCheckInterval; - private final NodeAddress nodeAddress; - private final List seedNodes; - - private NetworkConfiguration( - long defaultPort, - long maxInboundChannels, - long broadcastPort, - String listenAddress, - long channelBufferSize, - long peerConnectionTimeout, - long pingTimeout, - long listenPort, - long discoveryInterval, - long maxOutboundChannels, - long peerLivenessCheckInterval, - NodeAddress nodeAddress, - List seedNodes - ) { - this.defaultPort = defaultPort; - this.maxInboundChannels = maxInboundChannels; - this.broadcastPort = broadcastPort; - this.listenAddress = listenAddress; - this.channelBufferSize = channelBufferSize; - this.peerConnectionTimeout = peerConnectionTimeout; - this.pingTimeout = pingTimeout; - this.listenPort = listenPort; - this.discoveryInterval = discoveryInterval; - this.maxOutboundChannels = maxOutboundChannels; - this.peerLivenessCheckInterval = peerLivenessCheckInterval; - this.nodeAddress = nodeAddress; - this.seedNodes = seedNodes; - } + private final long defaultPort; + private final long maxInboundChannels; + private final long broadcastPort; + private final String listenAddress; + private final long channelBufferSize; + private final long peerConnectionTimeout; + private final long pingTimeout; + private final long listenPort; + private final long discoveryInterval; + private final long maxOutboundChannels; + private final long peerLivenessCheckInterval; + private final NodeAddress nodeAddress; + private final List seedNodes; - @JsonCreator - public static NetworkConfiguration create( - @JsonProperty(value = "defaultPort", required = true) long defaultPort, - @JsonProperty(value = "maxInboundChannels", required = true) long maxInboundChannels, - @JsonProperty(value = "broadcastPort", required = true) long broadcastPort, - @JsonProperty(value = "listenAddress", required = true) String listenAddress, - @JsonProperty(value = "channelBufferSize", required = true) long channelBufferSize, - @JsonProperty(value = "peerConnectionTimeout", required = true) long peerConnectionTimeout, - @JsonProperty(value = "pingTimeout", required = true) long pingTimeout, - @JsonProperty(value = "listenPort", required = true) long listenPort, - @JsonProperty(value = "discoveryInterval", required = true) long discoveryInterval, - @JsonProperty(value = "maxOutboundChannels", required = true) long maxOutboundChannels, - @JsonProperty(value = "peerLivenessCheckInterval", required = true) long peerLivenessCheckInterval, - @JsonProperty(value = "nodeAddress", required = true) NodeAddress nodeAddress, - @JsonProperty(value = "seedNodes", required = true) List seedNodes - ) { - return new NetworkConfiguration( - defaultPort, maxInboundChannels, broadcastPort, listenAddress, - channelBufferSize, peerConnectionTimeout, pingTimeout, listenPort, - discoveryInterval, maxOutboundChannels, peerLivenessCheckInterval, - nodeAddress, seedNodes - ); - } + private NetworkConfiguration( + long defaultPort, + long maxInboundChannels, + long broadcastPort, + String listenAddress, + long channelBufferSize, + long peerConnectionTimeout, + long pingTimeout, + long listenPort, + long discoveryInterval, + long maxOutboundChannels, + long peerLivenessCheckInterval, + NodeAddress nodeAddress, + List seedNodes) { + this.defaultPort = defaultPort; + this.maxInboundChannels = maxInboundChannels; + this.broadcastPort = broadcastPort; + this.listenAddress = listenAddress; + this.channelBufferSize = channelBufferSize; + this.peerConnectionTimeout = peerConnectionTimeout; + this.pingTimeout = pingTimeout; + this.listenPort = listenPort; + this.discoveryInterval = discoveryInterval; + this.maxOutboundChannels = maxOutboundChannels; + this.peerLivenessCheckInterval = peerLivenessCheckInterval; + this.nodeAddress = nodeAddress; + this.seedNodes = seedNodes; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @JsonCreator + public static NetworkConfiguration create( + @JsonProperty(value = "defaultPort", required = true) long defaultPort, + @JsonProperty(value = "maxInboundChannels", required = true) long maxInboundChannels, + @JsonProperty(value = "broadcastPort", required = true) long broadcastPort, + @JsonProperty(value = "listenAddress", required = true) String listenAddress, + @JsonProperty(value = "channelBufferSize", required = true) long channelBufferSize, + @JsonProperty(value = "peerConnectionTimeout", required = true) long peerConnectionTimeout, + @JsonProperty(value = "pingTimeout", required = true) long pingTimeout, + @JsonProperty(value = "listenPort", required = true) long listenPort, + @JsonProperty(value = "discoveryInterval", required = true) long discoveryInterval, + @JsonProperty(value = "maxOutboundChannels", required = true) long maxOutboundChannels, + @JsonProperty(value = "peerLivenessCheckInterval", required = true) + long peerLivenessCheckInterval, + @JsonProperty(value = "nodeAddress", required = true) NodeAddress nodeAddress, + @JsonProperty(value = "seedNodes", required = true) List seedNodes) { + return new NetworkConfiguration( + defaultPort, + maxInboundChannels, + broadcastPort, + listenAddress, + channelBufferSize, + peerConnectionTimeout, + pingTimeout, + listenPort, + discoveryInterval, + maxOutboundChannels, + peerLivenessCheckInterval, + nodeAddress, + seedNodes); + } - if (!(o instanceof NetworkConfiguration)) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - var that = (NetworkConfiguration) o; - return defaultPort == that.defaultPort - && maxInboundChannels == that.maxInboundChannels - && broadcastPort == that.broadcastPort - && channelBufferSize == that.channelBufferSize - && peerConnectionTimeout == that.peerConnectionTimeout - && pingTimeout == that.pingTimeout - && listenPort == that.listenPort - && discoveryInterval == that.discoveryInterval - && maxOutboundChannels == that.maxOutboundChannels - && peerLivenessCheckInterval == that.peerLivenessCheckInterval - && listenAddress.equals(that.listenAddress) - && nodeAddress.equals(that.nodeAddress) - && seedNodes.equals(that.seedNodes); - } + if (!(o instanceof NetworkConfiguration)) { + return false; + } - @Override - public int hashCode() { - return Objects.hash( - defaultPort, - maxInboundChannels, - broadcastPort, - listenAddress, - channelBufferSize, - peerConnectionTimeout, - pingTimeout, - listenPort, - discoveryInterval, - maxOutboundChannels, - peerLivenessCheckInterval, - nodeAddress, - seedNodes - ); - } + var that = (NetworkConfiguration) o; + return defaultPort == that.defaultPort + && maxInboundChannels == that.maxInboundChannels + && broadcastPort == that.broadcastPort + && channelBufferSize == that.channelBufferSize + && peerConnectionTimeout == that.peerConnectionTimeout + && pingTimeout == that.pingTimeout + && listenPort == that.listenPort + && discoveryInterval == that.discoveryInterval + && maxOutboundChannels == that.maxOutboundChannels + && peerLivenessCheckInterval == that.peerLivenessCheckInterval + && listenAddress.equals(that.listenAddress) + && nodeAddress.equals(that.nodeAddress) + && seedNodes.equals(that.seedNodes); + } - @Override - public String toString() { - return "{" - + "defaultPort=" + defaultPort - + ", maxInboundChannels=" + maxInboundChannels - + ", broadcastPort=" + broadcastPort - + ", listenAddress='" + listenAddress + '\'' - + ", channelBufferSize=" + channelBufferSize - + ", peerConnectionTimeout=" + peerConnectionTimeout - + ", pingTimeout=" + pingTimeout - + ", listenPort=" + listenPort - + ", discoveryInterval=" + discoveryInterval - + ", maxOutboundChannels=" + maxOutboundChannels - + ", peerLivenessCheckInterval=" + peerLivenessCheckInterval - + ", nodeAddress=" + nodeAddress - + ", seedNodes=" + seedNodes - + '}'; - } + @Override + public int hashCode() { + return Objects.hash( + defaultPort, + maxInboundChannels, + broadcastPort, + listenAddress, + channelBufferSize, + peerConnectionTimeout, + pingTimeout, + listenPort, + discoveryInterval, + maxOutboundChannels, + peerLivenessCheckInterval, + nodeAddress, + seedNodes); + } + @Override + public String toString() { + return "{" + + "defaultPort=" + + defaultPort + + ", maxInboundChannels=" + + maxInboundChannels + + ", broadcastPort=" + + broadcastPort + + ", listenAddress='" + + listenAddress + + '\'' + + ", channelBufferSize=" + + channelBufferSize + + ", peerConnectionTimeout=" + + peerConnectionTimeout + + ", pingTimeout=" + + pingTimeout + + ", listenPort=" + + listenPort + + ", discoveryInterval=" + + discoveryInterval + + ", maxOutboundChannels=" + + maxOutboundChannels + + ", peerLivenessCheckInterval=" + + peerLivenessCheckInterval + + ", nodeAddress=" + + nodeAddress + + ", seedNodes=" + + seedNodes + + '}'; + } - public long getDefaultPort() { - return defaultPort; - } + public long getDefaultPort() { + return defaultPort; + } - public long getMaxInboundChannels() { - return maxInboundChannels; - } + public long getMaxInboundChannels() { + return maxInboundChannels; + } - public long getBroadcastPort() { - return broadcastPort; - } + public long getBroadcastPort() { + return broadcastPort; + } - public String getListenAddress() { - return listenAddress; - } + public String getListenAddress() { + return listenAddress; + } - public long getChannelBufferSize() { - return channelBufferSize; - } + public long getChannelBufferSize() { + return channelBufferSize; + } - public long getPeerConnectionTimeout() { - return peerConnectionTimeout; - } + public long getPeerConnectionTimeout() { + return peerConnectionTimeout; + } - public long getPingTimeout() { - return pingTimeout; - } + public long getPingTimeout() { + return pingTimeout; + } - public long getListenPort() { - return listenPort; - } + public long getListenPort() { + return listenPort; + } - public long getDiscoveryInterval() { - return discoveryInterval; - } + public long getDiscoveryInterval() { + return discoveryInterval; + } - public long getMaxOutboundChannels() { - return maxOutboundChannels; - } + public long getMaxOutboundChannels() { + return maxOutboundChannels; + } - public long getPeerLivenessCheckInterval() { - return peerLivenessCheckInterval; - } + public long getPeerLivenessCheckInterval() { + return peerLivenessCheckInterval; + } - public NodeAddress getNodeAddress() { - return nodeAddress; - } + public NodeAddress getNodeAddress() { + return nodeAddress; + } - public List getSeedNodes() { - return seedNodes; - } + public List getSeedNodes() { + return seedNodes; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkData.java index b221fa9552..b8b655945a 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkData.java @@ -66,55 +66,53 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkData { - private final NetworkDataMessages messages; - private final NetworkDataNetworking networking; + private final NetworkDataMessages messages; + private final NetworkDataNetworking networking; - private NetworkData(NetworkDataMessages messages, NetworkDataNetworking networking) { - this.messages = messages; - this.networking = networking; - } + private NetworkData(NetworkDataMessages messages, NetworkDataNetworking networking) { + this.messages = messages; + this.networking = networking; + } - @JsonCreator - public static NetworkData create( - @JsonProperty(value = "messages", required = true) NetworkDataMessages messages, - @JsonProperty(value = "networking", required = true) NetworkDataNetworking networking - ) { - return new NetworkData(messages, networking); - } + @JsonCreator + public static NetworkData create( + @JsonProperty(value = "messages", required = true) NetworkDataMessages messages, + @JsonProperty(value = "networking", required = true) NetworkDataNetworking networking) { + return new NetworkData(messages, networking); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkData)) { - return false; - } + if (!(o instanceof NetworkData)) { + return false; + } - var that = (NetworkData) o; - return messages.equals(that.messages) && networking.equals(that.networking); - } + var that = (NetworkData) o; + return messages.equals(that.messages) && networking.equals(that.networking); + } - @Override - public int hashCode() { - return Objects.hash(messages, networking); - } + @Override + public int hashCode() { + return Objects.hash(messages, networking); + } - @Override - public String toString() { - return "{" + "messages=" + messages + ", networking=" + networking + '}'; - } + @Override + public String toString() { + return "{" + "messages=" + messages + ", networking=" + networking + '}'; + } - public NetworkDataMessages getMessages() { - return messages; - } + public NetworkDataMessages getMessages() { + return messages; + } - public NetworkDataNetworking getNetworking() { - return networking; - } + public NetworkDataNetworking getNetworking() { + return networking; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessages.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessages.java index f5b11f380e..a6f0929acd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessages.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessages.java @@ -66,55 +66,54 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkDataMessages { - private final NetworkDataMessagesInbound inbound; - private final NetworkDataMessagesOutbound outbound; + private final NetworkDataMessagesInbound inbound; + private final NetworkDataMessagesOutbound outbound; - private NetworkDataMessages(NetworkDataMessagesInbound inbound, NetworkDataMessagesOutbound outbound) { - this.inbound = inbound; - this.outbound = outbound; - } + private NetworkDataMessages( + NetworkDataMessagesInbound inbound, NetworkDataMessagesOutbound outbound) { + this.inbound = inbound; + this.outbound = outbound; + } - @JsonCreator - public static NetworkDataMessages create( - @JsonProperty(value = "inbound", required = true) NetworkDataMessagesInbound inbound, - @JsonProperty(value = "outbound", required = true) NetworkDataMessagesOutbound outbound - ) { - return new NetworkDataMessages(inbound, outbound); - } + @JsonCreator + public static NetworkDataMessages create( + @JsonProperty(value = "inbound", required = true) NetworkDataMessagesInbound inbound, + @JsonProperty(value = "outbound", required = true) NetworkDataMessagesOutbound outbound) { + return new NetworkDataMessages(inbound, outbound); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkDataMessages)) { - return false; - } + if (!(o instanceof NetworkDataMessages)) { + return false; + } - var that = (NetworkDataMessages) o; - return inbound.equals(that.inbound) && outbound.equals(that.outbound); - } + var that = (NetworkDataMessages) o; + return inbound.equals(that.inbound) && outbound.equals(that.outbound); + } - @Override - public int hashCode() { - return Objects.hash(inbound, outbound); - } + @Override + public int hashCode() { + return Objects.hash(inbound, outbound); + } - @Override - public String toString() { - return "{" + "inbound=" + inbound + ", outbound=" + outbound + '}'; - } + @Override + public String toString() { + return "{" + "inbound=" + inbound + ", outbound=" + outbound + '}'; + } - public NetworkDataMessagesInbound getInbound() { - return inbound; - } + public NetworkDataMessagesInbound getInbound() { + return inbound; + } - public NetworkDataMessagesOutbound getOutbound() { - return outbound; - } + public NetworkDataMessagesOutbound getOutbound() { + return outbound; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesInbound.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesInbound.java index e38db04895..ef17483264 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesInbound.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesInbound.java @@ -66,62 +66,67 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkDataMessagesInbound { - private final long processed; - private final long discarded; - private final long received; + private final long processed; + private final long discarded; + private final long received; - private NetworkDataMessagesInbound(long processed, long discarded, long received) { - this.processed = processed; - this.discarded = discarded; - this.received = received; - } + private NetworkDataMessagesInbound(long processed, long discarded, long received) { + this.processed = processed; + this.discarded = discarded; + this.received = received; + } - @JsonCreator - public static NetworkDataMessagesInbound create( - @JsonProperty(value = "processed", required = true) long processed, - @JsonProperty(value = "discarded", required = true) long discarded, - @JsonProperty(value = "received", required = true) long received - ) { - return new NetworkDataMessagesInbound(processed, discarded, received); - } + @JsonCreator + public static NetworkDataMessagesInbound create( + @JsonProperty(value = "processed", required = true) long processed, + @JsonProperty(value = "discarded", required = true) long discarded, + @JsonProperty(value = "received", required = true) long received) { + return new NetworkDataMessagesInbound(processed, discarded, received); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkDataMessagesInbound)) { - return false; - } + if (!(o instanceof NetworkDataMessagesInbound)) { + return false; + } - var that = (NetworkDataMessagesInbound) o; - return processed == that.processed && discarded == that.discarded && received == that.received; - } + var that = (NetworkDataMessagesInbound) o; + return processed == that.processed && discarded == that.discarded && received == that.received; + } - @Override - public int hashCode() { - return Objects.hash(processed, discarded, received); - } + @Override + public int hashCode() { + return Objects.hash(processed, discarded, received); + } - @Override - public String toString() { - return "{" + "processed=" + processed + ", discarded=" + discarded + ", received=" + received + '}'; - } + @Override + public String toString() { + return "{" + + "processed=" + + processed + + ", discarded=" + + discarded + + ", received=" + + received + + '}'; + } - public long getProcessed() { - return processed; - } + public long getProcessed() { + return processed; + } - public long getDiscarded() { - return discarded; - } + public long getDiscarded() { + return discarded; + } - public long getReceived() { - return received; - } + public long getReceived() { + return received; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesOutbound.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesOutbound.java index 9c38c58413..0b60823433 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesOutbound.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataMessagesOutbound.java @@ -66,69 +66,79 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkDataMessagesOutbound { - private final long processed; - private final long aborted; - private final long pending; - private final long sent; + private final long processed; + private final long aborted; + private final long pending; + private final long sent; - private NetworkDataMessagesOutbound(long processed, long aborted, long pending, long sent) { - this.processed = processed; - this.aborted = aborted; - this.pending = pending; - this.sent = sent; - } + private NetworkDataMessagesOutbound(long processed, long aborted, long pending, long sent) { + this.processed = processed; + this.aborted = aborted; + this.pending = pending; + this.sent = sent; + } - @JsonCreator - public static NetworkDataMessagesOutbound create( - @JsonProperty(value = "processed", required = true) long processed, - @JsonProperty(value = "aborted", required = true) long aborted, - @JsonProperty(value = "pending", required = true) long pending, - @JsonProperty(value = "sent", required = true) long sent - ) { - return new NetworkDataMessagesOutbound(processed, aborted, pending, sent); - } + @JsonCreator + public static NetworkDataMessagesOutbound create( + @JsonProperty(value = "processed", required = true) long processed, + @JsonProperty(value = "aborted", required = true) long aborted, + @JsonProperty(value = "pending", required = true) long pending, + @JsonProperty(value = "sent", required = true) long sent) { + return new NetworkDataMessagesOutbound(processed, aborted, pending, sent); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkDataMessagesOutbound)) { - return false; - } + if (!(o instanceof NetworkDataMessagesOutbound)) { + return false; + } - var that = (NetworkDataMessagesOutbound) o; - return processed == that.processed && aborted == that.aborted && pending == that.pending && sent == that.sent; - } + var that = (NetworkDataMessagesOutbound) o; + return processed == that.processed + && aborted == that.aborted + && pending == that.pending + && sent == that.sent; + } - @Override - public int hashCode() { - return Objects.hash(processed, aborted, pending, sent); - } + @Override + public int hashCode() { + return Objects.hash(processed, aborted, pending, sent); + } - @Override - public String toString() { - return "{" + "processed=" + processed + ", aborted=" + aborted + ", pending=" + pending + ", sent=" + sent + '}'; - } + @Override + public String toString() { + return "{" + + "processed=" + + processed + + ", aborted=" + + aborted + + ", pending=" + + pending + + ", sent=" + + sent + + '}'; + } - public long getProcessed() { - return processed; - } + public long getProcessed() { + return processed; + } - public long getAborted() { - return aborted; - } + public long getAborted() { + return aborted; + } - public long getPending() { - return pending; - } + public long getPending() { + return pending; + } - public long getSent() { - return sent; - } + public long getSent() { + return sent; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworking.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworking.java index 6d8341b95f..fc026e4bb4 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworking.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworking.java @@ -66,69 +66,83 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkDataNetworking { - private final NetworkDataNetworkingUdp udp; - private final NetworkDataNetworkingTcp tcp; - private final long receivedBytes; - private final long sentBytes; + private final NetworkDataNetworkingUdp udp; + private final NetworkDataNetworkingTcp tcp; + private final long receivedBytes; + private final long sentBytes; - private NetworkDataNetworking(NetworkDataNetworkingUdp udp, NetworkDataNetworkingTcp tcp, long receivedBytes, long sentBytes) { - this.udp = udp; - this.tcp = tcp; - this.receivedBytes = receivedBytes; - this.sentBytes = sentBytes; - } + private NetworkDataNetworking( + NetworkDataNetworkingUdp udp, + NetworkDataNetworkingTcp tcp, + long receivedBytes, + long sentBytes) { + this.udp = udp; + this.tcp = tcp; + this.receivedBytes = receivedBytes; + this.sentBytes = sentBytes; + } - @JsonCreator - public static NetworkDataNetworking create( - @JsonProperty(value = "udp", required = true) NetworkDataNetworkingUdp udp, - @JsonProperty(value = "tcp", required = true) NetworkDataNetworkingTcp tcp, - @JsonProperty(value = "receivedBytes", required = true) long receivedBytes, - @JsonProperty(value = "sentBytes", required = true) long sentBytes - ) { - return new NetworkDataNetworking(udp, tcp, receivedBytes, sentBytes); - } + @JsonCreator + public static NetworkDataNetworking create( + @JsonProperty(value = "udp", required = true) NetworkDataNetworkingUdp udp, + @JsonProperty(value = "tcp", required = true) NetworkDataNetworkingTcp tcp, + @JsonProperty(value = "receivedBytes", required = true) long receivedBytes, + @JsonProperty(value = "sentBytes", required = true) long sentBytes) { + return new NetworkDataNetworking(udp, tcp, receivedBytes, sentBytes); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkDataNetworking)) { - return false; - } + if (!(o instanceof NetworkDataNetworking)) { + return false; + } - var that = (NetworkDataNetworking) o; - return receivedBytes == that.receivedBytes && sentBytes == that.sentBytes && udp.equals(that.udp) && tcp.equals(that.tcp); - } + var that = (NetworkDataNetworking) o; + return receivedBytes == that.receivedBytes + && sentBytes == that.sentBytes + && udp.equals(that.udp) + && tcp.equals(that.tcp); + } - @Override - public int hashCode() { - return Objects.hash(udp, tcp, receivedBytes, sentBytes); - } + @Override + public int hashCode() { + return Objects.hash(udp, tcp, receivedBytes, sentBytes); + } - @Override - public String toString() { - return "{" + "udp=" + udp + ", tcp=" + tcp + ", receivedBytes=" + receivedBytes + ", sentBytes=" + sentBytes + '}'; - } + @Override + public String toString() { + return "{" + + "udp=" + + udp + + ", tcp=" + + tcp + + ", receivedBytes=" + + receivedBytes + + ", sentBytes=" + + sentBytes + + '}'; + } - public NetworkDataNetworkingUdp getUdp() { - return udp; - } + public NetworkDataNetworkingUdp getUdp() { + return udp; + } - public NetworkDataNetworkingTcp getTcp() { - return tcp; - } + public NetworkDataNetworkingTcp getTcp() { + return tcp; + } - public long getReceivedBytes() { - return receivedBytes; - } + public long getReceivedBytes() { + return receivedBytes; + } - public long getSentBytes() { - return sentBytes; - } + public long getSentBytes() { + return sentBytes; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingTcp.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingTcp.java index 4abf6273d0..ec71d97de2 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingTcp.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingTcp.java @@ -66,73 +66,80 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkDataNetworkingTcp { - private final long outOpened; - private final long droppedMessages; - private final long closed; - private final long inOpened; + private final long outOpened; + private final long droppedMessages; + private final long closed; + private final long inOpened; - private NetworkDataNetworkingTcp(long outOpened, long droppedMessages, long closed, long inOpened) { - this.outOpened = outOpened; - this.droppedMessages = droppedMessages; - this.closed = closed; - this.inOpened = inOpened; - } + private NetworkDataNetworkingTcp( + long outOpened, long droppedMessages, long closed, long inOpened) { + this.outOpened = outOpened; + this.droppedMessages = droppedMessages; + this.closed = closed; + this.inOpened = inOpened; + } - @JsonCreator - public static NetworkDataNetworkingTcp create( - @JsonProperty(value = "outOpened", required = true) long outOpened, - @JsonProperty(value = "droppedMessages", required = true) long droppedMessages, - @JsonProperty(value = "closed", required = true) long closed, - @JsonProperty(value = "inOpened", required = true) long inOpened - ) { - return new NetworkDataNetworkingTcp(outOpened, droppedMessages, closed, inOpened); - } + @JsonCreator + public static NetworkDataNetworkingTcp create( + @JsonProperty(value = "outOpened", required = true) long outOpened, + @JsonProperty(value = "droppedMessages", required = true) long droppedMessages, + @JsonProperty(value = "closed", required = true) long closed, + @JsonProperty(value = "inOpened", required = true) long inOpened) { + return new NetworkDataNetworkingTcp(outOpened, droppedMessages, closed, inOpened); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkDataNetworkingTcp)) { - return false; - } + if (!(o instanceof NetworkDataNetworkingTcp)) { + return false; + } - var that = (NetworkDataNetworkingTcp) o; - return outOpened == that.outOpened - && droppedMessages == that.droppedMessages - && closed == that.closed - && inOpened == that.inOpened; - } + var that = (NetworkDataNetworkingTcp) o; + return outOpened == that.outOpened + && droppedMessages == that.droppedMessages + && closed == that.closed + && inOpened == that.inOpened; + } - @Override - public int hashCode() { - return Objects.hash(outOpened, droppedMessages, closed, inOpened); - } + @Override + public int hashCode() { + return Objects.hash(outOpened, droppedMessages, closed, inOpened); + } - @Override - public String toString() { - return "{" + "outOpened=" + outOpened + ", droppedMessages=" + droppedMessages + ", closed=" + closed - + ", inOpened=" + inOpened + '}'; - } + @Override + public String toString() { + return "{" + + "outOpened=" + + outOpened + + ", droppedMessages=" + + droppedMessages + + ", closed=" + + closed + + ", inOpened=" + + inOpened + + '}'; + } - public long getOutOpened() { - return outOpened; - } + public long getOutOpened() { + return outOpened; + } - public long getDroppedMessages() { - return droppedMessages; - } + public long getDroppedMessages() { + return droppedMessages; + } - public long getClosed() { - return closed; - } + public long getClosed() { + return closed; + } - public long getInOpened() { - return inOpened; - } + public long getInOpened() { + return inOpened; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingUdp.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingUdp.java index 9596efac8a..bbc126f9fa 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingUdp.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkDataNetworkingUdp.java @@ -66,48 +66,46 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkDataNetworkingUdp { - private final long droppedMessages; + private final long droppedMessages; - private NetworkDataNetworkingUdp(long droppedMessages) { - this.droppedMessages = droppedMessages; - } + private NetworkDataNetworkingUdp(long droppedMessages) { + this.droppedMessages = droppedMessages; + } - @JsonCreator - public static NetworkDataNetworkingUdp create( - @JsonProperty(value = "droppedMessages", required = true) long droppedMessages - ) { - return new NetworkDataNetworkingUdp(droppedMessages); - } + @JsonCreator + public static NetworkDataNetworkingUdp create( + @JsonProperty(value = "droppedMessages", required = true) long droppedMessages) { + return new NetworkDataNetworkingUdp(droppedMessages); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkDataNetworkingUdp)) { - return false; - } + if (!(o instanceof NetworkDataNetworkingUdp)) { + return false; + } - var that = (NetworkDataNetworkingUdp) o; - return droppedMessages == that.droppedMessages; - } + var that = (NetworkDataNetworkingUdp) o; + return droppedMessages == that.droppedMessages; + } - @Override - public int hashCode() { - return Objects.hash(droppedMessages); - } + @Override + public int hashCode() { + return Objects.hash(droppedMessages); + } - @Override - public String toString() { - return "{" + "droppedMessages=" + droppedMessages + '}'; - } + @Override + public String toString() { + return "{" + "droppedMessages=" + droppedMessages + '}'; + } - public long getDroppedMessages() { - return droppedMessages; - } + public long getDroppedMessages() { + return droppedMessages; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkId.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkId.java index f30680cc15..f549ea4a71 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkId.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkId.java @@ -66,41 +66,41 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkId { - private final int networkId; + private final int networkId; - private NetworkId(int networkId) { - this.networkId = networkId; - } + private NetworkId(int networkId) { + this.networkId = networkId; + } - @JsonCreator - public static NetworkId create(@JsonProperty(value = "networkId", required = true) int networkId) { - return new NetworkId(networkId); - } + @JsonCreator + public static NetworkId create( + @JsonProperty(value = "networkId", required = true) int networkId) { + return new NetworkId(networkId); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - return o instanceof NetworkId && networkId == ((NetworkId) o).networkId; - } + return o instanceof NetworkId && networkId == ((NetworkId) o).networkId; + } - @Override - public int hashCode() { - return Objects.hash(networkId); - } + @Override + public int hashCode() { + return Objects.hash(networkId); + } - @Override - public String toString() { - return "NetworkId(" + networkId + ')'; - } + @Override + public String toString() { + return "NetworkId(" + networkId + ')'; + } - public int getNetworkId() { - return networkId; - } + public int getNetworkId() { + return networkId; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkPeer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkPeer.java index 4b534b4b5a..0d2db7b29a 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkPeer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkPeer.java @@ -67,59 +67,54 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.NodeAddress; - import java.util.List; import java.util.Objects; public final class NetworkPeer { - private final NodeAddress address; - private final List channels; + private final NodeAddress address; + private final List channels; - private NetworkPeer(NodeAddress address, List channels) { - this.address = address; - this.channels = channels; - } + private NetworkPeer(NodeAddress address, List channels) { + this.address = address; + this.channels = channels; + } - @JsonCreator - public static NetworkPeer create( - @JsonProperty(value = "address", required = true) NodeAddress address, - @JsonProperty(value = "channels", required = true) List channels - ) { - return new NetworkPeer(address, channels); - } + @JsonCreator + public static NetworkPeer create( + @JsonProperty(value = "address", required = true) NodeAddress address, + @JsonProperty(value = "channels", required = true) List channels) { + return new NetworkPeer(address, channels); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkPeer)) { - return false; - } + if (!(o instanceof NetworkPeer)) { + return false; + } - var that = (NetworkPeer) o; - return address.equals(that.address) && channels.equals(that.channels); - } + var that = (NetworkPeer) o; + return address.equals(that.address) && channels.equals(that.channels); + } - @Override - public int hashCode() { - return Objects.hash(address, channels); - } + @Override + public int hashCode() { + return Objects.hash(address, channels); + } - @Override - public String toString() { - return "{" - + "address=" + address - + ", channels=" + channels - + '}'; - } + @Override + public String toString() { + return "{" + "address=" + address + ", channels=" + channels + '}'; + } - public NodeAddress getAddress() { - return address; - } + public NodeAddress getAddress() { + return address; + } - public List getChannels() { - return channels; - } + public List getChannels() { + return channels; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkStats.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkStats.java index 50eab27f38..193e4c59ce 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkStats.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/NetworkStats.java @@ -66,46 +66,45 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class NetworkStats { - private final long tps; + private final long tps; - private NetworkStats(long tps) { - this.tps = tps; - } + private NetworkStats(long tps) { + this.tps = tps; + } - @JsonCreator - public static NetworkStats create(@JsonProperty(value = "tps", required = true) long tps) { - return new NetworkStats(tps); - } + @JsonCreator + public static NetworkStats create(@JsonProperty(value = "tps", required = true) long tps) { + return new NetworkStats(tps); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof NetworkStats)) { - return false; - } + if (!(o instanceof NetworkStats)) { + return false; + } - var that = (NetworkStats) o; - return tps == that.tps; - } + var that = (NetworkStats) o; + return tps == that.tps; + } - @Override - public int hashCode() { - return Objects.hash(tps); - } + @Override + public int hashCode() { + return Objects.hash(tps); + } - @Override - public String toString() { - return "TPS:" + tps; - } + @Override + public String toString() { + return "TPS:" + tps; + } - public long getTps() { - return tps; - } + public long getTps() { + return tps; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Notification.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Notification.java index fda7377e98..28f96396cb 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Notification.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Notification.java @@ -1,5 +1,4 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: @@ -65,66 +64,64 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; import java.util.Optional; -import static java.util.Objects.requireNonNull; - public final class Notification { - private final String type; - private final String symbol; - private final String rri; + private final String type; + private final String symbol; + private final String rri; - private Notification(String type, String symbol, String rri) { - this.type = type; - this.symbol = symbol; - this.rri = rri; - } + private Notification(String type, String symbol, String rri) { + this.type = type; + this.symbol = symbol; + this.rri = rri; + } - @JsonCreator - public static Notification create( - @JsonProperty(value = "type", required = true) String type, - @JsonProperty("symbol") String symbol, - @JsonProperty("rri") String rri - ) { - requireNonNull(type); + @JsonCreator + public static Notification create( + @JsonProperty(value = "type", required = true) String type, + @JsonProperty("symbol") String symbol, + @JsonProperty("rri") String rri) { + requireNonNull(type); - return new Notification(type, symbol, rri); - } + return new Notification(type, symbol, rri); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof Notification)) { - return false; - } + if (!(o instanceof Notification)) { + return false; + } - var that = (Notification) o; - return type.equals(that.type) - && Objects.equals(symbol, that.symbol) - && Objects.equals(rri, that.rri); - } + var that = (Notification) o; + return type.equals(that.type) + && Objects.equals(symbol, that.symbol) + && Objects.equals(rri, that.rri); + } - @Override - public int hashCode() { - return Objects.hash(type, symbol, rri); - } + @Override + public int hashCode() { + return Objects.hash(type, symbol, rri); + } - public Optional getSymbol() { - return Optional.ofNullable(symbol); - } + public Optional getSymbol() { + return Optional.ofNullable(symbol); + } - public Optional getRri() { - return Optional.ofNullable(rri); - } + public Optional getRri() { + return Optional.ofNullable(rri); + } - public String getType() { - return type; - } + public String getType() { + return type; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/PerUpSubstateFee.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/PerUpSubstateFee.java index 7120ae273f..863b336cc5 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/PerUpSubstateFee.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/PerUpSubstateFee.java @@ -67,134 +67,145 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class PerUpSubstateFee { - private final UInt256 preparedStake; - private final UInt256 validatorRegisteredCopy; - private final UInt256 preparedUnstakeOwnership; - private final UInt256 tokenResource; - private final UInt256 validatorOwnerCopy; - private final UInt256 validatorMetaData; - private final UInt256 allowDelegationFlag; - private final UInt256 validatorFeeCopy; - - private PerUpSubstateFee( - UInt256 preparedStake, - UInt256 validatorRegisteredCopy, - UInt256 preparedUnstakeOwnership, - UInt256 tokenResource, - UInt256 validatorOwnerCopy, - UInt256 validatorMetaData, - UInt256 allowDelegationFlag, - UInt256 validatorFeeCopy - ) { - this.preparedStake = preparedStake; - this.validatorRegisteredCopy = validatorRegisteredCopy; - this.preparedUnstakeOwnership = preparedUnstakeOwnership; - this.tokenResource = tokenResource; - this.validatorOwnerCopy = validatorOwnerCopy; - this.validatorMetaData = validatorMetaData; - this.allowDelegationFlag = allowDelegationFlag; - this.validatorFeeCopy = validatorFeeCopy; - } - - @JsonCreator - public static PerUpSubstateFee create( - @JsonProperty(value = "PreparedStake", required = true) UInt256 preparedStake, - @JsonProperty(value = "ValidatorRegisteredCopy", required = true) UInt256 validatorRegisteredCopy, - @JsonProperty(value = "PreparedUnstakeOwnership", required = true) UInt256 preparedUnstakeOwnership, - @JsonProperty(value = "TokenResource", required = true) UInt256 tokenResource, - @JsonProperty(value = "ValidatorOwnerCopy", required = true) UInt256 validatorOwnerCopy, - @JsonProperty(value = "ValidatorMetaData", required = true) UInt256 validatorMetaData, - @JsonProperty(value = "AllowDelegationFlag", required = true) UInt256 allowDelegationFlag, - @JsonProperty(value = "ValidatorFeeCopy", required = true) UInt256 validatorFeeCopy - ) { - return new PerUpSubstateFee( - preparedStake, validatorRegisteredCopy, preparedUnstakeOwnership, tokenResource, - validatorOwnerCopy, validatorMetaData, allowDelegationFlag, validatorFeeCopy - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof PerUpSubstateFee)) { - return false; - } - - var that = (PerUpSubstateFee) o; - return preparedStake.equals(that.preparedStake) - && validatorRegisteredCopy.equals(that.validatorRegisteredCopy) - && preparedUnstakeOwnership.equals(that.preparedUnstakeOwnership) - && tokenResource.equals(that.tokenResource) - && validatorOwnerCopy.equals(that.validatorOwnerCopy) - && validatorMetaData.equals(that.validatorMetaData) - && allowDelegationFlag.equals(that.allowDelegationFlag) - && validatorFeeCopy.equals(that.validatorFeeCopy); - } - - @Override - public int hashCode() { - return Objects.hash( - preparedStake, - validatorRegisteredCopy, - preparedUnstakeOwnership, - tokenResource, - validatorOwnerCopy, - validatorMetaData, - allowDelegationFlag, - validatorFeeCopy - ); - } - - @Override - public String toString() { - return "{" - + "preparedStake=" + preparedStake - + ", validatorRegisteredCopy=" + validatorRegisteredCopy - + ", preparedUnstakeOwnership=" + preparedUnstakeOwnership - + ", tokenResource=" + tokenResource - + ", validatorOwnerCopy=" + validatorOwnerCopy - + ", validatorMetaData=" + validatorMetaData - + ", allowDelegationFlag=" + allowDelegationFlag - + ", validatorFeeCopy=" + validatorFeeCopy - + '}'; - } - - public UInt256 getPreparedStake() { - return preparedStake; - } - - public UInt256 getValidatorRegisteredCopy() { - return validatorRegisteredCopy; - } - - public UInt256 getPreparedUnstakeOwnership() { - return preparedUnstakeOwnership; - } - - public UInt256 getTokenResource() { - return tokenResource; - } - - public UInt256 getValidatorOwnerCopy() { - return validatorOwnerCopy; - } - - public UInt256 getValidatorMetaData() { - return validatorMetaData; - } - - public UInt256 getAllowDelegationFlag() { - return allowDelegationFlag; - } - - public UInt256 getValidatorFeeCopy() { - return validatorFeeCopy; - } + private final UInt256 preparedStake; + private final UInt256 validatorRegisteredCopy; + private final UInt256 preparedUnstakeOwnership; + private final UInt256 tokenResource; + private final UInt256 validatorOwnerCopy; + private final UInt256 validatorMetaData; + private final UInt256 allowDelegationFlag; + private final UInt256 validatorFeeCopy; + + private PerUpSubstateFee( + UInt256 preparedStake, + UInt256 validatorRegisteredCopy, + UInt256 preparedUnstakeOwnership, + UInt256 tokenResource, + UInt256 validatorOwnerCopy, + UInt256 validatorMetaData, + UInt256 allowDelegationFlag, + UInt256 validatorFeeCopy) { + this.preparedStake = preparedStake; + this.validatorRegisteredCopy = validatorRegisteredCopy; + this.preparedUnstakeOwnership = preparedUnstakeOwnership; + this.tokenResource = tokenResource; + this.validatorOwnerCopy = validatorOwnerCopy; + this.validatorMetaData = validatorMetaData; + this.allowDelegationFlag = allowDelegationFlag; + this.validatorFeeCopy = validatorFeeCopy; + } + + @JsonCreator + public static PerUpSubstateFee create( + @JsonProperty(value = "PreparedStake", required = true) UInt256 preparedStake, + @JsonProperty(value = "ValidatorRegisteredCopy", required = true) + UInt256 validatorRegisteredCopy, + @JsonProperty(value = "PreparedUnstakeOwnership", required = true) + UInt256 preparedUnstakeOwnership, + @JsonProperty(value = "TokenResource", required = true) UInt256 tokenResource, + @JsonProperty(value = "ValidatorOwnerCopy", required = true) UInt256 validatorOwnerCopy, + @JsonProperty(value = "ValidatorMetaData", required = true) UInt256 validatorMetaData, + @JsonProperty(value = "AllowDelegationFlag", required = true) UInt256 allowDelegationFlag, + @JsonProperty(value = "ValidatorFeeCopy", required = true) UInt256 validatorFeeCopy) { + return new PerUpSubstateFee( + preparedStake, + validatorRegisteredCopy, + preparedUnstakeOwnership, + tokenResource, + validatorOwnerCopy, + validatorMetaData, + allowDelegationFlag, + validatorFeeCopy); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof PerUpSubstateFee)) { + return false; + } + + var that = (PerUpSubstateFee) o; + return preparedStake.equals(that.preparedStake) + && validatorRegisteredCopy.equals(that.validatorRegisteredCopy) + && preparedUnstakeOwnership.equals(that.preparedUnstakeOwnership) + && tokenResource.equals(that.tokenResource) + && validatorOwnerCopy.equals(that.validatorOwnerCopy) + && validatorMetaData.equals(that.validatorMetaData) + && allowDelegationFlag.equals(that.allowDelegationFlag) + && validatorFeeCopy.equals(that.validatorFeeCopy); + } + + @Override + public int hashCode() { + return Objects.hash( + preparedStake, + validatorRegisteredCopy, + preparedUnstakeOwnership, + tokenResource, + validatorOwnerCopy, + validatorMetaData, + allowDelegationFlag, + validatorFeeCopy); + } + + @Override + public String toString() { + return "{" + + "preparedStake=" + + preparedStake + + ", validatorRegisteredCopy=" + + validatorRegisteredCopy + + ", preparedUnstakeOwnership=" + + preparedUnstakeOwnership + + ", tokenResource=" + + tokenResource + + ", validatorOwnerCopy=" + + validatorOwnerCopy + + ", validatorMetaData=" + + validatorMetaData + + ", allowDelegationFlag=" + + allowDelegationFlag + + ", validatorFeeCopy=" + + validatorFeeCopy + + '}'; + } + + public UInt256 getPreparedStake() { + return preparedStake; + } + + public UInt256 getValidatorRegisteredCopy() { + return validatorRegisteredCopy; + } + + public UInt256 getPreparedUnstakeOwnership() { + return preparedUnstakeOwnership; + } + + public UInt256 getTokenResource() { + return tokenResource; + } + + public UInt256 getValidatorOwnerCopy() { + return validatorOwnerCopy; + } + + public UInt256 getValidatorMetaData() { + return validatorMetaData; + } + + public UInt256 getAllowDelegationFlag() { + return allowDelegationFlag; + } + + public UInt256 getValidatorFeeCopy() { + return validatorFeeCopy; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Proof.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Proof.java index 564650f6c4..81ae9a3f7d 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Proof.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Proof.java @@ -66,65 +66,61 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; public final class Proof { - private final String opaque; - private final List sigs; - private final ProofHeader header; + private final String opaque; + private final List sigs; + private final ProofHeader header; - private Proof(String opaque, List sigs, ProofHeader header) { - this.opaque = opaque; - this.sigs = sigs; - this.header = header; - } + private Proof(String opaque, List sigs, ProofHeader header) { + this.opaque = opaque; + this.sigs = sigs; + this.header = header; + } - @JsonCreator - public static Proof create( - @JsonProperty(value = "opaque", required = true) String opaque, - @JsonProperty(value = "sigs", required = true) List sigs, - @JsonProperty(value = "header", required = true) ProofHeader header - ) { - return new Proof(opaque, sigs, header); - } + @JsonCreator + public static Proof create( + @JsonProperty(value = "opaque", required = true) String opaque, + @JsonProperty(value = "sigs", required = true) List sigs, + @JsonProperty(value = "header", required = true) ProofHeader header) { + return new Proof(opaque, sigs, header); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof Proof)) { - return false; - } + if (!(o instanceof Proof)) { + return false; + } - var proof = (Proof) o; - return opaque.equals(proof.opaque) && sigs.equals(proof.sigs) && header.equals(proof.header); - } + var proof = (Proof) o; + return opaque.equals(proof.opaque) && sigs.equals(proof.sigs) && header.equals(proof.header); + } - @Override - public int hashCode() { - return Objects.hash(opaque, sigs, header); - } + @Override + public int hashCode() { + return Objects.hash(opaque, sigs, header); + } - @Override - public String toString() { - return "{opaque:" + opaque - + ", sigs:" + sigs - + ", header:" + header + '}'; - } + @Override + public String toString() { + return "{opaque:" + opaque + ", sigs:" + sigs + ", header:" + header + '}'; + } - public String getOpaque() { - return opaque; - } + public String getOpaque() { + return opaque; + } - public List getSigs() { - return sigs; - } + public List getSigs() { + return sigs; + } - public ProofHeader getHeader() { - return header; - } + public ProofHeader getHeader() { + return header; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ProofHeader.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ProofHeader.java index 7991895cd3..40ecdcd43f 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ProofHeader.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ProofHeader.java @@ -66,99 +66,111 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; public final class ProofHeader { - private final long view; - private final long epoch; - private final long version; - private final long timestamp; - private final String accumulator; - private final List nextValidators; - - private ProofHeader( - long view, long epoch, long version, long timestamp, String accumulator, List nextValidators - ) { - this.view = view; - this.epoch = epoch; - this.version = version; - this.timestamp = timestamp; - this.accumulator = accumulator; - this.nextValidators = nextValidators; - } - - @JsonCreator - public static ProofHeader create( - @JsonProperty(value = "view", required = true) long view, - @JsonProperty(value = "epoch", required = true) long epoch, - @JsonProperty(value = "version", required = true) long version, - @JsonProperty(value = "timestamp", required = true) long timestamp, - @JsonProperty(value = "accumulator", required = true) String accumulator, - @JsonProperty("nextValidators") List nextValidators - ) { - return new ProofHeader( - view, epoch, version, timestamp, accumulator, - nextValidators == null ? List.of() : nextValidators - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof ProofHeader)) { - return false; - } - - var that = (ProofHeader) o; - return view == that.view - && epoch == that.epoch - && version == that.version - && timestamp == that.timestamp - && accumulator.equals(that.accumulator) - && nextValidators.equals(that.nextValidators); - } - - @Override - public int hashCode() { - return Objects.hash(view, epoch, version, timestamp, accumulator, nextValidators); - } - - @Override - public String toString() { - return "{view:" + view - + ", epoch:" + epoch - + ", version:" + version - + ", timestamp:" + timestamp - + ", accumulator:" + accumulator - + ", nextValidators" + nextValidators + '}'; - } - - public long getView() { - return view; - } - - public long getEpoch() { - return epoch; - } - - public long getVersion() { - return version; - } - - public long getTimestamp() { - return timestamp; - } - - public String getAccumulator() { - return accumulator; - } - - public List getNextValidators() { - return nextValidators; - } + private final long view; + private final long epoch; + private final long version; + private final long timestamp; + private final String accumulator; + private final List nextValidators; + + private ProofHeader( + long view, + long epoch, + long version, + long timestamp, + String accumulator, + List nextValidators) { + this.view = view; + this.epoch = epoch; + this.version = version; + this.timestamp = timestamp; + this.accumulator = accumulator; + this.nextValidators = nextValidators; + } + + @JsonCreator + public static ProofHeader create( + @JsonProperty(value = "view", required = true) long view, + @JsonProperty(value = "epoch", required = true) long epoch, + @JsonProperty(value = "version", required = true) long version, + @JsonProperty(value = "timestamp", required = true) long timestamp, + @JsonProperty(value = "accumulator", required = true) String accumulator, + @JsonProperty("nextValidators") List nextValidators) { + return new ProofHeader( + view, + epoch, + version, + timestamp, + accumulator, + nextValidators == null ? List.of() : nextValidators); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ProofHeader)) { + return false; + } + + var that = (ProofHeader) o; + return view == that.view + && epoch == that.epoch + && version == that.version + && timestamp == that.timestamp + && accumulator.equals(that.accumulator) + && nextValidators.equals(that.nextValidators); + } + + @Override + public int hashCode() { + return Objects.hash(view, epoch, version, timestamp, accumulator, nextValidators); + } + + @Override + public String toString() { + return "{view:" + + view + + ", epoch:" + + epoch + + ", version:" + + version + + ", timestamp:" + + timestamp + + ", accumulator:" + + accumulator + + ", nextValidators" + + nextValidators + + '}'; + } + + public long getView() { + return view; + } + + public long getEpoch() { + return epoch; + } + + public long getVersion() { + return version; + } + + public long getTimestamp() { + return timestamp; + } + + public String getAccumulator() { + return accumulator; + } + + public List getNextValidators() { + return nextValidators; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/RadixEngineData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/RadixEngineData.java index b22551bef3..b0f2e1c679 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/RadixEngineData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/RadixEngineData.java @@ -66,66 +66,70 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class RadixEngineData { - private final long invalidProposedCommands; - private final long systemTransactions; - private final long userTransactions; + private final long invalidProposedCommands; + private final long systemTransactions; + private final long userTransactions; - private RadixEngineData(long invalidProposedCommands, long systemTransactions, long userTransactions) { - this.invalidProposedCommands = invalidProposedCommands; - this.systemTransactions = systemTransactions; - this.userTransactions = userTransactions; - } + private RadixEngineData( + long invalidProposedCommands, long systemTransactions, long userTransactions) { + this.invalidProposedCommands = invalidProposedCommands; + this.systemTransactions = systemTransactions; + this.userTransactions = userTransactions; + } - @JsonCreator - public static RadixEngineData create( - @JsonProperty(value = "invalidProposedCommands", required = true) long invalidProposedCommands, - @JsonProperty(value = "systemTransactions", required = true) long systemTransactions, - @JsonProperty(value = "userTransactions", required = true) long userTransactions - ) { - return new RadixEngineData(invalidProposedCommands, systemTransactions, userTransactions); - } + @JsonCreator + public static RadixEngineData create( + @JsonProperty(value = "invalidProposedCommands", required = true) + long invalidProposedCommands, + @JsonProperty(value = "systemTransactions", required = true) long systemTransactions, + @JsonProperty(value = "userTransactions", required = true) long userTransactions) { + return new RadixEngineData(invalidProposedCommands, systemTransactions, userTransactions); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof RadixEngineData)) { - return false; - } + if (!(o instanceof RadixEngineData)) { + return false; + } - var that = (RadixEngineData) o; - return invalidProposedCommands == that.invalidProposedCommands - && systemTransactions == that.systemTransactions - && userTransactions == that.userTransactions; - } + var that = (RadixEngineData) o; + return invalidProposedCommands == that.invalidProposedCommands + && systemTransactions == that.systemTransactions + && userTransactions == that.userTransactions; + } - @Override - public int hashCode() { - return Objects.hash(invalidProposedCommands, systemTransactions, userTransactions); - } + @Override + public int hashCode() { + return Objects.hash(invalidProposedCommands, systemTransactions, userTransactions); + } - @Override - public String toString() { - return "{invalidProposedCommands:" + invalidProposedCommands - + ", systemTransactions:" + systemTransactions - + ", userTransactions:" + userTransactions + '}'; - } + @Override + public String toString() { + return "{invalidProposedCommands:" + + invalidProposedCommands + + ", systemTransactions:" + + systemTransactions + + ", userTransactions:" + + userTransactions + + '}'; + } - public long getInvalidProposedCommands() { - return invalidProposedCommands; - } + public long getInvalidProposedCommands() { + return invalidProposedCommands; + } - public long getSystemTransactions() { - return systemTransactions; - } + public long getSystemTransactions() { + return systemTransactions; + } - public long getUserTransactions() { - return userTransactions; - } + public long getUserTransactions() { + return userTransactions; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWrite.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWrite.java index c33b8bc086..5064a34ac6 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWrite.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWrite.java @@ -66,55 +66,53 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ReadWrite { - private final long read; - private final long write; + private final long read; + private final long write; - private ReadWrite(long read, long write) { - this.read = read; - this.write = write; - } + private ReadWrite(long read, long write) { + this.read = read; + this.write = write; + } - @JsonCreator - public static ReadWrite create( - @JsonProperty(value = "read", required = true) long read, - @JsonProperty(value = "write", required = true) long write - ) { - return new ReadWrite(read, write); - } + @JsonCreator + public static ReadWrite create( + @JsonProperty(value = "read", required = true) long read, + @JsonProperty(value = "write", required = true) long write) { + return new ReadWrite(read, write); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ReadWrite)) { - return false; - } + if (!(o instanceof ReadWrite)) { + return false; + } - var that = (ReadWrite) o; - return read == that.read && write == that.write; - } + var that = (ReadWrite) o; + return read == that.read && write == that.write; + } - @Override - public int hashCode() { - return Objects.hash(read, write); - } + @Override + public int hashCode() { + return Objects.hash(read, write); + } - @Override - public String toString() { - return "{read:" + read + ", write: " + write + '}'; - } + @Override + public String toString() { + return "{read:" + read + ", write: " + write + '}'; + } - public long getRead() { - return read; - } + public long getRead() { + return read; + } - public long getWrite() { - return write; - } + public long getWrite() { + return write; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWriteStats.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWriteStats.java index 75d3203c5d..52031326b3 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWriteStats.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ReadWriteStats.java @@ -66,69 +66,70 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class ReadWriteStats { - private final long read; - private final long write; - private final long total; - private final ReadWrite bytes; + private final long read; + private final long write; + private final long total; + private final ReadWrite bytes; - private ReadWriteStats(long read, long write, long total, ReadWrite bytes) { - this.read = read; - this.write = write; - this.total = total; - this.bytes = bytes; - } + private ReadWriteStats(long read, long write, long total, ReadWrite bytes) { + this.read = read; + this.write = write; + this.total = total; + this.bytes = bytes; + } - @JsonCreator - public static ReadWriteStats create( - @JsonProperty(value = "read", required = true) long read, - @JsonProperty(value = "write", required = true) long write, - @JsonProperty(value = "total", required = true) long total, - @JsonProperty(value = "bytes", required = true) ReadWrite bytes - ) { - return new ReadWriteStats(read, write, total, bytes); - } + @JsonCreator + public static ReadWriteStats create( + @JsonProperty(value = "read", required = true) long read, + @JsonProperty(value = "write", required = true) long write, + @JsonProperty(value = "total", required = true) long total, + @JsonProperty(value = "bytes", required = true) ReadWrite bytes) { + return new ReadWriteStats(read, write, total, bytes); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ReadWriteStats)) { - return false; - } + if (!(o instanceof ReadWriteStats)) { + return false; + } - var that = (ReadWriteStats) o; - return read == that.read && write == that.write && total == that.total && bytes.equals(that.bytes); - } + var that = (ReadWriteStats) o; + return read == that.read + && write == that.write + && total == that.total + && bytes.equals(that.bytes); + } - @Override - public int hashCode() { - return Objects.hash(read, write, total, bytes); - } + @Override + public int hashCode() { + return Objects.hash(read, write, total, bytes); + } - @Override - public String toString() { - return "{read:" + read + ", write:" + write + ", total:" + total + ", bytes:" + bytes + '}'; - } + @Override + public String toString() { + return "{read:" + read + ", write:" + write + ", total:" + total + ", bytes:" + bytes + '}'; + } - public long getRead() { - return read; - } + public long getRead() { + return read; + } - public long getWrite() { - return write; - } + public long getWrite() { + return write; + } - public long getTotal() { - return total; - } + public long getTotal() { + return total; + } - public ReadWrite getBytes() { - return bytes; - } + public ReadWrite getBytes() { + return bytes; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SignatureDetails.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SignatureDetails.java index be1e5eb459..98bc99c77a 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SignatureDetails.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SignatureDetails.java @@ -68,64 +68,61 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.exception.PublicKeyException; - import java.util.Objects; public final class SignatureDetails { - private final ECPublicKey key; - private final String signature; - private final long timestamp; + private final ECPublicKey key; + private final String signature; + private final long timestamp; - private SignatureDetails(ECPublicKey key, String signature, long timestamp) { - this.key = key; - this.signature = signature; - this.timestamp = timestamp; - } + private SignatureDetails(ECPublicKey key, String signature, long timestamp) { + this.key = key; + this.signature = signature; + this.timestamp = timestamp; + } - @JsonCreator - public static SignatureDetails create( - @JsonProperty(value = "key", required = true) String key, - @JsonProperty(value = "signature", required = true) String signature, - @JsonProperty(value = "timestamp", required = true) long timestamp - ) throws PublicKeyException { - return new SignatureDetails(ECPublicKey.fromHex(key), signature, timestamp); - } + @JsonCreator + public static SignatureDetails create( + @JsonProperty(value = "key", required = true) String key, + @JsonProperty(value = "signature", required = true) String signature, + @JsonProperty(value = "timestamp", required = true) long timestamp) + throws PublicKeyException { + return new SignatureDetails(ECPublicKey.fromHex(key), signature, timestamp); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof SignatureDetails)) { - return false; - } + if (!(o instanceof SignatureDetails)) { + return false; + } - var that = (SignatureDetails) o; - return timestamp == that.timestamp && key.equals(that.key) && signature.equals(that.signature); - } + var that = (SignatureDetails) o; + return timestamp == that.timestamp && key.equals(that.key) && signature.equals(that.signature); + } - @Override - public int hashCode() { - return Objects.hash(key, signature, timestamp); - } + @Override + public int hashCode() { + return Objects.hash(key, signature, timestamp); + } - @Override - public String toString() { - return "{key:" + key.toHex() - + ", signature:" + signature - + ", timestamp:" + timestamp + '}'; - } + @Override + public String toString() { + return "{key:" + key.toHex() + ", signature:" + signature + ", timestamp:" + timestamp + '}'; + } - public ECPublicKey getKey() { - return key; - } + public ECPublicKey getKey() { + return key; + } - public String getSignature() { - return signature; - } + public String getSignature() { + return signature; + } - public long getTimestamp() { - return timestamp; - } + public long getTimestamp() { + return timestamp; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Size.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Size.java index 882586ab6c..fa0f681e43 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Size.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Size.java @@ -66,46 +66,45 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class Size { - private final long size; + private final long size; - private Size(long size) { - this.size = size; - } + private Size(long size) { + this.size = size; + } - @JsonCreator - public static Size create(@JsonProperty(value = "size", required = true) long size) { - return new Size(size); - } + @JsonCreator + public static Size create(@JsonProperty(value = "size", required = true) long size) { + return new Size(size); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof Size)) { - return false; - } + if (!(o instanceof Size)) { + return false; + } - var sizeDTO = (Size) o; - return size == sizeDTO.size; - } + var sizeDTO = (Size) o; + return size == sizeDTO.size; + } - @Override - public int hashCode() { - return Objects.hash(size); - } + @Override + public int hashCode() { + return Objects.hash(size); + } - @Override - public String toString() { - return "{size:" + size + '}'; - } + @Override + public String toString() { + return "{size:" + size + '}'; + } - public long getSize() { - return size; - } + public long getSize() { + return size; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/StakePositions.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/StakePositions.java index 5e2958a0bb..7f12b8a126 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/StakePositions.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/StakePositions.java @@ -64,65 +64,68 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.networks.Network; import com.radixdlt.utils.UInt256; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class StakePositions { - private final ValidatorAddress validator; - private final UInt256 amount; + private final ValidatorAddress validator; + private final UInt256 amount; - private StakePositions(ValidatorAddress validator, UInt256 amount) { - this.validator = validator; - this.amount = amount; - } + private StakePositions(ValidatorAddress validator, UInt256 amount) { + this.validator = validator; + this.amount = amount; + } - @JsonCreator - public static StakePositions create( - @JsonProperty(value = "validator", required = true) ValidatorAddress validator, - @JsonProperty(value = "amount", required = true) UInt256 amount - ) { - requireNonNull(validator); - requireNonNull(amount); + @JsonCreator + public static StakePositions create( + @JsonProperty(value = "validator", required = true) ValidatorAddress validator, + @JsonProperty(value = "amount", required = true) UInt256 amount) { + requireNonNull(validator); + requireNonNull(amount); - return new StakePositions(validator, amount); - } + return new StakePositions(validator, amount); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof StakePositions)) { - return false; - } + if (!(o instanceof StakePositions)) { + return false; + } - var that = (StakePositions) o; - return validator.equals(that.validator) && amount.equals(that.amount); - } + var that = (StakePositions) o; + return validator.equals(that.validator) && amount.equals(that.amount); + } - @Override - public int hashCode() { - return Objects.hash(validator, amount); - } + @Override + public int hashCode() { + return Objects.hash(validator, amount); + } - @Override - public String toString() { - return "StakePositionsDTO(" + "validator=" + validator.toString(Network.MAINNET.getId()) + ", amount=" + amount + ')'; - } + @Override + public String toString() { + return "StakePositionsDTO(" + + "validator=" + + validator.toString(Network.MAINNET.getId()) + + ", amount=" + + amount + + ')'; + } - public ValidatorAddress getValidator() { - return validator; - } + public ValidatorAddress getValidator() { + return validator; + } - public UInt256 getAmount() { - return amount; - } + public UInt256 getAmount() { + return amount; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncConfiguration.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncConfiguration.java index 3b13779f28..12fec77cc4 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncConfiguration.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncConfiguration.java @@ -66,90 +66,104 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class SyncConfiguration { - private final long maxLedgerUpdatesRate; - private final long requestTimeout; - private final long syncCheckInterval; - private final long syncCheckMaxPeers; - private final long ledgerStatusUpdateMaxPeersToNotify; - - private SyncConfiguration( - long maxLedgerUpdatesRate, - long requestTimeout, - long syncCheckInterval, - long syncCheckMaxPeers, - long ledgerStatusUpdateMaxPeersToNotify - ) { - this.maxLedgerUpdatesRate = maxLedgerUpdatesRate; - this.requestTimeout = requestTimeout; - this.syncCheckInterval = syncCheckInterval; - this.syncCheckMaxPeers = syncCheckMaxPeers; - this.ledgerStatusUpdateMaxPeersToNotify = ledgerStatusUpdateMaxPeersToNotify; - } - - @JsonCreator - public static SyncConfiguration create( - @JsonProperty(value = "maxLedgerUpdatesRate", required = true) long maxLedgerUpdatesRate, - @JsonProperty(value = "requestTimeout", required = true) long requestTimeout, - @JsonProperty(value = "syncCheckInterval", required = true) long syncCheckInterval, - @JsonProperty(value = "syncCheckMaxPeers", required = true) long syncCheckMaxPeers, - @JsonProperty(value = "ledgerStatusUpdateMaxPeersToNotify", required = true) long ledgerStatusUpdateMaxPeersToNotify - ) { - return new SyncConfiguration(maxLedgerUpdatesRate, requestTimeout, syncCheckInterval, syncCheckMaxPeers, ledgerStatusUpdateMaxPeersToNotify); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof SyncConfiguration)) { - return false; - } - - var that = (SyncConfiguration) o; - return maxLedgerUpdatesRate == that.maxLedgerUpdatesRate - && requestTimeout == that.requestTimeout - && syncCheckInterval == that.syncCheckInterval - && syncCheckMaxPeers == that.syncCheckMaxPeers - && ledgerStatusUpdateMaxPeersToNotify == that.ledgerStatusUpdateMaxPeersToNotify; - } - - @Override - public int hashCode() { - return Objects.hash(maxLedgerUpdatesRate, requestTimeout, syncCheckInterval, syncCheckMaxPeers, ledgerStatusUpdateMaxPeersToNotify); - } - - @Override - public String toString() { - return "{maxLedgerUpdatesRate:" + maxLedgerUpdatesRate - + ", requestTimeout:" + requestTimeout - + ", syncCheckInterval:" + syncCheckInterval - + ", syncCheckMaxPeers:" + syncCheckMaxPeers - + ", ledgerStatusUpdateMaxPeersToNotify:" + ledgerStatusUpdateMaxPeersToNotify + '}'; - } - - public long getMaxLedgerUpdatesRate() { - return maxLedgerUpdatesRate; - } - - public long getRequestTimeout() { - return requestTimeout; - } - - public long getSyncCheckInterval() { - return syncCheckInterval; - } - - public long getSyncCheckMaxPeers() { - return syncCheckMaxPeers; - } - - public long getLedgerStatusUpdateMaxPeersToNotify() { - return ledgerStatusUpdateMaxPeersToNotify; - } + private final long maxLedgerUpdatesRate; + private final long requestTimeout; + private final long syncCheckInterval; + private final long syncCheckMaxPeers; + private final long ledgerStatusUpdateMaxPeersToNotify; + + private SyncConfiguration( + long maxLedgerUpdatesRate, + long requestTimeout, + long syncCheckInterval, + long syncCheckMaxPeers, + long ledgerStatusUpdateMaxPeersToNotify) { + this.maxLedgerUpdatesRate = maxLedgerUpdatesRate; + this.requestTimeout = requestTimeout; + this.syncCheckInterval = syncCheckInterval; + this.syncCheckMaxPeers = syncCheckMaxPeers; + this.ledgerStatusUpdateMaxPeersToNotify = ledgerStatusUpdateMaxPeersToNotify; + } + + @JsonCreator + public static SyncConfiguration create( + @JsonProperty(value = "maxLedgerUpdatesRate", required = true) long maxLedgerUpdatesRate, + @JsonProperty(value = "requestTimeout", required = true) long requestTimeout, + @JsonProperty(value = "syncCheckInterval", required = true) long syncCheckInterval, + @JsonProperty(value = "syncCheckMaxPeers", required = true) long syncCheckMaxPeers, + @JsonProperty(value = "ledgerStatusUpdateMaxPeersToNotify", required = true) + long ledgerStatusUpdateMaxPeersToNotify) { + return new SyncConfiguration( + maxLedgerUpdatesRate, + requestTimeout, + syncCheckInterval, + syncCheckMaxPeers, + ledgerStatusUpdateMaxPeersToNotify); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof SyncConfiguration)) { + return false; + } + + var that = (SyncConfiguration) o; + return maxLedgerUpdatesRate == that.maxLedgerUpdatesRate + && requestTimeout == that.requestTimeout + && syncCheckInterval == that.syncCheckInterval + && syncCheckMaxPeers == that.syncCheckMaxPeers + && ledgerStatusUpdateMaxPeersToNotify == that.ledgerStatusUpdateMaxPeersToNotify; + } + + @Override + public int hashCode() { + return Objects.hash( + maxLedgerUpdatesRate, + requestTimeout, + syncCheckInterval, + syncCheckMaxPeers, + ledgerStatusUpdateMaxPeersToNotify); + } + + @Override + public String toString() { + return "{maxLedgerUpdatesRate:" + + maxLedgerUpdatesRate + + ", requestTimeout:" + + requestTimeout + + ", syncCheckInterval:" + + syncCheckInterval + + ", syncCheckMaxPeers:" + + syncCheckMaxPeers + + ", ledgerStatusUpdateMaxPeersToNotify:" + + ledgerStatusUpdateMaxPeersToNotify + + '}'; + } + + public long getMaxLedgerUpdatesRate() { + return maxLedgerUpdatesRate; + } + + public long getRequestTimeout() { + return requestTimeout; + } + + public long getSyncCheckInterval() { + return syncCheckInterval; + } + + public long getSyncCheckMaxPeers() { + return syncCheckMaxPeers; + } + + public long getLedgerStatusUpdateMaxPeersToNotify() { + return ledgerStatusUpdateMaxPeersToNotify; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncData.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncData.java index 1fbd6828d4..f64caf6d23 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncData.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/SyncData.java @@ -66,106 +66,118 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class SyncData { - private final long processed; - private final long targetStateVersion; - private final long remoteRequestsProcessed; - private final long invalidCommandsReceived; - private final long lastReadMillis; - private final long targetCurrentDiff; - - private SyncData( - long processed, - long targetStateVersion, - long remoteRequestsProcessed, - long invalidCommandsReceived, - long lastReadMillis, - long targetCurrentDiff - ) { - this.processed = processed; - this.targetStateVersion = targetStateVersion; - this.remoteRequestsProcessed = remoteRequestsProcessed; - this.invalidCommandsReceived = invalidCommandsReceived; - this.lastReadMillis = lastReadMillis; - this.targetCurrentDiff = targetCurrentDiff; - } - - @JsonCreator - public static SyncData create( - @JsonProperty(value = "processed", required = true) long processed, - @JsonProperty(value = "targetStateVersion", required = true) long targetStateVersion, - @JsonProperty(value = "remoteRequestsProcessed", required = true) long remoteRequestsProcessed, - @JsonProperty(value = "invalidCommandsReceived", required = true) long invalidCommandsReceived, - @JsonProperty(value = "lastReadMillis", required = true) long lastReadMillis, - @JsonProperty(value = "targetCurrentDiff", required = true) long targetCurrentDiff - ) { - return new SyncData( - processed, targetStateVersion, remoteRequestsProcessed, - invalidCommandsReceived, lastReadMillis, targetCurrentDiff - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof SyncData)) { - return false; - } - - var syncData = (SyncData) o; - return processed == syncData.processed - && targetStateVersion == syncData.targetStateVersion - && remoteRequestsProcessed == syncData.remoteRequestsProcessed - && invalidCommandsReceived == syncData.invalidCommandsReceived - && lastReadMillis == syncData.lastReadMillis - && targetCurrentDiff == syncData.targetCurrentDiff; - } - - @Override - public int hashCode() { - return Objects.hash( - processed, targetStateVersion, remoteRequestsProcessed, - invalidCommandsReceived, lastReadMillis, targetCurrentDiff - ); - } - - @Override - public String toString() { - return "{processed:" + processed - + ", targetStateVersion:" + targetStateVersion - + ", remoteRequestsProcessed:" + remoteRequestsProcessed - + ", invalidCommandsReceived:" + invalidCommandsReceived - + ", lastReadMillis:" + lastReadMillis - + ", targetCurrentDiff:" + targetCurrentDiff + '}'; - } - - public long getProcessed() { - return processed; - } - - public long getTargetStateVersion() { - return targetStateVersion; - } - - public long getRemoteRequestsProcessed() { - return remoteRequestsProcessed; - } - - public long getInvalidCommandsReceived() { - return invalidCommandsReceived; - } - - public long getLastReadMillis() { - return lastReadMillis; - } - - public long getTargetCurrentDiff() { - return targetCurrentDiff; - } + private final long processed; + private final long targetStateVersion; + private final long remoteRequestsProcessed; + private final long invalidCommandsReceived; + private final long lastReadMillis; + private final long targetCurrentDiff; + + private SyncData( + long processed, + long targetStateVersion, + long remoteRequestsProcessed, + long invalidCommandsReceived, + long lastReadMillis, + long targetCurrentDiff) { + this.processed = processed; + this.targetStateVersion = targetStateVersion; + this.remoteRequestsProcessed = remoteRequestsProcessed; + this.invalidCommandsReceived = invalidCommandsReceived; + this.lastReadMillis = lastReadMillis; + this.targetCurrentDiff = targetCurrentDiff; + } + + @JsonCreator + public static SyncData create( + @JsonProperty(value = "processed", required = true) long processed, + @JsonProperty(value = "targetStateVersion", required = true) long targetStateVersion, + @JsonProperty(value = "remoteRequestsProcessed", required = true) + long remoteRequestsProcessed, + @JsonProperty(value = "invalidCommandsReceived", required = true) + long invalidCommandsReceived, + @JsonProperty(value = "lastReadMillis", required = true) long lastReadMillis, + @JsonProperty(value = "targetCurrentDiff", required = true) long targetCurrentDiff) { + return new SyncData( + processed, + targetStateVersion, + remoteRequestsProcessed, + invalidCommandsReceived, + lastReadMillis, + targetCurrentDiff); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof SyncData)) { + return false; + } + + var syncData = (SyncData) o; + return processed == syncData.processed + && targetStateVersion == syncData.targetStateVersion + && remoteRequestsProcessed == syncData.remoteRequestsProcessed + && invalidCommandsReceived == syncData.invalidCommandsReceived + && lastReadMillis == syncData.lastReadMillis + && targetCurrentDiff == syncData.targetCurrentDiff; + } + + @Override + public int hashCode() { + return Objects.hash( + processed, + targetStateVersion, + remoteRequestsProcessed, + invalidCommandsReceived, + lastReadMillis, + targetCurrentDiff); + } + + @Override + public String toString() { + return "{processed:" + + processed + + ", targetStateVersion:" + + targetStateVersion + + ", remoteRequestsProcessed:" + + remoteRequestsProcessed + + ", invalidCommandsReceived:" + + invalidCommandsReceived + + ", lastReadMillis:" + + lastReadMillis + + ", targetCurrentDiff:" + + targetCurrentDiff + + '}'; + } + + public long getProcessed() { + return processed; + } + + public long getTargetStateVersion() { + return targetStateVersion; + } + + public long getRemoteRequestsProcessed() { + return remoteRequestsProcessed; + } + + public long getInvalidCommandsReceived() { + return invalidCommandsReceived; + } + + public long getLastReadMillis() { + return lastReadMillis; + } + + public long getTargetCurrentDiff() { + return targetCurrentDiff; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TimeDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TimeDTO.java index 4786efed0d..1d6c76b058 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TimeDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TimeDTO.java @@ -66,46 +66,45 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class TimeDTO { - private final long time; + private final long time; - private TimeDTO(long time) { - this.time = time; - } + private TimeDTO(long time) { + this.time = time; + } - @JsonCreator - public static TimeDTO create(@JsonProperty(value = "time", required = true) long time) { - return new TimeDTO(time); - } + @JsonCreator + public static TimeDTO create(@JsonProperty(value = "time", required = true) long time) { + return new TimeDTO(time); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof TimeDTO)) { - return false; - } + if (!(o instanceof TimeDTO)) { + return false; + } - var timeDTO = (TimeDTO) o; - return time == timeDTO.time; - } + var timeDTO = (TimeDTO) o; + return time == timeDTO.time; + } - @Override - public int hashCode() { - return Objects.hash(time); - } + @Override + public int hashCode() { + return Objects.hash(time); + } - @Override - public String toString() { - return "{time:" + time + '}'; - } + @Override + public String toString() { + return "{time:" + time + '}'; + } - public long getTime() { - return time; - } + public long getTime() { + return time; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenBalances.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenBalances.java index b847159232..b9e06b30c0 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenBalances.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenBalances.java @@ -64,63 +64,61 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.AccountAddress; - import java.util.List; import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class TokenBalances { - private final AccountAddress owner; - private final List tokenBalances; + private final AccountAddress owner; + private final List tokenBalances; - private TokenBalances(AccountAddress owner, List tokenBalances) { - this.owner = owner; - this.tokenBalances = tokenBalances; - } + private TokenBalances(AccountAddress owner, List tokenBalances) { + this.owner = owner; + this.tokenBalances = tokenBalances; + } - @JsonCreator - public static TokenBalances create( - @JsonProperty(value = "owner", required = true) AccountAddress owner, - @JsonProperty(value = "tokenBalances", required = true) List tokenBalances - ) { - requireNonNull(owner); - requireNonNull(tokenBalances); + @JsonCreator + public static TokenBalances create( + @JsonProperty(value = "owner", required = true) AccountAddress owner, + @JsonProperty(value = "tokenBalances", required = true) List tokenBalances) { + requireNonNull(owner); + requireNonNull(tokenBalances); - return new TokenBalances(owner, tokenBalances); - } + return new TokenBalances(owner, tokenBalances); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TokenBalances)) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TokenBalances)) { + return false; + } - var that = (TokenBalances) o; - return owner.equals(that.owner) && tokenBalances.equals(that.tokenBalances); - } + var that = (TokenBalances) o; + return owner.equals(that.owner) && tokenBalances.equals(that.tokenBalances); + } - @Override - public int hashCode() { - return Objects.hash(owner, tokenBalances); - } + @Override + public int hashCode() { + return Objects.hash(owner, tokenBalances); + } - @Override - public String toString() { - return "TokenBalancesDTO(" + "owner=" + owner + ", tokenBalances=" + tokenBalances + ')'; - } + @Override + public String toString() { + return "TokenBalancesDTO(" + "owner=" + owner + ", tokenBalances=" + tokenBalances + ')'; + } - public AccountAddress getOwner() { - return owner; - } + public AccountAddress getOwner() { + return owner; + } - public List getTokenBalances() { - return tokenBalances; - } + public List getTokenBalances() { + return tokenBalances; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenInfo.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenInfo.java index 55d61b971b..c98dbbb5c0 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenInfo.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TokenInfo.java @@ -64,175 +64,204 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.utils.UInt256; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class TokenInfo { - private final String name; - private final String rri; - private final String symbol; - private final String description; - private final UInt256 granularity; - private final boolean isSupplyMutable; - private final UInt256 currentSupply; - private final UInt256 totalBurned; - private final UInt256 totalMinted; - private final String tokenInfoURL; - private final String iconURL; - - private TokenInfo( - String name, - String rri, - String symbol, - String description, - UInt256 granularity, - boolean isSupplyMutable, - UInt256 currentSupply, - UInt256 totalBurned, - UInt256 totalMinted, - String tokenInfoURL, - String iconURL - ) { - this.name = name; - this.rri = rri; - this.symbol = symbol; - this.description = description; - this.granularity = granularity; - this.isSupplyMutable = isSupplyMutable; - this.currentSupply = currentSupply; - this.totalBurned = totalBurned; - this.totalMinted = totalMinted; - this.tokenInfoURL = tokenInfoURL; - this.iconURL = iconURL; - } - - @JsonCreator - public static TokenInfo create( - @JsonProperty(value = "name", required = true) String name, - @JsonProperty(value = "rri", required = true) String rri, - @JsonProperty(value = "symbol", required = true) String symbol, - @JsonProperty(value = "description", required = true) String description, - @JsonProperty(value = "granularity", required = true) UInt256 granularity, - @JsonProperty(value = "isSupplyMutable", required = true) boolean isSupplyMutable, - @JsonProperty(value = "currentSupply", required = true) UInt256 currentSupply, - @JsonProperty(value = "totalBurned", required = true) UInt256 totalMinted, - @JsonProperty(value = "totalMinted", required = true) UInt256 totalBurned, - @JsonProperty(value = "tokenInfoURL", required = true) String tokenInfoURL, - @JsonProperty(value = "iconURL", required = true) String iconURL - ) { - requireNonNull(name); - requireNonNull(rri); - requireNonNull(symbol); - requireNonNull(description); - requireNonNull(granularity); - requireNonNull(isSupplyMutable); - requireNonNull(currentSupply); - requireNonNull(totalBurned); - requireNonNull(totalMinted); - requireNonNull(tokenInfoURL); - requireNonNull(iconURL); - - return new TokenInfo( - name, rri, symbol, description, granularity, - isSupplyMutable, currentSupply, totalBurned, totalMinted, - tokenInfoURL, iconURL - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TokenInfo)) { - return false; - } - - var that = (TokenInfo) o; - return isSupplyMutable == that.isSupplyMutable - && name.equals(that.name) - && rri.equals(that.rri) - && symbol.equals(that.symbol) - && description.equals(that.description) - && granularity.equals(that.granularity) - && currentSupply.equals(that.currentSupply) - && totalBurned.equals(that.totalBurned) - && totalMinted.equals(that.totalMinted) - && tokenInfoURL.equals(that.tokenInfoURL) - && iconURL.equals(that.iconURL); - } - - @Override - public int hashCode() { - return Objects.hash( - name, rri, symbol, description, granularity, - isSupplyMutable, currentSupply, totalBurned, totalMinted, - tokenInfoURL, iconURL - ); - } - - @Override - public String toString() { - return "TokenInfo{" - + "name='" + name + '\'' - + ", rri='" + rri + '\'' - + ", symbol='" + symbol + '\'' - + ", description='" + description + '\'' - + ", granularity=" + granularity - + ", isSupplyMutable=" + isSupplyMutable - + ", currentSupply=" + currentSupply - + ", totalBurned=" + totalBurned - + ", totalMinted=" + totalMinted - + ", tokenInfoURL='" + tokenInfoURL + '\'' - + ", iconURL='" + iconURL + '\'' + '}'; - } - - public String getName() { - return name; - } - - public String getRri() { - return rri; - } - - public String getSymbol() { - return symbol; - } - - public String getDescription() { - return description; - } - - public UInt256 getGranularity() { - return granularity; - } - - public boolean isSupplyMutable() { - return isSupplyMutable; - } - - public UInt256 getCurrentSupply() { - return currentSupply; - } - - public UInt256 getTotalBurned() { - return totalBurned; - } - - public UInt256 getTotalMinted() { - return totalMinted; - } - - public String getTokenInfoURL() { - return tokenInfoURL; - } - - public String getIconURL() { - return iconURL; - } + private final String name; + private final String rri; + private final String symbol; + private final String description; + private final UInt256 granularity; + private final boolean isSupplyMutable; + private final UInt256 currentSupply; + private final UInt256 totalBurned; + private final UInt256 totalMinted; + private final String tokenInfoURL; + private final String iconURL; + + private TokenInfo( + String name, + String rri, + String symbol, + String description, + UInt256 granularity, + boolean isSupplyMutable, + UInt256 currentSupply, + UInt256 totalBurned, + UInt256 totalMinted, + String tokenInfoURL, + String iconURL) { + this.name = name; + this.rri = rri; + this.symbol = symbol; + this.description = description; + this.granularity = granularity; + this.isSupplyMutable = isSupplyMutable; + this.currentSupply = currentSupply; + this.totalBurned = totalBurned; + this.totalMinted = totalMinted; + this.tokenInfoURL = tokenInfoURL; + this.iconURL = iconURL; + } + + @JsonCreator + public static TokenInfo create( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty(value = "rri", required = true) String rri, + @JsonProperty(value = "symbol", required = true) String symbol, + @JsonProperty(value = "description", required = true) String description, + @JsonProperty(value = "granularity", required = true) UInt256 granularity, + @JsonProperty(value = "isSupplyMutable", required = true) boolean isSupplyMutable, + @JsonProperty(value = "currentSupply", required = true) UInt256 currentSupply, + @JsonProperty(value = "totalBurned", required = true) UInt256 totalMinted, + @JsonProperty(value = "totalMinted", required = true) UInt256 totalBurned, + @JsonProperty(value = "tokenInfoURL", required = true) String tokenInfoURL, + @JsonProperty(value = "iconURL", required = true) String iconURL) { + requireNonNull(name); + requireNonNull(rri); + requireNonNull(symbol); + requireNonNull(description); + requireNonNull(granularity); + requireNonNull(isSupplyMutable); + requireNonNull(currentSupply); + requireNonNull(totalBurned); + requireNonNull(totalMinted); + requireNonNull(tokenInfoURL); + requireNonNull(iconURL); + + return new TokenInfo( + name, + rri, + symbol, + description, + granularity, + isSupplyMutable, + currentSupply, + totalBurned, + totalMinted, + tokenInfoURL, + iconURL); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TokenInfo)) { + return false; + } + + var that = (TokenInfo) o; + return isSupplyMutable == that.isSupplyMutable + && name.equals(that.name) + && rri.equals(that.rri) + && symbol.equals(that.symbol) + && description.equals(that.description) + && granularity.equals(that.granularity) + && currentSupply.equals(that.currentSupply) + && totalBurned.equals(that.totalBurned) + && totalMinted.equals(that.totalMinted) + && tokenInfoURL.equals(that.tokenInfoURL) + && iconURL.equals(that.iconURL); + } + + @Override + public int hashCode() { + return Objects.hash( + name, + rri, + symbol, + description, + granularity, + isSupplyMutable, + currentSupply, + totalBurned, + totalMinted, + tokenInfoURL, + iconURL); + } + + @Override + public String toString() { + return "TokenInfo{" + + "name='" + + name + + '\'' + + ", rri='" + + rri + + '\'' + + ", symbol='" + + symbol + + '\'' + + ", description='" + + description + + '\'' + + ", granularity=" + + granularity + + ", isSupplyMutable=" + + isSupplyMutable + + ", currentSupply=" + + currentSupply + + ", totalBurned=" + + totalBurned + + ", totalMinted=" + + totalMinted + + ", tokenInfoURL='" + + tokenInfoURL + + '\'' + + ", iconURL='" + + iconURL + + '\'' + + '}'; + } + + public String getName() { + return name; + } + + public String getRri() { + return rri; + } + + public String getSymbol() { + return symbol; + } + + public String getDescription() { + return description; + } + + public UInt256 getGranularity() { + return granularity; + } + + public boolean isSupplyMutable() { + return isSupplyMutable; + } + + public UInt256 getCurrentSupply() { + return currentSupply; + } + + public UInt256 getTotalBurned() { + return totalBurned; + } + + public UInt256 getTotalMinted() { + return totalMinted; + } + + public String getTokenInfoURL() { + return tokenInfoURL; + } + + public String getIconURL() { + return iconURL; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionDTO.java index 7757af8bbd..18ce1df6de 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionDTO.java @@ -64,143 +64,151 @@ package com.radixdlt.client.lib.dto; -import org.bouncycastle.util.encoders.Hex; +import static java.util.Objects.requireNonNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.TxTimestamp; import com.radixdlt.identifiers.AID; import com.radixdlt.utils.UInt256; - import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; - -import static java.util.Objects.requireNonNull; +import org.bouncycastle.util.encoders.Hex; public final class TransactionDTO { - private final AID txID; - private final long stateVersion; - private final long size; - private final TxTimestamp sentAt; - private final UInt256 fee; - private final String message; - private final List actions; - private final List events; - private final byte[] raw; - - private TransactionDTO( - AID txID, - long stateVersion, - long size, - TxTimestamp sentAt, - UInt256 fee, - String message, - List actions, - List events, - byte[] raw - ) { - this.txID = txID; - this.stateVersion = stateVersion; - this.size = size; - this.sentAt = sentAt; - this.fee = fee; - this.message = message; - this.actions = actions; - this.events = events; - this.raw = raw; - } - - @JsonCreator - public static TransactionDTO create( - @JsonProperty(value = "size", required = true) long size, - @JsonProperty(value = "txID", required = true) AID txID, - @JsonProperty(value = "timestamp", required = true) TxTimestamp sentAt, - @JsonProperty(value = "fee", required = true) UInt256 fee, - @JsonProperty(value = "message", required = false) String message, - @JsonProperty(value = "actions", required = true) List actions, - @JsonProperty(value = "events", required = true) List events, - @JsonProperty(value = "raw", required = true) String blob, - @JsonProperty(value = "stateVersion", required = true) long stateVersion, - @JsonProperty(value = "accountingEntries", required = true) List entries - ) { - requireNonNull(txID); - requireNonNull(sentAt); - requireNonNull(fee); - requireNonNull(actions); - requireNonNull(blob); - - return new TransactionDTO(txID, stateVersion, size, sentAt, fee, message, actions, events, Hex.decode(blob)); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TransactionDTO)) { - return false; - } - - var that = (TransactionDTO) o; - return txID.equals(that.txID) - && size == that.size - && stateVersion == that.stateVersion - && sentAt.equals(that.sentAt) - && fee.equals(that.fee) - && Objects.equals(message, that.message) - && Arrays.equals(raw, that.raw) - && actions.equals(that.actions) - && events.equals(that.events); - } - - @Override - public int hashCode() { - return Objects.hash(txID, stateVersion, size, sentAt, fee, message, actions, events, Arrays.hashCode(raw)); - } - - @Override - public String toString() { - return "Transaction(" - + "txID=" + txID - + ", stateVersion=" + stateVersion - + ", size=" + size - + ", sentAt=" + sentAt - + ", fee=" + fee - + ", message='" + message + '\'' - + ", actions=" + actions - + ", events=" + events - + ", raw=" + Hex.toHexString(raw) - + ')'; - } - - public AID getTxID() { - return txID; - } - - public TxTimestamp getSentAt() { - return sentAt; - } - - public UInt256 getFee() { - return fee; - } - - public Optional getMessage() { - return Optional.ofNullable(message); - } - - public List getActions() { - return actions; - } - - public List getEvents() { - return events; - } - - public byte[] getRaw() { - return raw; - } + private final AID txID; + private final long stateVersion; + private final long size; + private final TxTimestamp sentAt; + private final UInt256 fee; + private final String message; + private final List actions; + private final List events; + private final byte[] raw; + + private TransactionDTO( + AID txID, + long stateVersion, + long size, + TxTimestamp sentAt, + UInt256 fee, + String message, + List actions, + List events, + byte[] raw) { + this.txID = txID; + this.stateVersion = stateVersion; + this.size = size; + this.sentAt = sentAt; + this.fee = fee; + this.message = message; + this.actions = actions; + this.events = events; + this.raw = raw; + } + + @JsonCreator + public static TransactionDTO create( + @JsonProperty(value = "size", required = true) long size, + @JsonProperty(value = "txID", required = true) AID txID, + @JsonProperty(value = "timestamp", required = true) TxTimestamp sentAt, + @JsonProperty(value = "fee", required = true) UInt256 fee, + @JsonProperty(value = "message", required = false) String message, + @JsonProperty(value = "actions", required = true) List actions, + @JsonProperty(value = "events", required = true) List events, + @JsonProperty(value = "raw", required = true) String blob, + @JsonProperty(value = "stateVersion", required = true) long stateVersion, + @JsonProperty(value = "accountingEntries", required = true) List entries) { + requireNonNull(txID); + requireNonNull(sentAt); + requireNonNull(fee); + requireNonNull(actions); + requireNonNull(blob); + + return new TransactionDTO( + txID, stateVersion, size, sentAt, fee, message, actions, events, Hex.decode(blob)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TransactionDTO)) { + return false; + } + + var that = (TransactionDTO) o; + return txID.equals(that.txID) + && size == that.size + && stateVersion == that.stateVersion + && sentAt.equals(that.sentAt) + && fee.equals(that.fee) + && Objects.equals(message, that.message) + && Arrays.equals(raw, that.raw) + && actions.equals(that.actions) + && events.equals(that.events); + } + + @Override + public int hashCode() { + return Objects.hash( + txID, stateVersion, size, sentAt, fee, message, actions, events, Arrays.hashCode(raw)); + } + + @Override + public String toString() { + return "Transaction(" + + "txID=" + + txID + + ", stateVersion=" + + stateVersion + + ", size=" + + size + + ", sentAt=" + + sentAt + + ", fee=" + + fee + + ", message='" + + message + + '\'' + + ", actions=" + + actions + + ", events=" + + events + + ", raw=" + + Hex.toHexString(raw) + + ')'; + } + + public AID getTxID() { + return txID; + } + + public TxTimestamp getSentAt() { + return sentAt; + } + + public UInt256 getFee() { + return fee; + } + + public Optional getMessage() { + return Optional.ofNullable(message); + } + + public List getActions() { + return actions; + } + + public List getEvents() { + return events; + } + + public byte[] getRaw() { + return raw; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionHistory.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionHistory.java index 3650d42015..c919fc90a3 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionHistory.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionHistory.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,75 +64,77 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; import java.util.OptionalLong; -import static java.util.Objects.requireNonNull; - public final class TransactionHistory { - private final Long nextOffset; - private final List transactions; - private final long totalCount; - - private TransactionHistory(Long nextOffset, List transactions, long totalCount) { - this.nextOffset = nextOffset; - this.transactions = transactions; - this.totalCount = totalCount; - } - - @JsonCreator - public static TransactionHistory create( - @JsonProperty("cursor") Long nextOffset, - @JsonProperty(value = "transactions", required = true) List transactions, - @JsonProperty(value = "totalCount", required = true) long totalCount - ) { - requireNonNull(transactions); - - return new TransactionHistory(nextOffset, transactions, totalCount); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TransactionHistory)) { - return false; - } - - var that = (TransactionHistory) o; - return totalCount == that.totalCount - && Objects.equals(nextOffset, that.nextOffset) - && transactions.equals(that.transactions); - } - - @Override - public int hashCode() { - return Objects.hash(nextOffset, transactions, totalCount); - } - - @Override - public String toString() { - return "TransactionHistory2(" - + "nextOffset=" + nextOffset - + ", transactions=" + transactions - + ", totalCount=" + totalCount + ')'; - } - - public OptionalLong getNextOffset() { - return nextOffset == null ? OptionalLong.empty() : OptionalLong.of(nextOffset); - } - - public List getTransactions() { - return transactions; - } - - public long getTotalCount() { - return totalCount; - } + private final Long nextOffset; + private final List transactions; + private final long totalCount; + + private TransactionHistory(Long nextOffset, List transactions, long totalCount) { + this.nextOffset = nextOffset; + this.transactions = transactions; + this.totalCount = totalCount; + } + + @JsonCreator + public static TransactionHistory create( + @JsonProperty("cursor") Long nextOffset, + @JsonProperty(value = "transactions", required = true) List transactions, + @JsonProperty(value = "totalCount", required = true) long totalCount) { + requireNonNull(transactions); + + return new TransactionHistory(nextOffset, transactions, totalCount); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TransactionHistory)) { + return false; + } + + var that = (TransactionHistory) o; + return totalCount == that.totalCount + && Objects.equals(nextOffset, that.nextOffset) + && transactions.equals(that.transactions); + } + + @Override + public int hashCode() { + return Objects.hash(nextOffset, transactions, totalCount); + } + + @Override + public String toString() { + return "TransactionHistory2(" + + "nextOffset=" + + nextOffset + + ", transactions=" + + transactions + + ", totalCount=" + + totalCount + + ')'; + } + + public OptionalLong getNextOffset() { + return nextOffset == null ? OptionalLong.empty() : OptionalLong.of(nextOffset); + } + + public List getTransactions() { + return transactions; + } + + public long getTotalCount() { + return totalCount; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatus.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatus.java index c4e81336f2..222bc0c3af 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatus.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatus.java @@ -65,8 +65,8 @@ package com.radixdlt.client.lib.dto; public enum TransactionStatus { - PENDING, - CONFIRMED, - FAILED, - TRANSACTION_NOT_FOUND + PENDING, + CONFIRMED, + FAILED, + TRANSACTION_NOT_FOUND } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatusDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatusDTO.java index 6fd4e3194d..981be3584f 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatusDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionStatusDTO.java @@ -64,63 +64,61 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.identifiers.AID; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class TransactionStatusDTO { - private final AID txId; - private final TransactionStatus status; + private final AID txId; + private final TransactionStatus status; - private TransactionStatusDTO(AID txId, TransactionStatus status) { - this.txId = txId; - this.status = status; - } + private TransactionStatusDTO(AID txId, TransactionStatus status) { + this.txId = txId; + this.status = status; + } - @JsonCreator - public static TransactionStatusDTO create( - @JsonProperty(value = "txID", required = true) AID txId, - @JsonProperty(value = "status", required = true) TransactionStatus status - ) { - requireNonNull(txId); - requireNonNull(status); + @JsonCreator + public static TransactionStatusDTO create( + @JsonProperty(value = "txID", required = true) AID txId, + @JsonProperty(value = "status", required = true) TransactionStatus status) { + requireNonNull(txId); + requireNonNull(status); - return new TransactionStatusDTO(txId, status); - } + return new TransactionStatusDTO(txId, status); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof TransactionStatusDTO)) { - return false; - } + if (!(o instanceof TransactionStatusDTO)) { + return false; + } - var that = (TransactionStatusDTO) o; - return txId.equals(that.txId) && status == that.status; - } + var that = (TransactionStatusDTO) o; + return txId.equals(that.txId) && status == that.status; + } - @Override - public int hashCode() { - return Objects.hash(txId, status); - } + @Override + public int hashCode() { + return Objects.hash(txId, status); + } - @Override - public String toString() { - return "TransactionStatusDTO(" + txId + ", " + status + ')'; - } + @Override + public String toString() { + return "TransactionStatusDTO(" + txId + ", " + status + ')'; + } - public AID getTxId() { - return txId; - } + public AID getTxId() { + return txId; + } - public TransactionStatus getStatus() { - return status; - } + public TransactionStatus getStatus() { + return status; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionsDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionsDTO.java index a4610a2455..c092dfabdd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionsDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TransactionsDTO.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,76 +64,78 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Objects; import java.util.OptionalLong; -import static java.util.Objects.requireNonNull; - public final class TransactionsDTO { - private final Long nextOffset; - private final List transactions; - private final long totalCount; - - private TransactionsDTO(Long nextOffset, List transactions, long totalCount) { - this.nextOffset = nextOffset; - this.transactions = transactions; - this.totalCount = totalCount; - } - - @JsonCreator - public static TransactionsDTO create( - @JsonProperty("nextOffset") Long nextOffset, - @JsonProperty(value = "transactions", required = true) List transactions, - @JsonProperty(value = "count", required = true) long count, - @JsonProperty(value = "totalCount", required = true) long totalCount - ) { - requireNonNull(transactions); - - return new TransactionsDTO(nextOffset, transactions, totalCount); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TransactionsDTO)) { - return false; - } - - var that = (TransactionsDTO) o; - return totalCount == that.totalCount - && Objects.equals(nextOffset, that.nextOffset) - && transactions.equals(that.transactions); - } - - @Override - public int hashCode() { - return Objects.hash(nextOffset, transactions, totalCount); - } - - @Override - public String toString() { - return "TransactionsDTO(" - + "nextOffset=" + nextOffset - + ", transactions=" + transactions - + ", totalCount=" + totalCount + ')'; - } - - public OptionalLong getNextOffset() { - return nextOffset == null ? OptionalLong.empty() : OptionalLong.of(nextOffset); - } - - public List getTransactions() { - return transactions; - } - - public long getTotalCount() { - return totalCount; - } + private final Long nextOffset; + private final List transactions; + private final long totalCount; + + private TransactionsDTO(Long nextOffset, List transactions, long totalCount) { + this.nextOffset = nextOffset; + this.transactions = transactions; + this.totalCount = totalCount; + } + + @JsonCreator + public static TransactionsDTO create( + @JsonProperty("nextOffset") Long nextOffset, + @JsonProperty(value = "transactions", required = true) List transactions, + @JsonProperty(value = "count", required = true) long count, + @JsonProperty(value = "totalCount", required = true) long totalCount) { + requireNonNull(transactions); + + return new TransactionsDTO(nextOffset, transactions, totalCount); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TransactionsDTO)) { + return false; + } + + var that = (TransactionsDTO) o; + return totalCount == that.totalCount + && Objects.equals(nextOffset, that.nextOffset) + && transactions.equals(that.transactions); + } + + @Override + public int hashCode() { + return Objects.hash(nextOffset, transactions, totalCount); + } + + @Override + public String toString() { + return "TransactionsDTO(" + + "nextOffset=" + + nextOffset + + ", transactions=" + + transactions + + ", totalCount=" + + totalCount + + ')'; + } + + public OptionalLong getNextOffset() { + return nextOffset == null ? OptionalLong.empty() : OptionalLong.of(nextOffset); + } + + public List getTransactions() { + return transactions; + } + + public long getTotalCount() { + return totalCount; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlob.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlob.java index 962bf73264..3986429273 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlob.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlob.java @@ -64,78 +64,82 @@ package com.radixdlt.client.lib.dto; -import org.bouncycastle.util.encoders.Hex; +import static java.util.Objects.requireNonNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Arrays; import java.util.List; - -import static java.util.Objects.requireNonNull; +import org.bouncycastle.util.encoders.Hex; public final class TxBlob { - private final byte[] blob; - private final byte[] hashToSign; - private final List notifications; - - private TxBlob(byte[] blob, byte[] hashToSign, List notifications) { - this.blob = blob; - this.hashToSign = hashToSign; - this.notifications = notifications; - } - - @JsonCreator - public static TxBlob create( - @JsonProperty(value = "blob", required = true) String blob, - @JsonProperty(value = "hashOfBlobToSign", required = true) String hashToSign, - @JsonProperty("notifications") List notifications - ) { - requireNonNull(blob); - requireNonNull(hashToSign); - - return new TxBlob(Hex.decode(blob), Hex.decode(hashToSign), notifications == null ? List.of() : notifications); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TxBlob)) { - return false; - } - - var txBlob = (TxBlob) o; - return Arrays.equals(blob, txBlob.blob) - && Arrays.equals(hashToSign, txBlob.hashToSign) - && notifications.equals(txBlob.notifications); - } - - @Override - public int hashCode() { - int result = Arrays.hashCode(blob); - result = 31 * result + Arrays.hashCode(hashToSign) + notifications.hashCode(); - return result; - } - - @Override - public String toString() { - return "TxBlobDTO(blob=" + Hex.toHexString(blob) - + ", hashToSign=" + Hex.toHexString(hashToSign) - + ", notifications= " + notifications + ')'; - } - - public byte[] getBlob() { - return blob; - } - - public byte[] getHashToSign() { - return hashToSign; - } - - public List getNotifications() { - return notifications; - } + private final byte[] blob; + private final byte[] hashToSign; + private final List notifications; + + private TxBlob(byte[] blob, byte[] hashToSign, List notifications) { + this.blob = blob; + this.hashToSign = hashToSign; + this.notifications = notifications; + } + + @JsonCreator + public static TxBlob create( + @JsonProperty(value = "blob", required = true) String blob, + @JsonProperty(value = "hashOfBlobToSign", required = true) String hashToSign, + @JsonProperty("notifications") List notifications) { + requireNonNull(blob); + requireNonNull(hashToSign); + + return new TxBlob( + Hex.decode(blob), + Hex.decode(hashToSign), + notifications == null ? List.of() : notifications); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TxBlob)) { + return false; + } + + var txBlob = (TxBlob) o; + return Arrays.equals(blob, txBlob.blob) + && Arrays.equals(hashToSign, txBlob.hashToSign) + && notifications.equals(txBlob.notifications); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(blob); + result = 31 * result + Arrays.hashCode(hashToSign) + notifications.hashCode(); + return result; + } + + @Override + public String toString() { + return "TxBlobDTO(blob=" + + Hex.toHexString(blob) + + ", hashToSign=" + + Hex.toHexString(hashToSign) + + ", notifications= " + + notifications + + ')'; + } + + public byte[] getBlob() { + return blob; + } + + public byte[] getHashToSign() { + return hashToSign; + } + + public List getNotifications() { + return notifications; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlobDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlobDTO.java index 4a7164dd44..bd8957e86a 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlobDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxBlobDTO.java @@ -64,68 +64,65 @@ package com.radixdlt.client.lib.dto; -import org.bouncycastle.util.encoders.Hex; +import static java.util.Objects.requireNonNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.identifiers.AID; - import java.util.Arrays; import java.util.Objects; - -import static java.util.Objects.requireNonNull; +import org.bouncycastle.util.encoders.Hex; public final class TxBlobDTO { - private final AID txId; - private final byte[] blob; - - private TxBlobDTO(AID txId, byte[] blob) { - this.txId = txId; - this.blob = blob; - } - - @JsonCreator - public static TxBlobDTO create( - @JsonProperty(value = "txID", required = true) AID txId, - @JsonProperty(value = "blob", required = true) String blob - ) { - requireNonNull(txId); - requireNonNull(blob); - - return new TxBlobDTO(txId, Hex.decode(blob)); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof TxBlobDTO)) { - return false; - } - - var txBlobDTO = (TxBlobDTO) o; - return txId.equals(txBlobDTO.txId) && Arrays.equals(blob, txBlobDTO.blob); - } - - @Override - public int hashCode() { - int result = Objects.hash(txId); - result = 31 * result + Arrays.hashCode(blob); - return result; - } - - @Override - public String toString() { - return "{" + txId.toJson() + ", " + Hex.toHexString(blob) + '}'; - } - - public AID getTxId() { - return txId; - } - - public byte[] getBlob() { - return blob; - } + private final AID txId; + private final byte[] blob; + + private TxBlobDTO(AID txId, byte[] blob) { + this.txId = txId; + this.blob = blob; + } + + @JsonCreator + public static TxBlobDTO create( + @JsonProperty(value = "txID", required = true) AID txId, + @JsonProperty(value = "blob", required = true) String blob) { + requireNonNull(txId); + requireNonNull(blob); + + return new TxBlobDTO(txId, Hex.decode(blob)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof TxBlobDTO)) { + return false; + } + + var txBlobDTO = (TxBlobDTO) o; + return txId.equals(txBlobDTO.txId) && Arrays.equals(blob, txBlobDTO.blob); + } + + @Override + public int hashCode() { + int result = Objects.hash(txId); + result = 31 * result + Arrays.hashCode(blob); + return result; + } + + @Override + public String toString() { + return "{" + txId.toJson() + ", " + Hex.toHexString(blob) + '}'; + } + + public AID getTxId() { + return txId; + } + + public byte[] getBlob() { + return blob; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxDTO.java index 1ed6d99837..a72197f0c9 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/TxDTO.java @@ -64,53 +64,52 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.identifiers.AID; - import java.util.Objects; -import static java.util.Objects.requireNonNull; - public final class TxDTO { - private final AID txId; + private final AID txId; - private TxDTO(AID txId) { - this.txId = txId; - } + private TxDTO(AID txId) { + this.txId = txId; + } - @JsonCreator - public static TxDTO create(@JsonProperty(value = "txID", required = true) AID txId) { - requireNonNull(txId); + @JsonCreator + public static TxDTO create(@JsonProperty(value = "txID", required = true) AID txId) { + requireNonNull(txId); - return new TxDTO(txId); - } + return new TxDTO(txId); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof TxDTO)) { - return false; - } + if (!(o instanceof TxDTO)) { + return false; + } - var txDTO = (TxDTO) o; - return txId.equals(txDTO.txId); - } + var txDTO = (TxDTO) o; + return txId.equals(txDTO.txId); + } - @Override - public int hashCode() { - return Objects.hash(txId); - } + @Override + public int hashCode() { + return Objects.hash(txId); + } - @Override - public String toString() { - return "{" + txId.toJson() + '}'; - } + @Override + public String toString() { + return "{" + txId.toJson() + '}'; + } - public AID getTxId() { - return txId; - } + public AID getTxId() { + return txId; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/UnstakePositions.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/UnstakePositions.java index 302b409988..11cf5c90d2 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/UnstakePositions.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/UnstakePositions.java @@ -64,101 +64,106 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; import java.util.Optional; -import static java.util.Objects.requireNonNull; - public final class UnstakePositions { - private final UInt256 amount; - private final UInt256 validatorTotalOwnership; - private final UInt256 validatorTotalStake; - private final ValidatorAddress validator; - private final int epochsUntil; - - private UnstakePositions( - UInt256 amount, - UInt256 validatorTotalOwnership, - UInt256 validatorTotalStake, - ValidatorAddress validator, - int epochsUntil - ) { - this.amount = amount; - this.validatorTotalOwnership = validatorTotalOwnership; - this.validatorTotalStake = validatorTotalStake; - this.validator = validator; - this.epochsUntil = epochsUntil; - } - - @JsonCreator - public static UnstakePositions create( - @JsonProperty(value = "amount", required = true) UInt256 amount, - @JsonProperty(value = "validator", required = true) ValidatorAddress validator, - @JsonProperty(value = "validatorTotalOwnership") UInt256 validatorTotalOwnership, - @JsonProperty(value = "validatorTotalStake") UInt256 validatorTotalStake, - @JsonProperty(value = "epochsUntil", required = true) int epochsUntil - ) { - requireNonNull(amount); - requireNonNull(validator); - - return new UnstakePositions(amount, validatorTotalOwnership, validatorTotalStake, validator, epochsUntil); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof UnstakePositions)) { - return false; - } - - var that = (UnstakePositions) o; - return epochsUntil == that.epochsUntil - && amount.equals(that.amount) - && validatorTotalOwnership.equals(that.validatorTotalOwnership) - && validatorTotalStake.equals(that.validatorTotalStake) - && validator.equals(that.validator); - } - - @Override - public int hashCode() { - return Objects.hash(amount, validatorTotalOwnership, validatorTotalStake, validator, epochsUntil); - } - - @Override - public String toString() { - return "UnstakePositions(" - + "amount=" + amount - + ", validatorTotalOwnership=" + validatorTotalOwnership - + ", validatorTotalStake=" + validatorTotalStake - + ", validator=" + validator - + ", epochsUntil=" + epochsUntil + ')'; - } - - public UInt256 getAmount() { - return amount; - } - - public ValidatorAddress getValidator() { - return validator; - } - - public int getEpochsUntil() { - return epochsUntil; - } - - public Optional getValidatorTotalOwnership() { - return Optional.ofNullable(validatorTotalOwnership); - } - - public Optional getValidatorTotalStake() { - return Optional.ofNullable(validatorTotalStake); - } + private final UInt256 amount; + private final UInt256 validatorTotalOwnership; + private final UInt256 validatorTotalStake; + private final ValidatorAddress validator; + private final int epochsUntil; + + private UnstakePositions( + UInt256 amount, + UInt256 validatorTotalOwnership, + UInt256 validatorTotalStake, + ValidatorAddress validator, + int epochsUntil) { + this.amount = amount; + this.validatorTotalOwnership = validatorTotalOwnership; + this.validatorTotalStake = validatorTotalStake; + this.validator = validator; + this.epochsUntil = epochsUntil; + } + + @JsonCreator + public static UnstakePositions create( + @JsonProperty(value = "amount", required = true) UInt256 amount, + @JsonProperty(value = "validator", required = true) ValidatorAddress validator, + @JsonProperty(value = "validatorTotalOwnership") UInt256 validatorTotalOwnership, + @JsonProperty(value = "validatorTotalStake") UInt256 validatorTotalStake, + @JsonProperty(value = "epochsUntil", required = true) int epochsUntil) { + requireNonNull(amount); + requireNonNull(validator); + + return new UnstakePositions( + amount, validatorTotalOwnership, validatorTotalStake, validator, epochsUntil); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof UnstakePositions)) { + return false; + } + + var that = (UnstakePositions) o; + return epochsUntil == that.epochsUntil + && amount.equals(that.amount) + && validatorTotalOwnership.equals(that.validatorTotalOwnership) + && validatorTotalStake.equals(that.validatorTotalStake) + && validator.equals(that.validator); + } + + @Override + public int hashCode() { + return Objects.hash( + amount, validatorTotalOwnership, validatorTotalStake, validator, epochsUntil); + } + + @Override + public String toString() { + return "UnstakePositions(" + + "amount=" + + amount + + ", validatorTotalOwnership=" + + validatorTotalOwnership + + ", validatorTotalStake=" + + validatorTotalStake + + ", validator=" + + validator + + ", epochsUntil=" + + epochsUntil + + ')'; + } + + public UInt256 getAmount() { + return amount; + } + + public ValidatorAddress getValidator() { + return validator; + } + + public int getEpochsUntil() { + return epochsUntil; + } + + public Optional getValidatorTotalOwnership() { + return Optional.ofNullable(validatorTotalOwnership); + } + + public Optional getValidatorTotalStake() { + return Optional.ofNullable(validatorTotalStake); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Updates.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Updates.java index ea9146055c..0b813048a2 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Updates.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/Updates.java @@ -67,100 +67,101 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.AccountAddress; - import java.util.List; import java.util.Objects; import java.util.Optional; public final class Updates { - private final Optional validatorFee; - private final Optional registered; - private final Optional owner; - private final List stakes; - private final List unstakes; - - private Updates( - Optional validatorFee, - Optional registered, - Optional owner, - List stakes, - List unstakes - ) { - this.validatorFee = validatorFee; - this.registered = registered; - this.owner = owner; - this.stakes = stakes; - this.unstakes = unstakes; - } - - @JsonCreator - public static Updates create( - @JsonProperty("validatorFee") Double validatorFee, - @JsonProperty("registered") Boolean registered, - @JsonProperty("owner") AccountAddress owner, - @JsonProperty("stakes") List stakes, - @JsonProperty("unstakes") List unstakes - ) { - return new Updates( - Optional.ofNullable(validatorFee), - Optional.ofNullable(registered), - Optional.ofNullable(owner), - stakes == null ? List.of() : stakes, - unstakes == null ? List.of() : unstakes - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof Updates)) { - return false; - } - - var updates = (Updates) o; - return validatorFee.equals(updates.validatorFee) - && registered.equals(updates.registered) - && owner.equals(updates.owner) - && stakes.equals(updates.stakes) - && unstakes.equals(updates.unstakes); - } - - @Override - public int hashCode() { - return Objects.hash(validatorFee, registered, owner, stakes, unstakes); - } - - @Override - public String toString() { - return "{" - + "validatorFee=" + validatorFee - + ", registered=" + registered - + ", owner=" + owner - + ", stakes=" + stakes - + ", unstakes=" + unstakes - + '}'; - } - - public Optional getValidatorFee() { - return validatorFee; - } - - public Optional getRegistered() { - return registered; - } - - public Optional getOwner() { - return owner; - } - - public List getStakes() { - return stakes; - } - - public List getUnstakes() { - return unstakes; - } + private final Optional validatorFee; + private final Optional registered; + private final Optional owner; + private final List stakes; + private final List unstakes; + + private Updates( + Optional validatorFee, + Optional registered, + Optional owner, + List stakes, + List unstakes) { + this.validatorFee = validatorFee; + this.registered = registered; + this.owner = owner; + this.stakes = stakes; + this.unstakes = unstakes; + } + + @JsonCreator + public static Updates create( + @JsonProperty("validatorFee") Double validatorFee, + @JsonProperty("registered") Boolean registered, + @JsonProperty("owner") AccountAddress owner, + @JsonProperty("stakes") List stakes, + @JsonProperty("unstakes") List unstakes) { + return new Updates( + Optional.ofNullable(validatorFee), + Optional.ofNullable(registered), + Optional.ofNullable(owner), + stakes == null ? List.of() : stakes, + unstakes == null ? List.of() : unstakes); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof Updates)) { + return false; + } + + var updates = (Updates) o; + return validatorFee.equals(updates.validatorFee) + && registered.equals(updates.registered) + && owner.equals(updates.owner) + && stakes.equals(updates.stakes) + && unstakes.equals(updates.unstakes); + } + + @Override + public int hashCode() { + return Objects.hash(validatorFee, registered, owner, stakes, unstakes); + } + + @Override + public String toString() { + return "{" + + "validatorFee=" + + validatorFee + + ", registered=" + + registered + + ", owner=" + + owner + + ", stakes=" + + stakes + + ", unstakes=" + + unstakes + + '}'; + } + + public Optional getValidatorFee() { + return validatorFee; + } + + public Optional getRegistered() { + return registered; + } + + public Optional getOwner() { + return owner; + } + + public List getStakes() { + return stakes; + } + + public List getUnstakes() { + return unstakes; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorDTO.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorDTO.java index dc25e0d251..bad6d9350b 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorDTO.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorDTO.java @@ -69,178 +69,198 @@ import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class ValidatorDTO { - private final ValidatorAddress address; - private final AccountAddress ownerAddress; - private final double uptimePercentage; - private final double validatorFee; - private final UInt256 totalDelegatedStake; - private final UInt256 ownerDelegation; - private final String infoURL; - private final String name; - private final long proposalsCompleted; - private final long proposalsMissed; - private final boolean registered; - private final boolean isExternalStakeAccepted; - - private ValidatorDTO( - ValidatorAddress address, - AccountAddress ownerAddress, - double uptimePercentage, - double validatorFee, - UInt256 totalDelegatedStake, - UInt256 ownerDelegation, - String infoURL, - String name, - long proposalsCompleted, - long proposalsMissed, - boolean registered, - boolean isExternalStakeAccepted - ) { - this.address = address; - this.ownerAddress = ownerAddress; - this.uptimePercentage = uptimePercentage; - this.validatorFee = validatorFee; - this.totalDelegatedStake = totalDelegatedStake; - this.ownerDelegation = ownerDelegation; - this.infoURL = infoURL; - this.name = name; - this.proposalsCompleted = proposalsCompleted; - this.proposalsMissed = proposalsMissed; - this.registered = registered; - this.isExternalStakeAccepted = isExternalStakeAccepted; - } - - @JsonCreator - public static ValidatorDTO create( - @JsonProperty(value = "address", required = true) ValidatorAddress address, - @JsonProperty(value = "ownerAddress", required = true) AccountAddress ownerAddress, - @JsonProperty(value = "uptimePercentage", required = true) double uptimePercentage, - @JsonProperty(value = "validatorFee", required = true) double validatorFee, - @JsonProperty(value = "totalDelegatedStake", required = true) UInt256 totalDelegatedStake, - @JsonProperty(value = "ownerDelegation", required = true) UInt256 ownerDelegation, - @JsonProperty(value = "infoURL", required = true) String infoURL, - @JsonProperty(value = "name", required = true) String name, - @JsonProperty(value = "proposalsCompleted", required = true) long proposalsCompleted, - @JsonProperty(value = "proposalsMissed", required = true) long proposalsMissed, - @JsonProperty(value = "registered", required = true) boolean registered, - @JsonProperty(value = "isExternalStakeAccepted", required = true) boolean isExternalStakeAccepted - ) { - return new ValidatorDTO( - address, ownerAddress, uptimePercentage, validatorFee, totalDelegatedStake, - ownerDelegation, infoURL, name, proposalsCompleted, proposalsMissed, registered, isExternalStakeAccepted - ); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof ValidatorDTO)) { - return false; - } - - var that = (ValidatorDTO) o; - return Double.compare(that.uptimePercentage, uptimePercentage) == 0 - && Double.compare(that.validatorFee, validatorFee) == 0 - && proposalsCompleted == that.proposalsCompleted - && proposalsMissed == that.proposalsMissed - && registered == that.registered - && isExternalStakeAccepted == that.isExternalStakeAccepted - && address.equals(that.address) - && ownerAddress.equals(that.ownerAddress) - && totalDelegatedStake.equals(that.totalDelegatedStake) - && ownerDelegation.equals(that.ownerDelegation) - && infoURL.equals(that.infoURL) - && name.equals(that.name); - } - - @Override - public int hashCode() { - return Objects.hash( - address, - ownerAddress, - uptimePercentage, - validatorFee, - totalDelegatedStake, - ownerDelegation, - infoURL, - name, - proposalsCompleted, - proposalsMissed, - registered, - isExternalStakeAccepted - ); - } - - @Override - public String toString() { - return "{" - + "address=" + address - + ", ownerAddress=" + ownerAddress - + ", uptimePercentage=" + uptimePercentage - + ", validatorFee=" + validatorFee - + ", totalDelegatedStake=" + totalDelegatedStake - + ", ownerDelegation=" + ownerDelegation - + ", infoURL='" + infoURL + '\'' - + ", name='" + name + '\'' - + ", proposalsCompleted=" + proposalsCompleted - + ", proposalsMissed=" + proposalsMissed - + ", registered=" + registered - + ", isExternalStakeAccepted=" + isExternalStakeAccepted - + '}'; - } - - public ValidatorAddress getAddress() { - return address; - } - - public AccountAddress getOwnerAddress() { - return ownerAddress; - } - - public double getUptimePercentage() { - return uptimePercentage; - } - - public double getValidatorFee() { - return validatorFee; - } - - public UInt256 getTotalDelegatedStake() { - return totalDelegatedStake; - } - - public UInt256 getOwnerDelegation() { - return ownerDelegation; - } - - public String getInfoURL() { - return infoURL; - } - - public String getName() { - return name; - } - - public long getProposalsCompleted() { - return proposalsCompleted; - } - - public long getProposalsMissed() { - return proposalsMissed; - } - - public boolean isRegistered() { - return registered; - } - - public boolean isExternalStakeAccepted() { - return isExternalStakeAccepted; - } + private final ValidatorAddress address; + private final AccountAddress ownerAddress; + private final double uptimePercentage; + private final double validatorFee; + private final UInt256 totalDelegatedStake; + private final UInt256 ownerDelegation; + private final String infoURL; + private final String name; + private final long proposalsCompleted; + private final long proposalsMissed; + private final boolean registered; + private final boolean isExternalStakeAccepted; + + private ValidatorDTO( + ValidatorAddress address, + AccountAddress ownerAddress, + double uptimePercentage, + double validatorFee, + UInt256 totalDelegatedStake, + UInt256 ownerDelegation, + String infoURL, + String name, + long proposalsCompleted, + long proposalsMissed, + boolean registered, + boolean isExternalStakeAccepted) { + this.address = address; + this.ownerAddress = ownerAddress; + this.uptimePercentage = uptimePercentage; + this.validatorFee = validatorFee; + this.totalDelegatedStake = totalDelegatedStake; + this.ownerDelegation = ownerDelegation; + this.infoURL = infoURL; + this.name = name; + this.proposalsCompleted = proposalsCompleted; + this.proposalsMissed = proposalsMissed; + this.registered = registered; + this.isExternalStakeAccepted = isExternalStakeAccepted; + } + + @JsonCreator + public static ValidatorDTO create( + @JsonProperty(value = "address", required = true) ValidatorAddress address, + @JsonProperty(value = "ownerAddress", required = true) AccountAddress ownerAddress, + @JsonProperty(value = "uptimePercentage", required = true) double uptimePercentage, + @JsonProperty(value = "validatorFee", required = true) double validatorFee, + @JsonProperty(value = "totalDelegatedStake", required = true) UInt256 totalDelegatedStake, + @JsonProperty(value = "ownerDelegation", required = true) UInt256 ownerDelegation, + @JsonProperty(value = "infoURL", required = true) String infoURL, + @JsonProperty(value = "name", required = true) String name, + @JsonProperty(value = "proposalsCompleted", required = true) long proposalsCompleted, + @JsonProperty(value = "proposalsMissed", required = true) long proposalsMissed, + @JsonProperty(value = "registered", required = true) boolean registered, + @JsonProperty(value = "isExternalStakeAccepted", required = true) + boolean isExternalStakeAccepted) { + return new ValidatorDTO( + address, + ownerAddress, + uptimePercentage, + validatorFee, + totalDelegatedStake, + ownerDelegation, + infoURL, + name, + proposalsCompleted, + proposalsMissed, + registered, + isExternalStakeAccepted); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ValidatorDTO)) { + return false; + } + + var that = (ValidatorDTO) o; + return Double.compare(that.uptimePercentage, uptimePercentage) == 0 + && Double.compare(that.validatorFee, validatorFee) == 0 + && proposalsCompleted == that.proposalsCompleted + && proposalsMissed == that.proposalsMissed + && registered == that.registered + && isExternalStakeAccepted == that.isExternalStakeAccepted + && address.equals(that.address) + && ownerAddress.equals(that.ownerAddress) + && totalDelegatedStake.equals(that.totalDelegatedStake) + && ownerDelegation.equals(that.ownerDelegation) + && infoURL.equals(that.infoURL) + && name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash( + address, + ownerAddress, + uptimePercentage, + validatorFee, + totalDelegatedStake, + ownerDelegation, + infoURL, + name, + proposalsCompleted, + proposalsMissed, + registered, + isExternalStakeAccepted); + } + + @Override + public String toString() { + return "{" + + "address=" + + address + + ", ownerAddress=" + + ownerAddress + + ", uptimePercentage=" + + uptimePercentage + + ", validatorFee=" + + validatorFee + + ", totalDelegatedStake=" + + totalDelegatedStake + + ", ownerDelegation=" + + ownerDelegation + + ", infoURL='" + + infoURL + + '\'' + + ", name='" + + name + + '\'' + + ", proposalsCompleted=" + + proposalsCompleted + + ", proposalsMissed=" + + proposalsMissed + + ", registered=" + + registered + + ", isExternalStakeAccepted=" + + isExternalStakeAccepted + + '}'; + } + + public ValidatorAddress getAddress() { + return address; + } + + public AccountAddress getOwnerAddress() { + return ownerAddress; + } + + public double getUptimePercentage() { + return uptimePercentage; + } + + public double getValidatorFee() { + return validatorFee; + } + + public UInt256 getTotalDelegatedStake() { + return totalDelegatedStake; + } + + public UInt256 getOwnerDelegation() { + return ownerDelegation; + } + + public String getInfoURL() { + return infoURL; + } + + public String getName() { + return name; + } + + public long getProposalsCompleted() { + return proposalsCompleted; + } + + public long getProposalsMissed() { + return proposalsMissed; + } + + public boolean isRegistered() { + return registered; + } + + public boolean isExternalStakeAccepted() { + return isExternalStakeAccepted; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorEntry.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorEntry.java index f1c83f0562..b5749ee60b 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorEntry.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorEntry.java @@ -68,55 +68,52 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.utils.UInt256; - import java.util.Objects; public final class ValidatorEntry { - private final UInt256 stake; - private final ValidatorAddress address; + private final UInt256 stake; + private final ValidatorAddress address; - private ValidatorEntry(UInt256 stake, ValidatorAddress address) { - this.stake = stake; - this.address = address; - } + private ValidatorEntry(UInt256 stake, ValidatorAddress address) { + this.stake = stake; + this.address = address; + } - @JsonCreator - public static ValidatorEntry create( - @JsonProperty("stake") UInt256 stake, - @JsonProperty("address") ValidatorAddress address - ) { - return new ValidatorEntry(stake, address); - } + @JsonCreator + public static ValidatorEntry create( + @JsonProperty("stake") UInt256 stake, @JsonProperty("address") ValidatorAddress address) { + return new ValidatorEntry(stake, address); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ValidatorEntry)) { - return false; - } + if (!(o instanceof ValidatorEntry)) { + return false; + } - var that = (ValidatorEntry) o; - return stake.equals(that.stake) && address.equals(that.address); - } + var that = (ValidatorEntry) o; + return stake.equals(that.stake) && address.equals(that.address); + } - @Override - public int hashCode() { - return Objects.hash(stake, address); - } + @Override + public int hashCode() { + return Objects.hash(stake, address); + } - @Override - public String toString() { - return "{stake:" + stake + ", address:" + address + '}'; - } + @Override + public String toString() { + return "{stake:" + stake + ", address:" + address + '}'; + } - public UInt256 getStake() { - return stake; - } + public UInt256 getStake() { + return stake; + } - public ValidatorAddress getAddress() { - return address; - } + public ValidatorAddress getAddress() { + return address; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorsResponse.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorsResponse.java index 5ebdd0cfe0..d4ac231b04 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorsResponse.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/ValidatorsResponse.java @@ -64,64 +64,62 @@ package com.radixdlt.client.lib.dto; +import static java.util.Objects.requireNonNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.radixdlt.client.lib.api.NavigationCursor; - import java.util.List; import java.util.Objects; import java.util.Optional; -import static java.util.Objects.requireNonNull; - public final class ValidatorsResponse { - private final NavigationCursor cursor; - private final List validators; + private final NavigationCursor cursor; + private final List validators; - private ValidatorsResponse(NavigationCursor cursor, List validators) { - this.cursor = cursor; - this.validators = validators; - } + private ValidatorsResponse(NavigationCursor cursor, List validators) { + this.cursor = cursor; + this.validators = validators; + } - @JsonCreator - public static ValidatorsResponse create( - @JsonProperty("cursor") NavigationCursor cursor, - @JsonProperty(value = "validators", required = true) List validators - ) { - requireNonNull(validators); + @JsonCreator + public static ValidatorsResponse create( + @JsonProperty("cursor") NavigationCursor cursor, + @JsonProperty(value = "validators", required = true) List validators) { + requireNonNull(validators); - return new ValidatorsResponse(cursor, validators); - } + return new ValidatorsResponse(cursor, validators); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (!(o instanceof ValidatorsResponse)) { - return false; - } + if (!(o instanceof ValidatorsResponse)) { + return false; + } - var that = (ValidatorsResponse) o; - return cursor.equals(that.cursor) && validators.equals(that.validators); - } + var that = (ValidatorsResponse) o; + return cursor.equals(that.cursor) && validators.equals(that.validators); + } - @Override - public int hashCode() { - return Objects.hash(cursor, validators); - } + @Override + public int hashCode() { + return Objects.hash(cursor, validators); + } - @Override - public String toString() { - return "ValidatorsResponseDTO(cursor=" + cursor + ", validators=" + validators + ')'; - } + @Override + public String toString() { + return "ValidatorsResponseDTO(cursor=" + cursor + ", validators=" + validators + ')'; + } - public Optional getCursor() { - return Optional.ofNullable(cursor); - } + public Optional getCursor() { + return Optional.ofNullable(cursor); + } - public List getValidators() { - return validators; - } + public List getValidators() { + return validators; + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressDeserializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressDeserializer.java index 2767439154..cc898b281e 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressDeserializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressDeserializer.java @@ -71,20 +71,20 @@ import com.radixdlt.identifiers.AccountAddressing; import com.radixdlt.networks.Addressing; import com.radixdlt.serialization.DeserializeException; - import java.io.IOException; public class AccountAddressDeserializer extends StdDeserializer { - private final AccountAddressing addressing; + private final AccountAddressing addressing; - public AccountAddressDeserializer(Addressing networkAddressing) { - super(AccountAddress.class); - addressing = networkAddressing.forAccounts(); - } + public AccountAddressDeserializer(Addressing networkAddressing) { + super(AccountAddress.class); + addressing = networkAddressing.forAccounts(); + } - @Override - public AccountAddress deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { - var value = parser.getText(); - return AccountAddress.create(addressing.parseOrThrow(value, DeserializeException::new)); - } + @Override + public AccountAddress deserialize(JsonParser parser, DeserializationContext ctxt) + throws IOException { + var value = parser.getText(); + return AccountAddress.create(addressing.parseOrThrow(value, DeserializeException::new)); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressSerializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressSerializer.java index 5baedd0d86..0ed14ff49d 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressSerializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/AccountAddressSerializer.java @@ -70,19 +70,19 @@ import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.identifiers.AccountAddressing; import com.radixdlt.networks.Addressing; - import java.io.IOException; public class AccountAddressSerializer extends StdSerializer { - private final AccountAddressing addressing; + private final AccountAddressing addressing; - public AccountAddressSerializer(Addressing networkAddressing) { - super(AccountAddress.class); - addressing = networkAddressing.forAccounts(); - } + public AccountAddressSerializer(Addressing networkAddressing) { + super(AccountAddress.class); + addressing = networkAddressing.forAccounts(); + } - @Override - public void serialize(AccountAddress value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeString(addressing.of(value.getAddress())); - } + @Override + public void serialize(AccountAddress value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeString(addressing.of(value.getAddress())); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeyDeserializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeyDeserializer.java index e701726345..79b14c9f95 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeyDeserializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeyDeserializer.java @@ -70,22 +70,22 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.serialization.DeserializeException; - import java.io.IOException; public class ECPublicKeyDeserializer extends StdDeserializer { - public ECPublicKeyDeserializer() { - super(ECPublicKey.class); - } + public ECPublicKeyDeserializer() { + super(ECPublicKey.class); + } - @Override - public ECPublicKey deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { - var value = parser.getText(); + @Override + public ECPublicKey deserialize(JsonParser parser, DeserializationContext ctxt) + throws IOException { + var value = parser.getText(); - try { - return ECPublicKey.fromHex(value); - } catch (PublicKeyException e) { - throw new DeserializeException("Error while parsing address " + value, e); - } - } + try { + return ECPublicKey.fromHex(value); + } catch (PublicKeyException e) { + throw new DeserializeException("Error while parsing address " + value, e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeySerializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeySerializer.java index 89500084a3..d16354acbd 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeySerializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ECPublicKeySerializer.java @@ -68,16 +68,16 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.radixdlt.crypto.ECPublicKey; - import java.io.IOException; public class ECPublicKeySerializer extends StdSerializer { - public ECPublicKeySerializer() { - super(ECPublicKey.class); - } + public ECPublicKeySerializer() { + super(ECPublicKey.class); + } - @Override - public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeString(value.toHex()); - } + @Override + public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeString(value.toHex()); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressDeserializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressDeserializer.java index 0499224ad9..1e9859528e 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressDeserializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressDeserializer.java @@ -71,25 +71,25 @@ import com.radixdlt.identifiers.NodeAddressing; import com.radixdlt.networks.Addressing; import com.radixdlt.serialization.DeserializeException; - import java.io.IOException; public class NodeAddressDeserializer extends StdDeserializer { - private final NodeAddressing addressing; + private final NodeAddressing addressing; - public NodeAddressDeserializer(Addressing networkAddressing) { - super(NodeAddress.class); - addressing = networkAddressing.forNodes(); - } + public NodeAddressDeserializer(Addressing networkAddressing) { + super(NodeAddress.class); + addressing = networkAddressing.forNodes(); + } - @Override - public NodeAddress deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { - var value = parser.getText(); + @Override + public NodeAddress deserialize(JsonParser parser, DeserializationContext ctxt) + throws IOException { + var value = parser.getText(); - try { - return NodeAddress.of(addressing.parse(value)); - } catch (IllegalArgumentException e) { - throw new DeserializeException("Error while parsing address " + value, e); - } - } + try { + return NodeAddress.of(addressing.parse(value)); + } catch (IllegalArgumentException e) { + throw new DeserializeException("Error while parsing address " + value, e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressSerializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressSerializer.java index e57714a3e4..8db9c6cb87 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressSerializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/NodeAddressSerializer.java @@ -70,19 +70,19 @@ import com.radixdlt.client.lib.api.NodeAddress; import com.radixdlt.identifiers.NodeAddressing; import com.radixdlt.networks.Addressing; - import java.io.IOException; public class NodeAddressSerializer extends StdSerializer { - private final NodeAddressing addressing; + private final NodeAddressing addressing; - public NodeAddressSerializer(Addressing networkAddressing) { - super(NodeAddress.class); - addressing = networkAddressing.forNodes(); - } + public NodeAddressSerializer(Addressing networkAddressing) { + super(NodeAddress.class); + addressing = networkAddressing.forNodes(); + } - @Override - public void serialize(NodeAddress value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeString(addressing.of(value.getAddress())); - } + @Override + public void serialize(NodeAddress value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeString(addressing.of(value.getAddress())); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressDeserializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressDeserializer.java index 4f1677514d..c9b4b77bad 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressDeserializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressDeserializer.java @@ -71,20 +71,20 @@ import com.radixdlt.identifiers.ValidatorAddressing; import com.radixdlt.networks.Addressing; import com.radixdlt.serialization.DeserializeException; - import java.io.IOException; public class ValidatorAddressDeserializer extends StdDeserializer { - private final ValidatorAddressing addressing; + private final ValidatorAddressing addressing; - public ValidatorAddressDeserializer(Addressing networkAddressing) { - super(ValidatorAddress.class); - addressing = networkAddressing.forValidators(); - } + public ValidatorAddressDeserializer(Addressing networkAddressing) { + super(ValidatorAddress.class); + addressing = networkAddressing.forValidators(); + } - @Override - public ValidatorAddress deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { - var value = parser.getText(); - return ValidatorAddress.of(addressing.parseOrThrow(value, DeserializeException::new)); - } + @Override + public ValidatorAddress deserialize(JsonParser parser, DeserializationContext ctxt) + throws IOException { + var value = parser.getText(); + return ValidatorAddress.of(addressing.parseOrThrow(value, DeserializeException::new)); + } } diff --git a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressSerializer.java b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressSerializer.java index 8344a8122d..fb08254d3c 100644 --- a/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressSerializer.java +++ b/radixdlt-java/radixdlt-java/src/main/java/com/radixdlt/client/lib/dto/serializer/ValidatorAddressSerializer.java @@ -70,19 +70,19 @@ import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.identifiers.ValidatorAddressing; import com.radixdlt.networks.Addressing; - import java.io.IOException; public class ValidatorAddressSerializer extends StdSerializer { - private final ValidatorAddressing addressing; + private final ValidatorAddressing addressing; - public ValidatorAddressSerializer(Addressing networkAddressing) { - super(ValidatorAddress.class); - addressing = networkAddressing.forValidators(); - } + public ValidatorAddressSerializer(Addressing networkAddressing) { + super(ValidatorAddress.class); + addressing = networkAddressing.forValidators(); + } - @Override - public void serialize(ValidatorAddress value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeString(addressing.of(value.getAddress())); - } + @Override + public void serialize(ValidatorAddress value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeString(addressing.of(value.getAddress())); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/addressing/AddressSerializationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/addressing/AddressSerializationTest.java index 8cd75ae560..109e828a91 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/addressing/AddressSerializationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/addressing/AddressSerializationTest.java @@ -64,7 +64,7 @@ package com.radixdlt.client.lib.addressing; -import org.junit.Test; +import static org.junit.Assert.assertEquals; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; @@ -84,49 +84,53 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.networks.Addressing; import com.radixdlt.utils.UInt256; - import java.util.List; - -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class AddressSerializationTest { - @Test - public void variousTypesOfAddressesSerializedAndDeserializedCorrectly() throws JsonProcessingException { - var keyPair1 = ECKeyPair.generateNew(); - var keyPair2 = ECKeyPair.generateNew(); - var keyPair3 = ECKeyPair.generateNew(); + @Test + public void variousTypesOfAddressesSerializedAndDeserializedCorrectly() + throws JsonProcessingException { + var keyPair1 = ECKeyPair.generateNew(); + var keyPair2 = ECKeyPair.generateNew(); + var keyPair3 = ECKeyPair.generateNew(); - var accountAddress = AccountAddress.create(keyPair1.getPublicKey()); - var validatorAddress = ValidatorAddress.of(keyPair2.getPublicKey()); - var nodeAddress = NodeAddress.of(keyPair3.getPublicKey()); + var accountAddress = AccountAddress.create(keyPair1.getPublicKey()); + var validatorAddress = ValidatorAddress.of(keyPair2.getPublicKey()); + var nodeAddress = NodeAddress.of(keyPair3.getPublicKey()); - var action = new StakeAction(accountAddress, validatorAddress, UInt256.EIGHT); - var peerDetails = NetworkPeer.create(nodeAddress, List.of()); + var action = new StakeAction(accountAddress, validatorAddress, UInt256.EIGHT); + var peerDetails = NetworkPeer.create(nodeAddress, List.of()); - var module = new SimpleModule(); - int networkId = 1; - var networkAddressing = Addressing.ofNetworkId(networkId); + var module = new SimpleModule(); + int networkId = 1; + var networkAddressing = Addressing.ofNetworkId(networkId); - module.addSerializer(AccountAddress.class, new AccountAddressSerializer(networkAddressing)); - module.addSerializer(ValidatorAddress.class, new ValidatorAddressSerializer(networkAddressing)); - module.addSerializer(NodeAddress.class, new NodeAddressSerializer(networkAddressing)); - module.addDeserializer(AccountAddress.class, new AccountAddressDeserializer(networkAddressing)); - module.addDeserializer(ValidatorAddress.class, new ValidatorAddressDeserializer(networkAddressing)); - module.addDeserializer(NodeAddress.class, new NodeAddressDeserializer(networkAddressing)); + module.addSerializer(AccountAddress.class, new AccountAddressSerializer(networkAddressing)); + module.addSerializer(ValidatorAddress.class, new ValidatorAddressSerializer(networkAddressing)); + module.addSerializer(NodeAddress.class, new NodeAddressSerializer(networkAddressing)); + module.addDeserializer(AccountAddress.class, new AccountAddressDeserializer(networkAddressing)); + module.addDeserializer( + ValidatorAddress.class, new ValidatorAddressDeserializer(networkAddressing)); + module.addDeserializer(NodeAddress.class, new NodeAddressDeserializer(networkAddressing)); - var objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_ABSENT) - .registerModule(module); + var objectMapper = + new ObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) + .registerModule(module); - var result = objectMapper.writeValueAsString(action); - assertEquals(action.toJSON(networkId), result); + var result = objectMapper.writeValueAsString(action); + assertEquals(action.toJSON(networkId), result); - var restored = objectMapper.readValue(result, StakeAction.class); - assertEquals(action, restored); + var restored = objectMapper.readValue(result, StakeAction.class); + assertEquals(action, restored); - var peerResult = objectMapper.writeValueAsString(peerDetails); - assertEquals(String.format("{\"address\":\"%s\",\"channels\":[]}", nodeAddress.toString(networkId)), peerResult); + var peerResult = objectMapper.writeValueAsString(peerDetails); + assertEquals( + String.format("{\"address\":\"%s\",\"channels\":[]}", nodeAddress.toString(networkId)), + peerResult); - var restoredPeer = objectMapper.readValue(peerResult, NetworkPeer.class); - assertEquals(nodeAddress, restoredPeer.getAddress()); - } + var restoredPeer = objectMapper.readValue(peerResult, NetworkPeer.class); + assertEquals(nodeAddress, restoredPeer.getAddress()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiAccountTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiAccountTest.java index 7eaad61cc1..8a23ca5268 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiAccountTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiAccountTest.java @@ -61,10 +61,15 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Ignore; -import org.junit.Test; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -76,188 +81,233 @@ import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static com.radixdlt.client.lib.api.token.Amount.amount; +import org.junit.Ignore; +import org.junit.Test; public class AsyncRadixApiAccountTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String TOKEN_BALANCES = "{\"result\":{\"owner\":\"ddx1qsp8n0nx0muaewav2ksx99wwsu9swq5mlndjmn3gm" - + "9vl9q2mzmup0xq904xyj\",\"tokenBalances\":[{\"amount\":\"1000000000000000000000000000\",\"rri\":\"xrd_dr1qyrs8" - + "qwl\"}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String TX_HISTORY = "{\"result\":{\"cursor\":\"1577836:800000000\",\"transactions\":[{\"fee\":" - + "\"0\",\"txID\":\"407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b3\",\"sentAt\":\"1970-01-19T" - + "06:17:16.800Z\",\"actions\":[{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Othe" - + "r\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"" - + "},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{" - + "\"amount\":\"100000000000000000000\",\"validator\":\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnum" - + "c3jtmxl\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"StakeTok" - + "ens\"},{\"amount\":\"100000000000000000000\",\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh" - + "59rq9964vjryzf9\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"" - + "StakeTokens\"},{\"type\":\"Other\"}]}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String ERROR_RESPONSE = "{\"id\":\"2\",\"jsonrpc\":\"2.0\",\"error\":{\"code\":2523,\"data\":" - + "[\"0000000000000000000000000000000000000000000000000000000000000000\"],\"message\":\"Transaction with id 00" - + "00000000000000000000000000000000000000000000000000000000000000 not found\"}}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String TOKEN_BALANCES = + "{\"result\":{\"owner\":\"ddx1qsp8n0nx0muaewav2ksx99wwsu9swq5mlndjmn3gm9vl9q2mzmup0xq904xyj\",\"tokenBalances\":[{\"amount\":\"1000000000000000000000000000\",\"rri\":\"xrd_dr1qyrs8qwl\"}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String TX_HISTORY = + "{\"result\":{\"cursor\":\"1577836:800000000\",\"transactions\":[{\"fee\":\"0\",\"txID\":\"407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b3\",\"sentAt\":\"1970-01-19T06:17:16.800Z\",\"actions\":[{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"amount\":\"100000000000000000000\",\"validator\":\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnumc3jtmxl\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"StakeTokens\"},{\"amount\":\"100000000000000000000\",\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"StakeTokens\"},{\"type\":\"Other\"}]}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String ERROR_RESPONSE = + "{\"id\":\"2\",\"jsonrpc\":\"2.0\",\"error\":{\"code\":2523,\"data\":[\"0000000000000000000000000000000000000000000000000000000000000000\"],\"message\":\"Transaction" + + " with id 0000000000000000000000000000000000000000000000000000000000000000 not" + + " found\"}}\n"; - private static final String STAKES_RESPONSE = "{\"result\":[{\"amount\":\"3455000000000000000000\",\"validator\":" - + "\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String STAKES_RESPONSE = + "{\"result\":[{\"amount\":\"3455000000000000000000\",\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String UNSTAKES_RESPONSE = "{\"result\":[{\"amount\":\"195000000000000000000\",\"epochsUntil\":" - + "500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"},{\"amount\":" - + "\"300000000000000000000\",\"validatorTotalOwnership\":\"107734488884306251968\",\"epochsUntil\":" - + "500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"validatorTotalStake\":" - + "\"24455003291307584010164\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String UNSTAKES_RESPONSE = + "{\"result\":[{\"amount\":\"195000000000000000000\",\"epochsUntil\":500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"},{\"amount\":\"300000000000000000000\",\"validatorTotalOwnership\":\"107734488884306251968\",\"epochsUntil\":500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"validatorTotalStake\":\"24455003291307584010164\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testTokenBalances() throws IOException { - prepareClient(TOKEN_BALANCES) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().balances(ACCOUNT_ADDRESS1).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(tokenBalancesDTO -> assertEquals(ACCOUNT_ADDRESS1, tokenBalancesDTO.getOwner())) - .map(TokenBalances::getTokenBalances) - .onSuccess(balances -> assertEquals(1, balances.size()))); - } + @Test + public void testTokenBalances() throws IOException { + prepareClient(TOKEN_BALANCES) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .balances(ACCOUNT_ADDRESS1) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + tokenBalancesDTO -> + assertEquals(ACCOUNT_ADDRESS1, tokenBalancesDTO.getOwner())) + .map(TokenBalances::getTokenBalances) + .onSuccess(balances -> assertEquals(1, balances.size()))); + } - @Test - public void testErrorResponse() throws IOException { - prepareClient(ERROR_RESPONSE) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().lookup(AID.ZERO).join() - .onFailure(failure -> assertEquals(2523, failure.code())) - .onSuccess(__ -> fail())); - } + @Test + public void testErrorResponse() throws IOException { + prepareClient(ERROR_RESPONSE) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .lookup(AID.ZERO) + .join() + .onFailure(failure -> assertEquals(2523, failure.code())) + .onSuccess(__ -> fail())); + } - @Test - public void listStakes() throws IOException { - prepareClient(STAKES_RESPONSE) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().stakes(ACCOUNT_ADDRESS1).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(stakePositionsDTOS -> assertEquals(1, stakePositionsDTOS.size())) - .onSuccess(stakePositionsDTOS -> assertEquals(amount(3455).tokens(), stakePositionsDTOS.get(0).getAmount()))); - } + @Test + public void listStakes() throws IOException { + prepareClient(STAKES_RESPONSE) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .stakes(ACCOUNT_ADDRESS1) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(stakePositionsDTOS -> assertEquals(1, stakePositionsDTOS.size())) + .onSuccess( + stakePositionsDTOS -> + assertEquals( + amount(3455).tokens(), stakePositionsDTOS.get(0).getAmount()))); + } - @Test - public void listUnStakes() throws IOException { - prepareClient(UNSTAKES_RESPONSE) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().unstakes(ACCOUNT_ADDRESS1).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(unstakePositionsDTOS -> assertEquals(2, unstakePositionsDTOS.size())) - .onSuccess(unstakePositionsDTOS -> assertEquals(amount(195).tokens(), unstakePositionsDTOS.get(0).getAmount())) - .onSuccess(unstakePositionsDTOS -> assertEquals(amount(300).tokens(), unstakePositionsDTOS.get(1).getAmount()))); - } + @Test + public void listUnStakes() throws IOException { + prepareClient(UNSTAKES_RESPONSE) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .unstakes(ACCOUNT_ADDRESS1) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(unstakePositionsDTOS -> assertEquals(2, unstakePositionsDTOS.size())) + .onSuccess( + unstakePositionsDTOS -> + assertEquals( + amount(195).tokens(), unstakePositionsDTOS.get(0).getAmount())) + .onSuccess( + unstakePositionsDTOS -> + assertEquals( + amount(300).tokens(), unstakePositionsDTOS.get(1).getAmount()))); + } - @Test - @Ignore("Online test") - public void makeStake() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> makeStake(client, amount(1000).tokens())); - } + @Test + @Ignore("Online test") + public void makeStake() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> makeStake(client, amount(1000).tokens())); + } - @Test - @Ignore("Online test") - public void makeUnStake() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> makeUnStake(client, amount(100).tokens())); - } + @Test + @Ignore("Online test") + public void makeUnStake() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> makeUnStake(client, amount(100).tokens())); + } - @Test - @Ignore("Online test") - public void transferUnStake() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> transferUnStake(client, amount(100).tokens())); - } + @Test + @Ignore("Online test") + public void transferUnStake() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> transferUnStake(client, amount(100).tokens())); + } - private void transferUnStake(RadixApi client, UInt256 amount) { - client.local().accountInfo() - .map(account -> TransactionRequest.createBuilder(account.getAddress()) - .transfer(account.getAddress(), ACCOUNT_ADDRESS1, amount, "xrd_dr1qyrs8qwl") - .build()) - .flatMap(request -> client.local().submitTxSingleStep(request) - .onFailure(failure -> fail(failure.toString()))) - .join(); - } + private void transferUnStake(RadixApi client, UInt256 amount) { + client + .local() + .accountInfo() + .map( + account -> + TransactionRequest.createBuilder(account.getAddress()) + .transfer(account.getAddress(), ACCOUNT_ADDRESS1, amount, "xrd_dr1qyrs8qwl") + .build()) + .flatMap( + request -> + client + .local() + .submitTxSingleStep(request) + .onFailure(failure -> fail(failure.toString()))) + .join(); + } - private void makeStake(RadixApi client, UInt256 amount) { - client.local().validatorInfo() - .map(account -> TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) - .build()) - .flatMap(request -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess(transaction -> client.transaction().finalize(transaction, true))) - .join(); - } + private void makeStake(RadixApi client, UInt256 amount) { + client + .local() + .validatorInfo() + .map( + account -> + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) + .build()) + .flatMap( + request -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess(transaction -> client.transaction().finalize(transaction, true))) + .join(); + } - private void makeUnStake(RadixApi client, UInt256 amount) { - client.local().validatorInfo() - .map(account -> TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .unstake(ACCOUNT_ADDRESS1, account.getAddress(), amount) - .build()) - .flatMap(request -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess(transaction -> client.transaction().finalize(transaction, true))) - .join(); - } + private void makeUnStake(RadixApi client, UInt256 amount) { + client + .local() + .validatorInfo() + .map( + account -> + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .unstake(ACCOUNT_ADDRESS1, account.getAddress(), amount) + .build()) + .flatMap( + request -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess(transaction -> client.transaction().finalize(transaction, true))) + .join(); + } - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiApiTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiApiTest.java index 5a3f7b580b..3340580bb6 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiApiTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiApiTest.java @@ -61,77 +61,93 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; public class AsyncRadixApiApiTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"endpoints\":[\"/metrics\",\"/system\"," - + "\"/account\",\"/validation\",\"/universe\",\"/faucet\",\"/chaos\",\"/health\",\"/version\"," - + "\"/developer\",\"/archive\",\"/construction\"]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String DATA = "{\"result\":{\"elapsed\":{\"apidb\":{\"balance\":{\"read\":1672,\"" - + "write\":4790},\"flush\":{\"time\":630722},\"transaction\":{\"read\":0,\"write\":1453},\"token\":" - + "{\"read\":134,\"write\":842}}},\"count\":{\"apidb\":{\"flush\":{\"count\":1627},\"balance\":{\"t" - + "otal\":50,\"read\":26,\"bytes\":{\"read\":1532,\"write\":4263},\"write\":24},\"queue\":{\"size\"" - + ":6},\"transaction\":{\"total\":7,\"read\":0,\"bytes\":{\"read\":0,\"write\":13923},\"write\":7}," - + "\"token\":{\"total\":2,\"read\":1,\"bytes\":{\"read\":245,\"write\":245},\"write\":1}}}},\"id\":" - + "\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"endpoints\":[\"/metrics\",\"/system\",\"/account\",\"/validation\",\"/universe\",\"/faucet\",\"/chaos\",\"/health\",\"/version\",\"/developer\",\"/archive\",\"/construction\"]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String DATA = + "{\"result\":{\"elapsed\":{\"apidb\":{\"balance\":{\"read\":1672,\"write\":4790},\"flush\":{\"time\":630722},\"transaction\":{\"read\":0,\"write\":1453},\"token\":{\"read\":134,\"write\":842}}},\"count\":{\"apidb\":{\"flush\":{\"count\":1627},\"balance\":{\"total\":50,\"read\":26,\"bytes\":{\"read\":1532,\"write\":4263},\"write\":24},\"queue\":{\"size\":6},\"transaction\":{\"total\":7,\"read\":0,\"bytes\":{\"read\":0,\"write\":13923},\"write\":7},\"token\":{\"total\":2,\"read\":1,\"bytes\":{\"read\":245,\"write\":245},\"write\":1}}}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws IOException { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.api().configuration().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configurationDTO -> assertEquals(12, configurationDTO.getEndpoints().size()))); - } + @Test + public void testConfiguration() throws IOException { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .api() + .configuration() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + configurationDTO -> + assertEquals(12, configurationDTO.getEndpoints().size()))); + } - @Test - public void testData() throws IOException { - prepareClient(DATA) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.api().data().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertNotNull(data.getCount())) - .onSuccess(data -> assertNotNull(data.getElapsed())) - .onSuccess(data -> assertEquals(630722, data.getElapsed().getApiDb().getFlush().getTime())) - .onSuccess(data -> assertEquals(1672, data.getElapsed().getApiDb().getBalance().getRead())) - .onSuccess(data -> assertEquals(6, data.getCount().getApiDb().getQueue().getSize()))); - } + @Test + public void testData() throws IOException { + prepareClient(DATA) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .api() + .data() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertNotNull(data.getCount())) + .onSuccess(data -> assertNotNull(data.getElapsed())) + .onSuccess( + data -> + assertEquals(630722, data.getElapsed().getApiDb().getFlush().getTime())) + .onSuccess( + data -> + assertEquals(1672, data.getElapsed().getApiDb().getBalance().getRead())) + .onSuccess( + data -> assertEquals(6, data.getCount().getApiDb().getQueue().getSize()))); + } - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiConsensusTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiConsensusTest.java index ee9bc4b15e..b01c4ec06e 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiConsensusTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiConsensusTest.java @@ -61,72 +61,86 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; public class AsyncRadixApiConsensusTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"pacemakerTimeout\":3000,\"bftSyncPatienceMs\":200}," - + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String DATA = "{\"result\":{\"timeoutQuorums\":1,\"rejected\":0,\"voteQuorums\":79981," - + "\"vertexStoreForks\":1,\"sync\":{\"requestsSent\":0,\"requestTimeouts\":0},\"timeout\":1,\"stateVers" - + "ion\":159960,\"processed\":159959,\"timedOutViews\":1,\"vertexStoreSize\":2,\"vertexStoreRebuilds\":" - + "0,\"consensusEvents\":319926,\"indirectParent\":1,\"proposalsMade\":79981},\"id\":\"2\",\"jsonrpc\":" - + "\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"pacemakerTimeout\":3000,\"bftSyncPatienceMs\":200}," + + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String DATA = + "{\"result\":{\"timeoutQuorums\":1,\"rejected\":0,\"voteQuorums\":79981,\"vertexStoreForks\":1,\"sync\":{\"requestsSent\":0,\"requestTimeouts\":0},\"timeout\":1,\"stateVersion\":159960,\"processed\":159959,\"timedOutViews\":1,\"vertexStoreSize\":2,\"vertexStoreRebuilds\":0,\"consensusEvents\":319926,\"indirectParent\":1,\"proposalsMade\":79981},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws IOException { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.consensus().configuration().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configuration -> assertEquals(200L, configuration.getBftSyncPatienceMs())) - .onSuccess(configuration -> assertEquals(3000L, configuration.getPacemakerTimeout()))); - } + @Test + public void testConfiguration() throws IOException { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .consensus() + .configuration() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + configuration -> assertEquals(200L, configuration.getBftSyncPatienceMs())) + .onSuccess( + configuration -> assertEquals(3000L, configuration.getPacemakerTimeout()))); + } - @Test - public void testData() throws IOException { - prepareClient(DATA) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.consensus().data().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertEquals(159959L, data.getProcessed())) - .onSuccess(data -> assertEquals(159960L, data.getStateVersion()))); - } + @Test + public void testData() throws IOException { + prepareClient(DATA) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .consensus() + .data() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertEquals(159959L, data.getProcessed())) + .onSuccess(data -> assertEquals(159960L, data.getStateVersion()))); + } - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiCreationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiCreationTest.java index 83ff64612f..f9446b4f77 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiCreationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiCreationTest.java @@ -61,11 +61,16 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.bouncycastle.util.encoders.Hex; -import org.junit.Ignore; -import org.junit.Test; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -81,215 +86,286 @@ import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Ignore; +import org.junit.Test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import static com.radixdlt.client.lib.api.token.Amount.amount; - -//TODO: test remaining actions!!! +// TODO: test remaining actions!!! public class AsyncRadixApiCreationTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - public static final ECKeyPair KEY_PAIR3 = keyPairOf(3); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS3 = AccountAddress.create(KEY_PAIR3.getPublicKey()); - private static final ValidatorAddress VALIDATOR_ADDRESS = ValidatorAddress.of(KEY_PAIR3.getPublicKey()); - - private static final String NETWORK_ID = "{\"result\":{\"networkId\":2},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String BUILT_TRANSACTION = "{\"result\":{\"fee\":\"73800000000000000\",\"transaction\":{\"blob\":" - + "\"060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000" - + "000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817" - + "98010000000000000000000000000000000000000000033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a" - + "06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17f" - + "ec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000000000000000000000000000000" - + "00000000000000000000000000000000a000b0c54657374206d657373616765\",\"hashOfBlobToSign\":\"76448a9f09e5bb9fbce1" - + "1844731e8a7e28601733100787462401f47916bbc4ac\"}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}"; - - private static final String BLOB = "060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100" - + "000000000000000000000000000000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029" - + "bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17ff600070000000002" - + "0500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000" - + "00000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000" - + "00000000000000000000000000000000000000000000000000000000000a000b0c54657374206d657373616765"; - - private static final String TX_ID = "a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41a4272e38f1aa5"; - private static final String SIG = "3045022100f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb40220388" - + "dfd1e25b25a80366fc70415334d1e4af259aee5685e93a8cd03004e1a8157"; - private static final String PUB_KEY = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; - - private static final String FINALIZE_TRANSACTION = "{\"result\":{\"blob\":\"060a104c95b2f14ca1c6500b519ad696bee17b7a" - + "982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000000000000000000000000010630b5806c800002050" - + "0040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000000" - + "00033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81" - + "798010000000000000000000000000000000000000000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07c" - + "d85c778e4b8cef3ca7abac09b95c709ee501000000000000000000000000000000000000000000000000000000000000000a000b0c546" - + "57374206d6573736167650a01f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb4388dfd1e25b25a80366f" - + "c70415334d1e4af259aee5685e93a8cd03004e1a8157\",\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41" - + "a4272e38f1aa5\"},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; - private static final String SUBMIT_TRANSACTION = "{\"result\":{\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf4739" - + "2a2fd0e41a4272e38f1aa5\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; - - private final HttpClient client = mock(HttpClient.class); - - @Test - public void testBuildTransaction() throws IOException { - var hash = Hex.decode("76448a9f09e5bb9fbce11844731e8a7e28601733100787462401f47916bbc4ac"); - - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount(10).subunits(), "xrd_dr1qyrs8qwl") - .message("Test message") - .build(); - - prepareClient(BUILT_TRANSACTION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(dto -> assertEquals(amount(73800).micros(), dto.getFee())) - .onSuccess(dto -> assertArrayEquals(hash, dto.getTransaction().getHashToSign()))); - } - - @Test - public void testFinalizeTransaction() throws Exception { - var request = buildFinalizedTransaction(); - var txId = AID.from(TX_ID); - - prepareClient(FINALIZE_TRANSACTION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().finalize(request, false).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(dto -> assertEquals(txId, dto.getTxId()))); - } - - @Test - public void testSubmitTransaction() throws Exception { - var txId = AID.from(TX_ID); - var request = buildBlobDto(); - - prepareClient(SUBMIT_TRANSACTION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().submit(request).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(dto -> assertEquals(txId, dto.getTxId()))); - } - - @Test - @Ignore("Online test") - public void testBuildAndSubmitTransactionWithMessage() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.NINE, "xrd_dr1qyrs8qwl") - .message("Test message") - .build(); - - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransaction -> assertEquals(amount(73800).micros(), builtTransaction.getFee())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true).join() - .onFailure(failure -> fail(failure.toString())))); - } - - @Test - @Ignore("Online test") - public void testCreateFixedSupplyToken() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .createFixed(ACCOUNT_ADDRESS1, KEY_PAIR1.getPublicKey(), - "fix", "fix", "fix", - "https://some.host.com/", "https://some.other.host.com", - amount(1000).tokens()) - .build(); - - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().build(request) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransaction -> assertEquals(amount(1000109).millis(), builtTransaction.getFee())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true) - .join() - .onFailure(failure -> fail(failure.toString())))); - } - - @Test - @Ignore("Online test") - //TODO: for some reason operation succeeds only if transaction contains only one action - public void testRegisterValidator() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS3) - .registerValidator(VALIDATOR_ADDRESS, Optional.of("MyValidator"), Optional.of("http://my.validator.url.com/")) - .updateValidatorFee(VALIDATOR_ADDRESS, 3.1) - .updateValidatorOwner(VALIDATOR_ADDRESS, ACCOUNT_ADDRESS3) - .updateValidatorAllowDelegationFlag(VALIDATOR_ADDRESS, true) - .build(); - - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransaction -> assertEquals(amount(102600).micros(), builtTransaction.getFee())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR3)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true).join() - .onFailure(failure -> fail(failure.toString())))); - } - - private TxBlobDTO buildBlobDto() { - return TxBlobDTO.create(AID.from(TX_ID), BLOB); - } - - private FinalizedTransaction buildFinalizedTransaction() throws PublicKeyException { - var sig = ECDSASignature.decodeFromHexDer(SIG); - var publicKey = ECPublicKey.fromHex(PUB_KEY); - var blob = buildBlobDto(); - return FinalizedTransaction.create(blob.getBlob(), sig, publicKey, blob.getTxId()); - } - - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); - - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); - - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } - - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; - - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + public static final ECKeyPair KEY_PAIR3 = keyPairOf(3); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS3 = + AccountAddress.create(KEY_PAIR3.getPublicKey()); + private static final ValidatorAddress VALIDATOR_ADDRESS = + ValidatorAddress.of(KEY_PAIR3.getPublicKey()); + + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":2},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String BUILT_TRANSACTION = + "{\"result\":{\"fee\":\"73800000000000000\",\"transaction\":{\"blob\":" + + "\"060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000" + + "000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817" + + "98010000000000000000000000000000000000000000033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a" + + "06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17f" + + "ec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000000000000000000000000000000" + + "00000000000000000000000000000000a000b0c54657374206d657373616765\",\"hashOfBlobToSign\":\"76448a9f09e5bb9fbce1" + + "1844731e8a7e28601733100787462401f47916bbc4ac\"}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}"; + + private static final String BLOB = + "060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100" + + "000000000000000000000000000000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029" + + "bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17ff600070000000002" + + "0500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000" + + "00000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000" + + "00000000000000000000000000000000000000000000000000000000000a000b0c54657374206d657373616765"; + + private static final String TX_ID = + "a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41a4272e38f1aa5"; + private static final String SIG = + "3045022100f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb40220388" + + "dfd1e25b25a80366fc70415334d1e4af259aee5685e93a8cd03004e1a8157"; + private static final String PUB_KEY = + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; + + private static final String FINALIZE_TRANSACTION = + "{\"result\":{\"blob\":\"060a104c95b2f14ca1c6500b519ad696bee17b7a" + + "982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000000000000000000000000010630b5806c800002050" + + "0040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000000" + + "00033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81" + + "798010000000000000000000000000000000000000000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07c" + + "d85c778e4b8cef3ca7abac09b95c709ee501000000000000000000000000000000000000000000000000000000000000000a000b0c546" + + "57374206d6573736167650a01f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb4388dfd1e25b25a80366f" + + "c70415334d1e4af259aee5685e93a8cd03004e1a8157\",\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41" + + "a4272e38f1aa5\"},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; + private static final String SUBMIT_TRANSACTION = + "{\"result\":{\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf4739" + + "2a2fd0e41a4272e38f1aa5\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; + + private final HttpClient client = mock(HttpClient.class); + + @Test + public void testBuildTransaction() throws IOException { + var hash = Hex.decode("76448a9f09e5bb9fbce11844731e8a7e28601733100787462401f47916bbc4ac"); + + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount(10).subunits(), "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); + + prepareClient(BUILT_TRANSACTION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(dto -> assertEquals(amount(73800).micros(), dto.getFee())) + .onSuccess( + dto -> assertArrayEquals(hash, dto.getTransaction().getHashToSign()))); + } + + @Test + public void testFinalizeTransaction() throws Exception { + var request = buildFinalizedTransaction(); + var txId = AID.from(TX_ID); + + prepareClient(FINALIZE_TRANSACTION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .finalize(request, false) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(dto -> assertEquals(txId, dto.getTxId()))); + } + + @Test + public void testSubmitTransaction() throws Exception { + var txId = AID.from(TX_ID); + var request = buildBlobDto(); + + prepareClient(SUBMIT_TRANSACTION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .submit(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(dto -> assertEquals(txId, dto.getTxId()))); + } + + @Test + @Ignore("Online test") + public void testBuildAndSubmitTransactionWithMessage() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.NINE, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); + + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransaction -> + assertEquals(amount(73800).micros(), builtTransaction.getFee())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, true) + .join() + .onFailure(failure -> fail(failure.toString())))); + } + + @Test + @Ignore("Online test") + public void testCreateFixedSupplyToken() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .createFixed( + ACCOUNT_ADDRESS1, + KEY_PAIR1.getPublicKey(), + "fix", + "fix", + "fix", + "https://some.host.com/", + "https://some.other.host.com", + amount(1000).tokens()) + .build(); + + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransaction -> + assertEquals(amount(1000109).millis(), builtTransaction.getFee())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, true) + .join() + .onFailure(failure -> fail(failure.toString())))); + } + + @Test + @Ignore("Online test") + // TODO: for some reason operation succeeds only if transaction contains only one action + public void testRegisterValidator() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS3) + .registerValidator( + VALIDATOR_ADDRESS, + Optional.of("MyValidator"), + Optional.of("http://my.validator.url.com/")) + .updateValidatorFee(VALIDATOR_ADDRESS, 3.1) + .updateValidatorOwner(VALIDATOR_ADDRESS, ACCOUNT_ADDRESS3) + .updateValidatorAllowDelegationFlag(VALIDATOR_ADDRESS, true) + .build(); + + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransaction -> + assertEquals(amount(102600).micros(), builtTransaction.getFee())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR3)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, true) + .join() + .onFailure(failure -> fail(failure.toString())))); + } + + private TxBlobDTO buildBlobDto() { + return TxBlobDTO.create(AID.from(TX_ID), BLOB); + } + + private FinalizedTransaction buildFinalizedTransaction() throws PublicKeyException { + var sig = ECDSASignature.decodeFromHexDer(SIG); + var publicKey = ECPublicKey.fromHex(PUB_KEY); + var blob = buildBlobDto(); + return FinalizedTransaction.create(blob.getBlob(), sig, publicKey, blob.getTxId()); + } + + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); + + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); + + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } + + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; + + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiHistoryPaginationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiHistoryPaginationTest.java index 793272ac25..29dfb73c8b 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiHistoryPaginationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiHistoryPaginationTest.java @@ -61,10 +61,10 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Ignore; -import org.junit.Test; +import static org.junit.Assert.fail; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -75,13 +75,12 @@ import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; - -import static org.junit.Assert.fail; +import org.junit.Ignore; +import org.junit.Test; /* * Before running this test, launch in separate console local network (cd radixdlt-core/docker && ./scripts/rundocker.sh 2). @@ -92,95 +91,104 @@ * * Then run testTransactionHistoryInPages(). It should print list of transactions split into batches of 50 (see parameters) */ -//TODO: move to acceptance tests +// TODO: move to acceptance tests public class AsyncRadixApiHistoryPaginationTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); - @Test - @Ignore("Online test") - public void testAddManyTransactions() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> { - for (int i = 0; i < 20; i++) { - addTransaction(client, i); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - } + @Test + @Ignore("Online test") + public void testAddManyTransactions() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + for (int i = 0; i < 20; i++) { + addTransaction(client, i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + } - @Test - @Ignore("Online test") - public void testTransactionHistoryInPages() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> { - var cursorHolder = new AtomicReference<>(OptionalLong.empty()); - do { - client.account().history(ACCOUNT_ADDRESS1, 50, cursorHolder.get()).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(v -> v.getNextOffset().ifPresent(System.out::println)) - .onSuccess(v -> cursorHolder.set(v.getNextOffset())) - .map(TransactionHistory::getTransactions) - .map(this::formatTxns) - .onSuccess(System.out::println); - } while (cursorHolder.get().isPresent()); - }); - } + @Test + @Ignore("Online test") + public void testTransactionHistoryInPages() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + var cursorHolder = new AtomicReference<>(OptionalLong.empty()); + do { + client + .account() + .history(ACCOUNT_ADDRESS1, 50, cursorHolder.get()) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(v -> v.getNextOffset().ifPresent(System.out::println)) + .onSuccess(v -> cursorHolder.set(v.getNextOffset())) + .map(TransactionHistory::getTransactions) + .map(this::formatTxns) + .onSuccess(System.out::println); + } while (cursorHolder.get().isPresent()); + }); + } - private List formatTxns(List t) { - return t.stream() - .map(v -> String.format( - "%s (%s) - %s (%d:%d), Fee: %s%n", - v.getTxID(), - v.getMessage().orElse(""), - v.getSentAt().getInstant(), - v.getSentAt().getInstant().getEpochSecond(), - v.getSentAt().getInstant().getNano(), - v.getFee() - )) - .collect(Collectors.toList()); - } + private List formatTxns(List t) { + return t.stream() + .map( + v -> + String.format( + "%s (%s) - %s (%d:%d), Fee: %s%n", + v.getTxID(), + v.getMessage().orElse(""), + v.getSentAt().getInstant(), + v.getSentAt().getInstant().getEpochSecond(), + v.getSentAt().getInstant().getNano(), + v.getFee())) + .collect(Collectors.toList()); + } - private void addTransaction(RadixApi client, int count) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - ACCOUNT_ADDRESS2, - UInt256.from(count + 10), - "xrd_dr1qyrs8qwl" - ) - .message("Test message " + count) - .build(); + private void addTransaction(RadixApi client, int count) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer( + ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.from(count + 10), "xrd_dr1qyrs8qwl") + .message("Test message " + count) + .build(); - client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true).join()); - } + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client.transaction().finalize(finalizedTransaction, true).join()); + } - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiLocalTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiLocalTest.java index ad8087eb9f..159f2c6329 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiLocalTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiLocalTest.java @@ -61,9 +61,17 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Test; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -75,168 +83,210 @@ import com.radixdlt.networks.Network; import com.radixdlt.utils.Ints; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +public class AsyncRadixApiLocalTest { + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + private static final AccountAddressing ACCOUNTS = + Addressing.ofNetwork(Network.LOCALNET).forAccounts(); -import static com.radixdlt.client.lib.api.token.Amount.amount; + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); -public class AsyncRadixApiLocalTest { - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - private static final AccountAddressing ACCOUNTS = Addressing.ofNetwork(Network.LOCALNET).forAccounts(); - - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); - - private static final String BASE_URL = "http://localhost/"; - - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String ACCOUNT_INFO = "{\"result\":{\"address\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhk" - + "guhc269p3vhs27s5vq5h24sfvvdfj\",\"balance\":{\"stakes\":[],\"tokens\":[{\"amount\":\"1000000000000000" - + "000000000000\",\"rri\":\"xrd_dr1qyrs8qwl\"}],\"preparedStakes\":[]}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String VALIDATOR_INFO = "{\"result\":{\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897z" - + "k3gvt9uzh59rq9964vjryzf9\",\"epochInfo\":{\"current\":{\"owner\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhkg" - + "uhc269p3vhs27s5vq5h24sfvvdfj\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"stakes\":[{\"amou" - + "nt\":\"3949310000000000000000000\",\"delegator\":\"ddx1qsprdptw48agcfp7gh7ffmp8c2w08ut7820pnfqsae9yray93" - + "cmejxqkgsmrs\"}],\"validatorFee\":\"0.0\",\"registered\":true,\"totalStake\":\"394931000000000000000000" - + "0\",\"proposalsCompleted\":4111},\"updates\":{}},\"allowDelegation\":true,\"name\":\"\",\"url\":\"\"}," - + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String CURRENT_EPOCH = "{\"result\":{\"validators\":[{\"totalDelegatedStake\":" - + "\"5201130000000000000000000\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"address\":" - + "\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnumc3jtmxl\",\"proposalsCompleted\":513}," - + "{\"totalDelegatedStake\":\"5199060000000000000000000\",\"uptimePercentage\":\"100.00\"," - + "\"proposalsMissed\":0,\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"," - + "\"proposalsCompleted\":511}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - - private static final String SINGLE_STEP = "{\"result\":{\"txID\":\"c4741a62a721885dc3523afbf0297011671d8ce8969885b" - + "c0f6a6ffde9e39235\"},\"id\":\"6\",\"jsonrpc\":\"2.0\"}"; - private static final String BUILD_TRANSACTION = "{\"result\":{\"fee\":\"74200000000000000\",\"transaction\":{\"blo" - + "b\":\"06407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b300000005012100000000000000000000000" - + "00000000000000000000000000001079c81c2558000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f281" - + "5b16f81798010000000000000000000000000000000000000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef" - + "9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3733" - + "01858dc29a80000205000403fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975560100000000000000000" - + "00000000000000000000000000000056bc75e2d63100000000b0e54657374206d6573736167652031\",\"hashOfBlobToSign\":" - + "\"46a20c3ddd56a0fbac7622c52f26753ffacc5c5bf243f901c7210394c8d55198\"}},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; - private static final String FINALIZE_TRANSACTION = "{\"result\":{\"blob\":\"06407074cfe7b33d7e01c317eee743d33a9523" - + "60eb1c7ae64ab9caeb8d975329b30000000501210000000000000000000000000000000000000000000000000001079c81c25580000" - + "20500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000" - + "00000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f28" - + "15b16f81798010000000000000000000000000000000000000000033b2e373301858dc29a80000205000403fff97bd5755eeea42045" - + "3a14355235d382f6472f8568a18b2f057a1460297556010000000000000000000000000000000000000000000000056bc75e2d63100" - + "000000b0e54657374206d65737361676520310a00c07adf9012c81fed4205f14b7d7756808fecbf4615e39ad5b74c97057c532fb000" - + "6e798ed8aa457afa82908c0492d6e086d105374623b1ae430be39b4dd6bc96\",\"txID\":\"b3b2c41c08b4b93d533c824b015f6e1" - + "1e3370f1aeafb0116ee44aa3f4f442f37\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; - - private final HttpClient client = mock(HttpClient.class); - - @Test - public void testAccountInfo() throws IOException { - var accountAddress = AccountAddress.create( - ACCOUNTS.parseOrThrow("ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj", IllegalStateException::new) - ); - - prepareClient(ACCOUNT_INFO) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().accountInfo().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(localAccount -> assertEquals(accountAddress, localAccount.getAddress())) - .onSuccess(localAccount -> assertEquals(1, localAccount.getBalance().getTokens().size()))); - } - - @Test - public void testValidatorInfo() throws IOException { - prepareClient(VALIDATOR_INFO) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().validatorInfo().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(localValidatorInfo -> assertEquals(1, localValidatorInfo.getEpochInfo().getCurrent().getStakes().size())) - .onSuccess(localValidatorInfo -> assertTrue(localValidatorInfo.getEpochInfo().getCurrent().isRegistered()))); - } - - @Test - public void testCurrentEpoch() throws IOException { - prepareClient(CURRENT_EPOCH) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().currentEpoch().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(epochData -> assertEquals(2, epochData.getValidators().size()))); - } - - @Test - public void testSubmitTxSingleStep() throws IOException { - prepareClient(ACCOUNT_INFO, BUILD_TRANSACTION, FINALIZE_TRANSACTION, ACCOUNT_INFO, SINGLE_STEP) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().accountInfo().join().onSuccess(account -> transferFunds(client, account.getAddress()))) - .onSuccess(client -> client.local().accountInfo().join() - .map(account -> TransactionRequest.createBuilder(account.getAddress()) - .transfer(account.getAddress(), ACCOUNT_ADDRESS2, amount(5).tokens(), "xrd_dr1qyrs8qwl") - .message("Test message 2") - .build()) - .flatMap(request -> client.local().submitTxSingleStep(request).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(txData -> assertNotNull(txData.getTxId())))); - } - - private void transferFunds(RadixApi client, AccountAddress address) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - address, - amount(100).tokens(), - "xrd_dr1qyrs8qwl" - ) - .message("Test message 1") - .build(); - - client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .flatMap(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true).join()) - .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())); - } - - private Promise prepareClient(String... responseBodies) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); - - when(response.body()).thenReturn(NETWORK_ID, responseBodies); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); - - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } - - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; - - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + private static final String BASE_URL = "http://localhost/"; + + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String ACCOUNT_INFO = + "{\"result\":{\"address\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj\",\"balance\":{\"stakes\":[],\"tokens\":[{\"amount\":\"1000000000000000000000000000\",\"rri\":\"xrd_dr1qyrs8qwl\"}],\"preparedStakes\":[]}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String VALIDATOR_INFO = + "{\"result\":{\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"epochInfo\":{\"current\":{\"owner\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"stakes\":[{\"amount\":\"3949310000000000000000000\",\"delegator\":\"ddx1qsprdptw48agcfp7gh7ffmp8c2w08ut7820pnfqsae9yray93cmejxqkgsmrs\"}],\"validatorFee\":\"0.0\",\"registered\":true,\"totalStake\":\"3949310000000000000000000\",\"proposalsCompleted\":4111},\"updates\":{}},\"allowDelegation\":true,\"name\":\"\",\"url\":\"\"},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String CURRENT_EPOCH = + "{\"result\":{\"validators\":[{\"totalDelegatedStake\":\"5201130000000000000000000\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"address\":\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnumc3jtmxl\",\"proposalsCompleted\":513},{\"totalDelegatedStake\":\"5199060000000000000000000\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"proposalsCompleted\":511}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + + private static final String SINGLE_STEP = + "{\"result\":{\"txID\":\"c4741a62a721885dc3523afbf0297011671d8ce8969885b" + + "c0f6a6ffde9e39235\"},\"id\":\"6\",\"jsonrpc\":\"2.0\"}"; + private static final String BUILD_TRANSACTION = + "{\"result\":{\"fee\":\"74200000000000000\",\"transaction\":{\"blo" + + "b\":\"06407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b300000005012100000000000000000000000" + + "00000000000000000000000000001079c81c2558000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f281" + + "5b16f81798010000000000000000000000000000000000000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef" + + "9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3733" + + "01858dc29a80000205000403fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975560100000000000000000" + + "00000000000000000000000000000056bc75e2d63100000000b0e54657374206d6573736167652031\",\"hashOfBlobToSign\":" + + "\"46a20c3ddd56a0fbac7622c52f26753ffacc5c5bf243f901c7210394c8d55198\"}},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; + private static final String FINALIZE_TRANSACTION = + "{\"result\":{\"blob\":\"06407074cfe7b33d7e01c317eee743d33a9523" + + "60eb1c7ae64ab9caeb8d975329b30000000501210000000000000000000000000000000000000000000000000001079c81c25580000" + + "20500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000" + + "00000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f28" + + "15b16f81798010000000000000000000000000000000000000000033b2e373301858dc29a80000205000403fff97bd5755eeea42045" + + "3a14355235d382f6472f8568a18b2f057a1460297556010000000000000000000000000000000000000000000000056bc75e2d63100" + + "000000b0e54657374206d65737361676520310a00c07adf9012c81fed4205f14b7d7756808fecbf4615e39ad5b74c97057c532fb000" + + "6e798ed8aa457afa82908c0492d6e086d105374623b1ae430be39b4dd6bc96\",\"txID\":\"b3b2c41c08b4b93d533c824b015f6e1" + + "1e3370f1aeafb0116ee44aa3f4f442f37\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; + + private final HttpClient client = mock(HttpClient.class); + + @Test + public void testAccountInfo() throws IOException { + var accountAddress = + AccountAddress.create( + ACCOUNTS.parseOrThrow( + "ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj", + IllegalStateException::new)); + + prepareClient(ACCOUNT_INFO) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .accountInfo() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + localAccount -> assertEquals(accountAddress, localAccount.getAddress())) + .onSuccess( + localAccount -> + assertEquals(1, localAccount.getBalance().getTokens().size()))); + } + + @Test + public void testValidatorInfo() throws IOException { + prepareClient(VALIDATOR_INFO) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .validatorInfo() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + localValidatorInfo -> + assertEquals( + 1, + localValidatorInfo.getEpochInfo().getCurrent().getStakes().size())) + .onSuccess( + localValidatorInfo -> + assertTrue( + localValidatorInfo.getEpochInfo().getCurrent().isRegistered()))); + } + + @Test + public void testCurrentEpoch() throws IOException { + prepareClient(CURRENT_EPOCH) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .currentEpoch() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(epochData -> assertEquals(2, epochData.getValidators().size()))); + } + + @Test + public void testSubmitTxSingleStep() throws IOException { + prepareClient(ACCOUNT_INFO, BUILD_TRANSACTION, FINALIZE_TRANSACTION, ACCOUNT_INFO, SINGLE_STEP) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .accountInfo() + .join() + .onSuccess(account -> transferFunds(client, account.getAddress()))) + .onSuccess( + client -> + client + .local() + .accountInfo() + .join() + .map( + account -> + TransactionRequest.createBuilder(account.getAddress()) + .transfer( + account.getAddress(), + ACCOUNT_ADDRESS2, + amount(5).tokens(), + "xrd_dr1qyrs8qwl") + .message("Test message 2") + .build()) + .flatMap( + request -> + client + .local() + .submitTxSingleStep(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(txData -> assertNotNull(txData.getTxId())))); + } + + private void transferFunds(RadixApi client, AccountAddress address) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, address, amount(100).tokens(), "xrd_dr1qyrs8qwl") + .message("Test message 1") + .build(); + + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .flatMap( + finalizedTransaction -> + client.transaction().finalize(finalizedTransaction, true).join()) + .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())); + } + + private Promise prepareClient(String... responseBodies) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); + + when(response.body()).thenReturn(NETWORK_ID, responseBodies); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); + + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } + + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; + + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiMempoolTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiMempoolTest.java index 9dec5b3d91..c661f2c285 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiMempoolTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiMempoolTest.java @@ -61,69 +61,85 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; public class AsyncRadixApiMempoolTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"throttleMs\":5,\"maxSize\":10000},\"id\":\"2\"," - + "\"jsonrpc\":\"2.0\"}"; - private static final String DATA = "{\"result\":{\"addSuccess\":1273473,\"maxcount\":0,\"relayerSentCount\":0," - + "\"proposedTransaction\":0,\"count\":0,\"errors\":{\"other\":0,\"hook\":3,\"conflict\":0}},\"id\":\"2\"," - + "\"jsonrpc\":\"2.0\"}"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"throttleMs\":5,\"maxSize\":10000},\"id\":\"2\"," + "\"jsonrpc\":\"2.0\"}"; + private static final String DATA = + "{\"result\":{\"addSuccess\":1273473,\"maxcount\":0,\"relayerSentCount\":0," + + "\"proposedTransaction\":0,\"count\":0,\"errors\":{\"other\":0,\"hook\":3,\"conflict\":0}},\"id\":\"2\"," + + "\"jsonrpc\":\"2.0\"}"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws IOException { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.mempool().configuration().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configuration -> assertEquals(10000L, configuration.getMaxSize())) - .onSuccess(configuration -> assertEquals(5L, configuration.getThrottleMs()))); - } + @Test + public void testConfiguration() throws IOException { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .mempool() + .configuration() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(configuration -> assertEquals(10000L, configuration.getMaxSize())) + .onSuccess(configuration -> assertEquals(5L, configuration.getThrottleMs()))); + } - @Test - public void testData() throws IOException { - prepareClient(DATA) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.mempool().data().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertEquals(1273473L, data.getAddSuccess())) - .onSuccess(data -> assertEquals(3L, data.getErrors().getHook()))); - } + @Test + public void testData() throws IOException { + prepareClient(DATA) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .mempool() + .data() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertEquals(1273473L, data.getAddSuccess())) + .onSuccess(data -> assertEquals(3L, data.getErrors().getHook()))); + } - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiSyncTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiSyncTest.java index 88c82dcb26..8e2953f826 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiSyncTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiSyncTest.java @@ -61,70 +61,85 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; public class AsyncRadixApiSyncTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"maxLedgerUpdatesRate\":50,\"syncCheckMaxPeers\":10," - + "\"ledgerStatusUpdateMaxPeersToNotify\":10,\"syncCheckInterval\":3000,\"requestTimeout\":5000},\"id\":\"2\"," - + "\"jsonrpc\":\"2.0\"}\n"; - private static final String DATA = "{\"result\":{\"processed\":36898,\"invalidCommandsReceived\":0,\"targetCurrent" - + "Diff\":0,\"remoteRequestsProcessed\":38614,\"lastReadMillis\":0,\"targetStateVersion\":814181},\"id\":\"2\"" - + ",\"jsonrpc\":\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"maxLedgerUpdatesRate\":50,\"syncCheckMaxPeers\":10,\"ledgerStatusUpdateMaxPeersToNotify\":10,\"syncCheckInterval\":3000,\"requestTimeout\":5000},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String DATA = + "{\"result\":{\"processed\":36898,\"invalidCommandsReceived\":0,\"targetCurrentDiff\":0,\"remoteRequestsProcessed\":38614,\"lastReadMillis\":0,\"targetStateVersion\":814181},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws IOException { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.sync().configuration().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configuration -> assertEquals(50L, configuration.getMaxLedgerUpdatesRate())) - .onSuccess(configuration -> assertEquals(3000, configuration.getSyncCheckInterval()))); - } + @Test + public void testConfiguration() throws IOException { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .sync() + .configuration() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + configuration -> assertEquals(50L, configuration.getMaxLedgerUpdatesRate())) + .onSuccess( + configuration -> assertEquals(3000, configuration.getSyncCheckInterval()))); + } - @Test - public void testData() throws IOException { - prepareClient(DATA) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.sync().data().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertEquals(36898L, data.getProcessed())) - .onSuccess(data -> assertEquals(814181L, data.getTargetStateVersion()))); - } + @Test + public void testData() throws IOException { + prepareClient(DATA) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .sync() + .data() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertEquals(36898L, data.getProcessed())) + .onSuccess(data -> assertEquals(814181L, data.getTargetStateVersion()))); + } - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTest.java index a3b2fed77d..cfb83af481 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTest.java @@ -61,10 +61,14 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Ignore; -import org.junit.Test; +import static com.radixdlt.client.lib.api.async.RadixApi.connect; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -75,219 +79,308 @@ import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; +import org.junit.Ignore; +import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import static com.radixdlt.client.lib.api.async.RadixApi.connect; -import static com.radixdlt.client.lib.api.token.Amount.amount; - -//TODO: move to acceptance tests and repurpose to integration testing of the API's. +// TODO: move to acceptance tests and repurpose to integration testing of the API's. public class AsyncRadixApiTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); - @Test - @Ignore("Online test") - public void testBuildTransactionWithMessage() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - ACCOUNT_ADDRESS2, - UInt256.NINE, - "xrd_dr1qyrs8qwl" - ) - .message("Test message") - .build(); + @Test + @Ignore("Online test") + public void testBuildTransactionWithMessage() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.NINE, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().build(request) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransactionDTO -> assertEquals(UInt256.from(100000000000000000L), builtTransactionDTO.getFee())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false) - .join() - .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId()))))); - } + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransactionDTO -> + assertEquals( + UInt256.from(100000000000000000L), builtTransactionDTO.getFee())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .join() + .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), + txDTO.getTxId()))))); + } - @Test - @Ignore("Online test") - public void addManyTransactions() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> { - for (int i = 0; i < 20; i++) { - addTransaction(client, UInt256.from(i + 10)); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - } + @Test + @Ignore("Online test") + public void addManyTransactions() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + for (int i = 0; i < 20; i++) { + addTransaction(client, UInt256.from(i + 10)); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + } - @Test - @Ignore("Online test") - public void listStakes() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().stakes(ACCOUNT_ADDRESS1) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(stakePositionsDTOS -> System.out.println("Stake positions: " + stakePositionsDTOS.toString()))); - } + @Test + @Ignore("Online test") + public void listStakes() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .stakes(ACCOUNT_ADDRESS1) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + stakePositionsDTOS -> + System.out.println( + "Stake positions: " + stakePositionsDTOS.toString()))); + } - @Test - @Ignore("Online test") - public void listUnStakes() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().unstakes(ACCOUNT_ADDRESS1) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(unstakePositionsDTOS -> System.out.println("UnStake positions: " + unstakePositionsDTOS.toString()))); - } + @Test + @Ignore("Online test") + public void listUnStakes() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .unstakes(ACCOUNT_ADDRESS1) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + unstakePositionsDTOS -> + System.out.println( + "UnStake positions: " + unstakePositionsDTOS.toString()))); + } - @Test - @Ignore("Online test") - public void makeStake() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> makeStake(client, amount(200).tokens())); - } + @Test + @Ignore("Online test") + public void makeStake() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> makeStake(client, amount(200).tokens())); + } - @Test - @Ignore("Online test") - public void makeUnStake() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> makeUnStake(client, amount(100).tokens())); - } + @Test + @Ignore("Online test") + public void makeUnStake() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> makeUnStake(client, amount(100).tokens())); + } - @Test - @Ignore("Online test") - public void transferUnStake() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> transferUnStake(client, amount(100).tokens())); - } + @Test + @Ignore("Online test") + public void transferUnStake() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> transferUnStake(client, amount(100).tokens())); + } - @Test - @Ignore("Online test") - public void tryBasicAuthentication() { - connect("https://rcnet.radixdlt.com", 443, 443, BasicAuth.with("admin", "86RVCjoogDJioMZZVYYlaSAk")) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.network().addressBook().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(System.out::println)); - } + @Test + @Ignore("Online test") + public void tryBasicAuthentication() { + connect( + "https://rcnet.radixdlt.com", + 443, + 443, + BasicAuth.with("admin", "86RVCjoogDJioMZZVYYlaSAk")) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .network() + .addressBook() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(System.out::println)); + } - private void transferUnStake(RadixApi client, UInt256 amount) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS2) - .transfer( - ACCOUNT_ADDRESS2, - ACCOUNT_ADDRESS1, - amount, - "xrd_dr1qyrs8qwl" - ) - .message("Test message") - .build(); + private void transferUnStake(RadixApi client, UInt256 amount) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS2) + .transfer(ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS1, amount, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); - client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR2)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false).join() - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId())))); - } + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR2)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .join() + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), txDTO.getTxId())))); + } - private void makeStake(RadixApi client, UInt256 amount) { - client.local().validatorInfo() - .join() - .map(account -> TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) - .build()) - .onSuccess(request -> client.transaction().build(request) - .join() - .onFailure(failure -> fail(failure.toString())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .flatMap(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true).join()) - .onSuccess(System.out::println)); - } + private void makeStake(RadixApi client, UInt256 amount) { + client + .local() + .validatorInfo() + .join() + .map( + account -> + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) + .build()) + .onSuccess( + request -> + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .flatMap( + finalizedTransaction -> + client.transaction().finalize(finalizedTransaction, true).join()) + .onSuccess(System.out::println)); + } - private void makeUnStake(RadixApi client, UInt256 amount) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .unstake(ACCOUNT_ADDRESS1, ValidatorAddress.of(KEY_PAIR2.getPublicKey()), amount) - .build(); + private void makeUnStake(RadixApi client, UInt256 amount) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .unstake(ACCOUNT_ADDRESS1, ValidatorAddress.of(KEY_PAIR2.getPublicKey()), amount) + .build(); - client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false).join() - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId())))); - } + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .join() + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), txDTO.getTxId())))); + } - private void addTransaction(RadixApi client, UInt256 amount) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - ACCOUNT_ADDRESS2, - amount, - "xrd_dr1qyrs8qwl" - ) - .message("Test message") - .build(); + private void addTransaction(RadixApi client, UInt256 amount) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); - client.transaction().build(request).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransactionDTO -> assertEquals(amount(73800L).micros(), builtTransactionDTO.getFee())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false).join() - .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId())))); - } + client + .transaction() + .build(request) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransactionDTO -> + assertEquals(amount(73800L).micros(), builtTransactionDTO.getFee())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .join() + .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), txDTO.getTxId())))); + } - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTokenTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTokenTest.java index f092e44b4b..8566255faf 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTokenTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTokenTest.java @@ -61,67 +61,81 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.async; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.utils.functional.Promise; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; public class AsyncRadixApiTokenTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String NATIVE_TOKEN = "{\"result\":{\"tokenInfoURL\":\"\",\"symbol\":\"xrd\"," - + "\"isSupplyMutable\":true,\"granularity\":\"1\",\"totalBurned\":\"0\",\"name\":\"Rads\"," - + "\"rri\":\"xrd_dr1qyrs8qwl\",\"description\":\"Radix Tokens\",\"iconURL\":\"\"," - + "\"currentSupply\":\"8000161536692300000000000000\",\"totalMinted\":\"8000161536692300000000000000\"}," - + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String NATIVE_TOKEN = + "{\"result\":{\"tokenInfoURL\":\"\",\"symbol\":\"xrd\"," + + "\"isSupplyMutable\":true,\"granularity\":\"1\",\"totalBurned\":\"0\",\"name\":\"Rads\",\"rri\":\"xrd_dr1qyrs8qwl\",\"description\":\"Radix" + + " Tokens\",\"iconURL\":\"\",\"currentSupply\":\"8000161536692300000000000000\",\"totalMinted\":\"8000161536692300000000000000\"},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testNativeToken() throws IOException { - prepareClient(NATIVE_TOKEN) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.token().describeNative().join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); - } + @Test + public void testNativeToken() throws IOException { + prepareClient(NATIVE_TOKEN) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .token() + .describeNative() + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); + } - @Test - public void testTokenInfo() throws IOException { - prepareClient(NATIVE_TOKEN) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.token().describe("xrd_dr1qyrs8qwl").join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); - } + @Test + public void testTokenInfo() throws IOException { + prepareClient(NATIVE_TOKEN) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .token() + .describe("xrd_dr1qyrs8qwl") + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); + } - private Promise prepareClient(String responseBody) throws IOException { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - var completableFuture = new CompletableFuture>(); + private Promise prepareClient(String responseBody) throws IOException { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + var completableFuture = new CompletableFuture>(); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.sendAsync(any(), any())).thenReturn(completableFuture); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.sendAsync(any(), any())).thenReturn(completableFuture); - completableFuture.completeAsync(() -> response); - return AsyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + completableFuture.completeAsync(() -> response); + return AsyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTransactionPaginationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTransactionPaginationTest.java index cbded44d4e..055f778a94 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTransactionPaginationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/async/AsyncRadixApiTransactionPaginationTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,18 +64,16 @@ package com.radixdlt.client.lib.api.async; -import org.junit.Ignore; -import org.junit.Test; +import static org.junit.Assert.fail; import com.radixdlt.client.lib.dto.TransactionDTO; import com.radixdlt.client.lib.dto.TransactionsDTO; - import java.util.List; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; - -import static org.junit.Assert.fail; +import org.junit.Ignore; +import org.junit.Test; /* * Before running this test, launch in separate console local network (cd radixdlt-core/docker && ./scripts/rundocker.sh 2). @@ -85,42 +84,46 @@ * * Then run testTransactionHistoryInPages(). It should print list of transactions split into batches of 50 (see parameters) */ -//TODO: move to acceptance tests +// TODO: move to acceptance tests public class AsyncRadixApiTransactionPaginationTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - @Test - @Ignore("Online test") - public void testTransactionHistoryInPages() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> { - var cursorHolder = new AtomicReference<>(OptionalLong.empty()); - do { - client.transaction().list(100, cursorHolder.get()).join() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(v -> cursorHolder.set(v.getNextOffset())) - .map(TransactionsDTO::getTransactions) - .map(this::formatTxns) - .onSuccess(System.out::println); - } while (cursorHolder.get().isPresent()); - }); - } + @Test + @Ignore("Online test") + public void testTransactionHistoryInPages() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + var cursorHolder = new AtomicReference<>(OptionalLong.empty()); + do { + client + .transaction() + .list(100, cursorHolder.get()) + .join() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(v -> cursorHolder.set(v.getNextOffset())) + .map(TransactionsDTO::getTransactions) + .map(this::formatTxns) + .onSuccess(System.out::println); + } while (cursorHolder.get().isPresent()); + }); + } - private List formatTxns(List t) { - return t.stream() - .map(v -> String.format( - "%s (%s) - %s (%d:%d), Fee: %s%n", - v.getTxID(), - v.getMessage().orElse(""), - v.getSentAt().getInstant(), - v.getSentAt().getInstant().getEpochSecond(), - v.getSentAt().getInstant().getNano(), - v.getFee() - )) - .collect(Collectors.toList()); - } + private List formatTxns(List t) { + return t.stream() + .map( + v -> + String.format( + "%s (%s) - %s (%d:%d), Fee: %s%n", + v.getTxID(), + v.getMessage().orElse(""), + v.getSentAt().getInstant(), + v.getSentAt().getInstant().getEpochSecond(), + v.getSentAt().getInstant().getNano(), + v.getFee())) + .collect(Collectors.toList()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiAccountTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiAccountTest.java index 3541e452b3..146b30755f 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiAccountTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiAccountTest.java @@ -61,10 +61,15 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.sync; -import org.junit.Ignore; -import org.junit.Test; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -76,175 +81,215 @@ import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Result; - import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; +import org.junit.Ignore; +import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +public class SyncRadixApiAccountTest { + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); -import static com.radixdlt.client.lib.api.token.Amount.amount; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String TOKEN_BALANCES = + "{\"result\":{\"owner\":\"ddx1qsp8n0nx0muaewav2ksx99wwsu9swq5mlndjmn3gm9vl9q2mzmup0xq904xyj\",\"tokenBalances\":[{\"amount\":\"1000000000000000000000000000\",\"rri\":\"xrd_dr1qyrs8qwl\"}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String TX_HISTORY = + "{\"result\":{\"cursor\":\"1577836:800000000\",\"transactions\":[{\"fee\":\"0\",\"txID\":\"407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b3\",\"sentAt\":\"1970-01-19T06:17:16.800Z\",\"actions\":[{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"amount\":\"100000000000000000000\",\"validator\":\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnumc3jtmxl\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"StakeTokens\"},{\"amount\":\"100000000000000000000\",\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"StakeTokens\"},{\"type\":\"Other\"}]}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String ERROR_RESPONSE = + "{\"id\":\"2\",\"jsonrpc\":\"2.0\",\"error\":{\"code\":2523,\"data\":[\"0000000000000000000000000000000000000000000000000000000000000000\"],\"message\":\"Transaction" + + " with id 0000000000000000000000000000000000000000000000000000000000000000 not" + + " found\"}}\n"; -public class SyncRadixApiAccountTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String TOKEN_BALANCES = "{\"result\":{\"owner\":\"ddx1qsp8n0nx0muaewav2ksx99wwsu9swq5mlndjmn3gm" - + "9vl9q2mzmup0xq904xyj\",\"tokenBalances\":[{\"amount\":\"1000000000000000000000000000\",\"rri\":\"xrd_dr1qyrs8" - + "qwl\"}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String TX_HISTORY = "{\"result\":{\"cursor\":\"1577836:800000000\",\"transactions\":[{\"fee\":" - + "\"0\",\"txID\":\"407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b3\",\"sentAt\":\"1970-01-19T" - + "06:17:16.800Z\",\"actions\":[{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Othe" - + "r\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"" - + "},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{\"type\":\"Other\"},{" - + "\"amount\":\"100000000000000000000\",\"validator\":\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnum" - + "c3jtmxl\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"StakeTok" - + "ens\"},{\"amount\":\"100000000000000000000\",\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh" - + "59rq9964vjryzf9\",\"from\":\"ddx1qspzsu73jt6ps6g8l0rj2yya2euunqapv7j2qemgaaujyej2tlp3lcs99m6k9\",\"type\":\"" - + "StakeTokens\"},{\"type\":\"Other\"}]}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String ERROR_RESPONSE = "{\"id\":\"2\",\"jsonrpc\":\"2.0\",\"error\":{\"code\":2523,\"data\":" - + "[\"0000000000000000000000000000000000000000000000000000000000000000\"],\"message\":\"Transaction with id 00" - + "00000000000000000000000000000000000000000000000000000000000000 not found\"}}\n"; - - private static final String STAKES_RESPONSE = "{\"result\":[{\"amount\":\"3455000000000000000000\",\"validator\":" - + "\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - - private static final String UNSTAKES_RESPONSE = "{\"result\":[{\"amount\":\"195000000000000000000\",\"epochsUntil\":" - + "500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"},{\"amount\":" - + "\"300000000000000000000\",\"validatorTotalOwnership\":\"107734488884306251968\",\"epochsUntil\":" - + "500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"validatorTotalStake\":" - + "\"24455003291307584010164\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - - private final HttpClient client = mock(HttpClient.class); - - @Test - public void testTokenBalances() throws Exception { - prepareClient(TOKEN_BALANCES) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().balances(ACCOUNT_ADDRESS1) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(tokenBalancesDTO -> assertEquals(ACCOUNT_ADDRESS1, tokenBalancesDTO.getOwner())) - .map(TokenBalances::getTokenBalances) - .onSuccess(balances -> assertEquals(1, balances.size()))); - } - - @Test - public void testErrorResponse() throws Exception { - prepareClient(ERROR_RESPONSE) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.transaction().lookup(AID.ZERO) - .onFailure(failure -> assertEquals(2523, failure.code())) - .onSuccess(__ -> fail())); - } - - @Test - public void listStakes() throws Exception { - prepareClient(STAKES_RESPONSE) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().stakes(ACCOUNT_ADDRESS1) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(stakePositionsDTOS -> assertEquals(1, stakePositionsDTOS.size())) - .onSuccess(stakePositionsDTOS -> assertEquals(amount(3455).tokens(), stakePositionsDTOS.get(0).getAmount()))); - } - - @Test - public void listUnStakes() throws Exception { - prepareClient(UNSTAKES_RESPONSE) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.account().unstakes(ACCOUNT_ADDRESS1) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(unstakePositionsDTOS -> assertEquals(2, unstakePositionsDTOS.size())) - .onSuccess(unstakePositionsDTOS -> assertEquals(amount(195).tokens(), unstakePositionsDTOS.get(0).getAmount())) - .onSuccess(unstakePositionsDTOS -> assertEquals(amount(300).tokens(), unstakePositionsDTOS.get(1).getAmount()))); - } - - @Test - @Ignore("Online test") - public void makeStake() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> makeStake(client, amount(1000).tokens())); - } - - @Test - @Ignore("Online test") - public void makeUnStake() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> makeUnStake(client, amount(100).tokens())); - } - - @Test - @Ignore("Online test") - public void transferUnStake() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> transferUnStake(client, amount(100).tokens())); - } - - private void transferUnStake(RadixApi client, UInt256 amount) { - client.local().accountInfo() - .map(account -> TransactionRequest.createBuilder(account.getAddress()) - .transfer(account.getAddress(), ACCOUNT_ADDRESS1, amount, "xrd_dr1qyrs8qwl") - .build()) - .flatMap(request -> client.local().submitTxSingleStep(request) - .onFailure(failure -> fail(failure.toString()))); - } - - private void makeStake(RadixApi client, UInt256 amount) { - client.local().validatorInfo() - .map(account -> TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) - .build()) - .flatMap(request -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess(transaction -> client.transaction().finalize(transaction, true))); - } - - private void makeUnStake(RadixApi client, UInt256 amount) { - client.local().validatorInfo() - .map(account -> TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .unstake(ACCOUNT_ADDRESS1, account.getAddress(), amount) - .build()) - .flatMap(request -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess(transaction -> client.transaction().finalize(transaction, true))); - } - - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); - - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } - - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; - - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + private static final String STAKES_RESPONSE = + "{\"result\":[{\"amount\":\"3455000000000000000000\",\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + + private static final String UNSTAKES_RESPONSE = + "{\"result\":[{\"amount\":\"195000000000000000000\",\"epochsUntil\":500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"},{\"amount\":\"300000000000000000000\",\"validatorTotalOwnership\":\"107734488884306251968\",\"epochsUntil\":500,\"validator\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"validatorTotalStake\":\"24455003291307584010164\"}],\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + + private final HttpClient client = mock(HttpClient.class); + + @Test + public void testTokenBalances() throws Exception { + prepareClient(TOKEN_BALANCES) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .balances(ACCOUNT_ADDRESS1) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + tokenBalancesDTO -> + assertEquals(ACCOUNT_ADDRESS1, tokenBalancesDTO.getOwner())) + .map(TokenBalances::getTokenBalances) + .onSuccess(balances -> assertEquals(1, balances.size()))); + } + + @Test + public void testErrorResponse() throws Exception { + prepareClient(ERROR_RESPONSE) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .lookup(AID.ZERO) + .onFailure(failure -> assertEquals(2523, failure.code())) + .onSuccess(__ -> fail())); + } + + @Test + public void listStakes() throws Exception { + prepareClient(STAKES_RESPONSE) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .stakes(ACCOUNT_ADDRESS1) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(stakePositionsDTOS -> assertEquals(1, stakePositionsDTOS.size())) + .onSuccess( + stakePositionsDTOS -> + assertEquals( + amount(3455).tokens(), stakePositionsDTOS.get(0).getAmount()))); + } + + @Test + public void listUnStakes() throws Exception { + prepareClient(UNSTAKES_RESPONSE) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .account() + .unstakes(ACCOUNT_ADDRESS1) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(unstakePositionsDTOS -> assertEquals(2, unstakePositionsDTOS.size())) + .onSuccess( + unstakePositionsDTOS -> + assertEquals( + amount(195).tokens(), unstakePositionsDTOS.get(0).getAmount())) + .onSuccess( + unstakePositionsDTOS -> + assertEquals( + amount(300).tokens(), unstakePositionsDTOS.get(1).getAmount()))); + } + + @Test + @Ignore("Online test") + public void makeStake() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> makeStake(client, amount(1000).tokens())); + } + + @Test + @Ignore("Online test") + public void makeUnStake() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> makeUnStake(client, amount(100).tokens())); + } + + @Test + @Ignore("Online test") + public void transferUnStake() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(client -> transferUnStake(client, amount(100).tokens())); + } + + private void transferUnStake(RadixApi client, UInt256 amount) { + client + .local() + .accountInfo() + .map( + account -> + TransactionRequest.createBuilder(account.getAddress()) + .transfer(account.getAddress(), ACCOUNT_ADDRESS1, amount, "xrd_dr1qyrs8qwl") + .build()) + .flatMap( + request -> + client + .local() + .submitTxSingleStep(request) + .onFailure(failure -> fail(failure.toString()))); + } + + private void makeStake(RadixApi client, UInt256 amount) { + client + .local() + .validatorInfo() + .map( + account -> + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) + .build()) + .flatMap( + request -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess(transaction -> client.transaction().finalize(transaction, true))); + } + + private void makeUnStake(RadixApi client, UInt256 amount) { + client + .local() + .validatorInfo() + .map( + account -> + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .unstake(ACCOUNT_ADDRESS1, account.getAddress(), amount) + .build()) + .flatMap( + request -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess(transaction -> client.transaction().finalize(transaction, true))); + } + + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); + + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } + + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; + + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiApiTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiApiTest.java index d4a3434f0e..cd1524561b 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiApiTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiApiTest.java @@ -61,15 +61,8 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ -package com.radixdlt.client.lib.api.sync; - -import org.junit.Test; - -import com.radixdlt.utils.functional.Result; -import java.net.http.HttpClient; -import java.net.http.HttpResponse; -import java.util.Optional; +package com.radixdlt.client.lib.api.sync; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -78,54 +71,75 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.radixdlt.utils.functional.Result; +import java.net.http.HttpClient; +import java.net.http.HttpResponse; +import java.util.Optional; +import org.junit.Test; + public class SyncRadixApiApiTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"endpoints\":[\"/metrics\",\"/system\"," - + "\"/account\",\"/validation\",\"/universe\",\"/faucet\",\"/chaos\",\"/health\",\"/version\"," - + "\"/developer\",\"/archive\",\"/construction\"]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String DATA = "{\"result\":{\"elapsed\":{\"apidb\":{\"balance\":{\"read\":1672,\"" - + "write\":4790},\"flush\":{\"time\":630722},\"transaction\":{\"read\":0,\"write\":1453},\"token\":" - + "{\"read\":134,\"write\":842}}},\"count\":{\"apidb\":{\"flush\":{\"count\":1627},\"balance\":{\"t" - + "otal\":50,\"read\":26,\"bytes\":{\"read\":1532,\"write\":4263},\"write\":24},\"queue\":{\"size\"" - + ":6},\"transaction\":{\"total\":7,\"read\":0,\"bytes\":{\"read\":0,\"write\":13923},\"write\":7}," - + "\"token\":{\"total\":2,\"read\":1,\"bytes\":{\"read\":245,\"write\":245},\"write\":1}}}},\"id\":" - + "\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"endpoints\":[\"/metrics\",\"/system\",\"/account\",\"/validation\",\"/universe\",\"/faucet\",\"/chaos\",\"/health\",\"/version\",\"/developer\",\"/archive\",\"/construction\"]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String DATA = + "{\"result\":{\"elapsed\":{\"apidb\":{\"balance\":{\"read\":1672,\"write\":4790},\"flush\":{\"time\":630722},\"transaction\":{\"read\":0,\"write\":1453},\"token\":{\"read\":134,\"write\":842}}},\"count\":{\"apidb\":{\"flush\":{\"count\":1627},\"balance\":{\"total\":50,\"read\":26,\"bytes\":{\"read\":1532,\"write\":4263},\"write\":24},\"queue\":{\"size\":6},\"transaction\":{\"total\":7,\"read\":0,\"bytes\":{\"read\":0,\"write\":13923},\"write\":7},\"token\":{\"total\":2,\"read\":1,\"bytes\":{\"read\":245,\"write\":245},\"write\":1}}}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws Exception { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.api().configuration() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configurationDTO -> assertEquals(12, configurationDTO.getEndpoints().size()))); - } + @Test + public void testConfiguration() throws Exception { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .api() + .configuration() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + configurationDTO -> + assertEquals(12, configurationDTO.getEndpoints().size()))); + } - @Test - public void testData() throws Exception { - prepareClient(DATA) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.api().data() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertNotNull(data.getCount())) - .onSuccess(data -> assertNotNull(data.getElapsed())) - .onSuccess(data -> assertEquals(630722, data.getElapsed().getApiDb().getFlush().getTime())) - .onSuccess(data -> assertEquals(1672, data.getElapsed().getApiDb().getBalance().getRead())) - .onSuccess(data -> assertEquals(6, data.getCount().getApiDb().getQueue().getSize()))); - } + @Test + public void testData() throws Exception { + prepareClient(DATA) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .api() + .data() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertNotNull(data.getCount())) + .onSuccess(data -> assertNotNull(data.getElapsed())) + .onSuccess( + data -> + assertEquals(630722, data.getElapsed().getApiDb().getFlush().getTime())) + .onSuccess( + data -> + assertEquals(1672, data.getElapsed().getApiDb().getBalance().getRead())) + .onSuccess( + data -> assertEquals(6, data.getCount().getApiDb().getQueue().getSize()))); + } - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiConsensusTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiConsensusTest.java index b5784e7cbe..235b99133f 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiConsensusTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiConsensusTest.java @@ -61,15 +61,8 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ -package com.radixdlt.client.lib.api.sync; - -import org.junit.Test; - -import com.radixdlt.utils.functional.Result; -import java.net.http.HttpClient; -import java.net.http.HttpResponse; -import java.util.Optional; +package com.radixdlt.client.lib.api.sync; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -77,50 +70,69 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.radixdlt.utils.functional.Result; +import java.net.http.HttpClient; +import java.net.http.HttpResponse; +import java.util.Optional; +import org.junit.Test; + public class SyncRadixApiConsensusTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"pacemakerTimeout\":3000,\"bftSyncPatienceMs\":200}," - + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String DATA = "{\"result\":{\"timeoutQuorums\":1,\"rejected\":0,\"voteQuorums\":79981," - + "\"vertexStoreForks\":1,\"sync\":{\"requestsSent\":0,\"requestTimeouts\":0},\"timeout\":1,\"stateVers" - + "ion\":159960,\"processed\":159959,\"timedOutViews\":1,\"vertexStoreSize\":2,\"vertexStoreRebuilds\":" - + "0,\"consensusEvents\":319926,\"indirectParent\":1,\"proposalsMade\":79981},\"id\":\"2\",\"jsonrpc\":" - + "\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"pacemakerTimeout\":3000,\"bftSyncPatienceMs\":200}," + + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String DATA = + "{\"result\":{\"timeoutQuorums\":1,\"rejected\":0,\"voteQuorums\":79981,\"vertexStoreForks\":1,\"sync\":{\"requestsSent\":0,\"requestTimeouts\":0},\"timeout\":1,\"stateVersion\":159960,\"processed\":159959,\"timedOutViews\":1,\"vertexStoreSize\":2,\"vertexStoreRebuilds\":0,\"consensusEvents\":319926,\"indirectParent\":1,\"proposalsMade\":79981},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws Exception { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.consensus().configuration() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configuration -> assertEquals(200L, configuration.getBftSyncPatienceMs())) - .onSuccess(configuration -> assertEquals(3000L, configuration.getPacemakerTimeout()))); - } + @Test + public void testConfiguration() throws Exception { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .consensus() + .configuration() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + configuration -> assertEquals(200L, configuration.getBftSyncPatienceMs())) + .onSuccess( + configuration -> assertEquals(3000L, configuration.getPacemakerTimeout()))); + } - @Test - public void testData() throws Exception { - prepareClient(DATA) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.consensus().data() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertEquals(159959L, data.getProcessed())) - .onSuccess(data -> assertEquals(159960L, data.getStateVersion()))); - } + @Test + public void testData() throws Exception { + prepareClient(DATA) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .consensus() + .data() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertEquals(159959L, data.getProcessed())) + .onSuccess(data -> assertEquals(159960L, data.getStateVersion()))); + } - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiCreationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiCreationTest.java index 71b3eb967a..134a18af70 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiCreationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiCreationTest.java @@ -61,11 +61,16 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.sync; -import org.bouncycastle.util.encoders.Hex; -import org.junit.Ignore; -import org.junit.Test; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -81,235 +86,277 @@ import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Result; - import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Ignore; +import org.junit.Test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import static com.radixdlt.client.lib.api.token.Amount.amount; - -//TODO: test remaining actions!!! +// TODO: test remaining actions!!! public class SyncRadixApiCreationTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - public static final ECKeyPair KEY_PAIR3 = keyPairOf(3); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS3 = AccountAddress.create(KEY_PAIR3.getPublicKey()); - private static final ValidatorAddress VALIDATOR_ADDRESS = ValidatorAddress.of(KEY_PAIR3.getPublicKey()); - - private static final String NETWORK_ID = "{\"result\":{\"networkId\":2},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String BUILT_TRANSACTION = "{\"result\":{\"fee\":\"73800000000000000\",\"transaction\":{\"blob\":" - + "\"060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000" - + "000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817" - + "98010000000000000000000000000000000000000000033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a" - + "06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17f" - + "ec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000000000000000000000000000000" - + "00000000000000000000000000000000a000b0c54657374206d657373616765\",\"hashOfBlobToSign\":\"76448a9f09e5bb9fbce1" - + "1844731e8a7e28601733100787462401f47916bbc4ac\"}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}"; - - private static final String BLOB = "060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100" - + "000000000000000000000000000000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029" - + "bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17ff600070000000002" - + "0500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000" - + "00000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000" - + "00000000000000000000000000000000000000000000000000000000000a000b0c54657374206d657373616765"; - - private static final String TX_ID = "a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41a4272e38f1aa5"; - private static final String SIG = "3045022100f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb40220388" - + "dfd1e25b25a80366fc70415334d1e4af259aee5685e93a8cd03004e1a8157"; - private static final String PUB_KEY = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; - - private static final String FINALIZE_TRANSACTION = "{\"result\":{\"blob\":\"060a104c95b2f14ca1c6500b519ad696bee17b7a" - + "982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000000000000000000000000010630b5806c800002050" - + "0040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000000" - + "00033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81" - + "798010000000000000000000000000000000000000000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07c" - + "d85c778e4b8cef3ca7abac09b95c709ee501000000000000000000000000000000000000000000000000000000000000000a000b0c546" - + "57374206d6573736167650a01f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb4388dfd1e25b25a80366f" - + "c70415334d1e4af259aee5685e93a8cd03004e1a8157\",\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41" - + "a4272e38f1aa5\"},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; - private static final String SUBMIT_TRANSACTION = "{\"result\":{\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf4739" - + "2a2fd0e41a4272e38f1aa5\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; - - private final HttpClient client = mock(HttpClient.class); - - @Test - public void testBuildTransaction() throws Exception { - var hash = Hex.decode("76448a9f09e5bb9fbce11844731e8a7e28601733100787462401f47916bbc4ac"); - - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount(10).subunits(), "xrd_dr1qyrs8qwl") - .message("Test message") - .build(); - - prepareClient(BUILT_TRANSACTION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(dto -> assertEquals(amount(73800).micros(), dto.getFee())) - .onSuccess(dto -> assertArrayEquals(hash, dto.getTransaction().getHashToSign())) - ); - } - - @Test - public void testFinalizeTransaction() throws Exception { - var request = buildFinalizedTransaction(); - var txId = AID.from(TX_ID); - - prepareClient(FINALIZE_TRANSACTION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.transaction().finalize(request, false) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(dto -> assertEquals(txId, dto.getTxId())) - ); - } - - @Test - public void testSubmitTransaction() throws Exception { - var txId = AID.from(TX_ID); - var request = buildBlobDto(); - - prepareClient(SUBMIT_TRANSACTION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.transaction().submit(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(dto -> assertEquals(txId, dto.getTxId())) - ); - } - - @Test - @Ignore("Online test") - public void testBuildAndSubmitTransactionWithMessage() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.NINE, "xrd_dr1qyrs8qwl") - .message("Test message") - .build(); - - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransaction -> assertEquals(amount(73800).micros(), builtTransaction.getFee())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess( - finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true) - .onFailure(failure -> fail(failure.toString())) - ) - ); - } - - @Test - @Ignore("Online test") - public void testCreateFixedSupplyToken() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .createFixed(ACCOUNT_ADDRESS1, KEY_PAIR1.getPublicKey(), - "fix", "fix", "fix", - "https://some.host.com/", "https://some.other.host.com", - amount(1000).tokens() - ) - .build(); - - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.transaction() - .build(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - builtTransaction -> { - assertEquals(amount(100116600).micros(), builtTransaction.getFee()); - - var notifications = builtTransaction.getTransaction().getNotifications(); - assertEquals(1, notifications.size()); - assertEquals("TokenCreate", notifications.get(0).getType()); - assertEquals("fix", notifications.get(0).getSymbol().orElse("")); - assertEquals( - "fix_dr1qd5ah4xye2svl2smkk06st3q9lkqnkc6wgy0zxrrkmyszlawrz", - notifications.get(0).getRri().orElse("") - ); - }) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .onSuccess( - finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true) - .onFailure(failure -> fail(failure.toString())) - ) - ); - } - - @Test - @Ignore("Online test") - //TODO: for some reason operation succeeds only if transaction contains only one action - public void testRegisterValidator() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS3) - .registerValidator(VALIDATOR_ADDRESS, Optional.of("MyValidator"), Optional.of("http://my.validator.url.com/")) - .updateValidatorFee(VALIDATOR_ADDRESS, 3.1) - .updateValidatorOwner(VALIDATOR_ADDRESS, ACCOUNT_ADDRESS3) - .updateValidatorAllowDelegationFlag(VALIDATOR_ADDRESS, true) - .build(); - - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(builtTransaction -> assertEquals(amount(102600).micros(), builtTransaction.getFee())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR3)) - .onSuccess( - finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true) - .onFailure(failure -> fail(failure.toString())) - ) - ); - } - - private TxBlobDTO buildBlobDto() { - return TxBlobDTO.create(AID.from(TX_ID), BLOB); - } - - private FinalizedTransaction buildFinalizedTransaction() throws PublicKeyException { - var sig = ECDSASignature.decodeFromHexDer(SIG); - var publicKey = ECPublicKey.fromHex(PUB_KEY); - var blob = buildBlobDto(); - return FinalizedTransaction.create(blob.getBlob(), sig, publicKey, blob.getTxId()); - } - - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; - - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } - - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); - - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + public static final ECKeyPair KEY_PAIR3 = keyPairOf(3); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS3 = + AccountAddress.create(KEY_PAIR3.getPublicKey()); + private static final ValidatorAddress VALIDATOR_ADDRESS = + ValidatorAddress.of(KEY_PAIR3.getPublicKey()); + + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":2},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String BUILT_TRANSACTION = + "{\"result\":{\"fee\":\"73800000000000000\",\"transaction\":{\"blob\":" + + "\"060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000" + + "000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817" + + "98010000000000000000000000000000000000000000033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a" + + "06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17f" + + "ec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000000000000000000000000000000" + + "00000000000000000000000000000000a000b0c54657374206d657373616765\",\"hashOfBlobToSign\":\"76448a9f09e5bb9fbce1" + + "1844731e8a7e28601733100787462401f47916bbc4ac\"}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}"; + + private static final String BLOB = + "060a104c95b2f14ca1c6500b519ad696bee17b7a982810c5e4fe43d39b979bfbc300000001012100" + + "000000000000000000000000000000000000000000000000010630b5806c8000020500040279be667ef9dcbbac55a06295ce870b07029" + + "bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3730f52422c1c17ff600070000000002" + + "0500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000" + + "00000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5010000" + + "00000000000000000000000000000000000000000000000000000000000a000b0c54657374206d657373616765"; + + private static final String TX_ID = + "a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41a4272e38f1aa5"; + private static final String SIG = + "3045022100f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb40220388" + + "dfd1e25b25a80366fc70415334d1e4af259aee5685e93a8cd03004e1a8157"; + private static final String PUB_KEY = + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; + + private static final String FINALIZE_TRANSACTION = + "{\"result\":{\"blob\":\"060a104c95b2f14ca1c6500b519ad696bee17b7a" + + "982810c5e4fe43d39b979bfbc300000001012100000000000000000000000000000000000000000000000000010630b5806c800002050" + + "0040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000000000" + + "00033b2e3730f52422c1c17ff6000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81" + + "798010000000000000000000000000000000000000000033b2e3730f52422c1c17fec0205000402c6047f9441ed7d6d3045406e95c07c" + + "d85c778e4b8cef3ca7abac09b95c709ee501000000000000000000000000000000000000000000000000000000000000000a000b0c546" + + "57374206d6573736167650a01f179714d7577a105d0a37891bc149ed6ba519435dcc53340f0467611e0d31bb4388dfd1e25b25a80366f" + + "c70415334d1e4af259aee5685e93a8cd03004e1a8157\",\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf47392a2fd0e41" + + "a4272e38f1aa5\"},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; + private static final String SUBMIT_TRANSACTION = + "{\"result\":{\"txID\":\"a84843d8c51f92a872a926cd29a2074f1c85bf4739" + + "2a2fd0e41a4272e38f1aa5\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; + + private final HttpClient client = mock(HttpClient.class); + + @Test + public void testBuildTransaction() throws Exception { + var hash = Hex.decode("76448a9f09e5bb9fbce11844731e8a7e28601733100787462401f47916bbc4ac"); + + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount(10).subunits(), "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); + + prepareClient(BUILT_TRANSACTION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(dto -> assertEquals(amount(73800).micros(), dto.getFee())) + .onSuccess( + dto -> assertArrayEquals(hash, dto.getTransaction().getHashToSign()))); + } + + @Test + public void testFinalizeTransaction() throws Exception { + var request = buildFinalizedTransaction(); + var txId = AID.from(TX_ID); + + prepareClient(FINALIZE_TRANSACTION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .finalize(request, false) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(dto -> assertEquals(txId, dto.getTxId()))); + } + + @Test + public void testSubmitTransaction() throws Exception { + var txId = AID.from(TX_ID); + var request = buildBlobDto(); + + prepareClient(SUBMIT_TRANSACTION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .submit(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(dto -> assertEquals(txId, dto.getTxId()))); + } + + @Test + @Ignore("Online test") + public void testBuildAndSubmitTransactionWithMessage() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.NINE, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); + + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransaction -> + assertEquals(amount(73800).micros(), builtTransaction.getFee())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, true) + .onFailure(failure -> fail(failure.toString())))); + } + + @Test + @Ignore("Online test") + public void testCreateFixedSupplyToken() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .createFixed( + ACCOUNT_ADDRESS1, + KEY_PAIR1.getPublicKey(), + "fix", + "fix", + "fix", + "https://some.host.com/", + "https://some.other.host.com", + amount(1000).tokens()) + .build(); + + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransaction -> { + assertEquals(amount(100116600).micros(), builtTransaction.getFee()); + + var notifications = builtTransaction.getTransaction().getNotifications(); + assertEquals(1, notifications.size()); + assertEquals("TokenCreate", notifications.get(0).getType()); + assertEquals("fix", notifications.get(0).getSymbol().orElse("")); + assertEquals( + "fix_dr1qd5ah4xye2svl2smkk06st3q9lkqnkc6wgy0zxrrkmyszlawrz", + notifications.get(0).getRri().orElse("")); + }) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, true) + .onFailure(failure -> fail(failure.toString())))); + } + + @Test + @Ignore("Online test") + // TODO: for some reason operation succeeds only if transaction contains only one action + public void testRegisterValidator() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS3) + .registerValidator( + VALIDATOR_ADDRESS, + Optional.of("MyValidator"), + Optional.of("http://my.validator.url.com/")) + .updateValidatorFee(VALIDATOR_ADDRESS, 3.1) + .updateValidatorOwner(VALIDATOR_ADDRESS, ACCOUNT_ADDRESS3) + .updateValidatorAllowDelegationFlag(VALIDATOR_ADDRESS, true) + .build(); + + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + builtTransaction -> + assertEquals(amount(102600).micros(), builtTransaction.getFee())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR3)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, true) + .onFailure(failure -> fail(failure.toString())))); + } + + private TxBlobDTO buildBlobDto() { + return TxBlobDTO.create(AID.from(TX_ID), BLOB); + } + + private FinalizedTransaction buildFinalizedTransaction() throws PublicKeyException { + var sig = ECDSASignature.decodeFromHexDer(SIG); + var publicKey = ECPublicKey.fromHex(PUB_KEY); + var blob = buildBlobDto(); + return FinalizedTransaction.create(blob.getBlob(), sig, publicKey, blob.getTxId()); + } + + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; + + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } + + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); + + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiHistoryPaginationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiHistoryPaginationTest.java index b8a206f948..260aaa8bf4 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiHistoryPaginationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiHistoryPaginationTest.java @@ -61,28 +61,26 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ -package com.radixdlt.client.lib.api.sync; -import com.radixdlt.client.lib.dto.TransactionStatus; -import com.radixdlt.client.lib.dto.TransactionStatusDTO; -import com.radixdlt.utils.PrivateKeys; +package com.radixdlt.client.lib.api.sync; -import org.junit.Ignore; -import org.junit.Test; +import static org.junit.Assert.fail; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; import com.radixdlt.client.lib.dto.TransactionDTO; import com.radixdlt.client.lib.dto.TransactionHistory; +import com.radixdlt.client.lib.dto.TransactionStatus; +import com.radixdlt.client.lib.dto.TransactionStatusDTO; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.utils.PrivateKeys; import com.radixdlt.utils.UInt256; - import java.util.List; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; - -import static org.junit.Assert.fail; +import org.junit.Ignore; +import org.junit.Test; /* * Before running this test, launch in separate console local network (cd radixdlt-core/docker && ./scripts/rundocker.sh 2). @@ -93,93 +91,101 @@ * * Then run testTransactionHistoryInPages(). It should print list of transactions split into batches of 50 (see parameters) */ -//TODO: move to acceptance tests +// TODO: move to acceptance tests public class SyncRadixApiHistoryPaginationTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = PrivateKeys.ofNumeric(1); - public static final ECKeyPair KEY_PAIR2 = PrivateKeys.ofNumeric(2); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = PrivateKeys.ofNumeric(1); + public static final ECKeyPair KEY_PAIR2 = PrivateKeys.ofNumeric(2); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); - @Test - @Ignore("Online test") - public void testAddManyTransactions() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> { - for (int i = 0; i < 20; i++) { - addTransaction(client, i); - } - }); - } + @Test + @Ignore("Online test") + public void testAddManyTransactions() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + for (int i = 0; i < 20; i++) { + addTransaction(client, i); + } + }); + } - @Test - @Ignore("Online test") - public void testTransactionHistoryInPages() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> { - var cursorHolder = new AtomicReference<>(OptionalLong.empty()); - do { - client.account().history(ACCOUNT_ADDRESS1, 50, cursorHolder.get()) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(v -> v.getNextOffset().ifPresent(System.out::println)) - .onSuccess(v -> cursorHolder.set(v.getNextOffset())) - .map(TransactionHistory::getTransactions) - .map(this::formatTxns) - .onSuccess(System.out::println); - } while (cursorHolder.get().isPresent()); - }); - } + @Test + @Ignore("Online test") + public void testTransactionHistoryInPages() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + var cursorHolder = new AtomicReference<>(OptionalLong.empty()); + do { + client + .account() + .history(ACCOUNT_ADDRESS1, 50, cursorHolder.get()) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(v -> v.getNextOffset().ifPresent(System.out::println)) + .onSuccess(v -> cursorHolder.set(v.getNextOffset())) + .map(TransactionHistory::getTransactions) + .map(this::formatTxns) + .onSuccess(System.out::println); + } while (cursorHolder.get().isPresent()); + }); + } - private List formatTxns(List t) { - return t.stream() - .map(v -> String.format( - "%s (%s) - %s (%d:%d), Fee: %s%n", - v.getTxID(), - v.getMessage().orElse(""), - v.getSentAt().getInstant(), - v.getSentAt().getInstant().getEpochSecond(), - v.getSentAt().getInstant().getNano(), - v.getFee() - )) - .collect(Collectors.toList()); - } + private List formatTxns(List t) { + return t.stream() + .map( + v -> + String.format( + "%s (%s) - %s (%d:%d), Fee: %s%n", + v.getTxID(), + v.getMessage().orElse(""), + v.getSentAt().getInstant(), + v.getSentAt().getInstant().getEpochSecond(), + v.getSentAt().getInstant().getNano(), + v.getFee())) + .collect(Collectors.toList()); + } - private void addTransaction(RadixApi client, int count) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - ACCOUNT_ADDRESS2, - UInt256.from(count + 10), - "xrd_dr1qyrs8qwl" - ) - .message("Test message " + count) - .build(); + private void addTransaction(RadixApi client, int count) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer( + ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.from(count + 10), "xrd_dr1qyrs8qwl") + .message("Test message " + count) + .build(); - client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) - .flatMap(transaction -> client.transaction().finalize(transaction, true)) - .onSuccess(txBlobDTO -> { - var status = new AtomicReference(); - do { - safeSleep(125); - client.transaction().status(txBlobDTO.getTxId()) - .onSuccess(System.out::println) - .onSuccess(status::set); - } while (status.get().getStatus() != TransactionStatus.CONFIRMED); - }); - } + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .map(builtTransaction -> builtTransaction.toFinalized(KEY_PAIR1)) + .flatMap(transaction -> client.transaction().finalize(transaction, true)) + .onSuccess( + txBlobDTO -> { + var status = new AtomicReference(); + do { + safeSleep(125); + client + .transaction() + .status(txBlobDTO.getTxId()) + .onSuccess(System.out::println) + .onSuccess(status::set); + } while (status.get().getStatus() != TransactionStatus.CONFIRMED); + }); + } - private static void safeSleep(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + private static void safeSleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiLocalTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiLocalTest.java index c9b33adfc2..5a780537a7 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiLocalTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiLocalTest.java @@ -61,9 +61,17 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.sync; -import org.junit.Test; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -75,160 +83,193 @@ import com.radixdlt.networks.Network; import com.radixdlt.utils.Ints; import com.radixdlt.utils.functional.Result; - import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; +import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +public class SyncRadixApiLocalTest { + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + private static final AccountAddressing ACCOUNTS = + Addressing.ofNetwork(Network.LOCALNET).forAccounts(); -import static com.radixdlt.client.lib.api.token.Amount.amount; + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); -public class SyncRadixApiLocalTest { - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - private static final AccountAddressing ACCOUNTS = Addressing.ofNetwork(Network.LOCALNET).forAccounts(); - - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); - - private static final String BASE_URL = "http://localhost/"; - - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String ACCOUNT_INFO = "{\"result\":{\"address\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhk" - + "guhc269p3vhs27s5vq5h24sfvvdfj\",\"balance\":{\"stakes\":[],\"tokens\":[{\"amount\":\"1000000000000000" - + "000000000000\",\"rri\":\"xrd_dr1qyrs8qwl\"}],\"preparedStakes\":[]}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String VALIDATOR_INFO = "{\"result\":{\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897z" - + "k3gvt9uzh59rq9964vjryzf9\",\"epochInfo\":{\"current\":{\"owner\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhkg" - + "uhc269p3vhs27s5vq5h24sfvvdfj\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"stakes\":[{\"amou" - + "nt\":\"3949310000000000000000000\",\"delegator\":\"ddx1qsprdptw48agcfp7gh7ffmp8c2w08ut7820pnfqsae9yray93" - + "cmejxqkgsmrs\"}],\"validatorFee\":\"0.0\",\"registered\":true,\"totalStake\":\"394931000000000000000000" - + "0\",\"proposalsCompleted\":4111},\"updates\":{}},\"allowDelegation\":true,\"name\":\"\",\"url\":\"\"}," - + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private static final String CURRENT_EPOCH = "{\"result\":{\"validators\":[{\"totalDelegatedStake\":" - + "\"5201130000000000000000000\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"address\":" - + "\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnumc3jtmxl\",\"proposalsCompleted\":513}," - + "{\"totalDelegatedStake\":\"5199060000000000000000000\",\"uptimePercentage\":\"100.00\"," - + "\"proposalsMissed\":0,\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\"," - + "\"proposalsCompleted\":511}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - - private static final String SINGLE_STEP = "{\"result\":{\"txID\":\"c4741a62a721885dc3523afbf0297011671d8ce8969885b" - + "c0f6a6ffde9e39235\"},\"id\":\"6\",\"jsonrpc\":\"2.0\"}"; - private static final String BUILD_TRANSACTION = "{\"result\":{\"fee\":\"74200000000000000\",\"transaction\":{\"blo" - + "b\":\"06407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b300000005012100000000000000000000000" - + "00000000000000000000000000001079c81c2558000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f281" - + "5b16f81798010000000000000000000000000000000000000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef" - + "9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3733" - + "01858dc29a80000205000403fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975560100000000000000000" - + "00000000000000000000000000000056bc75e2d63100000000b0e54657374206d6573736167652031\",\"hashOfBlobToSign\":" - + "\"46a20c3ddd56a0fbac7622c52f26753ffacc5c5bf243f901c7210394c8d55198\"}},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; - private static final String FINALIZE_TRANSACTION = "{\"result\":{\"blob\":\"06407074cfe7b33d7e01c317eee743d33a9523" - + "60eb1c7ae64ab9caeb8d975329b30000000501210000000000000000000000000000000000000000000000000001079c81c25580000" - + "20500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000" - + "00000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f28" - + "15b16f81798010000000000000000000000000000000000000000033b2e373301858dc29a80000205000403fff97bd5755eeea42045" - + "3a14355235d382f6472f8568a18b2f057a1460297556010000000000000000000000000000000000000000000000056bc75e2d63100" - + "000000b0e54657374206d65737361676520310a00c07adf9012c81fed4205f14b7d7756808fecbf4615e39ad5b74c97057c532fb000" - + "6e798ed8aa457afa82908c0492d6e086d105374623b1ae430be39b4dd6bc96\",\"txID\":\"b3b2c41c08b4b93d533c824b015f6e1" - + "1e3370f1aeafb0116ee44aa3f4f442f37\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; - - private final HttpClient client = mock(HttpClient.class); - - @Test - public void testAccountInfo() throws Exception { - var accountAddress = AccountAddress.create( - ACCOUNTS.parseOrThrow("ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj", IllegalStateException::new) - ); - - prepareClient(ACCOUNT_INFO) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().accountInfo() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(localAccount -> assertEquals(accountAddress, localAccount.getAddress())) - .onSuccess(localAccount -> assertEquals(1, localAccount.getBalance().getTokens().size()))); - } - - @Test - public void testValidatorInfo() throws Exception { - prepareClient(VALIDATOR_INFO) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().validatorInfo() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(localValidatorInfo -> assertEquals(1, localValidatorInfo.getEpochInfo().getCurrent().getStakes().size())) - .onSuccess(localValidatorInfo -> assertTrue(localValidatorInfo.getEpochInfo().getCurrent().isRegistered()))); - } - - @Test - public void testCurrentEpoch() throws Exception { - prepareClient(CURRENT_EPOCH) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().currentEpoch() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(epochData -> assertEquals(2, epochData.getValidators().size()))); - } - - @Test - public void testSubmitTxSingleStep() throws Exception { - prepareClient(ACCOUNT_INFO, BUILD_TRANSACTION, FINALIZE_TRANSACTION, ACCOUNT_INFO, SINGLE_STEP) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.local().accountInfo().onSuccess(account -> transferFunds(client, account.getAddress()))) - .onSuccess(client -> client.local().accountInfo() - .map(account -> TransactionRequest.createBuilder(account.getAddress()) - .transfer(account.getAddress(), ACCOUNT_ADDRESS2, amount(5).tokens(), "xrd_dr1qyrs8qwl") - .message("Test message 2") - .build()) - .flatMap(request -> client.local().submitTxSingleStep(request) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(txData -> assertNotNull(txData.getTxId())))); - } - - private void transferFunds(RadixApi client, AccountAddress address) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - address, - amount(100).tokens(), - "xrd_dr1qyrs8qwl" - ) - .message("Test message 1") - .build(); - - client.transaction().build(request) - .onFailure(failure -> fail(failure.toString())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .flatMap(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true)) - .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())); - } - - private Result prepareClient(String... responseBodies) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); - - when(response.body()).thenReturn(NETWORK_ID, responseBodies); - when(client.send(any(), any())).thenReturn(response); - - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } - - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; - - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + private static final String BASE_URL = "http://localhost/"; + + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String ACCOUNT_INFO = + "{\"result\":{\"address\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj\",\"balance\":{\"stakes\":[],\"tokens\":[{\"amount\":\"1000000000000000000000000000\",\"rri\":\"xrd_dr1qyrs8qwl\"}],\"preparedStakes\":[]}},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String VALIDATOR_INFO = + "{\"result\":{\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"epochInfo\":{\"current\":{\"owner\":\"ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"stakes\":[{\"amount\":\"3949310000000000000000000\",\"delegator\":\"ddx1qsprdptw48agcfp7gh7ffmp8c2w08ut7820pnfqsae9yray93cmejxqkgsmrs\"}],\"validatorFee\":\"0.0\",\"registered\":true,\"totalStake\":\"3949310000000000000000000\",\"proposalsCompleted\":4111},\"updates\":{}},\"allowDelegation\":true,\"name\":\"\",\"url\":\"\"},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String CURRENT_EPOCH = + "{\"result\":{\"validators\":[{\"totalDelegatedStake\":\"5201130000000000000000000\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"address\":\"dv1qfwtmurydewmf64rnrektuh20g8r6svm0cpnpcuuay4ammw2cnumc3jtmxl\",\"proposalsCompleted\":513},{\"totalDelegatedStake\":\"5199060000000000000000000\",\"uptimePercentage\":\"100.00\",\"proposalsMissed\":0,\"address\":\"dv1q0llj774w40wafpqg5apgd2jxhfc9aj897zk3gvt9uzh59rq9964vjryzf9\",\"proposalsCompleted\":511}]},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + + private static final String SINGLE_STEP = + "{\"result\":{\"txID\":\"c4741a62a721885dc3523afbf0297011671d8ce8969885b" + + "c0f6a6ffde9e39235\"},\"id\":\"6\",\"jsonrpc\":\"2.0\"}"; + private static final String BUILD_TRANSACTION = + "{\"result\":{\"fee\":\"74200000000000000\",\"transaction\":{\"blo" + + "b\":\"06407074cfe7b33d7e01c317eee743d33a952360eb1c7ae64ab9caeb8d975329b300000005012100000000000000000000000" + + "00000000000000000000000000001079c81c2558000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f281" + + "5b16f81798010000000000000000000000000000000000000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef" + + "9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798010000000000000000000000000000000000000000033b2e3733" + + "01858dc29a80000205000403fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975560100000000000000000" + + "00000000000000000000000000000056bc75e2d63100000000b0e54657374206d6573736167652031\",\"hashOfBlobToSign\":" + + "\"46a20c3ddd56a0fbac7622c52f26753ffacc5c5bf243f901c7210394c8d55198\"}},\"id\":\"3\",\"jsonrpc\":\"2.0\"}"; + private static final String FINALIZE_TRANSACTION = + "{\"result\":{\"blob\":\"06407074cfe7b33d7e01c317eee743d33a9523" + + "60eb1c7ae64ab9caeb8d975329b30000000501210000000000000000000000000000000000000000000000000001079c81c25580000" + + "20500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980100000000000000000000000000000000" + + "00000000033b2e3c9ec8e3bb25aa8000000700000000020500040279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f28" + + "15b16f81798010000000000000000000000000000000000000000033b2e373301858dc29a80000205000403fff97bd5755eeea42045" + + "3a14355235d382f6472f8568a18b2f057a1460297556010000000000000000000000000000000000000000000000056bc75e2d63100" + + "000000b0e54657374206d65737361676520310a00c07adf9012c81fed4205f14b7d7756808fecbf4615e39ad5b74c97057c532fb000" + + "6e798ed8aa457afa82908c0492d6e086d105374623b1ae430be39b4dd6bc96\",\"txID\":\"b3b2c41c08b4b93d533c824b015f6e1" + + "1e3370f1aeafb0116ee44aa3f4f442f37\"},\"id\":\"4\",\"jsonrpc\":\"2.0\"}"; + + private final HttpClient client = mock(HttpClient.class); + + @Test + public void testAccountInfo() throws Exception { + var accountAddress = + AccountAddress.create( + ACCOUNTS.parseOrThrow( + "ddx1qspll7tm6464am4yypzn59p42g6a8qhkguhc269p3vhs27s5vq5h24sfvvdfj", + IllegalStateException::new)); + + prepareClient(ACCOUNT_INFO) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .accountInfo() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + localAccount -> assertEquals(accountAddress, localAccount.getAddress())) + .onSuccess( + localAccount -> + assertEquals(1, localAccount.getBalance().getTokens().size()))); + } + + @Test + public void testValidatorInfo() throws Exception { + prepareClient(VALIDATOR_INFO) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .validatorInfo() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + localValidatorInfo -> + assertEquals( + 1, + localValidatorInfo.getEpochInfo().getCurrent().getStakes().size())) + .onSuccess( + localValidatorInfo -> + assertTrue( + localValidatorInfo.getEpochInfo().getCurrent().isRegistered()))); + } + + @Test + public void testCurrentEpoch() throws Exception { + prepareClient(CURRENT_EPOCH) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .currentEpoch() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(epochData -> assertEquals(2, epochData.getValidators().size()))); + } + + @Test + public void testSubmitTxSingleStep() throws Exception { + prepareClient(ACCOUNT_INFO, BUILD_TRANSACTION, FINALIZE_TRANSACTION, ACCOUNT_INFO, SINGLE_STEP) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .local() + .accountInfo() + .onSuccess(account -> transferFunds(client, account.getAddress()))) + .onSuccess( + client -> + client + .local() + .accountInfo() + .map( + account -> + TransactionRequest.createBuilder(account.getAddress()) + .transfer( + account.getAddress(), + ACCOUNT_ADDRESS2, + amount(5).tokens(), + "xrd_dr1qyrs8qwl") + .message("Test message 2") + .build()) + .flatMap( + request -> + client + .local() + .submitTxSingleStep(request) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(txData -> assertNotNull(txData.getTxId())))); + } + + private void transferFunds(RadixApi client, AccountAddress address) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, address, amount(100).tokens(), "xrd_dr1qyrs8qwl") + .message("Test message 1") + .build(); + + client + .transaction() + .build(request) + .onFailure(failure -> fail(failure.toString())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .flatMap(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true)) + .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())); + } + + private Result prepareClient(String... responseBodies) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); + + when(response.body()).thenReturn(NETWORK_ID, responseBodies); + when(client.send(any(), any())).thenReturn(response); + + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } + + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; + + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiMempoolTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiMempoolTest.java index 0d47295726..89b08f64ff 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiMempoolTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiMempoolTest.java @@ -61,15 +61,8 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ -package com.radixdlt.client.lib.api.sync; - -import org.junit.Test; - -import com.radixdlt.utils.functional.Result; -import java.net.http.HttpClient; -import java.net.http.HttpResponse; -import java.util.Optional; +package com.radixdlt.client.lib.api.sync; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -77,48 +70,68 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.radixdlt.utils.functional.Result; +import java.net.http.HttpClient; +import java.net.http.HttpResponse; +import java.util.Optional; +import org.junit.Test; + public class SyncRadixApiMempoolTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"throttleMs\":5,\"maxSize\":10000},\"id\":\"2\"," - + "\"jsonrpc\":\"2.0\"}"; - private static final String DATA = "{\"result\":{\"addSuccess\":1273473,\"maxcount\":0,\"relayerSentCount\":0," - + "\"proposedTransaction\":0,\"count\":0,\"errors\":{\"other\":0,\"hook\":3,\"conflict\":0}},\"id\":\"2\"," - + "\"jsonrpc\":\"2.0\"}"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"throttleMs\":5,\"maxSize\":10000},\"id\":\"2\"," + "\"jsonrpc\":\"2.0\"}"; + private static final String DATA = + "{\"result\":{\"addSuccess\":1273473,\"maxcount\":0,\"relayerSentCount\":0," + + "\"proposedTransaction\":0,\"count\":0,\"errors\":{\"other\":0,\"hook\":3,\"conflict\":0}},\"id\":\"2\"," + + "\"jsonrpc\":\"2.0\"}"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws Exception { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.mempool().configuration() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configuration -> assertEquals(10000L, configuration.getMaxSize())) - .onSuccess(configuration -> assertEquals(5L, configuration.getThrottleMs()))); - } + @Test + public void testConfiguration() throws Exception { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .mempool() + .configuration() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(configuration -> assertEquals(10000L, configuration.getMaxSize())) + .onSuccess(configuration -> assertEquals(5L, configuration.getThrottleMs()))); + } - @Test - public void testData() throws Exception { - prepareClient(DATA) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> client.mempool().data() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertEquals(1273473L, data.getAddSuccess())) - .onSuccess(data -> assertEquals(3L, data.getErrors().getHook()))); - } + @Test + public void testData() throws Exception { + prepareClient(DATA) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .mempool() + .data() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertEquals(1273473L, data.getAddSuccess())) + .onSuccess(data -> assertEquals(3L, data.getErrors().getHook()))); + } - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiSyncTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiSyncTest.java index db8969c537..6bc5dc43fe 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiSyncTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiSyncTest.java @@ -61,15 +61,8 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ -package com.radixdlt.client.lib.api.sync; - -import org.junit.Test; - -import com.radixdlt.utils.functional.Result; -import java.net.http.HttpClient; -import java.net.http.HttpResponse; -import java.util.Optional; +package com.radixdlt.client.lib.api.sync; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -77,48 +70,68 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.radixdlt.utils.functional.Result; +import java.net.http.HttpClient; +import java.net.http.HttpResponse; +import java.util.Optional; +import org.junit.Test; + public class SyncRadixApiSyncTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String CONFIGURATION = "{\"result\":{\"maxLedgerUpdatesRate\":50,\"syncCheckMaxPeers\":10," - + "\"ledgerStatusUpdateMaxPeersToNotify\":10,\"syncCheckInterval\":3000,\"requestTimeout\":5000},\"id\":\"2\"," - + "\"jsonrpc\":\"2.0\"}\n"; - private static final String DATA = "{\"result\":{\"processed\":36898,\"invalidCommandsReceived\":0,\"targetCurrent" - + "Diff\":0,\"remoteRequestsProcessed\":38614,\"lastReadMillis\":0,\"targetStateVersion\":814181},\"id\":\"2\"" - + ",\"jsonrpc\":\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String CONFIGURATION = + "{\"result\":{\"maxLedgerUpdatesRate\":50,\"syncCheckMaxPeers\":10,\"ledgerStatusUpdateMaxPeersToNotify\":10,\"syncCheckInterval\":3000,\"requestTimeout\":5000},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String DATA = + "{\"result\":{\"processed\":36898,\"invalidCommandsReceived\":0,\"targetCurrentDiff\":0,\"remoteRequestsProcessed\":38614,\"lastReadMillis\":0,\"targetStateVersion\":814181},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testConfiguration() throws Exception { - prepareClient(CONFIGURATION) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.sync().configuration() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(configuration -> assertEquals(50L, configuration.getMaxLedgerUpdatesRate())) - .onSuccess(configuration -> assertEquals(3000, configuration.getSyncCheckInterval()))); - } + @Test + public void testConfiguration() throws Exception { + prepareClient(CONFIGURATION) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .sync() + .configuration() + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + configuration -> assertEquals(50L, configuration.getMaxLedgerUpdatesRate())) + .onSuccess( + configuration -> assertEquals(3000, configuration.getSyncCheckInterval()))); + } - @Test - public void testData() throws Exception { - prepareClient(DATA) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.sync().data() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(data -> assertEquals(36898L, data.getProcessed())) - .onSuccess(data -> assertEquals(814181L, data.getTargetStateVersion()))); - } + @Test + public void testData() throws Exception { + prepareClient(DATA) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .sync() + .data() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(data -> assertEquals(36898L, data.getProcessed())) + .onSuccess(data -> assertEquals(814181L, data.getTargetStateVersion()))); + } - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTest.java index 24fbc7e458..52fa30c2d5 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTest.java @@ -61,10 +61,14 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.sync; -import org.junit.Ignore; -import org.junit.Test; +import static com.radixdlt.client.lib.api.sync.RadixApi.connect; +import static com.radixdlt.client.lib.api.token.Amount.amount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -76,201 +80,285 @@ import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Failure; +import org.junit.Ignore; +import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import static com.radixdlt.client.lib.api.sync.RadixApi.connect; -import static com.radixdlt.client.lib.api.token.Amount.amount; - -//TODO: move to acceptance tests and repurpose to integration testing of the API's. +// TODO: move to acceptance tests and repurpose to integration testing of the API's. public class SyncRadixApiTest { - private static final String BASE_URL = "http://localhost/"; - public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); - public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); - private static final AccountAddress ACCOUNT_ADDRESS1 = AccountAddress.create(KEY_PAIR1.getPublicKey()); - private static final AccountAddress ACCOUNT_ADDRESS2 = AccountAddress.create(KEY_PAIR2.getPublicKey()); + private static final String BASE_URL = "http://localhost/"; + public static final ECKeyPair KEY_PAIR1 = keyPairOf(1); + public static final ECKeyPair KEY_PAIR2 = keyPairOf(2); + private static final AccountAddress ACCOUNT_ADDRESS1 = + AccountAddress.create(KEY_PAIR1.getPublicKey()); + private static final AccountAddress ACCOUNT_ADDRESS2 = + AccountAddress.create(KEY_PAIR2.getPublicKey()); - @Test - @Ignore("Online test") - public void testBuildTransactionWithMessage() { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer( - ACCOUNT_ADDRESS1, - ACCOUNT_ADDRESS2, - UInt256.NINE, - "xrd_dr1qyrs8qwl" - ) - .message("Test message") - .build(); + @Test + @Ignore("Online test") + public void testBuildTransactionWithMessage() { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, UInt256.NINE, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); - connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> client.transaction().build(request) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(builtTransactionDTO -> assertEquals(UInt256.from(100000000000000000L), builtTransactionDTO.getFee())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false) - .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId()))))); - } + connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + client -> + client + .transaction() + .build(request) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + builtTransactionDTO -> + assertEquals( + UInt256.from(100000000000000000L), builtTransactionDTO.getFee())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), + txDTO.getTxId()))))); + } - @Test - @Ignore("Online test") - public void addManyTransactions() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> { - for (int i = 0; i < 20; i++) { - addTransaction(client, UInt256.from(i + 10)); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - } + @Test + @Ignore("Online test") + public void addManyTransactions() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + client -> { + for (int i = 0; i < 20; i++) { + addTransaction(client, UInt256.from(i + 10)); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + } - @Test - @Ignore("Online test") - public void listStakes() { - connect(BASE_URL) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> client.account().stakes(ACCOUNT_ADDRESS1) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(stakePositionsDTOS -> System.out.println("Stake positions: " + stakePositionsDTOS.toString()))); - } + @Test + @Ignore("Online test") + public void listStakes() { + connect(BASE_URL) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + client -> + client + .account() + .stakes(ACCOUNT_ADDRESS1) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + stakePositionsDTOS -> + System.out.println( + "Stake positions: " + stakePositionsDTOS.toString()))); + } - @Test - @Ignore("Online test") - public void listUnStakes() { - connect(BASE_URL) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> client.account().unstakes(ACCOUNT_ADDRESS1) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(unstakePositionsDTOS -> System.out.println("UnStake positions: " + unstakePositionsDTOS.toString()))); - } + @Test + @Ignore("Online test") + public void listUnStakes() { + connect(BASE_URL) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + client -> + client + .account() + .unstakes(ACCOUNT_ADDRESS1) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + unstakePositionsDTOS -> + System.out.println( + "UnStake positions: " + unstakePositionsDTOS.toString()))); + } - @Test - @Ignore("Online test") - public void makeStake() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> makeStake(client, amount(200).tokens())); - } + @Test + @Ignore("Online test") + public void makeStake() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess(client -> makeStake(client, amount(200).tokens())); + } - @Test - @Ignore("Online test") - public void makeUnStake() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> makeUnStake(client, amount(100).tokens())); - } + @Test + @Ignore("Online test") + public void makeUnStake() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess(client -> makeUnStake(client, amount(100).tokens())); + } - @Test - @Ignore("Online test") - public void transferUnStake() { - connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> transferUnStake(client, amount(100).tokens())); - } + @Test + @Ignore("Online test") + public void transferUnStake() { + connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess(client -> transferUnStake(client, amount(100).tokens())); + } - @Test - @Ignore("Online test") - public void tryBasicAuthentication() { - connect("https://rcnet.radixdlt.com", 443, 443, BasicAuth.with("admin", "86RVCjoogDJioMZZVYYlaSAk")) - .map(RadixApi::withTrace) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(client -> client.network().addressBook() - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(System.out::println)); - } + @Test + @Ignore("Online test") + public void tryBasicAuthentication() { + connect( + "https://rcnet.radixdlt.com", + 443, + 443, + BasicAuth.with("admin", "86RVCjoogDJioMZZVYYlaSAk")) + .map(RadixApi::withTrace) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + client -> + client + .network() + .addressBook() + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess(System.out::println)); + } - private static void reportFailure(Failure failure) { - fail(failure.toString()); - } + private static void reportFailure(Failure failure) { + fail(failure.toString()); + } - private void transferUnStake(RadixApi client, UInt256 amount) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS2) - .transfer( - ACCOUNT_ADDRESS2, - ACCOUNT_ADDRESS1, - amount, - "xrd_dr1qyrs8qwl" - ) - .message("Test message") - .build(); + private void transferUnStake(RadixApi client, UInt256 amount) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS2) + .transfer(ACCOUNT_ADDRESS2, ACCOUNT_ADDRESS1, amount, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); - client.transaction().build(request) - .onFailure(SyncRadixApiTest::reportFailure) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR2)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false) - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId())))); - } + client + .transaction() + .build(request) + .onFailure(SyncRadixApiTest::reportFailure) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR2)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), txDTO.getTxId())))); + } - private void makeStake(RadixApi client, UInt256 amount) { - client.local().validatorInfo() - .map(account -> TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) - .build()) - .onSuccess(request -> client.transaction().build(request) - .onFailure(SyncRadixApiTest::reportFailure) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .flatMap(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, true)) - .onSuccess(System.out::println)); - } + private void makeStake(RadixApi client, UInt256 amount) { + client + .local() + .validatorInfo() + .map( + account -> + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .stake(ACCOUNT_ADDRESS1, account.getAddress(), amount) + .build()) + .onSuccess( + request -> + client + .transaction() + .build(request) + .onFailure(SyncRadixApiTest::reportFailure) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .flatMap( + finalizedTransaction -> + client.transaction().finalize(finalizedTransaction, true)) + .onSuccess(System.out::println)); + } - private void makeUnStake(RadixApi client, UInt256 amount) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .unstake(ACCOUNT_ADDRESS1, ValidatorAddress.of(KEY_PAIR2.getPublicKey()), amount) - .build(); + private void makeUnStake(RadixApi client, UInt256 amount) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .unstake(ACCOUNT_ADDRESS1, ValidatorAddress.of(KEY_PAIR2.getPublicKey()), amount) + .build(); - client.transaction().build(request) - .onFailure(SyncRadixApiTest::reportFailure) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false) - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId())))); - } + client + .transaction() + .build(request) + .onFailure(SyncRadixApiTest::reportFailure) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), txDTO.getTxId())))); + } - private void addTransaction(RadixApi client, UInt256 amount) { - var request = TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) - .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount, "xrd_dr1qyrs8qwl") - .message("Test message") - .build(); + private void addTransaction(RadixApi client, UInt256 amount) { + var request = + TransactionRequest.createBuilder(ACCOUNT_ADDRESS1) + .transfer(ACCOUNT_ADDRESS1, ACCOUNT_ADDRESS2, amount, "xrd_dr1qyrs8qwl") + .message("Test message") + .build(); - client.transaction().build(request) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(builtTransactionDTO -> assertEquals(amount(73800L).micros(), builtTransactionDTO.getFee())) - .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) - .onSuccess(finalizedTransaction -> client.transaction().finalize(finalizedTransaction, false) - .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) - .onSuccess(submittableTransaction -> client.transaction().submit(submittableTransaction) - .onFailure(SyncRadixApiTest::reportFailure) - .onSuccess(txDTO -> assertEquals(submittableTransaction.getTxId(), txDTO.getTxId())))); - } + client + .transaction() + .build(request) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + builtTransactionDTO -> + assertEquals(amount(73800L).micros(), builtTransactionDTO.getFee())) + .map(builtTransactionDTO -> builtTransactionDTO.toFinalized(KEY_PAIR1)) + .onSuccess( + finalizedTransaction -> + client + .transaction() + .finalize(finalizedTransaction, false) + .onSuccess(txDTO -> assertNotNull(txDTO.getTxId())) + .onSuccess( + submittableTransaction -> + client + .transaction() + .submit(submittableTransaction) + .onFailure(SyncRadixApiTest::reportFailure) + .onSuccess( + txDTO -> + assertEquals( + submittableTransaction.getTxId(), txDTO.getTxId())))); + } - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTokenTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTokenTest.java index 815b291b7f..07cf716601 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTokenTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTokenTest.java @@ -61,77 +61,89 @@ * Work. You assume all risks associated with Your use of the Work and the exercise of * permissions under this License. */ + package com.radixdlt.client.lib.api.sync; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.utils.Ints; import com.radixdlt.utils.functional.Result; - import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.junit.Test; public class SyncRadixApiTokenTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - private static final String NETWORK_ID = "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; - private static final String NATIVE_TOKEN = "{\"result\":{\"tokenInfoURL\":\"\",\"symbol\":\"xrd\"," - + "\"isSupplyMutable\":true,\"granularity\":\"1\",\"totalBurned\":\"0\",\"name\":\"Rads\"," - + "\"rri\":\"xrd_dr1qyrs8qwl\",\"description\":\"Radix Tokens\",\"iconURL\":\"\"," - + "\"currentSupply\":\"8000161536692300000000000000\",\"totalMinted\":\"8000161536692300000000000000\"}," - + "\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; + private static final String NETWORK_ID = + "{\"result\":{\"networkId\":99},\"id\":\"1\",\"jsonrpc\":\"2.0\"}"; + private static final String NATIVE_TOKEN = + "{\"result\":{\"tokenInfoURL\":\"\",\"symbol\":\"xrd\"," + + "\"isSupplyMutable\":true,\"granularity\":\"1\",\"totalBurned\":\"0\",\"name\":\"Rads\",\"rri\":\"xrd_dr1qyrs8qwl\",\"description\":\"Radix" + + " Tokens\",\"iconURL\":\"\",\"currentSupply\":\"8000161536692300000000000000\",\"totalMinted\":\"8000161536692300000000000000\"},\"id\":\"2\",\"jsonrpc\":\"2.0\"}\n"; - private final HttpClient client = mock(HttpClient.class); + private final HttpClient client = mock(HttpClient.class); - @Test - public void testNativeToken() throws Exception { - prepareClient(NATIVE_TOKEN) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.token().describeNative() - .onFailure(failure -> fail(failure.toString())) - .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); - } + @Test + public void testNativeToken() throws Exception { + prepareClient(NATIVE_TOKEN) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .token() + .describeNative() + .onFailure(failure -> fail(failure.toString())) + .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); + } - @Test - public void testTokenInfo() throws Exception { - prepareClient(NATIVE_TOKEN) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(client -> client.token().describe("xrd_dr1qyrs8qwl") - .onFailure(failure -> fail(failure.toString())) - .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); - } + @Test + public void testTokenInfo() throws Exception { + prepareClient(NATIVE_TOKEN) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> + client + .token() + .describe("xrd_dr1qyrs8qwl") + .onFailure(failure -> fail(failure.toString())) + .onSuccess(tokenInfoDTO -> assertEquals("Rads", tokenInfoDTO.getName()))); + } - private static ECKeyPair keyPairOf(int pk) { - var privateKey = new byte[ECKeyPair.BYTES]; + private static ECKeyPair keyPairOf(int pk) { + var privateKey = new byte[ECKeyPair.BYTES]; - Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); + Ints.copyTo(pk, privateKey, ECKeyPair.BYTES - Integer.BYTES); - try { - return ECKeyPair.fromPrivateKey(privateKey); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } + try { + return ECKeyPair.fromPrivateKey(privateKey); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } - private Result prepareClient(String responseBody) throws Exception { - @SuppressWarnings("unchecked") - var response = (HttpResponse) mock(HttpResponse.class); + private Result prepareClient(String responseBody) throws Exception { + @SuppressWarnings("unchecked") + var response = (HttpResponse) mock(HttpResponse.class); - when(response.body()).thenReturn(NETWORK_ID, responseBody); - when(client.send(any(), any())).thenReturn(response); + when(response.body()).thenReturn(NETWORK_ID, responseBody); + when(client.send(any(), any())).thenReturn(response); - return SyncRadixApi.connect(BASE_URL, RadixApi.DEFAULT_PRIMARY_PORT, RadixApi.DEFAULT_SECONDARY_PORT, client, Optional.empty()); - } + return SyncRadixApi.connect( + BASE_URL, + RadixApi.DEFAULT_PRIMARY_PORT, + RadixApi.DEFAULT_SECONDARY_PORT, + client, + Optional.empty()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTransactionPaginationTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTransactionPaginationTest.java index aa64d5993e..033cdfb70f 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTransactionPaginationTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/api/sync/SyncRadixApiTransactionPaginationTest.java @@ -1,9 +1,10 @@ -/* - * Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * * radixfoundation.org/licenses/LICENSE-v1 + * * The Licensor hereby grants permission for the Canonical version of the Work to be * published, distributed and used under or by reference to the Licensor’s trademark * Radix ® and use of any unregistered trade names, logos or get-up. @@ -63,17 +64,16 @@ package com.radixdlt.client.lib.api.sync; +import static org.junit.Assert.fail; + import com.radixdlt.client.lib.dto.TransactionDTO; import com.radixdlt.client.lib.dto.TransactionsDTO; -import org.junit.Ignore; -import org.junit.Test; - import java.util.List; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; - -import static org.junit.Assert.fail; +import org.junit.Ignore; +import org.junit.Test; /* * Before running this test, launch in separate console local network (cd radixdlt-core/docker && ./scripts/rundocker.sh 2). @@ -84,41 +84,44 @@ * * Then run testTransactionHistoryInPages(). It should print list of transactions split into batches of 50 (see parameters) */ -//TODO: move to acceptance tests +// TODO: move to acceptance tests public class SyncRadixApiTransactionPaginationTest { - private static final String BASE_URL = "http://localhost/"; + private static final String BASE_URL = "http://localhost/"; - @Test - @Ignore("Online test") - public void testTransactionHistoryInPages() { - RadixApi.connect(BASE_URL) - .map(RadixApi::withTrace) - .onFailure(failure -> fail(failure.toString())) - .onSuccess( - client -> { - var cursorHolder = new AtomicReference<>(OptionalLong.empty()); - do { - client.transaction().list(100, cursorHolder.get()) - .onFailure(failure -> fail(failure.toString())) - .onSuccess(v -> cursorHolder.set(v.getNextOffset())) - .map(TransactionsDTO::getTransactions) - .map(this::formatTxns) - .onSuccess(System.out::println); - } while (cursorHolder.get().isPresent()); - }); - } + @Test + @Ignore("Online test") + public void testTransactionHistoryInPages() { + RadixApi.connect(BASE_URL) + .map(RadixApi::withTrace) + .onFailure(failure -> fail(failure.toString())) + .onSuccess( + client -> { + var cursorHolder = new AtomicReference<>(OptionalLong.empty()); + do { + client + .transaction() + .list(100, cursorHolder.get()) + .onFailure(failure -> fail(failure.toString())) + .onSuccess(v -> cursorHolder.set(v.getNextOffset())) + .map(TransactionsDTO::getTransactions) + .map(this::formatTxns) + .onSuccess(System.out::println); + } while (cursorHolder.get().isPresent()); + }); + } - private List formatTxns(List t) { - return t.stream() - .map(v -> String.format( - "%s (%s) - %s (%d:%d), Fee: %s%n", - v.getTxID(), - v.getMessage().orElse(""), - v.getSentAt().getInstant(), - v.getSentAt().getInstant().getEpochSecond(), - v.getSentAt().getInstant().getNano(), - v.getFee() - )) - .collect(Collectors.toList()); - } + private List formatTxns(List t) { + return t.stream() + .map( + v -> + String.format( + "%s (%s) - %s (%d:%d), Fee: %s%n", + v.getTxID(), + v.getMessage().orElse(""), + v.getSentAt().getInstant(), + v.getSentAt().getInstant().getEpochSecond(), + v.getSentAt().getInstant().getNano(), + v.getFee())) + .collect(Collectors.toList()); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/dto/DtoTest.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/dto/DtoTest.java index adaf80f90b..8fbc7406e8 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/dto/DtoTest.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/client/lib/dto/DtoTest.java @@ -64,374 +64,377 @@ package com.radixdlt.client.lib.dto; -import org.junit.Test; - import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import org.junit.Test; public class DtoTest { - @Test - public void testAccountBalance() { - EqualsVerifier.forClass(AccountBalance.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testAction() { - EqualsVerifier.forClass(Action.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testAddressBookEntry() { - EqualsVerifier.forClass(AddressBookEntry.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testApiConfiguration() { - EqualsVerifier.forClass(ApiConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testApiDataCount() { - EqualsVerifier.forClass(ApiDataCount.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testApiDataElapsed() { - EqualsVerifier.forClass(ApiDataElapsed.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testApiData() { - EqualsVerifier.forClass(ApiData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testApiDbCount() { - EqualsVerifier.forClass(ApiDbCount.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testApiDbElapsed() { - EqualsVerifier.forClass(ApiDbElapsed.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testBalance() { - EqualsVerifier.forClass(Balance.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testBalanceStakes() { - EqualsVerifier.forClass(BalanceStakes.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testBuiltTransaction() { - EqualsVerifier.forClass(BuiltTransaction.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testChannelType() { - EqualsVerifier.forClass(ChannelType.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testCheckpoint() { - EqualsVerifier.forClass(Checkpoint.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testConsensusConfiguration() { - EqualsVerifier.forClass(ConsensusConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testConsensusData() { - EqualsVerifier.forClass(ConsensusData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testConsensusDataSync() { - EqualsVerifier.forClass(ConsensusDataSync.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testCount() { - EqualsVerifier.forClass(Count.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testCurrentEpochInfo() { - EqualsVerifier.forClass(CurrentEpochInfo.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testDelegatedStake() { - EqualsVerifier.forClass(DelegatedStake.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testEpochData() { - EqualsVerifier.forClass(EpochData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testEpochInfo() { - EqualsVerifier.forClass(EpochInfo.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testEpochValidatorData() { - EqualsVerifier.forClass(EpochValidatorData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testFeeTable() { - EqualsVerifier.forClass(FeeTable.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testFinalizedTransaction() { - EqualsVerifier.forClass(FinalizedTransaction.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testForkDetails() { - EqualsVerifier.forClass(ForkDetails.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testForkDetailsConfiguration() { - EqualsVerifier.forClass(ForkDetailsConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testLocalAccount() { - EqualsVerifier.forClass(LocalAccount.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testLocalValidatorInfo() { - EqualsVerifier.forClass(LocalValidatorInfo.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testMempoolConfiguration() { - EqualsVerifier.forClass(MempoolConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testMempoolDataErrors() { - EqualsVerifier.forClass(MempoolDataErrors.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testMempoolData() { - EqualsVerifier.forClass(MempoolData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkChannel() { - EqualsVerifier.forClass(NetworkChannel.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkConfiguration() { - EqualsVerifier.forClass(NetworkConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkData() { - EqualsVerifier.forClass(NetworkData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkDataMessagesInbound() { - EqualsVerifier.forClass(NetworkDataMessagesInbound.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkDataMessages() { - EqualsVerifier.forClass(NetworkDataMessages.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkDataMessagesOutbound() { - EqualsVerifier.forClass(NetworkDataMessagesOutbound.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkDataNetworking() { - EqualsVerifier.forClass(NetworkDataNetworking.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkDataNetworkingTcp() { - EqualsVerifier.forClass(NetworkDataNetworkingTcp.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkDataNetworkingUdp() { - EqualsVerifier.forClass(NetworkDataNetworkingUdp.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkId() { - EqualsVerifier.forClass(NetworkId.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkPeer() { - EqualsVerifier.forClass(NetworkPeer.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNetworkStats() { - EqualsVerifier.forClass(NetworkStats.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testNotification() { - EqualsVerifier.forClass(Notification.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testPerUpSubstateFee() { - EqualsVerifier.forClass(PerUpSubstateFee.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testProofHeader() { - EqualsVerifier.forClass(ProofHeader.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testProof() { - EqualsVerifier.forClass(Proof.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testRadixEngineData() { - EqualsVerifier.forClass(RadixEngineData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testReadWrite() { - EqualsVerifier.forClass(ReadWrite.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testReadWriteStats() { - EqualsVerifier.forClass(ReadWriteStats.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testSignatureDetails() { - EqualsVerifier.forClass(SignatureDetails.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testSize() { - EqualsVerifier.forClass(Size.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testStakePositions() { - EqualsVerifier.forClass(StakePositions.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testSyncConfiguration() { - EqualsVerifier.forClass(SyncConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testSyncData() { - EqualsVerifier.forClass(SyncData.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTimeDTO() { - EqualsVerifier.forClass(TimeDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTokenBalances() { - EqualsVerifier.forClass(TokenBalances.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTokenInfo() { - EqualsVerifier.forClass(TokenInfo.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTransactionDTO() { - EqualsVerifier.forClass(TransactionDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTransactionsDTO() { - EqualsVerifier.forClass(TransactionsDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTransaction2DTO() { - EqualsVerifier.forClass(TransactionDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTransactionHistory2() { - EqualsVerifier.forClass(TransactionHistory.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTransactionStatusDTO() { - EqualsVerifier.forClass(TransactionStatusDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTransactionStatus() { - EqualsVerifier.forClass(TransactionStatus.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTxBlobDTO() { - EqualsVerifier.forClass(TxBlobDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTxBlob() { - EqualsVerifier.forClass(TxBlob.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testTxDTO() { - EqualsVerifier.forClass(TxDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testUnstakePositions() { - EqualsVerifier.forClass(UnstakePositions.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testUpdates() { - EqualsVerifier.forClass(Updates.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testValidatorDTO() { - EqualsVerifier.forClass(ValidatorDTO.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testValidatorEntry() { - EqualsVerifier.forClass(ValidatorEntry.class).suppress(Warning.NULL_FIELDS).verify(); - } - - @Test - public void testValidatorsResponse() { - EqualsVerifier.forClass(ValidatorsResponse.class).suppress(Warning.NULL_FIELDS).verify(); - } + @Test + public void testAccountBalance() { + EqualsVerifier.forClass(AccountBalance.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testAction() { + EqualsVerifier.forClass(Action.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testAddressBookEntry() { + EqualsVerifier.forClass(AddressBookEntry.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testApiConfiguration() { + EqualsVerifier.forClass(ApiConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testApiDataCount() { + EqualsVerifier.forClass(ApiDataCount.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testApiDataElapsed() { + EqualsVerifier.forClass(ApiDataElapsed.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testApiData() { + EqualsVerifier.forClass(ApiData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testApiDbCount() { + EqualsVerifier.forClass(ApiDbCount.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testApiDbElapsed() { + EqualsVerifier.forClass(ApiDbElapsed.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testBalance() { + EqualsVerifier.forClass(Balance.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testBalanceStakes() { + EqualsVerifier.forClass(BalanceStakes.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testBuiltTransaction() { + EqualsVerifier.forClass(BuiltTransaction.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testChannelType() { + EqualsVerifier.forClass(ChannelType.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testCheckpoint() { + EqualsVerifier.forClass(Checkpoint.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testConsensusConfiguration() { + EqualsVerifier.forClass(ConsensusConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testConsensusData() { + EqualsVerifier.forClass(ConsensusData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testConsensusDataSync() { + EqualsVerifier.forClass(ConsensusDataSync.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testCount() { + EqualsVerifier.forClass(Count.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testCurrentEpochInfo() { + EqualsVerifier.forClass(CurrentEpochInfo.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testDelegatedStake() { + EqualsVerifier.forClass(DelegatedStake.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testEpochData() { + EqualsVerifier.forClass(EpochData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testEpochInfo() { + EqualsVerifier.forClass(EpochInfo.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testEpochValidatorData() { + EqualsVerifier.forClass(EpochValidatorData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testFeeTable() { + EqualsVerifier.forClass(FeeTable.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testFinalizedTransaction() { + EqualsVerifier.forClass(FinalizedTransaction.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testForkDetails() { + EqualsVerifier.forClass(ForkDetails.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testForkDetailsConfiguration() { + EqualsVerifier.forClass(ForkDetailsConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testLocalAccount() { + EqualsVerifier.forClass(LocalAccount.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testLocalValidatorInfo() { + EqualsVerifier.forClass(LocalValidatorInfo.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testMempoolConfiguration() { + EqualsVerifier.forClass(MempoolConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testMempoolDataErrors() { + EqualsVerifier.forClass(MempoolDataErrors.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testMempoolData() { + EqualsVerifier.forClass(MempoolData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkChannel() { + EqualsVerifier.forClass(NetworkChannel.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkConfiguration() { + EqualsVerifier.forClass(NetworkConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkData() { + EqualsVerifier.forClass(NetworkData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkDataMessagesInbound() { + EqualsVerifier.forClass(NetworkDataMessagesInbound.class) + .suppress(Warning.NULL_FIELDS) + .verify(); + } + + @Test + public void testNetworkDataMessages() { + EqualsVerifier.forClass(NetworkDataMessages.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkDataMessagesOutbound() { + EqualsVerifier.forClass(NetworkDataMessagesOutbound.class) + .suppress(Warning.NULL_FIELDS) + .verify(); + } + + @Test + public void testNetworkDataNetworking() { + EqualsVerifier.forClass(NetworkDataNetworking.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkDataNetworkingTcp() { + EqualsVerifier.forClass(NetworkDataNetworkingTcp.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkDataNetworkingUdp() { + EqualsVerifier.forClass(NetworkDataNetworkingUdp.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkId() { + EqualsVerifier.forClass(NetworkId.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkPeer() { + EqualsVerifier.forClass(NetworkPeer.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNetworkStats() { + EqualsVerifier.forClass(NetworkStats.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testNotification() { + EqualsVerifier.forClass(Notification.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testPerUpSubstateFee() { + EqualsVerifier.forClass(PerUpSubstateFee.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testProofHeader() { + EqualsVerifier.forClass(ProofHeader.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testProof() { + EqualsVerifier.forClass(Proof.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testRadixEngineData() { + EqualsVerifier.forClass(RadixEngineData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testReadWrite() { + EqualsVerifier.forClass(ReadWrite.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testReadWriteStats() { + EqualsVerifier.forClass(ReadWriteStats.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testSignatureDetails() { + EqualsVerifier.forClass(SignatureDetails.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testSize() { + EqualsVerifier.forClass(Size.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testStakePositions() { + EqualsVerifier.forClass(StakePositions.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testSyncConfiguration() { + EqualsVerifier.forClass(SyncConfiguration.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testSyncData() { + EqualsVerifier.forClass(SyncData.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTimeDTO() { + EqualsVerifier.forClass(TimeDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTokenBalances() { + EqualsVerifier.forClass(TokenBalances.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTokenInfo() { + EqualsVerifier.forClass(TokenInfo.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTransactionDTO() { + EqualsVerifier.forClass(TransactionDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTransactionsDTO() { + EqualsVerifier.forClass(TransactionsDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTransaction2DTO() { + EqualsVerifier.forClass(TransactionDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTransactionHistory2() { + EqualsVerifier.forClass(TransactionHistory.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTransactionStatusDTO() { + EqualsVerifier.forClass(TransactionStatusDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTransactionStatus() { + EqualsVerifier.forClass(TransactionStatus.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTxBlobDTO() { + EqualsVerifier.forClass(TxBlobDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTxBlob() { + EqualsVerifier.forClass(TxBlob.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testTxDTO() { + EqualsVerifier.forClass(TxDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testUnstakePositions() { + EqualsVerifier.forClass(UnstakePositions.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testUpdates() { + EqualsVerifier.forClass(Updates.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testValidatorDTO() { + EqualsVerifier.forClass(ValidatorDTO.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testValidatorEntry() { + EqualsVerifier.forClass(ValidatorEntry.class).suppress(Warning.NULL_FIELDS).verify(); + } + + @Test + public void testValidatorsResponse() { + EqualsVerifier.forClass(ValidatorsResponse.class).suppress(Warning.NULL_FIELDS).verify(); + } } diff --git a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/test/util/TypedMocks.java b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/test/util/TypedMocks.java index 2bedc4d36b..6845fb6e8c 100644 --- a/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/test/util/TypedMocks.java +++ b/radixdlt-java/radixdlt-java/src/test/java/com/radixdlt/test/util/TypedMocks.java @@ -66,28 +66,25 @@ import static org.mockito.Mockito.mock; -/** - * Typed mock wrappers to avoid compiler warnings where they are not - * warranted. - */ +/** Typed mock wrappers to avoid compiler warnings where they are not warranted. */ public final class TypedMocks { - private TypedMocks() { - throw new IllegalStateException("Can't construct"); - } + private TypedMocks() { + throw new IllegalStateException("Can't construct"); + } - /** - * Runtime checked typed mock, primarily for types with type arguments. - * - * @param The type of the mock without type arguments, eg {@code List} - * @param The type of the mock with type arguments, eg {@code List} - * @param cls The raw class for the mocked type - * @return the mock, cast to type {@code U}. If {@code U} is not assignable - * from {@code T}, then a {@code ClassCastException} will occur. - */ - public static U rmock(Class cls) { - @SuppressWarnings("unchecked") - U value = (U) mock(cls); - return value; - } + /** + * Runtime checked typed mock, primarily for types with type arguments. + * + * @param The type of the mock without type arguments, eg {@code List} + * @param The type of the mock with type arguments, eg {@code List} + * @param cls The raw class for the mocked type + * @return the mock, cast to type {@code U}. If {@code U} is not assignable from {@code T}, then a + * {@code ClassCastException} will occur. + */ + public static U rmock(Class cls) { + @SuppressWarnings("unchecked") + U value = (U) mock(cls); + return value; + } } diff --git a/radixdlt-regression/README.md b/radixdlt-regression/README.md index cfdb68c11d..827d5fcc91 100644 --- a/radixdlt-regression/README.md +++ b/radixdlt-regression/README.md @@ -2,7 +2,7 @@ ### Run locally -Start a local network: +Start a local network: ``` ../radixdlt-core/docker/scripts/rundocker.sh ``` @@ -15,7 +15,7 @@ Run the tests: By default, the tests will run again an archive node at localhost:8080|3333. You can change this with: ``` RADIXDLT_JSON_RPC_API_ROOT_URL=http://my-other-archive-node \ -RADIXDLT_JSON_RPC_API_PRIMARY_PORT=8080 \ +RADIXDLT_JSON_RPC_API_PRIMARY_PORT=8080 \ RADIXDLT_JSON_RPC_API_SECONDARY_PORT=3333 \ ../gradlew clean acceptanceTest ``` @@ -24,7 +24,7 @@ You can also run the tests against a testnet: ``` RADIXDLT_JSON_RPC_API_ROOT_URL=https://rcnet.radixdlt.com \ RADIXDLT_FAUCET_URL=https://rcnet-faucet.radixdlt.com \ -../gradlew clean acceptanceTest +../gradlew clean acceptanceTest ``` Run a single test via tags: @@ -32,5 +32,3 @@ Run a single test via tags: ``` ../gradlew clean acceptanceTest -Dcucumber.filter.tags="@single" ``` - - diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/RadixNetworkTest.java b/radixdlt-regression/src/main/java/com/radixdlt/test/RadixNetworkTest.java index 38c58d885f..93470d8e0e 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/RadixNetworkTest.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/RadixNetworkTest.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test; +import static org.awaitility.Awaitility.await; + import com.google.common.collect.Lists; import com.radixdlt.application.tokens.Amount; import com.radixdlt.client.lib.api.AccountAddress; @@ -10,89 +76,94 @@ import com.radixdlt.test.network.checks.CheckFailureException; import com.radixdlt.test.network.checks.Checks; import com.radixdlt.test.utils.TransactionUtils; +import java.util.List; +import java.util.stream.IntStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.awaitility.Durations; -import java.util.List; -import java.util.stream.IntStream; - -import static org.awaitility.Awaitility.await; - public abstract class RadixNetworkTest { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - protected final RadixNetwork radixNetwork; - protected final Checks checks; - private final List accounts; - protected final Account account1; - protected final Account account2; + protected final RadixNetwork radixNetwork; + protected final Checks checks; + private final List accounts; + protected final Account account1; + protected final Account account2; - // since test steps happen synchronously, this variable is used to store some state between steps - protected AID txBuffer; + // since test steps happen synchronously, this variable is used to store some state between steps + protected AID txBuffer; - public RadixNetworkTest() { - radixNetwork = RadixNetwork.initializeFromEnv(); - checks = Checks.forNodesAndCheckConfiguration(radixNetwork.getNodes(), radixNetwork.getConfiguration()); - accounts = Lists.newArrayList(); - IntStream.range(0, 6).forEach(i -> { - var account = radixNetwork.generateNewAccount(); - accounts.add(account); - }); - account1 = accounts.get(0); - account2 = accounts.get(1); - } + public RadixNetworkTest() { + radixNetwork = RadixNetwork.initializeFromEnv(); + checks = + Checks.forNodesAndCheckConfiguration( + radixNetwork.getNodes(), radixNetwork.getConfiguration()); + accounts = Lists.newArrayList(); + IntStream.range(0, 6) + .forEach( + i -> { + var account = radixNetwork.generateNewAccount(); + accounts.add(account); + }); + account1 = accounts.get(0); + account2 = accounts.get(1); + } - public Account getTestAccount(int number) { - return accounts.get(number); - } + public Account getTestAccount(int number) { + return accounts.get(number); + } - public String faucet(AccountAddress to) { - return radixNetwork.faucet(to); - } + public String faucet(AccountAddress to) { + return radixNetwork.faucet(to); + } - public RadixNetwork getNetwork() { - return radixNetwork; - } + public RadixNetwork getNetwork() { + return radixNetwork; + } - /** - * Calls the faucet for the given account and waits for transaction confirmation - * - * @return the txID of the faucet's transaction - */ - public String faucet(Account to) { - Balance balanceBeforeFaucet = to.getOwnNativeTokenBalance(); - String txID = faucet(to.getAddress()); - TransactionUtils.waitForConfirmation(to, AID.from(txID)); - await().atMost(Durations.TEN_SECONDS).until(() -> - // wait until the account's balance increases, just to be sure that the faucet delivered something - balanceBeforeFaucet.getAmount().compareTo(to.getOwnNativeTokenBalance().getAmount()) < 0 - ); - return txID; - } + /** + * Calls the faucet for the given account and waits for transaction confirmation + * + * @return the txID of the faucet's transaction + */ + public String faucet(Account to) { + Balance balanceBeforeFaucet = to.getOwnNativeTokenBalance(); + String txID = faucet(to.getAddress()); + TransactionUtils.waitForConfirmation(to, AID.from(txID)); + await() + .atMost(Durations.TEN_SECONDS) + .until( + () -> + // wait until the account's balance increases, just to be sure that the faucet + // delivered something + balanceBeforeFaucet.getAmount().compareTo(to.getOwnNativeTokenBalance().getAmount()) + < 0); + return txID; + } - /** - * Repeatedly calls the faucet until the given amount is credited - */ - public void faucet(Account to, Amount amount) { - Balance originalBalance = to.getOwnNativeTokenBalance(); - while (to.getOwnNativeTokenBalance().getAmount().subtract(originalBalance.getAmount()) - .compareTo(amount.toSubunits()) < 0) { - faucet(to); - } + /** Repeatedly calls the faucet until the given amount is credited */ + public void faucet(Account to, Amount amount) { + Balance originalBalance = to.getOwnNativeTokenBalance(); + while (to.getOwnNativeTokenBalance() + .getAmount() + .subtract(originalBalance.getAmount()) + .compareTo(amount.toSubunits()) + < 0) { + faucet(to); } + } - /** - * @throws IllegalArgumentException if such a check does not exist - * @throws CheckFailureException if the check failed - */ - public void runCheck(String name, Object... variables) { - boolean result = checks.runCheck(name, variables); - if (!result) { - throw new CheckFailureException(name); - } - logger.info("Check '{}' passed", name); + /** + * @throws IllegalArgumentException if such a check does not exist + * @throws CheckFailureException if the check failed + */ + public void runCheck(String name, Object... variables) { + boolean result = checks.runCheck(name, variables); + if (!result) { + throw new CheckFailureException(name); } - + logger.info("Check '{}' passed", name); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/account/Account.java b/radixdlt-regression/src/main/java/com/radixdlt/test/account/Account.java index 3c511e7581..bee58fb825 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/account/Account.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/account/Account.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.account; import com.radixdlt.application.tokens.Amount; @@ -16,210 +80,235 @@ import com.radixdlt.test.network.RadixNetworkConfiguration; import com.radixdlt.test.utils.TransactionUtils; import com.radixdlt.utils.UInt256; -import org.apache.commons.lang.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.time.Duration; import java.util.Optional; import java.util.stream.Collectors; +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * A wrapper around an imperative api client + a keypair - */ +/** A wrapper around an imperative api client + a keypair */ public final class Account implements ImperativeRadixApi, RadixAccount { - private static final Logger logger = LogManager.getLogger(); - - private final ImperativeRadixApi client; - private final ECKeyPair keyPair; - private final AccountAddress address; - private final TokenInfo nativeToken; - - private Account(ImperativeRadixApi client, ECKeyPair keyPair, TokenInfo nativeToken) { - this.client = client; - this.keyPair = keyPair; - this.address = AccountAddress.create(keyPair.getPublicKey()); - this.nativeToken = nativeToken; - } - - @Override - public Account withTrace() { - client.withTrace(); - return this; - } - - @Override - public ImperativeRadixApi withTimeout(Duration timeout) { - client.withTimeout(timeout); - return this; - } - - @Override - public Network network() { - return client.network(); - } - - @Override - public Transaction transaction() { - return client.transaction(); - } - - @Override - public Token token() { - return client.token(); - } - - @Override - public Local local() { - return client.local(); - } - - @Override - public SingleAccount account() { - return client.account(); - } - - @Override - public Validator validator() { - return client.validator(); - } - - @Override - public Api api() { - return client.api(); - } - - @Override - public Consensus consensus() { - return client.consensus(); - } - - @Override - public Mempool mempool() { - return client.mempool(); - } - - @Override - public RadixEngine radixEngine() { - return client.radixEngine(); - } - - @Override - public Sync sync() { - return client.sync(); - } - - @Override - public Ledger ledger() { - return client.ledger(); - } - - public AccountAddress getAddress() { - return address; - } - - /** - * resolves the address based on the network ID - */ - public String getAddressForNetwork() { - return Addressing.ofNetworkId(network().id().getNetworkId()).forAccounts().of(address.getAddress()); - } - - public ECKeyPair getKeyPair() { - return keyPair; - } - - public static Account initialize(String jsonRpcUrl, int primaryPort, int secondaryPort, ECKeyPair keyPair, - String basicAuthString) { - var api = (StringUtils.isBlank(basicAuthString)) + private static final Logger logger = LogManager.getLogger(); + + private final ImperativeRadixApi client; + private final ECKeyPair keyPair; + private final AccountAddress address; + private final TokenInfo nativeToken; + + private Account(ImperativeRadixApi client, ECKeyPair keyPair, TokenInfo nativeToken) { + this.client = client; + this.keyPair = keyPair; + this.address = AccountAddress.create(keyPair.getPublicKey()); + this.nativeToken = nativeToken; + } + + @Override + public Account withTrace() { + client.withTrace(); + return this; + } + + @Override + public ImperativeRadixApi withTimeout(Duration timeout) { + client.withTimeout(timeout); + return this; + } + + @Override + public Network network() { + return client.network(); + } + + @Override + public Transaction transaction() { + return client.transaction(); + } + + @Override + public Token token() { + return client.token(); + } + + @Override + public Local local() { + return client.local(); + } + + @Override + public SingleAccount account() { + return client.account(); + } + + @Override + public Validator validator() { + return client.validator(); + } + + @Override + public Api api() { + return client.api(); + } + + @Override + public Consensus consensus() { + return client.consensus(); + } + + @Override + public Mempool mempool() { + return client.mempool(); + } + + @Override + public RadixEngine radixEngine() { + return client.radixEngine(); + } + + @Override + public Sync sync() { + return client.sync(); + } + + @Override + public Ledger ledger() { + return client.ledger(); + } + + public AccountAddress getAddress() { + return address; + } + + /** resolves the address based on the network ID */ + public String getAddressForNetwork() { + return Addressing.ofNetworkId(network().id().getNetworkId()) + .forAccounts() + .of(address.getAddress()); + } + + public ECKeyPair getKeyPair() { + return keyPair; + } + + public static Account initialize( + String jsonRpcUrl, + int primaryPort, + int secondaryPort, + ECKeyPair keyPair, + String basicAuthString) { + var api = + (StringUtils.isBlank(basicAuthString)) ? ImperativeRadixApi.connect(jsonRpcUrl, primaryPort, secondaryPort) - : ImperativeRadixApi.connect(jsonRpcUrl, primaryPort, secondaryPort, parseBasicAuthString(basicAuthString)); - var nativeToken = api.token().describeNative(); - var newAccount = new Account(api, keyPair, nativeToken); - logger.trace("Generated new account with address: {}", newAccount.getAddress()); - logger.trace("New account connected to {}()", jsonRpcUrl); - logger.trace("Network's native token is {}({})", nativeToken.getName(), nativeToken.getSymbol()); - return newAccount; - } - - public static Account initialize(String jsonRpcUrl, int primaryPort, int secondaryPort) { - return initialize(jsonRpcUrl, primaryPort, secondaryPort, ECKeyPair.generateNew(), null); - } - - public static Account initialize(RadixNetworkConfiguration configuration) { - return initialize(configuration.getJsonRpcRootUrl(), configuration.getPrimaryPort(), configuration.getSecondaryPort(), - ECKeyPair.generateNew(), configuration.getBasicAuth()); - } - - public TokenInfo getNativeToken() { - return nativeToken; - } - - public Balance getOwnNativeTokenBalance() { - Balance zeroNativeTokenBalance = Balance.create(nativeToken.getRri(), UInt256.ZERO); - var balances = getOwnTokenBalances().getTokenBalances().stream().filter(balance -> - balance.getRri().equals(nativeToken.getRri())).collect(Collectors.toList()); - return balances.isEmpty() ? zeroNativeTokenBalance : balances.get(0); - } - - public TokenBalances getOwnTokenBalances() { - return client.account().balances(address); - } - - @Override - public TransactionDTO lookup(AID txID) { - return TransactionUtils.lookupTransaction(this, txID); - } - - @Override - public AID transfer(Account destination, Amount amount, Optional message) { - return TransactionUtils.nativeTokenTransfer(this, destination, amount, message); - } - - @Override - public AID transfer(Account destination, Amount amount, String rri, Optional message) { - var request = message - .map(s -> TransactionRequest.createBuilder(address) - .transfer(address, destination.getAddress(), amount.toSubunits(), rri).message(s).build()) - .orElseGet(() -> TransactionRequest.createBuilder(address) - .transfer(address, destination.getAddress(), amount.toSubunits(), rri).build()); - return TransactionUtils.buildFinalizeAndSubmitTransaction(this, request, true); - } - - @Override - public AID stake(ValidatorAddress validatorAddress, Amount amount, Optional message) { - return TransactionUtils.stake(this, validatorAddress, amount, message); - } - - @Override - public AID unstake(ValidatorAddress validatorAddress, Amount amount, Optional message) { - return TransactionUtils.unstake(this, validatorAddress, amount, message); - } - - @Override - public AID fixedSupplyToken(String symbol, String name, String description, String iconUrl, String tokenUrl, - Amount supply) { - return TransactionUtils.createFixedSupplyToken(this, symbol, name, description, iconUrl, tokenUrl, supply); - } - - @Override - public AID mutableSupplyToken(String symbol, String name, String description, String iconUrl, String tokenUrl) { - return TransactionUtils.createMutableSupplyToken(this, symbol, name, description, iconUrl, tokenUrl); - } - - @Override - public AID mint(Amount amount, String rri, Optional message) { - return TransactionUtils.mint(this, amount, rri, message); - } - - @Override - public AID burn(Amount amount, String rri, Optional message) { - return TransactionUtils.burn(this, amount, rri, message); - } - - private static BasicAuth parseBasicAuthString(String basicAuthString) { - var array = basicAuthString.split("\\:"); - return BasicAuth.with(array[0], array[1]); - } - + : ImperativeRadixApi.connect( + jsonRpcUrl, primaryPort, secondaryPort, parseBasicAuthString(basicAuthString)); + var nativeToken = api.token().describeNative(); + var newAccount = new Account(api, keyPair, nativeToken); + logger.trace("Generated new account with address: {}", newAccount.getAddress()); + logger.trace("New account connected to {}()", jsonRpcUrl); + logger.trace( + "Network's native token is {}({})", nativeToken.getName(), nativeToken.getSymbol()); + return newAccount; + } + + public static Account initialize(String jsonRpcUrl, int primaryPort, int secondaryPort) { + return initialize(jsonRpcUrl, primaryPort, secondaryPort, ECKeyPair.generateNew(), null); + } + + public static Account initialize(RadixNetworkConfiguration configuration) { + return initialize( + configuration.getJsonRpcRootUrl(), + configuration.getPrimaryPort(), + configuration.getSecondaryPort(), + ECKeyPair.generateNew(), + configuration.getBasicAuth()); + } + + public TokenInfo getNativeToken() { + return nativeToken; + } + + public Balance getOwnNativeTokenBalance() { + Balance zeroNativeTokenBalance = Balance.create(nativeToken.getRri(), UInt256.ZERO); + var balances = + getOwnTokenBalances().getTokenBalances().stream() + .filter(balance -> balance.getRri().equals(nativeToken.getRri())) + .collect(Collectors.toList()); + return balances.isEmpty() ? zeroNativeTokenBalance : balances.get(0); + } + + public TokenBalances getOwnTokenBalances() { + return client.account().balances(address); + } + + @Override + public TransactionDTO lookup(AID txID) { + return TransactionUtils.lookupTransaction(this, txID); + } + + @Override + public AID transfer(Account destination, Amount amount, Optional message) { + return TransactionUtils.nativeTokenTransfer(this, destination, amount, message); + } + + @Override + public AID transfer(Account destination, Amount amount, String rri, Optional message) { + var request = + message + .map( + s -> + TransactionRequest.createBuilder(address) + .transfer(address, destination.getAddress(), amount.toSubunits(), rri) + .message(s) + .build()) + .orElseGet( + () -> + TransactionRequest.createBuilder(address) + .transfer(address, destination.getAddress(), amount.toSubunits(), rri) + .build()); + return TransactionUtils.buildFinalizeAndSubmitTransaction(this, request, true); + } + + @Override + public AID stake(ValidatorAddress validatorAddress, Amount amount, Optional message) { + return TransactionUtils.stake(this, validatorAddress, amount, message); + } + + @Override + public AID unstake(ValidatorAddress validatorAddress, Amount amount, Optional message) { + return TransactionUtils.unstake(this, validatorAddress, amount, message); + } + + @Override + public AID fixedSupplyToken( + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + Amount supply) { + return TransactionUtils.createFixedSupplyToken( + this, symbol, name, description, iconUrl, tokenUrl, supply); + } + + @Override + public AID mutableSupplyToken( + String symbol, String name, String description, String iconUrl, String tokenUrl) { + return TransactionUtils.createMutableSupplyToken( + this, symbol, name, description, iconUrl, tokenUrl); + } + + @Override + public AID mint(Amount amount, String rri, Optional message) { + return TransactionUtils.mint(this, amount, rri, message); + } + + @Override + public AID burn(Amount amount, String rri, Optional message) { + return TransactionUtils.burn(this, amount, rri, message); + } + + private static BasicAuth parseBasicAuthString(String basicAuthString) { + var array = basicAuthString.split("\\:"); + return BasicAuth.with(array[0], array[1]); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/account/AsyncAccount.java b/radixdlt-regression/src/main/java/com/radixdlt/test/account/AsyncAccount.java index fa7a22484e..4e64be51b6 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/account/AsyncAccount.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/account/AsyncAccount.java @@ -1,6 +1,69 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.account; -import com.radixdlt.test.utils.TestingUtils; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.sync.RadixApi; import com.radixdlt.client.lib.dto.Balance; @@ -8,152 +71,162 @@ import com.radixdlt.client.lib.dto.TokenInfo; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.networks.Addressing; +import com.radixdlt.test.utils.TestingUtils; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Result; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.time.Duration; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * A wrapper around an async api client + a keypair - */ +/** A wrapper around an async api client + a keypair */ public final class AsyncAccount implements RadixApi { - private static final Logger logger = LogManager.getLogger(); - - private final RadixApi client; - private final ECKeyPair keyPair; - private final AccountAddress address; - private final TokenInfo nativeToken; - - private AsyncAccount(RadixApi client, ECKeyPair keyPair, TokenInfo nativeToken) { - this.client = client; - this.keyPair = keyPair; - this.address = AccountAddress.create(keyPair.getPublicKey()); - this.nativeToken = nativeToken; - } - - @Override - public AsyncAccount withTrace() { - client.withTrace(); - return this; - } - - @Override - public Addressing addressing() { - return client.addressing(); - } - - @Override - public RadixApi withTimeout(Duration timeout) { - client.withTimeout(timeout); - return this; - } - - @Override - public Network network() { - return client.network(); - } - - @Override - public Transaction transaction() { - return client.transaction(); - } - - @Override - public Token token() { - return client.token(); - } - - @Override - public Local local() { - return client.local(); - } - - @Override - public SingleAccount account() { - return client.account(); - } - - @Override - public Validator validator() { - return client.validator(); - } - - @Override - public Api api() { - return client.api(); - } - - @Override - public Consensus consensus() { - return client.consensus(); - } - - @Override - public Mempool mempool() { - return client.mempool(); - } - - @Override - public RadixEngine radixEngine() { - return client.radixEngine(); - } - - @Override - public Sync sync() { - return client.sync(); - } - - @Override - public Ledger ledger() { - return client.ledger(); - } - - public AccountAddress getAddress() { - return address; - } - - public ECKeyPair getKeyPair() { - return keyPair; - } - - public static Result initialize(String jsonRpcUrl) { - return RadixApi.connect(jsonRpcUrl) - .flatMap(api -> api.token().describeNative().map(nativeToken -> { - var newAccount = new AsyncAccount(api, ECKeyPair.generateNew(), nativeToken); - logger.trace("Generated new account with address: {}", newAccount.getAddress()); - logger.trace("New account connected to {}", jsonRpcUrl); - logger.trace("Network's native token is {}({})", nativeToken.getName(), nativeToken.getSymbol()); - return newAccount; - })); - } - - /** - * returns the native token - */ - public TokenInfo getNativeToken() { - return nativeToken; - } - - public Result ownNativeTokenBalance() { - Balance zeroNativeTokenBalance = Balance.create(nativeToken.getRri(), UInt256.ZERO); - return getOwnTokenBalances().map(tokenBalancesDTO -> { - if (tokenBalancesDTO.getTokenBalances().size() == 0) { + private static final Logger logger = LogManager.getLogger(); + + private final RadixApi client; + private final ECKeyPair keyPair; + private final AccountAddress address; + private final TokenInfo nativeToken; + + private AsyncAccount(RadixApi client, ECKeyPair keyPair, TokenInfo nativeToken) { + this.client = client; + this.keyPair = keyPair; + this.address = AccountAddress.create(keyPair.getPublicKey()); + this.nativeToken = nativeToken; + } + + @Override + public AsyncAccount withTrace() { + client.withTrace(); + return this; + } + + @Override + public Addressing addressing() { + return client.addressing(); + } + + @Override + public RadixApi withTimeout(Duration timeout) { + client.withTimeout(timeout); + return this; + } + + @Override + public Network network() { + return client.network(); + } + + @Override + public Transaction transaction() { + return client.transaction(); + } + + @Override + public Token token() { + return client.token(); + } + + @Override + public Local local() { + return client.local(); + } + + @Override + public SingleAccount account() { + return client.account(); + } + + @Override + public Validator validator() { + return client.validator(); + } + + @Override + public Api api() { + return client.api(); + } + + @Override + public Consensus consensus() { + return client.consensus(); + } + + @Override + public Mempool mempool() { + return client.mempool(); + } + + @Override + public RadixEngine radixEngine() { + return client.radixEngine(); + } + + @Override + public Sync sync() { + return client.sync(); + } + + @Override + public Ledger ledger() { + return client.ledger(); + } + + public AccountAddress getAddress() { + return address; + } + + public ECKeyPair getKeyPair() { + return keyPair; + } + + public static Result initialize(String jsonRpcUrl) { + return RadixApi.connect(jsonRpcUrl) + .flatMap( + api -> + api.token() + .describeNative() + .map( + nativeToken -> { + var newAccount = + new AsyncAccount(api, ECKeyPair.generateNew(), nativeToken); + logger.trace( + "Generated new account with address: {}", newAccount.getAddress()); + logger.trace("New account connected to {}", jsonRpcUrl); + logger.trace( + "Network's native token is {}({})", + nativeToken.getName(), + nativeToken.getSymbol()); + return newAccount; + })); + } + + /** returns the native token */ + public TokenInfo getNativeToken() { + return nativeToken; + } + + public Result ownNativeTokenBalance() { + Balance zeroNativeTokenBalance = Balance.create(nativeToken.getRri(), UInt256.ZERO); + return getOwnTokenBalances() + .map( + tokenBalancesDTO -> { + if (tokenBalancesDTO.getTokenBalances().size() == 0) { return zeroNativeTokenBalance; - } - var balances = tokenBalancesDTO.getTokenBalances().stream().filter(balance -> - balance.getRri().equals(nativeToken.getRri())).collect(Collectors.toList()); - return balances.isEmpty() ? zeroNativeTokenBalance : balances.get(0); - }); - } - - public Balance getOwnNativeTokenBalance() { - return ownNativeTokenBalance().fold(TestingUtils::toTestFailureException, balance -> balance); - } - - public Result getOwnTokenBalances() { - return client.account().balances(address); - } + } + var balances = + tokenBalancesDTO.getTokenBalances().stream() + .filter(balance -> balance.getRri().equals(nativeToken.getRri())) + .collect(Collectors.toList()); + return balances.isEmpty() ? zeroNativeTokenBalance : balances.get(0); + }); + } + + public Balance getOwnNativeTokenBalance() { + return ownNativeTokenBalance().fold(TestingUtils::toTestFailureException, balance -> balance); + } + + public Result getOwnTokenBalances() { + return client.account().balances(address); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/account/RadixAccount.java b/radixdlt-regression/src/main/java/com/radixdlt/test/account/RadixAccount.java index a2982682da..42ba480001 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/account/RadixAccount.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/account/RadixAccount.java @@ -1,33 +1,100 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.account; import com.radixdlt.application.tokens.Amount; import com.radixdlt.client.lib.api.ValidatorAddress; import com.radixdlt.client.lib.dto.TransactionDTO; import com.radixdlt.identifiers.AID; - import java.util.Optional; public interface RadixAccount { - TransactionDTO lookup(AID txID); - - /** - * transfers native tokens - */ - AID transfer(Account destination, Amount amount, Optional message); + TransactionDTO lookup(AID txID); - AID transfer(Account destination, Amount amount, String rri, Optional message); + /** transfers native tokens */ + AID transfer(Account destination, Amount amount, Optional message); - AID stake(ValidatorAddress validatorAddress, Amount amount, Optional message); + AID transfer(Account destination, Amount amount, String rri, Optional message); - AID unstake(ValidatorAddress validatorAddress, Amount amount, Optional message); + AID stake(ValidatorAddress validatorAddress, Amount amount, Optional message); - AID fixedSupplyToken(String symbol, String name, String description, String iconUrl, String tokenUrl, Amount supply); + AID unstake(ValidatorAddress validatorAddress, Amount amount, Optional message); - AID mutableSupplyToken(String symbol, String name, String description, String iconUrl, String tokenUrl); + AID fixedSupplyToken( + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + Amount supply); - AID mint(Amount amount, String rri, Optional message); + AID mutableSupplyToken( + String symbol, String name, String description, String iconUrl, String tokenUrl); - AID burn(Amount amount, String rri, Optional message); + AID mint(Amount amount, String rri, Optional message); + AID burn(Amount amount, String rri, Optional message); } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BIP32Path.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BIP32Path.java index e89761fde0..5fb2545157 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BIP32Path.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BIP32Path.java @@ -1,76 +1,140 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.google.common.base.Objects; import com.radixdlt.test.crypto.errors.HDPathException; /** - * A wrapper around some underlying - * BIP32 (BIP-32) implementation, - * that is easily swappable. + * A wrapper around some underlying BIP32 (BIP-32) + * implementation, that is easily swappable. * - * This class doesn't and shouldn't inherit from said wrapped implementation, but rather use - * it as a trampoline. Since all interfaces are forwarded to the underlying wrapped implementation - * this class should really be trivial. + *

This class doesn't and shouldn't inherit from said wrapped implementation, but rather use it + * as a trampoline. Since all interfaces are forwarded to the underlying wrapped implementation this + * class should really be trivial. * - * However, users are discouraged to construct instances of this class directly, they should + *

However, users are discouraged to construct instances of this class directly, they should * rather be using {@link DefaultHDPath}. */ final class BIP32Path implements HDPath { - private final HDPath path; + private final HDPath path; - private BIP32Path(String path) throws HDPathException { - this.path = BitcoinJBIP32Path.fromString(path); - } + private BIP32Path(String path) throws HDPathException { + this.path = BitcoinJBIP32Path.fromString(path); + } - public static BIP32Path fromString(String path) throws HDPathException { - return new BIP32Path(path); - } + public static BIP32Path fromString(String path) throws HDPathException { + return new BIP32Path(path); + } - @Override - public boolean isHardened() { - return path.isHardened(); - } + @Override + public boolean isHardened() { + return path.isHardened(); + } - @Override - public boolean hasPrivateKey() { - return path.hasPrivateKey(); - } + @Override + public boolean hasPrivateKey() { + return path.hasPrivateKey(); + } - @Override - public String toString() { - return path.toString(); - } + @Override + public String toString() { + return path.toString(); + } - @Override - public int depth() { - return path.depth(); - } + @Override + public int depth() { + return path.depth(); + } - @Override - public long index() { - return path.index(); - } + @Override + public long index() { + return path.index(); + } - @Override - public HDPath next() { - return path.next(); - } + @Override + public HDPath next() { + return path.next(); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BIP32Path bip32Path = (BIP32Path) o; - return Objects.equal(path, bip32Path.path); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hashCode(path); + if (o == null || getClass() != o.getClass()) { + return false; } + BIP32Path bip32Path = (BIP32Path) o; + return Objects.equal(path, bip32Path.path); + } + + @Override + public int hashCode() { + return Objects.hashCode(path); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJBIP32Path.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJBIP32Path.java index d67d14a021..4ca0588325 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJBIP32Path.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJBIP32Path.java @@ -1,126 +1,194 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.google.common.base.Objects; import com.radixdlt.test.crypto.errors.HDPathException; -import org.bitcoinj.crypto.ChildNumber; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import javax.annotation.Nullable; +import org.bitcoinj.crypto.ChildNumber; -/** - * A BIP32 path wrapping underlying implementation using BitcoinJ. - */ +/** A BIP32 path wrapping underlying implementation using BitcoinJ. */ public final class BitcoinJBIP32Path implements HDPath { - private static final String BIP32_HARDENED_MARKER_BITCOINJ = "H"; - - private final org.bitcoinj.crypto.HDPath path; - - private BitcoinJBIP32Path(org.bitcoinj.crypto.HDPath path) { - this.path = path; - } - - public static BitcoinJBIP32Path fromPath(HDPath path) { - try { - return fromString(path.toString()); - } catch (HDPathException e) { - throw new IllegalStateException("String representation of any path should be correct.", e); - } - } - - public static BitcoinJBIP32Path fromString(String path) throws HDPathException { - HDPaths.validateHDPathString(path); - return new BitcoinJBIP32Path(org.bitcoinj.crypto.HDPath.parsePath(toBitcoinJPath(path))); - } - - private static String toBitcoinJPath(String standardPath) { - // For some reason BitcoinJ chose to not use standard notation of hardened path components.... - return standardPath.replace(HDPaths.BIP32_HARDENED_MARKER_STANDARD, BIP32_HARDENED_MARKER_BITCOINJ); - } - - private static String standardizePath(String nonStandardPath) { - // For some reason BitcoinJ chose to not use standard notation of hardened path components.... - return nonStandardPath.replace(BIP32_HARDENED_MARKER_BITCOINJ, HDPaths.BIP32_HARDENED_MARKER_STANDARD); - } - - public int indexOfLastComponent() { - if (depth() == 0) { - throw new IllegalStateException("Trying to access component of a BIP32 path with 0 depth, this is undefined."); - } - return depth() - 1; - } - - private ChildNumber lastComponent() { - return path.get(indexOfLastComponent()); - } - - public List componentsUpTo(int index) { - return IntStream.range(0, index).mapToObj(path::get).collect(Collectors.toList()); - } - - List components() { - return componentsUpTo(depth()); - } - - @Override - public boolean isHardened() { - return lastComponent().isHardened(); - } - - @Override - public boolean hasPrivateKey() { - return path.hasPrivateKey(); - } - - @Override - public String toString() { - return standardizePath(path.toString()); - } - - @Override - public int depth() { - return path.size(); - } - - @Override - public long index() { - long index = lastComponent().num(); - if (!isHardened()) { - return index; - } - index += HDPaths.BIP32_HARDENED_VALUE_INCREMENT; - return index; - } - - @Override - public HDPath next() { - ArrayList nextPathComponents = new ArrayList<>(pathListFromBIP32Path(this, indexOfLastComponent())); - nextPathComponents.add(new ChildNumber(lastComponent().num() + 1, lastComponent().isHardened())); - org.bitcoinj.crypto.HDPath nextPath = new org.bitcoinj.crypto.HDPath(this.hasPrivateKey(), nextPathComponents); - return new BitcoinJBIP32Path(nextPath); - } - - private static List pathListFromBIP32Path(BitcoinJBIP32Path path, @Nullable Integer toIndex) { - return path.componentsUpTo(toIndex == null ? path.indexOfLastComponent() : toIndex); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BitcoinJBIP32Path that = (BitcoinJBIP32Path) o; - return Objects.equal(path, that.path); - } - - @Override - public int hashCode() { - return Objects.hashCode(path); - } + private static final String BIP32_HARDENED_MARKER_BITCOINJ = "H"; + + private final org.bitcoinj.crypto.HDPath path; + + private BitcoinJBIP32Path(org.bitcoinj.crypto.HDPath path) { + this.path = path; + } + + public static BitcoinJBIP32Path fromPath(HDPath path) { + try { + return fromString(path.toString()); + } catch (HDPathException e) { + throw new IllegalStateException("String representation of any path should be correct.", e); + } + } + + public static BitcoinJBIP32Path fromString(String path) throws HDPathException { + HDPaths.validateHDPathString(path); + return new BitcoinJBIP32Path(org.bitcoinj.crypto.HDPath.parsePath(toBitcoinJPath(path))); + } + + private static String toBitcoinJPath(String standardPath) { + // For some reason BitcoinJ chose to not use standard notation of hardened path components.... + return standardPath.replace( + HDPaths.BIP32_HARDENED_MARKER_STANDARD, BIP32_HARDENED_MARKER_BITCOINJ); + } + + private static String standardizePath(String nonStandardPath) { + // For some reason BitcoinJ chose to not use standard notation of hardened path components.... + return nonStandardPath.replace( + BIP32_HARDENED_MARKER_BITCOINJ, HDPaths.BIP32_HARDENED_MARKER_STANDARD); + } + + public int indexOfLastComponent() { + if (depth() == 0) { + throw new IllegalStateException( + "Trying to access component of a BIP32 path with 0 depth, this is undefined."); + } + return depth() - 1; + } + + private ChildNumber lastComponent() { + return path.get(indexOfLastComponent()); + } + + public List componentsUpTo(int index) { + return IntStream.range(0, index).mapToObj(path::get).collect(Collectors.toList()); + } + + List components() { + return componentsUpTo(depth()); + } + + @Override + public boolean isHardened() { + return lastComponent().isHardened(); + } + + @Override + public boolean hasPrivateKey() { + return path.hasPrivateKey(); + } + + @Override + public String toString() { + return standardizePath(path.toString()); + } + + @Override + public int depth() { + return path.size(); + } + + @Override + public long index() { + long index = lastComponent().num(); + if (!isHardened()) { + return index; + } + index += HDPaths.BIP32_HARDENED_VALUE_INCREMENT; + return index; + } + + @Override + public HDPath next() { + ArrayList nextPathComponents = + new ArrayList<>(pathListFromBIP32Path(this, indexOfLastComponent())); + nextPathComponents.add( + new ChildNumber(lastComponent().num() + 1, lastComponent().isHardened())); + org.bitcoinj.crypto.HDPath nextPath = + new org.bitcoinj.crypto.HDPath(this.hasPrivateKey(), nextPathComponents); + return new BitcoinJBIP32Path(nextPath); + } + + private static List pathListFromBIP32Path( + BitcoinJBIP32Path path, @Nullable Integer toIndex) { + return path.componentsUpTo(toIndex == null ? path.indexOfLastComponent() : toIndex); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BitcoinJBIP32Path that = (BitcoinJBIP32Path) o; + return Objects.equal(path, that.path); + } + + @Override + public int hashCode() { + return Objects.hashCode(path); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJHDKeyPairDerivation.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJHDKeyPairDerivation.java index fea716dbe7..905f547422 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJHDKeyPairDerivation.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJHDKeyPairDerivation.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.google.common.annotations.VisibleForTesting; @@ -6,76 +70,69 @@ import com.radixdlt.crypto.exception.PrivateKeyException; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.utils.Bytes; +import java.util.List; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicHierarchy; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.HDKeyDerivation; -import java.util.List; - @SecurityCritical({SecurityCritical.SecurityKind.KEY_GENERATION}) public final class BitcoinJHDKeyPairDerivation implements HDKeyPairDerivation { - /** - * A BIP32 extended root key. - */ - private final DeterministicKey bip32ExtendedRootKey; + /** A BIP32 extended root key. */ + private final DeterministicKey bip32ExtendedRootKey; - private final DeterministicHierarchy deterministicHierarchy; + private final DeterministicHierarchy deterministicHierarchy; - BitcoinJHDKeyPairDerivation(byte[] seed) { - this(HDKeyDerivation.createMasterPrivateKey(seed)); - } + BitcoinJHDKeyPairDerivation(byte[] seed) { + this(HDKeyDerivation.createMasterPrivateKey(seed)); + } - @VisibleForTesting - BitcoinJHDKeyPairDerivation(DeterministicKey bip32ExtendedRootKey) { - this.bip32ExtendedRootKey = bip32ExtendedRootKey; - this.deterministicHierarchy = new DeterministicHierarchy(bip32ExtendedRootKey); - } + @VisibleForTesting + BitcoinJHDKeyPairDerivation(DeterministicKey bip32ExtendedRootKey) { + this.bip32ExtendedRootKey = bip32ExtendedRootKey; + this.deterministicHierarchy = new DeterministicHierarchy(bip32ExtendedRootKey); + } - private static List pathListFromHDPath(HDPath path) { - return BitcoinJBIP32Path.fromPath(path).components(); - } + private static List pathListFromHDPath(HDPath path) { + return BitcoinJBIP32Path.fromPath(path).components(); + } - private DeterministicKey deriveKeyForHDPath(HDPath path) { - List pathList = pathListFromHDPath(path); - return deriveKeyForPath(pathList); - } + private DeterministicKey deriveKeyForHDPath(HDPath path) { + List pathList = pathListFromHDPath(path); + return deriveKeyForPath(pathList); + } - private DeterministicKey deriveKeyForPath(List path) { - return deterministicHierarchy.deriveChild( - path.subList(0, path.size() - 1), - false, - true, - path.get(path.size() - 1) - ); - } + private DeterministicKey deriveKeyForPath(List path) { + return deterministicHierarchy.deriveChild( + path.subList(0, path.size() - 1), false, true, path.get(path.size() - 1)); + } - @VisibleForTesting - String rootPrivateKeyHex() { - return Bytes.toHexString(bip32ExtendedRootKey.getPrivKeyBytes()); - } + @VisibleForTesting + String rootPrivateKeyHex() { + return Bytes.toHexString(bip32ExtendedRootKey.getPrivKeyBytes()); + } - @VisibleForTesting - String extendedRootKeyHex() { - return bip32ExtendedRootKey.serializePrivB58(NetworkParameters.fromID(NetworkParameters.ID_MAINNET)); - } + @VisibleForTesting + String extendedRootKeyHex() { + return bip32ExtendedRootKey.serializePrivB58( + NetworkParameters.fromID(NetworkParameters.ID_MAINNET)); + } - @VisibleForTesting - String rootPublicKeyHex() { - return Bytes.toHexString(bip32ExtendedRootKey.getPubKey()); - } + @VisibleForTesting + String rootPublicKeyHex() { + return Bytes.toHexString(bip32ExtendedRootKey.getPubKey()); + } - @Override - public HDKeyPair deriveKeyAtPath(HDPath path) { - DeterministicKey childKey = deriveKeyForHDPath(path); - try { - ECKeyPair ecKeyPair = ECKeyPair.fromPrivateKey(childKey.getPrivKeyBytes()); - return new HDKeyPair(ecKeyPair, path); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalStateException("Failed to generate ECKeyPair", e); - } + @Override + public HDKeyPair deriveKeyAtPath(HDPath path) { + DeterministicKey childKey = deriveKeyForHDPath(path); + try { + ECKeyPair ecKeyPair = ECKeyPair.fromPrivateKey(childKey.getPrivKeyBytes()); + return new HDKeyPair(ecKeyPair, path); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalStateException("Failed to generate ECKeyPair", e); } - + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJMnemonicToSeedConverter.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJMnemonicToSeedConverter.java index 1a87358ae6..e4d4d44140 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJMnemonicToSeedConverter.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/BitcoinJMnemonicToSeedConverter.java @@ -1,78 +1,143 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; -import com.radixdlt.test.crypto.errors.MnemonicException; -import org.bitcoinj.crypto.MnemonicCode; +import static org.bitcoinj.core.Utils.WHITESPACE_SPLITTER; +import com.radixdlt.test.crypto.errors.MnemonicException; import java.util.List; import java.util.Objects; - -import static org.bitcoinj.core.Utils.WHITESPACE_SPLITTER; +import org.bitcoinj.crypto.MnemonicCode; public final class BitcoinJMnemonicToSeedConverter { - private BitcoinJMnemonicToSeedConverter() { - throw new IllegalStateException("Can't construct."); - } - - public static byte[] seedFromMnemonicAndPassphrase(List words, String passphrase) throws MnemonicException { - validateMnemonic(words); - Objects.requireNonNull(passphrase); - return MnemonicCode.toSeed(words, passphrase); - } - - public static byte[] seedFromMnemonic(List words) throws MnemonicException { - return seedFromMnemonicAndPassphrase(words, HDPaths.BIP39_MNEMONIC_NO_PASSPHRASE); - } - - public static byte[] seedFromMnemonicStringAndPassphrase(String mnemonic, String passphrase) throws MnemonicException { - return seedFromMnemonicAndPassphrase(wordsFromMnemonicString(mnemonic), passphrase); + private BitcoinJMnemonicToSeedConverter() { + throw new IllegalStateException("Can't construct."); + } + + public static byte[] seedFromMnemonicAndPassphrase(List words, String passphrase) + throws MnemonicException { + validateMnemonic(words); + Objects.requireNonNull(passphrase); + return MnemonicCode.toSeed(words, passphrase); + } + + public static byte[] seedFromMnemonic(List words) throws MnemonicException { + return seedFromMnemonicAndPassphrase(words, HDPaths.BIP39_MNEMONIC_NO_PASSPHRASE); + } + + public static byte[] seedFromMnemonicStringAndPassphrase(String mnemonic, String passphrase) + throws MnemonicException { + return seedFromMnemonicAndPassphrase(wordsFromMnemonicString(mnemonic), passphrase); + } + + public static byte[] seedFromMnemonicString(String mnemonic) throws MnemonicException { + return seedFromMnemonicStringAndPassphrase(mnemonic, HDPaths.BIP39_MNEMONIC_NO_PASSPHRASE); + } + + static void validateMnemonic(List words) throws MnemonicException { + try { + MnemonicCode.INSTANCE.check(words); + } catch (org.bitcoinj.crypto.MnemonicException e) { + throw new MnemonicException("Mnemonic does not pass validation check", e); } - - public static byte[] seedFromMnemonicString(String mnemonic) throws MnemonicException { - return seedFromMnemonicStringAndPassphrase(mnemonic, HDPaths.BIP39_MNEMONIC_NO_PASSPHRASE); - } - - static void validateMnemonic(List words) throws MnemonicException { - try { - MnemonicCode.INSTANCE.check(words); - } catch (org.bitcoinj.crypto.MnemonicException e) { - throw new MnemonicException("Mnemonic does not pass validation check", e); - } - } - - static void validateMnemonicString(String mnemonic) throws MnemonicException { - validateMnemonic(wordsFromMnemonicString(mnemonic)); - } - - static boolean isValidMnemonic(List words) { - try { - validateMnemonic(words); - return true; - } catch (Exception e) { - return false; - } + } + + static void validateMnemonicString(String mnemonic) throws MnemonicException { + validateMnemonic(wordsFromMnemonicString(mnemonic)); + } + + static boolean isValidMnemonic(List words) { + try { + validateMnemonic(words); + return true; + } catch (Exception e) { + return false; } - - static boolean isValidMnemonicString(String mnemonic) { - return isValidMnemonic(wordsFromMnemonicString(mnemonic)); - } - - private static List wordsFromMnemonicString(String string) { - return WHITESPACE_SPLITTER.splitToList(string); - } - - static byte[] seedFromEntropyAndPassphrase(byte[] entropy, String passphrase) throws MnemonicException { - List mnemonicFromEntropy = null; - try { - mnemonicFromEntropy = MnemonicCode.INSTANCE.toMnemonic(entropy); - } catch (org.bitcoinj.crypto.MnemonicException.MnemonicLengthException e) { - throw new MnemonicException("Failed to created seed from entropy, incorrect length", e); - } - return seedFromMnemonicAndPassphrase(mnemonicFromEntropy, passphrase); - } - - static byte[] seedFromEntropy(byte[] entropy) throws MnemonicException { - return seedFromEntropyAndPassphrase(entropy, HDPaths.BIP39_MNEMONIC_NO_PASSPHRASE); + } + + static boolean isValidMnemonicString(String mnemonic) { + return isValidMnemonic(wordsFromMnemonicString(mnemonic)); + } + + private static List wordsFromMnemonicString(String string) { + return WHITESPACE_SPLITTER.splitToList(string); + } + + static byte[] seedFromEntropyAndPassphrase(byte[] entropy, String passphrase) + throws MnemonicException { + List mnemonicFromEntropy = null; + try { + mnemonicFromEntropy = MnemonicCode.INSTANCE.toMnemonic(entropy); + } catch (org.bitcoinj.crypto.MnemonicException.MnemonicLengthException e) { + throw new MnemonicException("Failed to created seed from entropy, incorrect length", e); } + return seedFromMnemonicAndPassphrase(mnemonicFromEntropy, passphrase); + } + static byte[] seedFromEntropy(byte[] entropy) throws MnemonicException { + return seedFromEntropyAndPassphrase(entropy, HDPaths.BIP39_MNEMONIC_NO_PASSPHRASE); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDKeyPairDerivation.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDKeyPairDerivation.java index 5d09d2e64c..06391482f8 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDKeyPairDerivation.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDKeyPairDerivation.java @@ -1,102 +1,185 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.radixdlt.test.crypto.errors.MnemonicException; import com.radixdlt.utils.Bytes; - import java.util.List; public final class DefaultHDKeyPairDerivation { - private DefaultHDKeyPairDerivation() { - throw new IllegalStateException("Can't construct."); - } + private DefaultHDKeyPairDerivation() { + throw new IllegalStateException("Can't construct."); + } - /** - * Creates a {@link HDKeyPairDerivation} from some {@code seed} (typically created from some - * BIP39 (BIP-39) mnemonic), - * which can be used to derived {@link HDKeyPair} from some {@link HDPath}. - * @param seed, typically created from some - * * BIP39 (BIP-39) mnemonic. - * @return a {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some {@link HDPath}. - */ - public static HDKeyPairDerivation fromSeed(byte[] seed) { - return new BitcoinJHDKeyPairDerivation(seed); - } + /** + * Creates a {@link HDKeyPairDerivation} from some {@code seed} (typically created from some BIP39 (BIP-39) + * mnemonic), which can be used to derived {@link HDKeyPair} from some {@link HDPath}. + * + * @param seed, typically created from some * BIP39 (BIP-39) + * mnemonic. + * @return a {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some + * {@link HDPath}. + */ + public static HDKeyPairDerivation fromSeed(byte[] seed) { + return new BitcoinJHDKeyPairDerivation(seed); + } - /** - * Creates a {@link HDKeyPairDerivation} from some {@code seedHex}, being a seed encoded as a hexadecimal string, where - * the seed is typically created from some - * BIP39 (BIP-39) mnemonic. - * The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from some {@link HDPath}. - * @param seedHex a seed encoded as a hexadecimal string, where the seed is typically created from some - * BIP39 (BIP-39) mnemonic. - * @return a {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some {@link HDPath}. - */ - public static HDKeyPairDerivation fromSeed(String seedHex) { - return fromSeed(Bytes.fromHexString(seedHex)); - } + /** + * Creates a {@link HDKeyPairDerivation} from some {@code seedHex}, being a seed encoded as a + * hexadecimal string, where the seed is typically created from some BIP39 (BIP-39) + * mnemonic. The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from some + * {@link HDPath}. + * + * @param seedHex a seed encoded as a hexadecimal string, where the seed is typically created from + * some BIP39 + * (BIP-39) mnemonic. + * @return a {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some + * {@link HDPath}. + */ + public static HDKeyPairDerivation fromSeed(String seedHex) { + return fromSeed(Bytes.fromHexString(seedHex)); + } - /** - * Creates a {@link HDKeyPairDerivation} from the - * BIP39 (BIP-39) mnemonic string + - * {@code passphrase}. The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from some {@link HDPath}. - * @param mnemonic a BIP39 (BIP-39) mnemonic string - - * assuming a space is used to separate the words. - * @param passphrase A non null passphrase used together with {@code mnemonic} to form a seed. - * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some {@link HDPath}, - * or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. - * @throws MnemonicException thrown if the {@code mnemonic} is invalid. - */ - public static HDKeyPairDerivation fromMnemonicStringAndPassphrase(String mnemonic, String passphrase) throws MnemonicException { - byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonicStringAndPassphrase(mnemonic, passphrase); - return fromSeed(seed); - } + /** + * Creates a {@link HDKeyPairDerivation} from the BIP39 (BIP-39) + * mnemonic string + {@code passphrase}. The {@link HDKeyPairDerivation} can be used to derived + * {@link HDKeyPair} from some {@link HDPath}. + * + * @param mnemonic a BIP39 (BIP-39) + * mnemonic string - assuming a space is used to separate the words. + * @param passphrase A non null passphrase used together with {@code mnemonic} to form a seed. + * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some + * {@link HDPath}, or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. + * @throws MnemonicException thrown if the {@code mnemonic} is invalid. + */ + public static HDKeyPairDerivation fromMnemonicStringAndPassphrase( + String mnemonic, String passphrase) throws MnemonicException { + byte[] seed = + DefaultMnemonicToSeedConverter.seedFromMnemonicStringAndPassphrase(mnemonic, passphrase); + return fromSeed(seed); + } - /** - * Creates a {@link HDKeyPairDerivation} from the - * BIP39 (BIP-39) mnemonic string, using - * the empty string as passphrase. The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from some {@link HDPath}. - * @param mnemonic a BIP39 (BIP-39) mnemonic string - - * assuming a space is used to separate the words. - * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some {@link HDPath}, - * or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. - * @throws MnemonicException thrown if the {@code mnemonic} is invalid. - */ - public static HDKeyPairDerivation fromMnemonicString(String mnemonic) throws MnemonicException { - byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonicString(mnemonic); - return fromSeed(seed); - } + /** + * Creates a {@link HDKeyPairDerivation} from the BIP39 (BIP-39) + * mnemonic string, using the empty string as passphrase. The {@link HDKeyPairDerivation} can be + * used to derived {@link HDKeyPair} from some {@link HDPath}. + * + * @param mnemonic a BIP39 (BIP-39) + * mnemonic string - assuming a space is used to separate the words. + * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some + * {@link HDPath}, or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. + * @throws MnemonicException thrown if the {@code mnemonic} is invalid. + */ + public static HDKeyPairDerivation fromMnemonicString(String mnemonic) throws MnemonicException { + byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonicString(mnemonic); + return fromSeed(seed); + } - /** - * Creates a {@link HDKeyPairDerivation} from the - * BIP39 (BIP-39) mnemonic words + - * {@code passphrase}. The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from some {@link HDPath}. - * @param words a list of words making up a - * BIP39 (BIP-39) mnemonic, - * together with the {@code passphrase}. - * @param passphrase A non null passphrase used together with {@code words} to form a seed. - * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some {@link HDPath}, - * or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. - * @throws MnemonicException thrown if the mnemonic {@code words} are invalid. - */ - public static HDKeyPairDerivation fromMnemonicWordsAndPassphrase(List words, String passphrase) throws MnemonicException { - byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonicAndPassphrase(words, passphrase); - return fromSeed(seed); - } + /** + * Creates a {@link HDKeyPairDerivation} from the BIP39 (BIP-39) + * mnemonic words + {@code passphrase}. The {@link HDKeyPairDerivation} can be used to derived + * {@link HDKeyPair} from some {@link HDPath}. + * + * @param words a list of words making up a BIP39 (BIP-39) + * mnemonic, together with the {@code passphrase}. + * @param passphrase A non null passphrase used together with {@code words} to form a seed. + * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some + * {@link HDPath}, or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. + * @throws MnemonicException thrown if the mnemonic {@code words} are invalid. + */ + public static HDKeyPairDerivation fromMnemonicWordsAndPassphrase( + List words, String passphrase) throws MnemonicException { + byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonicAndPassphrase(words, passphrase); + return fromSeed(seed); + } - /** - * Creates a {@link HDKeyPairDerivation} from the - * BIP39 (BIP-39) mnemonic words. - * The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from some {@link HDPath}. - * @param words a list of words making up a - * BIP39 (BIP-39) mnemonic, - * using the empty string as passphrase. - * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some {@link HDPath}, - * or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. - * @throws MnemonicException thrown if the mnemonic {@code words} are invalid. - */ - public static HDKeyPairDerivation fromMnemonicWords(List words) throws MnemonicException { - byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonic(words); - return fromSeed(seed); - } + /** + * Creates a {@link HDKeyPairDerivation} from the BIP39 (BIP-39) + * mnemonic words. The {@link HDKeyPairDerivation} can be used to derived {@link HDKeyPair} from + * some {@link HDPath}. + * + * @param words a list of words making up a BIP39 (BIP-39) + * mnemonic, using the empty string as passphrase. + * @return A {@link HDKeyPairDerivation} which an be used to derived {@link HDKeyPair} from some + * {@link HDPath}, or if the mnemonic is invalid, a {@link MnemonicException} will be thrown. + * @throws MnemonicException thrown if the mnemonic {@code words} are invalid. + */ + public static HDKeyPairDerivation fromMnemonicWords(List words) throws MnemonicException { + byte[] seed = DefaultMnemonicToSeedConverter.seedFromMnemonic(words); + return fromSeed(seed); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDPath.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDPath.java index 7153ba65e8..42401553b1 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDPath.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultHDPath.java @@ -1,23 +1,87 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.radixdlt.test.crypto.errors.HDPathException; public final class DefaultHDPath { - private DefaultHDPath() { - throw new IllegalStateException("Can't construct."); - } + private DefaultHDPath() { + throw new IllegalStateException("Can't construct."); + } - /** - * Tries to create a {@link HDPath} instance from the given {@code path}. If said path is not a valid HDPath, - * an exception will be thrown. - * @param path a string representing a hierarchy deterministic path, typically - * BIP32 (BIP-32) - * compliant. - * @return a valid hierarchy deterministic path ({@link HDPath}). - * @throws HDPathException thrown if {@code path} is invalid. - */ - public static HDPath of(String path) throws HDPathException { - return BIP32Path.fromString(path); - } + /** + * Tries to create a {@link HDPath} instance from the given {@code path}. If said path is not a + * valid HDPath, an exception will be thrown. + * + * @param path a string representing a hierarchy deterministic path, typically BIP32 (BIP-32) + * compliant. + * @return a valid hierarchy deterministic path ({@link HDPath}). + * @throws HDPathException thrown if {@code path} is invalid. + */ + public static HDPath of(String path) throws HDPathException { + return BIP32Path.fromString(path); + } } - diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultMnemonicToSeedConverter.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultMnemonicToSeedConverter.java index 22f8e5499b..d976aeb8ef 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultMnemonicToSeedConverter.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/DefaultMnemonicToSeedConverter.java @@ -1,92 +1,174 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.radixdlt.test.crypto.errors.MnemonicException; import java.util.List; /** - * Converter of a BIP39 (BIP-39) mnemonic code, - * to a seed (byte array) used to create some (HD root) key pair. + * Converter of a BIP39 + * (BIP-39) mnemonic code, to a seed (byte array) used to create some (HD root) key pair. */ public final class DefaultMnemonicToSeedConverter { - private DefaultMnemonicToSeedConverter() { - throw new IllegalStateException("Can't construct."); - } + private DefaultMnemonicToSeedConverter() { + throw new IllegalStateException("Can't construct."); + } - /** - * Returns a binary seed from a list of BIP39 mnemonic words + provided passphrase - * @param words list of BIP39 mnemonic words. - * @param passphrase BIP39 seed passphrase, can be empty, but must not be null. - * @return a binary seed from a list of BIP39 mnemonic words + provided passphrase - * @throws MnemonicException thrown if the mnemonic {@code words} is invalid. - */ - public static byte[] seedFromMnemonicAndPassphrase(List words, String passphrase) throws MnemonicException { - return BitcoinJMnemonicToSeedConverter.seedFromMnemonicAndPassphrase(words, passphrase); - } + /** + * Returns a binary seed from a list of BIP39 mnemonic words + provided passphrase + * + * @param words list of BIP39 mnemonic words. + * @param passphrase BIP39 seed passphrase, can be empty, but must not be null. + * @return a binary seed from a list of BIP39 mnemonic words + provided passphrase + * @throws MnemonicException thrown if the mnemonic {@code words} is invalid. + */ + public static byte[] seedFromMnemonicAndPassphrase(List words, String passphrase) + throws MnemonicException { + return BitcoinJMnemonicToSeedConverter.seedFromMnemonicAndPassphrase(words, passphrase); + } - /** - * Returns a binary seed from a list of BIP39 mnemonic words, using an empty passphrase ({@code ""}) - * @param words list of BIP39 mnemonic words. - * @return a binary seed from a list of BIP39 mnemonic words, using an empty passphrase ({@code ""}) - * @throws MnemonicException thrown if the mnemonic {@code words} is invalid. - */ - public static byte[] seedFromMnemonic(List words) throws MnemonicException { - return BitcoinJMnemonicToSeedConverter.seedFromMnemonic(words); - } + /** + * Returns a binary seed from a list of BIP39 mnemonic words, using an empty passphrase ({@code + * ""}) + * + * @param words list of BIP39 mnemonic words. + * @return a binary seed from a list of BIP39 mnemonic words, using an empty passphrase ({@code + * ""}) + * @throws MnemonicException thrown if the mnemonic {@code words} is invalid. + */ + public static byte[] seedFromMnemonic(List words) throws MnemonicException { + return BitcoinJMnemonicToSeedConverter.seedFromMnemonic(words); + } - /** - * Returns a binary seed from a BIP39 mnemonic string (assuming a space is used to separate the words) + provided passphrase - * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) - * @param passphrase BIP39 seed passphrase, can be empty, but must not be null. - * @return a binary seed from a list of BIP39 mnemonic words + provided passphrase - * @throws MnemonicException thrown if the {@code mnemonic} is invalid. - */ - public static byte[] seedFromMnemonicStringAndPassphrase(String mnemonic, String passphrase) throws MnemonicException { - return BitcoinJMnemonicToSeedConverter.seedFromMnemonicStringAndPassphrase(mnemonic, passphrase); - } + /** + * Returns a binary seed from a BIP39 mnemonic string (assuming a space is used to separate the + * words) + provided passphrase + * + * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) + * @param passphrase BIP39 seed passphrase, can be empty, but must not be null. + * @return a binary seed from a list of BIP39 mnemonic words + provided passphrase + * @throws MnemonicException thrown if the {@code mnemonic} is invalid. + */ + public static byte[] seedFromMnemonicStringAndPassphrase(String mnemonic, String passphrase) + throws MnemonicException { + return BitcoinJMnemonicToSeedConverter.seedFromMnemonicStringAndPassphrase( + mnemonic, passphrase); + } - /** - * Returns a binary seed from a BIP39 mnemonic string (assuming a space is used to separate the words), using an empty passphrase ({@code ""}) - * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) - * @return a binary seed from a list of BIP39 mnemonic words, using an empty passphrase ({@code ""}) - * @throws MnemonicException thrown if the {@code mnemonic} is invalid. - */ - public static byte[] seedFromMnemonicString(String mnemonic) throws MnemonicException { - return BitcoinJMnemonicToSeedConverter.seedFromMnemonicString(mnemonic); - } + /** + * Returns a binary seed from a BIP39 mnemonic string (assuming a space is used to separate the + * words), using an empty passphrase ({@code ""}) + * + * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) + * @return a binary seed from a list of BIP39 mnemonic words, using an empty passphrase ({@code + * ""}) + * @throws MnemonicException thrown if the {@code mnemonic} is invalid. + */ + public static byte[] seedFromMnemonicString(String mnemonic) throws MnemonicException { + return BitcoinJMnemonicToSeedConverter.seedFromMnemonicString(mnemonic); + } - /** - * Validates the list of words to see if they form a valid BIP39 mnemonic, if the words are invalid an exception is thrown. - * @param words a list of words to check if they form a valid BIP39 mnemonic. - * @throws MnemonicException thrown if the mnemonic {@code words} is invalid. - */ - public static void validateMnemonic(List words) throws MnemonicException { - BitcoinJMnemonicToSeedConverter.validateMnemonic(words); - } + /** + * Validates the list of words to see if they form a valid BIP39 mnemonic, if the words are + * invalid an exception is thrown. + * + * @param words a list of words to check if they form a valid BIP39 mnemonic. + * @throws MnemonicException thrown if the mnemonic {@code words} is invalid. + */ + public static void validateMnemonic(List words) throws MnemonicException { + BitcoinJMnemonicToSeedConverter.validateMnemonic(words); + } - /** - * Return {@code true} iff the mnemonic {@code words} form a valid BIP39 mnemonic, else {@code false } is returned. - * @param words a list of words to check if they form a valid BIP39 mnemonic. - */ - public static boolean isValidMnemonic(List words) { - return BitcoinJMnemonicToSeedConverter.isValidMnemonic(words); - } + /** + * Return {@code true} iff the mnemonic {@code words} form a valid BIP39 mnemonic, else {@code + * false } is returned. + * + * @param words a list of words to check if they form a valid BIP39 mnemonic. + */ + public static boolean isValidMnemonic(List words) { + return BitcoinJMnemonicToSeedConverter.isValidMnemonic(words); + } - /** - * Validates the {@code mnemonic} string (assuming a space is used to separate the words), - * form a valid BIP39 mnemonic, if the words are invalid an exception is thrown. - * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) - * @throws MnemonicException thrown if the {@code mnemonic} is invalid. - */ - public static void validateMnemonicString(String mnemonic) throws MnemonicException { - BitcoinJMnemonicToSeedConverter.validateMnemonicString(mnemonic); - } + /** + * Validates the {@code mnemonic} string (assuming a space is used to separate the words), form a + * valid BIP39 mnemonic, if the words are invalid an exception is thrown. + * + * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) + * @throws MnemonicException thrown if the {@code mnemonic} is invalid. + */ + public static void validateMnemonicString(String mnemonic) throws MnemonicException { + BitcoinJMnemonicToSeedConverter.validateMnemonicString(mnemonic); + } - /** - * Return {@code true} iff the {@code mnemonic} string (assuming a space is used to separate the words) - * form a valid BIP39 mnemonic, else {@code false } is returned. - * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) - */ - public static boolean isValidMnemonicString(String mnemonic) { - return BitcoinJMnemonicToSeedConverter.isValidMnemonicString(mnemonic); - } -} \ No newline at end of file + /** + * Return {@code true} iff the {@code mnemonic} string (assuming a space is used to separate the + * words) form a valid BIP39 mnemonic, else {@code false } is returned. + * + * @param mnemonic a BIP39 mnemonic string (assuming a space is used to separate the words) + */ + public static boolean isValidMnemonicString(String mnemonic) { + return BitcoinJMnemonicToSeedConverter.isValidMnemonicString(mnemonic); + } +} diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPair.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPair.java index 4d0db2de8b..75fe382fbb 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPair.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPair.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.google.common.annotations.VisibleForTesting; @@ -5,86 +69,84 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.utils.Bytes; - -/** - * A key pair which has been derived using some {@link HDPath}. - */ +/** A key pair which has been derived using some {@link HDPath}. */ public final class HDKeyPair { - private final ECKeyPair ecKeyPair; - private final HDPath path; + private final ECKeyPair ecKeyPair; + private final HDPath path; - // Not public since one should be using `HDKeyPairDerivation.deriveKeyAtPath` instead of using this directly. - // since we cannot assert that the `HDPath` and the `ECKeyPair` indeed matches. - HDKeyPair(ECKeyPair ecKeyPair, HDPath path) { - this.ecKeyPair = ecKeyPair; - this.path = path; - } + // Not public since one should be using `HDKeyPairDerivation.deriveKeyAtPath` instead of using + // this directly. + // since we cannot assert that the `HDPath` and the `ECKeyPair` indeed matches. + HDKeyPair(ECKeyPair ecKeyPair, HDPath path) { + this.ecKeyPair = ecKeyPair; + this.path = path; + } - /** - * Returns the {@link HDPath} that was used to derive the Key Pair returned by {@link #keyPair()}. - * @return the {@link HDPath} that was used to derive the Key Pair returned by {@link #keyPair()}. - */ - public HDPath path() { - return path; - } + /** + * Returns the {@link HDPath} that was used to derive the Key Pair returned by {@link #keyPair()}. + * + * @return the {@link HDPath} that was used to derive the Key Pair returned by {@link #keyPair()}. + */ + public HDPath path() { + return path; + } - /** - * The resulting (child) {@link ECKeyPair} after having derived it using the {@link HDPath} and {@link HDKeyPairDerivation}, together with - * some root key (from a seed). - * @return resulting (child) {@link ECKeyPair} after having derived it using the {@link HDPath} and {@link HDKeyPairDerivation}, together with - * * some root key (from a seed). - */ - public ECKeyPair keyPair() { - return ecKeyPair; - } + /** + * The resulting (child) {@link ECKeyPair} after having derived it using the {@link HDPath} and + * {@link HDKeyPairDerivation}, together with some root key (from a seed). + * + * @return resulting (child) {@link ECKeyPair} after having derived it using the {@link HDPath} + * and {@link HDKeyPairDerivation}, together with * some root key (from a seed). + */ + public ECKeyPair keyPair() { + return ecKeyPair; + } - @Override - public String toString() { - return path.toString(); - } + @Override + public String toString() { + return path.toString(); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - HDKeyPair hdKeyPair = (HDKeyPair) o; - return Objects.equal(ecKeyPair, hdKeyPair.ecKeyPair) - && - Objects.equal(path, hdKeyPair.path); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hashCode(ecKeyPair, path); + if (o == null || getClass() != o.getClass()) { + return false; } + HDKeyPair hdKeyPair = (HDKeyPair) o; + return Objects.equal(ecKeyPair, hdKeyPair.ecKeyPair) && Objects.equal(path, hdKeyPair.path); + } - @VisibleForTesting - String privateKeyHex() { - return Bytes.toHexString(ecKeyPair.getPrivateKey()); - } + @Override + public int hashCode() { + return Objects.hashCode(ecKeyPair, path); + } - @VisibleForTesting - String publicKeyHex() { - return Bytes.toHexString(ecKeyPair.getPublicKey().getCompressedBytes()); - } + @VisibleForTesting + String privateKeyHex() { + return Bytes.toHexString(ecKeyPair.getPrivateKey()); + } - @VisibleForTesting - boolean isHardened() { - return path.isHardened(); - } + @VisibleForTesting + String publicKeyHex() { + return Bytes.toHexString(ecKeyPair.getPublicKey().getCompressedBytes()); + } - @VisibleForTesting - int depth() { - return path.depth(); - } + @VisibleForTesting + boolean isHardened() { + return path.isHardened(); + } - @VisibleForTesting - long index() { - return path.index(); - } + @VisibleForTesting + int depth() { + return path.depth(); + } + + @VisibleForTesting + long index() { + return path.index(); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPairDerivation.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPairDerivation.java index 976824bcfa..c2a262bd59 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPairDerivation.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDKeyPairDerivation.java @@ -1,36 +1,102 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.radixdlt.test.crypto.errors.HDPathException; /** - * A type being able to derive hierarchy deterministic key pairs ({@link HDKeyPair}), - * from a {@link HDPath}, typically using - * BIP32 (BIP-32) - * child key derivation. + * A type being able to derive hierarchy deterministic key pairs ({@link HDKeyPair}), from a {@link + * HDPath}, typically using BIP32 (BIP-32) child + * key derivation. */ public interface HDKeyPairDerivation { - /** - * Derives an {@link HDKeyPair} for the given {@link HDPath} - * @param path used to derive {@link HDKeyPair} - * @return a derived {@link HDKeyPair} for the given {@link HDPath} - */ - HDKeyPair deriveKeyAtPath(HDPath path); + /** + * Derives an {@link HDKeyPair} for the given {@link HDPath} + * + * @param path used to derive {@link HDKeyPair} + * @return a derived {@link HDKeyPair} for the given {@link HDPath} + */ + HDKeyPair deriveKeyAtPath(HDPath path); - /** - * Tries to derives an {@link HDKeyPair} for the given {@code path} string, if the string is invalid, - * then {@link IllegalArgumentException} will be thrown, so use this method with caution and consider - * using {@link #deriveKeyAtPath(HDPath)} instead. - * @param path used to derive {@link HDKeyPair} - * @return a derived {@link HDKeyPair} for the given {@code path} string, if the string is valid path, else - * an {@link IllegalArgumentException} will be thrown. - */ - default HDKeyPair deriveKeyAtPath(String path) { - try { - HDPath hdPath = DefaultHDPath.of(path); - return deriveKeyAtPath(hdPath); - } catch (HDPathException e) { - throw new IllegalArgumentException("Failed to construct HD path " + e); - } + /** + * Tries to derives an {@link HDKeyPair} for the given {@code path} string, if the string is + * invalid, then {@link IllegalArgumentException} will be thrown, so use this method with caution + * and consider using {@link #deriveKeyAtPath(HDPath)} instead. + * + * @param path used to derive {@link HDKeyPair} + * @return a derived {@link HDKeyPair} for the given {@code path} string, if the string is valid + * path, else an {@link IllegalArgumentException} will be thrown. + */ + default HDKeyPair deriveKeyAtPath(String path) { + try { + HDPath hdPath = DefaultHDPath.of(path); + return deriveKeyAtPath(hdPath); + } catch (HDPathException e) { + throw new IllegalArgumentException("Failed to construct HD path " + e); } + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPath.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPath.java index 47ea80a1c0..7104ac78da 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPath.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPath.java @@ -1,62 +1,136 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; /** - * A path use to deterministically derive a key pair in some hierarchy given by some root key. The path is - * typically BIP32 (BIP-32) compliant. + * A path use to deterministically derive a key pair in some hierarchy given by some root key. The + * path is typically BIP32 + * (BIP-32) compliant. */ public interface HDPath { - /** - * The string representation of the BIP32 path, using standard notation "'" for hardened components, e.g. - * "m/44'/536'/2'/1/4 - * @return a string representation of the BIP32 path, using standard notation "'" for hardened components, e.g. - * "m/44'/536'/2'/1/4 - */ - @Override - String toString(); - - /** - * Is this a path to a private key? - * - * @return true if yes, false if no or a partial path - */ - boolean hasPrivateKey(); - - /** - * Whether the last component in the path is "hardened" or not, if the last component is not hardened, it does not mean - * that potentially earlier components are not hardened as well, i.e. this only looks at the last component. - * @return whether the last component in the path is "hardened" or not, if the last component is not hardened, it does not mean - * that potentially earlier components are not hardened as well, i.e. this only looks at the last component. - */ - boolean isHardened(); - - /** - * The number of components in the path, `1` is the lowest possible value, and most commonly 5 is the max depth, even though BIP32 - * supports a longer depth. The depth of "m/0" is 1, the depth of "m/0'/1" is 2 etc. - * @return number of components in the path, `1` is the lowest possible value, and most commonly 5 is the max depth, even though BIP32 - * supports a longer depth. The depth of "m/0" is 1, the depth of "m/0'/1" is 2 etc. - */ - int depth(); - - /** - * Returns the value of the last component, taking into account if it is hardened or not, i.e. the index of the path "m/0/0" is 0, but - * the index of the path "m/0/0'" - which is hardened - is 2147483648 - * (0 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT HARDENED_BITMASK}) - - * and the index of "m/0/1'" is 2147483649 - * (1 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT HARDENED_BITMASK}). - * - * @return the value of the last component, taking into account if it is hardened or not, i.e. the index of the path "m/0/0" is 0, but - * the index of the path "m/0/0'" - which is hardened - is 2147483648 - * (0 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT HARDENED_BITMASK}) - - * and the index of "m/0/1'" is 2147483649 - * (1 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT HARDENED_BITMASK}). - */ - long index(); - - - /** - * Returns the path to the subsequent child key key pair, e.g. identical to this path but with the value of {@link #index() + 1}. - * @return the path to the subsequent child key key pair, e.g. identical to this path but with the value of {@link #index()} + 1. - */ - HDPath next(); + /** + * The string representation of the BIP32 path, using standard notation "'" for hardened + * components, e.g. "m/44'/536'/2'/1/4 + * + * @return a string representation of the BIP32 path, using standard notation "'" for hardened + * components, e.g. "m/44'/536'/2'/1/4 + */ + @Override + String toString(); + + /** + * Is this a path to a private key? + * + * @return true if yes, false if no or a partial path + */ + boolean hasPrivateKey(); + + /** + * Whether the last component in the path is "hardened" or not, if the last component is not + * hardened, it does not mean that potentially earlier components are not hardened as well, i.e. + * this only looks at the last component. + * + * @return whether the last component in the path is "hardened" or not, if the last component is + * not hardened, it does not mean that potentially earlier components are not hardened as + * well, i.e. this only looks at the last component. + */ + boolean isHardened(); + + /** + * The number of components in the path, `1` is the lowest possible value, and most commonly 5 is + * the max depth, even though BIP32 supports a longer depth. The depth of "m/0" is 1, the depth of + * "m/0'/1" is 2 etc. + * + * @return number of components in the path, `1` is the lowest possible value, and most commonly 5 + * is the max depth, even though BIP32 supports a longer depth. The depth of "m/0" is 1, the + * depth of "m/0'/1" is 2 etc. + */ + int depth(); + + /** + * Returns the value of the last component, taking into account if it is hardened or not, i.e. the + * index of the path "m/0/0" is 0, but the index of the path "m/0/0'" - which is hardened - is + * 2147483648 (0 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT HARDENED_BITMASK}) - and the + * index of "m/0/1'" is 2147483649 (1 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT + * HARDENED_BITMASK}). + * + * @return the value of the last component, taking into account if it is hardened or not, i.e. the + * index of the path "m/0/0" is 0, but the index of the path "m/0/0'" - which is hardened - is + * 2147483648 (0 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT HARDENED_BITMASK}) - and the + * index of "m/0/1'" is 2147483649 (1 | {@link HDPaths#BIP32_HARDENED_VALUE_INCREMENT + * HARDENED_BITMASK}). + */ + long index(); + + /** + * Returns the path to the subsequent child key key pair, e.g. identical to this path but with the + * value of {@link #index() + 1}. + * + * @return the path to the subsequent child key key pair, e.g. identical to this path but with the + * value of {@link #index()} + 1. + */ + HDPath next(); } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPaths.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPaths.java index b820b370ff..cf0b20fe31 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPaths.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/HDPaths.java @@ -1,85 +1,145 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto; import com.radixdlt.test.crypto.errors.HDPathException; -import org.apache.logging.log4j.core.util.Integers; - import java.util.List; +import org.apache.logging.log4j.core.util.Integers; -/** - * A set of utility methods used for e.g. validating strings representing {@link HDPath}. - */ +/** A set of utility methods used for e.g. validating strings representing {@link HDPath}. */ public final class HDPaths { - private HDPaths() { - throw new IllegalStateException("Can't construct."); - } - - public static final String BIP39_MNEMONIC_NO_PASSPHRASE = ""; - public static final String BIP32_HARDENED_MARKER_STANDARD = "'"; + private HDPaths() { + throw new IllegalStateException("Can't construct."); + } - public static final String BIP32_PATH_SEPARATOR = "/"; - public static final String BIP32_PREFIX_PRIVATEKEY = "m"; + public static final String BIP39_MNEMONIC_NO_PASSPHRASE = ""; + public static final String BIP32_HARDENED_MARKER_STANDARD = "'"; - public static final long BIP32_HARDENED_VALUE_INCREMENT = 0x80000000L; + public static final String BIP32_PATH_SEPARATOR = "/"; + public static final String BIP32_PREFIX_PRIVATEKEY = "m"; - static void validateHDPathString(String path) throws HDPathException { - if (!isValidHDPath(path)) { - throw new HDPathException("Invalid BIP32 path"); - } - } + public static final long BIP32_HARDENED_VALUE_INCREMENT = 0x80000000L; - /** - * Checks if the {@code path} string is a valid BIP32 path or not, using the standard BIP32 - * hardened marker {@link #BIP32_HARDENED_MARKER_STANDARD}. - * - * @param path to validate - * @return true iff {@code path} is a valid BIP32 path, else false. - */ - static boolean isValidHDPath(String path) { - return isValidHDPath(path, BIP32_HARDENED_MARKER_STANDARD); + static void validateHDPathString(String path) throws HDPathException { + if (!isValidHDPath(path)) { + throw new HDPathException("Invalid BIP32 path"); } + } - /** - * Checks if the {@code path} string is a valid BIP32 path or not. - * - * @param path to validate - * @param hardenedMarker the string used to mark hardened paths - * @return true iff {@code path} is a valid BIP32 path, else false. - */ - static boolean isValidHDPath(String path, String hardenedMarker) { - // Check trivial paths - if (List.of("", BIP32_PREFIX_PRIVATEKEY, BIP32_PATH_SEPARATOR).contains(path)) { - return true; - } - if (path.startsWith("M/") || path.startsWith("m/")) { - path = path.substring(2); - } + /** + * Checks if the {@code path} string is a valid BIP32 path or not, using the standard BIP32 + * hardened marker {@link #BIP32_HARDENED_MARKER_STANDARD}. + * + * @param path to validate + * @return true iff {@code path} is a valid BIP32 path, else false. + */ + static boolean isValidHDPath(String path) { + return isValidHDPath(path, BIP32_HARDENED_MARKER_STANDARD); + } - if (path.isEmpty()) { - return false; - } + /** + * Checks if the {@code path} string is a valid BIP32 path or not. + * + * @param path to validate + * @param hardenedMarker the string used to mark hardened paths + * @return true iff {@code path} is a valid BIP32 path, else false. + */ + static boolean isValidHDPath(String path, String hardenedMarker) { + // Check trivial paths + if (List.of("", BIP32_PREFIX_PRIVATEKEY, BIP32_PATH_SEPARATOR).contains(path)) { + return true; + } + if (path.startsWith("M/") || path.startsWith("m/")) { + path = path.substring(2); + } - if (path.contains("//")) { - return false; - } + if (path.isEmpty()) { + return false; + } - for (String component : path.split(BIP32_PATH_SEPARATOR)) { - if (component.isEmpty()) { - return false; - } - if (component.endsWith(hardenedMarker)) { - component = component.replace(hardenedMarker, ""); - } - try { - if (Integers.parseInt(component) < 0) { - return false; - } - } catch (Exception e) { - return false; - } + if (path.contains("//")) { + return false; + } + for (String component : path.split(BIP32_PATH_SEPARATOR)) { + if (component.isEmpty()) { + return false; + } + if (component.endsWith(hardenedMarker)) { + component = component.replace(hardenedMarker, ""); + } + try { + if (Integers.parseInt(component) < 0) { + return false; } - - return true; + } catch (Exception e) { + return false; + } } + + return true; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/HDPathException.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/HDPathException.java index 9e7c66b4ab..192eaf938a 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/HDPathException.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/HDPathException.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto.errors; /** @@ -5,7 +69,7 @@ */ public class HDPathException extends Exception { - public HDPathException(String errorMessage) { - super(errorMessage); - } + public HDPathException(String errorMessage) { + super(errorMessage); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/MnemonicException.java b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/MnemonicException.java index 848e4b19a1..d39e972cb4 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/MnemonicException.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/crypto/errors/MnemonicException.java @@ -1,11 +1,73 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.crypto.errors; -/** - * Exception thrown when trying to calculate a seed using some invalid mnemonic. - */ +/** Exception thrown when trying to calculate a seed using some invalid mnemonic. */ public class MnemonicException extends Exception { - public MnemonicException(String errorMessage, Throwable throwable) { - super(errorMessage, throwable); - } + public MnemonicException(String errorMessage, Throwable throwable) { + super(errorMessage, throwable); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/DockerConfiguration.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/DockerConfiguration.java index 3ea7a94097..5f6c04bf28 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/DockerConfiguration.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/DockerConfiguration.java @@ -1,78 +1,157 @@ -package com.radixdlt.test.network; +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ +package com.radixdlt.test.network; import com.radixdlt.test.utils.TestingUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Configuration properties for using docker in the context of a radix (test) network - */ +/** Configuration properties for using docker in the context of a radix (test) network */ public class DockerConfiguration { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private final String socketUrl; - private final String containerName; - private final boolean shouldInitializeNetwork; - private final String image; - private final int initialNumberOfNodes; - private final String networkName; - private final String dockerLogin; + private final String socketUrl; + private final String containerName; + private final boolean shouldInitializeNetwork; + private final String image; + private final int initialNumberOfNodes; + private final String networkName; + private final String dockerLogin; - public DockerConfiguration(String socketUrl, String containerName, boolean shouldInitializeNetwork, String image, int initialNumberOfNodes, - String networkName, String dockerLogin) { - this.socketUrl = socketUrl; - this.containerName = containerName; - this.shouldInitializeNetwork = shouldInitializeNetwork; - this.image = image; - this.initialNumberOfNodes = initialNumberOfNodes; - this.networkName = networkName; - this.dockerLogin = dockerLogin; - } + public DockerConfiguration( + String socketUrl, + String containerName, + boolean shouldInitializeNetwork, + String image, + int initialNumberOfNodes, + String networkName, + String dockerLogin) { + this.socketUrl = socketUrl; + this.containerName = containerName; + this.shouldInitializeNetwork = shouldInitializeNetwork; + this.image = image; + this.initialNumberOfNodes = initialNumberOfNodes; + this.networkName = networkName; + this.dockerLogin = dockerLogin; + } - public static DockerConfiguration fromEnv() { - var socketUrl = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_DAEMON_URL", "unix:///var/run/docker.sock"); - var containerName = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_CONTAINER_NAME", "system-testing-core%d"); - if (containerName.contains("_")) { - logger.warn("Underscores in container names ({}) should be avoided", containerName); - } - var shouldInitializeNetworkString = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_INITIALIZE_NETWORK", "false"); - var shouldInitializeNetwork = Boolean.parseBoolean(shouldInitializeNetworkString); - var image = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_IMAGE", "radixdlt/radixdlt-core:develop"); - var initialNumberOfNodesString = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_INITIAL_NUMBER_OF_NODES", "3"); - var initialNumberOfNodes = Integer.parseInt(initialNumberOfNodesString); - var networkName = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_NETWORK_NAME", "system_testing_network"); - var dockerLogin = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_LOGIN_COMMAND", ""); - return new DockerConfiguration(socketUrl, containerName, shouldInitializeNetwork, image, initialNumberOfNodes, networkName, dockerLogin); + public static DockerConfiguration fromEnv() { + var socketUrl = + TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_DAEMON_URL", "unix:///var/run/docker.sock"); + var containerName = + TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_CONTAINER_NAME", "system-testing-core%d"); + if (containerName.contains("_")) { + logger.warn("Underscores in container names ({}) should be avoided", containerName); } + var shouldInitializeNetworkString = + TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_INITIALIZE_NETWORK", "false"); + var shouldInitializeNetwork = Boolean.parseBoolean(shouldInitializeNetworkString); + var image = + TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_IMAGE", "radixdlt/radixdlt-core:develop"); + var initialNumberOfNodesString = + TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_INITIAL_NUMBER_OF_NODES", "3"); + var initialNumberOfNodes = Integer.parseInt(initialNumberOfNodesString); + var networkName = + TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_NETWORK_NAME", "system_testing_network"); + var dockerLogin = TestingUtils.getEnvWithDefault("RADIXDLT_DOCKER_LOGIN_COMMAND", ""); + return new DockerConfiguration( + socketUrl, + containerName, + shouldInitializeNetwork, + image, + initialNumberOfNodes, + networkName, + dockerLogin); + } - public String getNetworkName() { - return networkName; - } + public String getNetworkName() { + return networkName; + } - public String getSocketUrl() { - return socketUrl; - } + public String getSocketUrl() { + return socketUrl; + } - public String getContainerName() { - return containerName; - } - - public boolean shouldInitializeNetwork() { - return shouldInitializeNetwork; - } + public String getContainerName() { + return containerName; + } - public String getImage() { - return image; - } + public boolean shouldInitializeNetwork() { + return shouldInitializeNetwork; + } - public int getInitialNumberOfNodes() { - return initialNumberOfNodes; - } + public String getImage() { + return image; + } - public String getDockerLogin() { - return dockerLogin; - } + public int getInitialNumberOfNodes() { + return initialNumberOfNodes; + } + public String getDockerLogin() { + return dockerLogin; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/FaucetException.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/FaucetException.java index 3084591ab8..4fd3f601dd 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/FaucetException.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/FaucetException.java @@ -1,12 +1,73 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network; -/** - * Thrown when something goes wrong with the faucet - */ +/** Thrown when something goes wrong with the faucet */ public class FaucetException extends RuntimeException { - public FaucetException(String message) { - super(message); - } - + public FaucetException(String message) { + super(message); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetwork.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetwork.java index feee3c1861..898a9fc6a7 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetwork.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetwork.java @@ -1,146 +1,220 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network; import com.github.dockerjava.api.exception.DockerClientException; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.test.account.Account; import com.radixdlt.test.network.client.RadixHttpClient; -import com.radixdlt.test.network.client.docker.DockerClient; import com.radixdlt.test.network.client.docker.DisabledDockerClient; -import com.radixdlt.test.network.client.docker.LocalDockerClient; +import com.radixdlt.test.network.client.docker.DockerClient; import com.radixdlt.test.network.client.docker.DockerNetworkCreator; +import com.radixdlt.test.network.client.docker.LocalDockerClient; import com.radixdlt.test.network.client.docker.RemoteDockerClient; import com.radixdlt.test.utils.universe.UniverseVariables; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * Represents an actual radix network with several running nodes. Keeps a list of the nodes, along with their addresses. - * Has many utility functions. Used when running tests against real networks (acceptance or system tests). + * Represents an actual radix network with several running nodes. Keeps a list of the nodes, along + * with their addresses. Has many utility functions. Used when running tests against real networks + * (acceptance or system tests). */ public class RadixNetwork { - private static final Logger logger = LogManager.getLogger(); - - private final RadixNetworkConfiguration configuration; - private final int networkId; - private final List nodes; - private final DockerClient dockerClient; - private final RadixHttpClient httpClient; - private final UniverseVariables universeVariables; - - private RadixNetwork(RadixNetworkConfiguration configuration, int networkId, List nodes, - RadixHttpClient httpClient, DockerClient dockerClient, UniverseVariables universeVariables) { - this.configuration = configuration; - this.networkId = networkId; - this.nodes = nodes; - this.httpClient = httpClient; - this.dockerClient = dockerClient; - this.universeVariables = universeVariables; - } - - public static RadixNetwork initializeFromEnv() { - var configuration = RadixNetworkConfiguration.fromEnv(); - prettyPrintConfiguration(configuration); - - // if we are using a local network, we may need to create it and store the universe variables - UniverseVariables universeVariables = null; - if (configuration.getDockerConfiguration().shouldInitializeNetwork() - && configuration.getType() == RadixNetworkConfiguration.Type.LOCALNET) { - var localDockerClient = new LocalDockerClient(configuration.getDockerConfiguration()); - universeVariables = DockerNetworkCreator.createNewLocalNetwork(configuration, localDockerClient); - } - - var networkId = configuration.pingJsonRpcApi(); - logger.info("Connected to JSON RPC API at {}", configuration.getJsonRpcRootUrl()); - - var dockerClient = createDockerClient(configuration); - var httpClient = RadixHttpClient.fromRadixNetworkConfiguration(configuration); - var radixNodes = RadixNetworkNodeLocator.locateNodes(configuration, httpClient, dockerClient); - if (radixNodes == null || radixNodes.size() == 0) { - throw new RuntimeException("No nodes found, cannot run tests"); - } - - logger.info("Done locating nodes, found {} in total.", radixNodes.size()); - radixNodes.forEach(node -> logger.debug(" * {}", node)); - return new RadixNetwork(configuration, networkId, radixNodes, httpClient, dockerClient, universeVariables); - } - - public Account generateNewAccount() { - return Account.initialize(configuration); - } - - /** - * will return the version from the /version endpoint of the first node (could be any node) - */ - public String getVersionOfFirstNode() { - return httpClient.getVersion(nodes.get(0).getRootUrl(), Optional.of(configuration.getSecondaryPort())); - } - - /** - * Calls the faucet to send tokens to the given address - */ - public String faucet(AccountAddress to) { - var faucets = nodes.stream().filter(node -> node.getAvailableServices() - .contains(RadixNode.ServiceType.FAUCET)).collect(Collectors.toList()); - if (faucets.isEmpty()) { - throw new FaucetException("No faucet found in this network"); - } - var nodeWithFaucet = faucets.get(0); - var address = to.toString(networkId); - var txID = httpClient.callFaucet(nodeWithFaucet.getRootUrl(), nodeWithFaucet.getSecondaryPort(), address); - logger.debug("Faucet successfully sent tokens to {}. TxID: {}", address, txID); - return txID; - } - - public DockerClient getDockerClient() { - return dockerClient; + private static final Logger logger = LogManager.getLogger(); + + private final RadixNetworkConfiguration configuration; + private final int networkId; + private final List nodes; + private final DockerClient dockerClient; + private final RadixHttpClient httpClient; + private final UniverseVariables universeVariables; + + private RadixNetwork( + RadixNetworkConfiguration configuration, + int networkId, + List nodes, + RadixHttpClient httpClient, + DockerClient dockerClient, + UniverseVariables universeVariables) { + this.configuration = configuration; + this.networkId = networkId; + this.nodes = nodes; + this.httpClient = httpClient; + this.dockerClient = dockerClient; + this.universeVariables = universeVariables; + } + + public static RadixNetwork initializeFromEnv() { + var configuration = RadixNetworkConfiguration.fromEnv(); + prettyPrintConfiguration(configuration); + + // if we are using a local network, we may need to create it and store the universe variables + UniverseVariables universeVariables = null; + if (configuration.getDockerConfiguration().shouldInitializeNetwork() + && configuration.getType() == RadixNetworkConfiguration.Type.LOCALNET) { + var localDockerClient = new LocalDockerClient(configuration.getDockerConfiguration()); + universeVariables = + DockerNetworkCreator.createNewLocalNetwork(configuration, localDockerClient); } - public RadixHttpClient getHttpClient() { - return httpClient; - } - - public List getNodes() { - return nodes; - } + var networkId = configuration.pingJsonRpcApi(); + logger.info("Connected to JSON RPC API at {}", configuration.getJsonRpcRootUrl()); - public RadixNetworkConfiguration getConfiguration() { - return configuration; + var dockerClient = createDockerClient(configuration); + var httpClient = RadixHttpClient.fromRadixNetworkConfiguration(configuration); + var radixNodes = RadixNetworkNodeLocator.locateNodes(configuration, httpClient, dockerClient); + if (radixNodes == null || radixNodes.size() == 0) { + throw new RuntimeException("No nodes found, cannot run tests"); } - public UniverseVariables getUniverseVariables() { - return universeVariables; + logger.info("Done locating nodes, found {} in total.", radixNodes.size()); + radixNodes.forEach(node -> logger.debug(" * {}", node)); + return new RadixNetwork( + configuration, networkId, radixNodes, httpClient, dockerClient, universeVariables); + } + + public Account generateNewAccount() { + return Account.initialize(configuration); + } + + /** will return the version from the /version endpoint of the first node (could be any node) */ + public String getVersionOfFirstNode() { + return httpClient.getVersion( + nodes.get(0).getRootUrl(), Optional.of(configuration.getSecondaryPort())); + } + + /** Calls the faucet to send tokens to the given address */ + public String faucet(AccountAddress to) { + var faucets = + nodes.stream() + .filter(node -> node.getAvailableServices().contains(RadixNode.ServiceType.FAUCET)) + .collect(Collectors.toList()); + if (faucets.isEmpty()) { + throw new FaucetException("No faucet found in this network"); } - - private static void prettyPrintConfiguration(RadixNetworkConfiguration configuration) { - logger.debug("Network configuration:"); - logger.debug("JSON-RPC URL: {}, type: {}", configuration.getJsonRpcRootUrl(), configuration.getType()); + var nodeWithFaucet = faucets.get(0); + var address = to.toString(networkId); + var txID = + httpClient.callFaucet( + nodeWithFaucet.getRootUrl(), nodeWithFaucet.getSecondaryPort(), address); + logger.debug("Faucet successfully sent tokens to {}. TxID: {}", address, txID); + return txID; + } + + public DockerClient getDockerClient() { + return dockerClient; + } + + public RadixHttpClient getHttpClient() { + return httpClient; + } + + public List getNodes() { + return nodes; + } + + public RadixNetworkConfiguration getConfiguration() { + return configuration; + } + + public UniverseVariables getUniverseVariables() { + return universeVariables; + } + + private static void prettyPrintConfiguration(RadixNetworkConfiguration configuration) { + logger.debug("Network configuration:"); + logger.debug( + "JSON-RPC URL: {}, type: {}", configuration.getJsonRpcRootUrl(), configuration.getType()); + } + + private static DockerClient createDockerClient(RadixNetworkConfiguration configuration) { + var dockerConfiguration = configuration.getDockerConfiguration(); + DockerClient dockerClient; + try { + switch (configuration.getType()) { + case LOCALNET: + dockerClient = new LocalDockerClient(dockerConfiguration); + break; + case TESTNET: + dockerClient = new RemoteDockerClient(configuration); + break; + default: + dockerClient = new DisabledDockerClient(); + } + logger.debug("Initialized a {} docker client", configuration.getType()); + } catch (DockerClientException e) { + logger.warn( + "Exception {} when trying to initialize a docker client. Client will be disabled.", + e.getMessage()); + dockerClient = new DisabledDockerClient(); } - - private static DockerClient createDockerClient(RadixNetworkConfiguration configuration) { - var dockerConfiguration = configuration.getDockerConfiguration(); - DockerClient dockerClient; - try { - switch (configuration.getType()) { - case LOCALNET: - dockerClient = new LocalDockerClient(dockerConfiguration); - break; - case TESTNET: - dockerClient = new RemoteDockerClient(configuration); - break; - default: - dockerClient = new DisabledDockerClient(); - } - logger.debug("Initialized a {} docker client", configuration.getType()); - } catch (DockerClientException e) { - logger.warn("Exception {} when trying to initialize a docker client. Client will be disabled.", e.getMessage()); - dockerClient = new DisabledDockerClient(); - } - return dockerClient; - } - + return dockerClient; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkConfiguration.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkConfiguration.java index 221987d9b0..2e23f11442 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkConfiguration.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkConfiguration.java @@ -1,141 +1,231 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network; +import static org.awaitility.Awaitility.await; + import com.radixdlt.client.lib.api.rpc.BasicAuth; import com.radixdlt.client.lib.api.sync.ImperativeRadixApi; import com.radixdlt.client.lib.api.sync.RadixApiException; import com.radixdlt.test.utils.TestingUtils; import com.radixdlt.utils.functional.Failure; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.awaitility.Durations; -import org.awaitility.core.ConditionTimeoutException; - import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.awaitility.Durations; +import org.awaitility.core.ConditionTimeoutException; -import static org.awaitility.Awaitility.await; - -/** - * Information needed for the initialization of a {@link RadixNetwork} - */ +/** Information needed for the initialization of a {@link RadixNetwork} */ public class RadixNetworkConfiguration { - private static final Logger logger = LogManager.getLogger(); - - private static final Duration NETWORK_PING_PATIENCE = Durations.ONE_MINUTE; - - public enum Type { - LOCALNET, - TESTNET; + private static final Logger logger = LogManager.getLogger(); + + private static final Duration NETWORK_PING_PATIENCE = Durations.ONE_MINUTE; + + public enum Type { + LOCALNET, + TESTNET; + } + + private final String jsonRpcRootUrl; + private final int primaryPort; + private final int secondaryPort; + private final String faucetUrl; + private final String basicAuth; + private final Type type; + private final DockerConfiguration dockerConfiguration; + private final SshConfiguration sshConfiguration; + + private RadixNetworkConfiguration( + String jsonRpcRootUrl, + int primaryPort, + int secondaryPort, + String faucetUrl, + String basicAuth, + DockerConfiguration dockerConfiguration, + SshConfiguration sshConfiguration) { + this.jsonRpcRootUrl = jsonRpcRootUrl; + this.primaryPort = primaryPort; + this.secondaryPort = secondaryPort; + this.faucetUrl = faucetUrl; + this.basicAuth = basicAuth; + this.type = determineType(jsonRpcRootUrl); + this.dockerConfiguration = dockerConfiguration; + this.sshConfiguration = sshConfiguration; + if (type != Type.LOCALNET && dockerConfiguration.shouldInitializeNetwork()) { + logger.warn("Cannot initialize a {} type of network", type); } - - private final String jsonRpcRootUrl; - private final int primaryPort; - private final int secondaryPort; - private final String faucetUrl; - private final String basicAuth; - private final Type type; - private final DockerConfiguration dockerConfiguration; - private final SshConfiguration sshConfiguration; - - private RadixNetworkConfiguration(String jsonRpcRootUrl, int primaryPort, int secondaryPort, String faucetUrl, String basicAuth, - DockerConfiguration dockerConfiguration, SshConfiguration sshConfiguration) { - this.jsonRpcRootUrl = jsonRpcRootUrl; - this.primaryPort = primaryPort; - this.secondaryPort = secondaryPort; - this.faucetUrl = faucetUrl; - this.basicAuth = basicAuth; - this.type = determineType(jsonRpcRootUrl); - this.dockerConfiguration = dockerConfiguration; - this.sshConfiguration = sshConfiguration; - if (type != Type.LOCALNET && dockerConfiguration.shouldInitializeNetwork()) { - logger.warn("Cannot initialize a {} type of network", type); - } + } + + public static RadixNetworkConfiguration fromEnv() { + try { + var jsonRpcRootUrlString = + TestingUtils.getEnvWithDefault("RADIXDLT_JSON_RPC_API_ROOT_URL", "http://localhost"); + var jsonRpcRootUrl = new URL(jsonRpcRootUrlString); + var primaryPort = + (jsonRpcRootUrl.getProtocol().equalsIgnoreCase("https")) + ? 443 + : Integer.parseInt( + TestingUtils.getEnvWithDefault("RADIXDLT_JSON_RPC_API_PRIMARY_PORT", "8080")); + var secondaryPort = + (jsonRpcRootUrl.getProtocol().equalsIgnoreCase("https")) + ? 443 + : Integer.parseInt( + TestingUtils.getEnvWithDefault("RADIXDLT_JSON_RPC_API_SECONDARY_PORT", "3333")); + var faucetUrl = TestingUtils.getEnvWithDefault("RADIXDLT_FAUCET_URL", ""); + var basicAuth = System.getenv("RADIXDLT_BASIC_AUTH"); + var dockerConfiguration = DockerConfiguration.fromEnv(); + var sshConfiguration = SshConfiguration.fromEnv(); + return new RadixNetworkConfiguration( + jsonRpcRootUrlString, + primaryPort, + secondaryPort, + faucetUrl, + basicAuth, + dockerConfiguration, + sshConfiguration); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Bad JSON-RPC URL", e); } - - public static RadixNetworkConfiguration fromEnv() { - try { - var jsonRpcRootUrlString = TestingUtils.getEnvWithDefault("RADIXDLT_JSON_RPC_API_ROOT_URL", "http://localhost"); - var jsonRpcRootUrl = new URL(jsonRpcRootUrlString); - var primaryPort = (jsonRpcRootUrl.getProtocol().equalsIgnoreCase("https")) ? 443 - : Integer.parseInt(TestingUtils.getEnvWithDefault("RADIXDLT_JSON_RPC_API_PRIMARY_PORT", "8080")); - var secondaryPort = (jsonRpcRootUrl.getProtocol().equalsIgnoreCase("https")) ? 443 - : Integer.parseInt(TestingUtils.getEnvWithDefault("RADIXDLT_JSON_RPC_API_SECONDARY_PORT", "3333")); - var faucetUrl = TestingUtils.getEnvWithDefault("RADIXDLT_FAUCET_URL", ""); - var basicAuth = System.getenv("RADIXDLT_BASIC_AUTH"); - var dockerConfiguration = DockerConfiguration.fromEnv(); - var sshConfiguration = SshConfiguration.fromEnv(); - return new RadixNetworkConfiguration(jsonRpcRootUrlString, primaryPort, secondaryPort, faucetUrl, basicAuth, - dockerConfiguration, sshConfiguration); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Bad JSON-RPC URL", e); - } - } - - public DockerConfiguration getDockerConfiguration() { - return dockerConfiguration; - } - - public SshConfiguration getSshConfiguration() { - return sshConfiguration; - } - - private static Type determineType(String jsonRpcUrlString) { - return jsonRpcUrlString.toLowerCase().contains("localhost") || jsonRpcUrlString.contains("127.0.0.1") - ? Type.LOCALNET : Type.TESTNET; - } - - public ImperativeRadixApi connect(Optional basicAuth) { - return basicAuth - .map(auth -> ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort, auth)) - .orElseGet(() -> ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort)); + } + + public DockerConfiguration getDockerConfiguration() { + return dockerConfiguration; + } + + public SshConfiguration getSshConfiguration() { + return sshConfiguration; + } + + private static Type determineType(String jsonRpcUrlString) { + return jsonRpcUrlString.toLowerCase().contains("localhost") + || jsonRpcUrlString.contains("127.0.0.1") + ? Type.LOCALNET + : Type.TESTNET; + } + + public ImperativeRadixApi connect(Optional basicAuth) { + return basicAuth + .map(auth -> ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort, auth)) + .orElseGet(() -> ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort)); + } + + /** + * Tries to connect and call the "networkId" method, making sure that we have a working json-rpc + * api + * + * @return the network id + */ + public int pingJsonRpcApi() { + try { + AtomicInteger networkId = new AtomicInteger(); + await() + .atMost(NETWORK_PING_PATIENCE) + .pollInterval(Durations.TWO_HUNDRED_MILLISECONDS) + .ignoreException(RadixApiException.class) + .until( + () -> { + networkId.set( + ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort) + .network() + .id() + .getNetworkId()); + return true; + }); + return networkId.intValue(); + } catch (ConditionTimeoutException e) { + throw new RadixApiException( + Failure.failure(-1, "Could not get the network's ID within " + NETWORK_PING_PATIENCE)); } + } - /** - * Tries to connect and call the "networkId" method, making sure that we have a working json-rpc api - * - * @return the network id - */ - public int pingJsonRpcApi() { - try { - AtomicInteger networkId = new AtomicInteger(); - await().atMost(NETWORK_PING_PATIENCE).pollInterval(Durations.TWO_HUNDRED_MILLISECONDS). - ignoreException(RadixApiException.class).until(() -> { - networkId.set(ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort).network().id().getNetworkId()); - return true; - }); - return networkId.intValue(); - } catch (ConditionTimeoutException e) { - throw new RadixApiException(Failure.failure(-1, "Could not get the network's ID within " - + NETWORK_PING_PATIENCE)); - } - } + public String getJsonRpcRootUrl() { + return jsonRpcRootUrl; + } - public String getJsonRpcRootUrl() { - return jsonRpcRootUrl; - } + public String getFaucetUrl() { + return faucetUrl; + } - public String getFaucetUrl() { - return faucetUrl; - } + public int getPrimaryPort() { + return primaryPort; + } - public int getPrimaryPort() { - return primaryPort; - } + public int getSecondaryPort() { + return secondaryPort; + } - public int getSecondaryPort() { - return secondaryPort; - } - - public String getBasicAuth() { - return basicAuth; - } - - public Type getType() { - return type; - } + public String getBasicAuth() { + return basicAuth; + } + public Type getType() { + return type; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkNodeLocator.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkNodeLocator.java index 40055e1c56..2d89d0b7a3 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkNodeLocator.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNetworkNodeLocator.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network; import com.github.dockerjava.api.exception.DockerException; @@ -11,151 +75,180 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.test.network.client.RadixHttpClient; import com.radixdlt.test.network.client.docker.DockerClient; -import org.apache.commons.compress.utils.Lists; -import org.apache.commons.lang.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * Has utilities for scanning and locating nodes in radix networks - */ +/** Has utilities for scanning and locating nodes in radix networks */ public class RadixNetworkNodeLocator { - private static final Logger logger = LogManager.getLogger(); - - private RadixNetworkNodeLocator() { - + private static final Logger logger = LogManager.getLogger(); + + private RadixNetworkNodeLocator() {} + + public static List locateNodes( + RadixNetworkConfiguration configuration, + RadixHttpClient httpClient, + DockerClient dockerClient) { + List radixNodes = Lists.newArrayList(); + tryAddFaucetToNodeList(configuration, radixNodes); + List peers = Lists.newArrayList(); + addSingleNodePeerToList(configuration, radixNodes); + + try { + if (StringUtils.isBlank(configuration.getBasicAuth())) { + peers = configuration.connect(Optional.empty()).network().peers(); + } else { + var credentials = configuration.getBasicAuth().split("\\:"); + var basicAuth = BasicAuth.with(credentials[0], credentials[1]); + peers = configuration.connect(Optional.of(basicAuth)).network().peers(); + } + } catch (RadixApiException e) { + if (e.getMessage().toLowerCase().contains("401 authorization required")) { + logger.warn( + "Could not fetch peers list from {} due to 401. The test will use only one archive" + + " node.", + configuration.getJsonRpcRootUrl()); + return radixNodes; + } } - public static List locateNodes(RadixNetworkConfiguration configuration, RadixHttpClient httpClient, - DockerClient dockerClient) { - List radixNodes = Lists.newArrayList(); - tryAddFaucetToNodeList(configuration, radixNodes); - List peers = Lists.newArrayList(); - addSingleNodePeerToList(configuration, radixNodes); - - try { - if (StringUtils.isBlank(configuration.getBasicAuth())) { - peers = configuration.connect(Optional.empty()).network().peers(); - } else { - var credentials = configuration.getBasicAuth().split("\\:"); - var basicAuth = BasicAuth.with(credentials[0], credentials[1]); - peers = configuration.connect(Optional.of(basicAuth)).network().peers(); - } - } catch (RadixApiException e) { - if (e.getMessage().toLowerCase().contains("401 authorization required")) { - logger.warn("Could not fetch peers list from {} due to 401. The test will use only one archive node.", - configuration.getJsonRpcRootUrl()); - return radixNodes; - } - } - - var peersSizePlusOne = peers.size() + 1; - switch (configuration.getType()) { - case LOCALNET: - logger.debug("Searching for {} local nodes...", peersSizePlusOne); - return locateLocalNodes(configuration, httpClient, dockerClient, peersSizePlusOne); - case TESTNET: - default: - logger.debug("Searching for {} testnet nodes", peersSizePlusOne); - // eventually, we will have a list of RadixNodes by parsing the peers list - return radixNodes; - } + var peersSizePlusOne = peers.size() + 1; + switch (configuration.getType()) { + case LOCALNET: + logger.debug("Searching for {} local nodes...", peersSizePlusOne); + return locateLocalNodes(configuration, httpClient, dockerClient, peersSizePlusOne); + case TESTNET: + default: + logger.debug("Searching for {} testnet nodes", peersSizePlusOne); + // eventually, we will have a list of RadixNodes by parsing the peers list + return radixNodes; } - - private static void tryAddFaucetToNodeList(RadixNetworkConfiguration configuration, List radixNodes) { - if (StringUtils.isNotBlank(configuration.getFaucetUrl())) { - // these ports might be wrong. Faucet config should be more robust - Set availableNodeServices = Sets.newHashSet(); - availableNodeServices.add(RadixNode.ServiceType.FAUCET); - var faucetNode = new RadixNode(configuration.getFaucetUrl(), configuration.getPrimaryPort(), - configuration.getSecondaryPort(), configuration.getDockerConfiguration().getContainerName(), - availableNodeServices); - var rootUrl = configuration.getFaucetUrl(); - var networkId = configuration.connect(Optional.empty()).network().id().getNetworkId(); - var randomAddress = AccountAddress.create(ECKeyPair.generateNew().getPublicKey()).toString(networkId); - try { - RadixHttpClient.fromRadixNetworkConfiguration(configuration).callFaucet(rootUrl, configuration.getPrimaryPort(), - randomAddress); - radixNodes.add(faucetNode); - logger.info("Found a primary faucet at {}", rootUrl); - } catch (FaucetException e) { - logger.warn("No faucet found at {}, test might fail if it actually requires a faucet", rootUrl); - } - } + } + + private static void tryAddFaucetToNodeList( + RadixNetworkConfiguration configuration, List radixNodes) { + if (StringUtils.isNotBlank(configuration.getFaucetUrl())) { + // these ports might be wrong. Faucet config should be more robust + Set availableNodeServices = Sets.newHashSet(); + availableNodeServices.add(RadixNode.ServiceType.FAUCET); + var faucetNode = + new RadixNode( + configuration.getFaucetUrl(), + configuration.getPrimaryPort(), + configuration.getSecondaryPort(), + configuration.getDockerConfiguration().getContainerName(), + availableNodeServices); + var rootUrl = configuration.getFaucetUrl(); + var networkId = configuration.connect(Optional.empty()).network().id().getNetworkId(); + var randomAddress = + AccountAddress.create(ECKeyPair.generateNew().getPublicKey()).toString(networkId); + try { + RadixHttpClient.fromRadixNetworkConfiguration(configuration) + .callFaucet(rootUrl, configuration.getPrimaryPort(), randomAddress); + radixNodes.add(faucetNode); + logger.info("Found a primary faucet at {}", rootUrl); + } catch (FaucetException e) { + logger.warn( + "No faucet found at {}, test might fail if it actually requires a faucet", rootUrl); + } } - - private static void addSingleNodePeerToList(RadixNetworkConfiguration configuration, List radixNodes) { - Set availableNodeServices = Sets.newHashSet(); - RadixNode singleNode = new RadixNode(configuration.getJsonRpcRootUrl(), configuration.getPrimaryPort(), - configuration.getSecondaryPort(), configuration.getDockerConfiguration().getContainerName(), + } + + private static void addSingleNodePeerToList( + RadixNetworkConfiguration configuration, List radixNodes) { + Set availableNodeServices = Sets.newHashSet(); + RadixNode singleNode = + new RadixNode( + configuration.getJsonRpcRootUrl(), + configuration.getPrimaryPort(), + configuration.getSecondaryPort(), + configuration.getDockerConfiguration().getContainerName(), availableNodeServices); - radixNodes.add(singleNode); + radixNodes.add(singleNode); + } + + private static List locateLocalNodes( + RadixNetworkConfiguration configuration, + RadixHttpClient httpClient, + DockerClient dockerClient, + int expectedNoOfNodes) { + var primaryPort = configuration.getPrimaryPort(); + var secondaryPort = configuration.getSecondaryPort(); + var dockerContainerName = configuration.getDockerConfiguration().getContainerName(); + return IntStream.range(0, expectedNoOfNodes) + .mapToObj( + counter -> { + var containerName = String.format(dockerContainerName, counter); + return figureOutNode( + configuration.getJsonRpcRootUrl(), + primaryPort + counter, + secondaryPort + counter, + containerName, + httpClient, + dockerClient); + }) + .collect(Collectors.toList()); + } + + /** + * Tries to figure out which endpoints are available (e.g. /account, /construction) and also tries + * to use the docker client to establish a connection to this node's container. + * + *

TODO handle exceptions better. Right now, the test will fail is anything goes wrong here + */ + private static RadixNode figureOutNode( + String jsonRpcRootUrl, + int primaryPort, + int secondaryPort, + String expectedContainerName, + RadixHttpClient httpClient, + DockerClient dockerClient) { + Set availableNodeServices = Sets.newHashSet(); + + var api = ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort); + var networkId = api.network().id().getNetworkId(); + availableNodeServices.add(RadixNode.ServiceType.ARCHIVE); + + var randomAddress = AccountAddress.create(ECKeyPair.generateNew().getPublicKey()); + api.account().balances(randomAddress); + availableNodeServices.add(RadixNode.ServiceType.ACCOUNT); + + try { + api.transaction().build(TransactionRequest.createBuilder(randomAddress).build()); + } catch (RadixApiException e) { + // this is expected since the random address has no funds. However, it tells us that + // /construction is available + if (!(e.getMessage().contains("Not enough balance"))) { + throw e; + } } + availableNodeServices.add(RadixNode.ServiceType.CONSTRUCTION); - private static List locateLocalNodes(RadixNetworkConfiguration configuration, RadixHttpClient httpClient, - DockerClient dockerClient, int expectedNoOfNodes) { - var primaryPort = configuration.getPrimaryPort(); - var secondaryPort = configuration.getSecondaryPort(); - var dockerContainerName = configuration.getDockerConfiguration().getContainerName(); - return IntStream.range(0, expectedNoOfNodes).mapToObj(counter -> { - var containerName = String.format(dockerContainerName, counter); - return figureOutNode(configuration.getJsonRpcRootUrl(), primaryPort + counter, secondaryPort + counter, - containerName, httpClient, dockerClient); - }).collect(Collectors.toList()); - } + api.api().data(); + availableNodeServices.add(RadixNode.ServiceType.SYSTEM); + + api.local().validatorInfo(); + availableNodeServices.add(RadixNode.ServiceType.VALIDATION); + + httpClient.callFaucet(jsonRpcRootUrl, secondaryPort, randomAddress.toString(networkId)); + availableNodeServices.add(RadixNode.ServiceType.FAUCET); - /** - * Tries to figure out which endpoints are available (e.g. /account, /construction) and also tries to use the - * docker client to establish a connection to this node's container. - *

- * TODO handle exceptions better. Right now, the test will fail is anything goes wrong here - */ - private static RadixNode figureOutNode(String jsonRpcRootUrl, int primaryPort, int secondaryPort, - String expectedContainerName, RadixHttpClient httpClient, DockerClient dockerClient) { - Set availableNodeServices = Sets.newHashSet(); - - var api = ImperativeRadixApi.connect(jsonRpcRootUrl, primaryPort, secondaryPort); - var networkId = api.network().id().getNetworkId(); - availableNodeServices.add(RadixNode.ServiceType.ARCHIVE); - - var randomAddress = AccountAddress.create(ECKeyPair.generateNew().getPublicKey()); - api.account().balances(randomAddress); - availableNodeServices.add(RadixNode.ServiceType.ACCOUNT); - - try { - api.transaction().build(TransactionRequest.createBuilder(randomAddress).build()); - } catch (RadixApiException e) { - // this is expected since the random address has no funds. However, it tells us that /construction is available - if (!(e.getMessage().contains("Not enough balance"))) { - throw e; - } - } - availableNodeServices.add(RadixNode.ServiceType.CONSTRUCTION); - - api.api().data(); - availableNodeServices.add(RadixNode.ServiceType.SYSTEM); - - api.local().validatorInfo(); - availableNodeServices.add(RadixNode.ServiceType.VALIDATION); - - httpClient.callFaucet(jsonRpcRootUrl, secondaryPort, randomAddress.toString(networkId)); - availableNodeServices.add(RadixNode.ServiceType.FAUCET); - - // check that the container name is correct. - try { - dockerClient.runCommand(expectedContainerName, "pwd"); - } catch (DockerException e) { - logger.trace("Docker client could not connect due to {} and will be disabled.", e.getMessage()); - } - - return new RadixNode(jsonRpcRootUrl, primaryPort, secondaryPort, expectedContainerName, availableNodeServices); + // check that the container name is correct. + try { + dockerClient.runCommand(expectedContainerName, "pwd"); + } catch (DockerException e) { + logger.trace( + "Docker client could not connect due to {} and will be disabled.", e.getMessage()); } + return new RadixNode( + jsonRpcRootUrl, primaryPort, secondaryPort, expectedContainerName, availableNodeServices); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNode.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNode.java index 15b84e164a..db7a3b45a8 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNode.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/RadixNode.java @@ -1,61 +1,130 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network; import java.util.Set; public class RadixNode { - private final String rootUrl; - private final int primaryPort; - private final int secondaryPort; - private final String containerName; - private final Set availableServices; - - public enum ServiceType { - ARCHIVE, - ACCOUNT, - CONSTRUCTION, - SYSTEM, - VALIDATION, - FAUCET, - DEVELOPER - } - - public RadixNode(String rootUrl, int primaryPort, int secondaryPort, String containerName, Set availableServices) { - this.rootUrl = rootUrl; - this.primaryPort = primaryPort; - this.secondaryPort = secondaryPort; - this.containerName = containerName; - this.availableServices = availableServices; - } - - public String getRootUrl() { - return rootUrl; - } - - public int getPrimaryPort() { - return primaryPort; - } - - public int getSecondaryPort() { - return secondaryPort; - } - - public String getContainerName() { - return containerName; - } - - public Set getAvailableServices() { - return availableServices; - } - - @Override - public String toString() { - return String.format("%s:%d|%d", rootUrl, primaryPort, secondaryPort); - } - - public String toStringWithServices() { - return String.format("%s:%d:%d, container: %S, services: %s", rootUrl, primaryPort, secondaryPort, containerName, - availableServices); - } + private final String rootUrl; + private final int primaryPort; + private final int secondaryPort; + private final String containerName; + private final Set availableServices; + + public enum ServiceType { + ARCHIVE, + ACCOUNT, + CONSTRUCTION, + SYSTEM, + VALIDATION, + FAUCET, + DEVELOPER + } + + public RadixNode( + String rootUrl, + int primaryPort, + int secondaryPort, + String containerName, + Set availableServices) { + this.rootUrl = rootUrl; + this.primaryPort = primaryPort; + this.secondaryPort = secondaryPort; + this.containerName = containerName; + this.availableServices = availableServices; + } + + public String getRootUrl() { + return rootUrl; + } + + public int getPrimaryPort() { + return primaryPort; + } + + public int getSecondaryPort() { + return secondaryPort; + } + + public String getContainerName() { + return containerName; + } + + public Set getAvailableServices() { + return availableServices; + } + + @Override + public String toString() { + return String.format("%s:%d|%d", rootUrl, primaryPort, secondaryPort); + } + public String toStringWithServices() { + return String.format( + "%s:%d:%d, container: %S, services: %s", + rootUrl, primaryPort, secondaryPort, containerName, availableServices); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/SshConfiguration.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/SshConfiguration.java index cd30c24431..4120237a56 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/SshConfiguration.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/SshConfiguration.java @@ -1,47 +1,110 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network; import com.radixdlt.test.utils.TestingUtils; -/** - * properties used to connect to testnet nodes via SSH - */ +/** properties used to connect to testnet nodes via SSH */ public class SshConfiguration { - private final String sshKeyLocation; - private final String sshKeyPassphrase; - private final String user; - private final int port; - - public static SshConfiguration fromEnv() { - var sshKeyLocation = TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_KEY_LOCATION", - System.getenv("HOME") + "/.ssh/id_rsa"); - var sshKeyPassphrase = TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_KEY_PASSPHRASE", ""); - var sshUser = TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_USER", ""); - var sshPort = TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_PORT", -1); - return new SshConfiguration(sshKeyLocation, sshKeyPassphrase, sshUser, sshPort); - } - - public SshConfiguration(String sshKeyLocation, String sshKeyPassphrase, String user, int port) { - this.sshKeyLocation = sshKeyLocation; - this.sshKeyPassphrase = sshKeyPassphrase; - this.user = user; - this.port = port; - } - - public String getSshKeyLocation() { - return sshKeyLocation; - } - - public String getSshKeyPassphrase() { - return sshKeyPassphrase; - } - - public String getUser() { - return user; - } - - public int getPort() { - return port; - } + private final String sshKeyLocation; + private final String sshKeyPassphrase; + private final String user; + private final int port; + + public static SshConfiguration fromEnv() { + var sshKeyLocation = + TestingUtils.getEnvWithDefault( + "RADIXDLT_SYSTEM_TESTING_SSH_KEY_LOCATION", System.getenv("HOME") + "/.ssh/id_rsa"); + var sshKeyPassphrase = + TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_KEY_PASSPHRASE", ""); + var sshUser = TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_USER", ""); + var sshPort = TestingUtils.getEnvWithDefault("RADIXDLT_SYSTEM_TESTING_SSH_PORT", -1); + return new SshConfiguration(sshKeyLocation, sshKeyPassphrase, sshUser, sshPort); + } + + public SshConfiguration(String sshKeyLocation, String sshKeyPassphrase, String user, int port) { + this.sshKeyLocation = sshKeyLocation; + this.sshKeyPassphrase = sshKeyPassphrase; + this.user = user; + this.port = port; + } + + public String getSshKeyLocation() { + return sshKeyLocation; + } + + public String getSshKeyPassphrase() { + return sshKeyPassphrase; + } + + public String getUser() { + return user; + } + public int getPort() { + return port; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Check.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Check.java index 035eb99a81..f0017199fd 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Check.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Check.java @@ -1,7 +1,70 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.checks; public interface Check { - boolean check(Object... options); - + boolean check(Object... options); } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/CheckFailureException.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/CheckFailureException.java index 362d9b8dc2..8888646aaf 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/CheckFailureException.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/CheckFailureException.java @@ -1,14 +1,76 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.checks; public class CheckFailureException extends Error { - public CheckFailureException(Check check) { - super(check.getClass().getSimpleName()); - } - - public CheckFailureException(String text) { - super(text); - } + public CheckFailureException(Check check) { + super(check.getClass().getSimpleName()); + } + public CheckFailureException(String text) { + super(text); + } } - diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Checks.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Checks.java index c8662b31d7..6be9f30624 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Checks.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/Checks.java @@ -1,47 +1,109 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.checks; import com.google.common.collect.Maps; import com.radixdlt.test.network.RadixNetworkConfiguration; import com.radixdlt.test.network.RadixNode; import com.radixdlt.test.network.client.RadixHttpClient; - import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; public class Checks { - private final Map, Check> checks; + private final Map, Check> checks; - /** - * Will run checks against the given nodes, using the given config - */ - public static Checks forNodesAndCheckConfiguration(List nodes, RadixNetworkConfiguration configuration) { - return new Checks(nodes, configuration); - } + /** Will run checks against the given nodes, using the given config */ + public static Checks forNodesAndCheckConfiguration( + List nodes, RadixNetworkConfiguration configuration) { + return new Checks(nodes, configuration); + } - private Checks(List nodes, RadixNetworkConfiguration configuration) { - checks = Maps.newHashMap(); + private Checks(List nodes, RadixNetworkConfiguration configuration) { + checks = Maps.newHashMap(); - // initialize the checks - var livenessCheck = new LivenessCheck(nodes, 3, RadixHttpClient.fromRadixNetworkConfiguration(configuration)); - checks.put(LivenessCheck.class, livenessCheck); - } + // initialize the checks + var livenessCheck = + new LivenessCheck(nodes, 3, RadixHttpClient.fromRadixNetworkConfiguration(configuration)); + checks.put(LivenessCheck.class, livenessCheck); + } - public boolean runCheck(String name, Object... options) { - AtomicReference checkWithMatchingName = new AtomicReference<>(); - checks.forEach((key, value) -> checkWithMatchingName.compareAndSet(null, value)); - if (checkWithMatchingName.get() == null) { - throw new IllegalArgumentException("Check named '" + name + "' not found"); - } - return checkWithMatchingName.get().check(options); + public boolean runCheck(String name, Object... options) { + AtomicReference checkWithMatchingName = new AtomicReference<>(); + checks.forEach((key, value) -> checkWithMatchingName.compareAndSet(null, value)); + if (checkWithMatchingName.get() == null) { + throw new IllegalArgumentException("Check named '" + name + "' not found"); } + return checkWithMatchingName.get().check(options); + } - public boolean runCheck(Class type, Object... options) { - if (!checks.containsKey(type)) { - throw new IllegalArgumentException("Check '" + type + "' not found"); - } - return checks.get(type).check(options); + public boolean runCheck(Class type, Object... options) { + if (!checks.containsKey(type)) { + throw new IllegalArgumentException("Check '" + type + "' not found"); } - + return checks.get(type).check(options); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/EpochView.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/EpochView.java index 1fc5539110..322d56d418 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/EpochView.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/EpochView.java @@ -1,34 +1,97 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.checks; public class EpochView { - private int epoch; - private int view; - - public EpochView(int epoch, int view) { - this.epoch = epoch; - this.view = view; - } + private int epoch; + private int view; - public int getEpoch() { - return epoch; - } + public EpochView(int epoch, int view) { + this.epoch = epoch; + this.view = view; + } - public void setEpoch(int epoch) { - this.epoch = epoch; - } + public int getEpoch() { + return epoch; + } - public int getView() { - return view; - } + public void setEpoch(int epoch) { + this.epoch = epoch; + } - public void setView(int view) { - this.view = view; - } + public int getView() { + return view; + } - @Override - public String toString() { - return "Epoch: " + epoch + ", view: " + view; - } + public void setView(int view) { + this.view = view; + } + @Override + public String toString() { + return "Epoch: " + epoch + ", view: " + view; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/LivenessCheck.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/LivenessCheck.java index aa37abec11..83b16ac12b 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/LivenessCheck.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/checks/LivenessCheck.java @@ -1,66 +1,137 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.checks; import com.radixdlt.test.network.RadixNode; import com.radixdlt.test.network.client.RadixHttpClient; import com.radixdlt.test.utils.TestingUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.Comparator; import java.util.List; import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * 1) We query every node for its highest QC and we calculate the max (biggest) of them all. - * 2) We wait for a few seconds (checks.liveness.patienceSeconds) - * 3) Repeat step #1. - * 4) If the value from step #3 is larger than that of step #1 then we have liveness. Otherwise we don't. + * 1) We query every node for its highest QC and we calculate the max (biggest) of them all. 2) We + * wait for a few seconds (checks.liveness.patienceSeconds) 3) Repeat step #1. 4) If the value from + * step #3 is larger than that of step #1 then we have liveness. Otherwise we don't. */ public class LivenessCheck implements Check { - private static final Logger logger = LogManager.getLogger(); - - private static final Comparator VIEW_COMPARATOR = Comparator.comparingLong(EpochView::getEpoch) - .thenComparingLong(EpochView::getView); + private static final Logger logger = LogManager.getLogger(); - private final List nodes; - private final int patienceSeconds; - private final RadixHttpClient client; + private static final Comparator VIEW_COMPARATOR = + Comparator.comparingLong(EpochView::getEpoch).thenComparingLong(EpochView::getView); - public LivenessCheck(List nodes, int patienceSeconds, RadixHttpClient client) { - this.nodes = nodes; - this.patienceSeconds = patienceSeconds; - this.client = client; - } + private final List nodes; + private final int patienceSeconds; + private final RadixHttpClient client; - @Override - public boolean check(Object... options) { - var patienceSeconds = determinePatience(options); - var highestQC = getMaxHighestQC(nodes); - TestingUtils.sleep(patienceSeconds); - var highestQCAfterAWhile = getMaxHighestQC(nodes); + public LivenessCheck(List nodes, int patienceSeconds, RadixHttpClient client) { + this.nodes = nodes; + this.patienceSeconds = patienceSeconds; + this.client = client; + } - var comparisonResult = VIEW_COMPARATOR.compare(highestQC, highestQCAfterAWhile); - logger.trace("First: {}, second: {}, result: {}", highestQC, highestQCAfterAWhile, comparisonResult); - return comparisonResult == -1; - } + @Override + public boolean check(Object... options) { + var patienceSeconds = determinePatience(options); + var highestQC = getMaxHighestQC(nodes); + TestingUtils.sleep(patienceSeconds); + var highestQCAfterAWhile = getMaxHighestQC(nodes); - private int determinePatience(Object... options) { - return (options.length == 1) ? Integer.parseInt(options[0].toString()) : patienceSeconds; - } + var comparisonResult = VIEW_COMPARATOR.compare(highestQC, highestQCAfterAWhile); + logger.trace( + "First: {}, second: {}, result: {}", highestQC, highestQCAfterAWhile, comparisonResult); + return comparisonResult == -1; + } - private EpochView getMaxHighestQC(List nodes) { - var maybeHighestView = nodes.stream().map(node -> { - try { - var metrics = client.getMetrics(node.getRootUrl() + ":" + node.getSecondaryPort()); - logger.trace("Node ({}}) gave: {}", node, new EpochView(metrics.getEpoch(), metrics.getView())); - return new EpochView(metrics.getEpoch(), metrics.getView()); - } catch (Exception e) { - logger.trace("Could not get epoch/view for {}: {}", node, e.getMessage()); - return null; - } - }).filter(Objects::nonNull).max(VIEW_COMPARATOR); - return maybeHighestView.orElseThrow(); - } + private int determinePatience(Object... options) { + return (options.length == 1) ? Integer.parseInt(options[0].toString()) : patienceSeconds; + } + private EpochView getMaxHighestQC(List nodes) { + var maybeHighestView = + nodes.stream() + .map( + node -> { + try { + var metrics = + client.getMetrics(node.getRootUrl() + ":" + node.getSecondaryPort()); + logger.trace( + "Node ({}}) gave: {}", + node, + new EpochView(metrics.getEpoch(), metrics.getView())); + return new EpochView(metrics.getEpoch(), metrics.getView()); + } catch (Exception e) { + logger.trace("Could not get epoch/view for {}: {}", node, e.getMessage()); + return null; + } + }) + .filter(Objects::nonNull) + .max(VIEW_COMPARATOR); + return maybeHighestView.orElseThrow(); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/HttpException.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/HttpException.java index 20eb66457e..f83c0e488a 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/HttpException.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/HttpException.java @@ -1,16 +1,80 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client; public class HttpException extends RuntimeException { - public HttpException(String message) { - super(message); - } + public HttpException(String message) { + super(message); + } - public HttpException(Exception e) { - super(e); - } + public HttpException(Exception e) { + super(e); + } - public HttpException(int code, String url) { - super("Request to " + url + " failed: " + code); - } + public HttpException(int code, String url) { + super("Request to " + url + " failed: " + code); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/Metrics.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/Metrics.java index 2c61e3af0d..d7ddf4468e 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/Metrics.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/Metrics.java @@ -1,50 +1,110 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client; import com.google.common.collect.Maps; - import java.util.Map; -/** - * holds the response from /metrics - */ +/** holds the response from /metrics */ public class Metrics { - private final Map metrics; + private final Map metrics; - public Metrics() { - this(""); - } + public Metrics() { + this(""); + } - public Metrics(String text) { - metrics = Maps.newHashMap(); - for (var line : text.split("\\R")) { - if (line.startsWith("#")) { - continue; - } - var key = line.split("\\s+")[0]; - var value = line.split("\\s+")[1]; - metrics.put(key, Double.parseDouble(value)); - } + public Metrics(String text) { + metrics = Maps.newHashMap(); + for (var line : text.split("\\R")) { + if (line.startsWith("#")) { + continue; + } + var key = line.split("\\s+")[0]; + var value = line.split("\\s+")[1]; + metrics.put(key, Double.parseDouble(value)); } + } - public int getEpoch() { - return metrics.get("info_epochmanager_currentview_epoch").intValue(); - } + public int getEpoch() { + return metrics.get("info_epochmanager_currentview_epoch").intValue(); + } - public int getView() { - return metrics.get("info_epochmanager_currentview_view").intValue(); - } + public int getView() { + return metrics.get("info_epochmanager_currentview_view").intValue(); + } - public long getTargetVersion() { - return metrics.get("info_counters_sync_target_state_version").longValue(); - } + public long getTargetVersion() { + return metrics.get("info_counters_sync_target_state_version").longValue(); + } - public long getCurrentVersion() { - return metrics.get("info_counters_ledger_state_version").longValue(); - } - - public long getVersionDiff() { - return metrics.get("info_counters_sync_target_current_diff").longValue(); - } + public long getCurrentVersion() { + return metrics.get("info_counters_ledger_state_version").longValue(); + } + public long getVersionDiff() { + return metrics.get("info_counters_sync_target_current_diff").longValue(); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/RadixHttpClient.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/RadixHttpClient.java index ad594cee70..f23524ee86 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/RadixHttpClient.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/RadixHttpClient.java @@ -1,116 +1,188 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client; import com.radixdlt.client.lib.api.sync.RadixApiException; import com.radixdlt.test.network.FaucetException; import com.radixdlt.test.network.RadixNetworkConfiguration; import com.radixdlt.utils.functional.Failure; -import kong.unirest.HttpResponse; -import kong.unirest.JsonNode; -import kong.unirest.Unirest; -import kong.unirest.json.JSONObject; - -import javax.net.ssl.SSLContext; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Objects; import java.util.Optional; +import javax.net.ssl.SSLContext; +import kong.unirest.HttpResponse; +import kong.unirest.JsonNode; +import kong.unirest.Unirest; +import kong.unirest.json.JSONObject; /** * A small HTTP client that consumes the non-JSON-RPC methods e.g. /health - *

- * Also consumes the JSON-RPC methods that are not part of the RadixApi client e.g. /faucet + * + *

Also consumes the JSON-RPC methods that are not part of the RadixApi client e.g. /faucet */ public class RadixHttpClient { - public enum HealthStatus { - BOOTING, - SYNCING, - UP - } + public enum HealthStatus { + BOOTING, + SYNCING, + UP + } - private static final String HEALTH_PATH = "/health"; - private static final String METRICS_PATH = "/metrics"; - private static final String VERSION_PATH = "/version"; - private static final String FAUCET_PATH = "/faucet"; + private static final String HEALTH_PATH = "/health"; + private static final String METRICS_PATH = "/metrics"; + private static final String VERSION_PATH = "/version"; + private static final String FAUCET_PATH = "/faucet"; - public static RadixHttpClient fromRadixNetworkConfiguration(RadixNetworkConfiguration configuration) { - return new RadixHttpClient(configuration.getBasicAuth()); - } + public static RadixHttpClient fromRadixNetworkConfiguration( + RadixNetworkConfiguration configuration) { + return new RadixHttpClient(configuration.getBasicAuth()); + } - public RadixHttpClient(String basicAuth) { - if (basicAuth != null && !basicAuth.isBlank()) { - var credentials = basicAuth.split("\\:"); - Unirest.config().setDefaultBasicAuth(credentials[0], credentials[1]); - } - try { - var sc = SSLContext.getInstance("TLSv1.2"); - sc.init(null, null, new SecureRandom()); - Unirest.config().sslContext(sc); - Unirest.config().verifySsl(true); - } catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new IllegalArgumentException(e); // highly unlikely, algorithm is standard - } + public RadixHttpClient(String basicAuth) { + if (basicAuth != null && !basicAuth.isBlank()) { + var credentials = basicAuth.split("\\:"); + Unirest.config().setDefaultBasicAuth(credentials[0], credentials[1]); } - - public HealthStatus getHealthStatus(String rootUrl) { - var response = getResponseAsJsonNode(rootUrl + HEALTH_PATH); - return HealthStatus.valueOf(response.getBody().getObject().getString("status")); + try { + var sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, null, new SecureRandom()); + Unirest.config().sslContext(sc); + Unirest.config().verifySsl(true); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IllegalArgumentException(e); // highly unlikely, algorithm is standard } + } - public Metrics getMetrics(String rootUrl) { - var url = rootUrl + METRICS_PATH; - var response = Unirest.get(url).asString().getBody(); - return new Metrics(response); - } + public HealthStatus getHealthStatus(String rootUrl) { + var response = getResponseAsJsonNode(rootUrl + HEALTH_PATH); + return HealthStatus.valueOf(response.getBody().getObject().getString("status")); + } + + public Metrics getMetrics(String rootUrl) { + var url = rootUrl + METRICS_PATH; + var response = Unirest.get(url).asString().getBody(); + return new Metrics(response); + } - /** - * @param port if empty, will default to whatever port RADIXDLT_JSON_RPC_API_ROOT_URL has (usually 80) - */ - public String getVersion(String rootUrl, Optional port) { - var url = port.isPresent() ? String.format("%s:%d%s", rootUrl, port.get(), VERSION_PATH) + /** + * @param port if empty, will default to whatever port RADIXDLT_JSON_RPC_API_ROOT_URL has (usually + * 80) + */ + public String getVersion(String rootUrl, Optional port) { + var url = + port.isPresent() + ? String.format("%s:%d%s", rootUrl, port.get(), VERSION_PATH) : String.format("%s%s", rootUrl, VERSION_PATH); - var response = getResponseAsJsonNode(url); - return response.getBody().getObject().getString("version"); - } + var response = getResponseAsJsonNode(url); + return response.getBody().getObject().getString("version"); + } - public String callFaucet(String rootUrl, int port, String address) { - var faucetBody = new JSONObject(); - faucetBody.put("jsonrpc", "2.0"); - faucetBody.put("id", "1"); - faucetBody.put("method", "faucet.request_tokens"); - var params = new JSONObject(); - params.put("address", address); - faucetBody.put("params", params); + public String callFaucet(String rootUrl, int port, String address) { + var faucetBody = new JSONObject(); + faucetBody.put("jsonrpc", "2.0"); + faucetBody.put("id", "1"); + faucetBody.put("method", "faucet.request_tokens"); + var params = new JSONObject(); + params.put("address", address); + faucetBody.put("params", params); - var jsonBodyString = faucetBody.toString(5); - var response = Unirest.post(rootUrl + ":" + port + FAUCET_PATH).body(jsonBodyString).asJson(); - if (response.isSuccess()) { - var responseBody = response.getBody().getObject(); - if (responseBody.has("error")) { - var responseErrorMessage = responseBody.getJSONObject("error").getString("message"); - var errorMessage = responseErrorMessage.toLowerCase().contains("not enough balance") ? "Faucet is out of tokens!" - : responseErrorMessage; - throw new FaucetException(errorMessage); - } - return response.getBody().getObject().getJSONObject("result").getString("txID"); - } else { - var bodyString = Objects.isNull(response.getBody()) ? response.getStatusText() + "(" + response.getStatus() + ")" - : response.getBody().toString(); - throw new FaucetException(bodyString); - } + var jsonBodyString = faucetBody.toString(5); + var response = Unirest.post(rootUrl + ":" + port + FAUCET_PATH).body(jsonBodyString).asJson(); + if (response.isSuccess()) { + var responseBody = response.getBody().getObject(); + if (responseBody.has("error")) { + var responseErrorMessage = responseBody.getJSONObject("error").getString("message"); + var errorMessage = + responseErrorMessage.toLowerCase().contains("not enough balance") + ? "Faucet is out of tokens!" + : responseErrorMessage; + throw new FaucetException(errorMessage); + } + return response.getBody().getObject().getJSONObject("result").getString("txID"); + } else { + var bodyString = + Objects.isNull(response.getBody()) + ? response.getStatusText() + "(" + response.getStatus() + ")" + : response.getBody().toString(); + throw new FaucetException(bodyString); } + } - private HttpResponse getResponseAsJsonNode(String url) { - var response = Unirest.get(url).asJson(); - if (response.isSuccess()) { - return response; - } else { - var message = response.getBody() == null - ? "(" + response.getStatus() + ") " + response.getStatusText() - : response.getBody().getObject().toString(); - throw new RadixApiException(Failure.failure(response.getStatus(), "Call to " + url + " failed: " + message)); - } + private HttpResponse getResponseAsJsonNode(String url) { + var response = Unirest.get(url).asJson(); + if (response.isSuccess()) { + return response; + } else { + var message = + response.getBody() == null + ? "(" + response.getStatus() + ") " + response.getStatusText() + : response.getBody().getObject().toString(); + throw new RadixApiException( + Failure.failure(response.getStatus(), "Call to " + url + " failed: " + message)); } - + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DisabledDockerClient.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DisabledDockerClient.java index a86ae19405..d7a4a2e755 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DisabledDockerClient.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DisabledDockerClient.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client.docker; import com.github.dockerjava.api.exception.DockerClientException; @@ -6,30 +70,29 @@ public class DisabledDockerClient implements DockerClient { - private static final Logger logger = LoggerFactory.getLogger(DisabledDockerClient.class); - - public void connect() { - logger.debug("Docker client is disabled, cannot connect."); - } + private static final Logger logger = LoggerFactory.getLogger(DisabledDockerClient.class); - public String runCommand(String nodeLocator, String... commands) { - logger.trace("Disabled client cannot commands"); - return null; - } + public void connect() { + logger.debug("Docker client is disabled, cannot connect."); + } - @Override - public void restartNode(String nodeLocator) { - throw new DockerClientException("Disabled client cannot restart node " + nodeLocator); - } + public String runCommand(String nodeLocator, String... commands) { + logger.trace("Disabled client cannot commands"); + return null; + } - @Override - public void stopNode(String nodeLocator) { - throw new DockerClientException("Disabled client cannot stop node " + nodeLocator); - } + @Override + public void restartNode(String nodeLocator) { + throw new DockerClientException("Disabled client cannot restart node " + nodeLocator); + } - @Override - public void cleanup(String... parameters) { - logger.debug("Disabled docker client cannot cleanup"); - } + @Override + public void stopNode(String nodeLocator) { + throw new DockerClientException("Disabled client cannot stop node " + nodeLocator); + } + @Override + public void cleanup(String... parameters) { + logger.debug("Disabled docker client cannot cleanup"); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerClient.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerClient.java index b177bcece4..5a802c3166 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerClient.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerClient.java @@ -1,21 +1,82 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client.docker; -/** - * Connects to some docker daemon(s) and executes docker commands - */ +/** Connects to some docker daemon(s) and executes docker commands */ public interface DockerClient { - String runCommand(String nodeLocator, String... commands); - - void restartNode(String nodeLocator); + String runCommand(String nodeLocator, String... commands); - void stopNode(String nodeLocator); + void restartNode(String nodeLocator); - /** - * restores the network to its original state, if possible - * - * @param cleanupParameters extra information needed for cleanup - */ - void cleanup(String... cleanupParameters); + void stopNode(String nodeLocator); + /** + * restores the network to its original state, if possible + * + * @param cleanupParameters extra information needed for cleanup + */ + void cleanup(String... cleanupParameters); } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerNetworkCreator.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerNetworkCreator.java index 29dc72e88f..2df9d06c8d 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerNetworkCreator.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/DockerNetworkCreator.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client.docker; +import static org.awaitility.Awaitility.await; + import com.github.dockerjava.api.model.Capability; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.HostConfig; @@ -11,6 +77,11 @@ import com.radixdlt.test.utils.TestingUtils; import com.radixdlt.test.utils.universe.UniverseUtils; import com.radixdlt.test.utils.universe.UniverseVariables; +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import kong.unirest.json.JSONObject; import org.apache.commons.compress.utils.Lists; import org.apache.commons.lang.StringUtils; @@ -18,274 +89,343 @@ import org.apache.logging.log4j.Logger; import org.awaitility.Durations; -import java.time.Duration; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.awaitility.Awaitility.await; - -/** - * Creates radix node networks using docker - */ +/** Creates radix node networks using docker */ public class DockerNetworkCreator { - private static final Logger logger = LogManager.getLogger(); - - private static final int MAX_NUMBER_OF_NODES = 3; - private static final String DOCKER_EXEC_COMMAND = "docker exec "; - - private DockerNetworkCreator() { + private static final Logger logger = LogManager.getLogger(); - } + private static final int MAX_NUMBER_OF_NODES = 3; + private static final String DOCKER_EXEC_COMMAND = "docker exec "; - public static UniverseVariables createNewLocalNetwork(RadixNetworkConfiguration configuration, LocalDockerClient dockerClient) { - var numberOfNodes = configuration.getDockerConfiguration().getInitialNumberOfNodes(); - logger.info("Initializing new docker network with {} nodes...", numberOfNodes); - var variables = UniverseUtils.generateEnvironmentVariables(MAX_NUMBER_OF_NODES); - - // docker network stuff - var networkName = configuration.getDockerConfiguration().getNetworkName(); - if (!Boolean.parseBoolean(System.getenv("RADIXDLT_DOCKER_DO_NOT_WIPE_NETWORK"))) { - dockerClient.createNetwork(networkName); - } else { - return variables; - } - - // actually starting the radix network - IntStream.range(0, numberOfNodes).forEach(nodeNumber -> { - var dockerContainerName = configuration.getDockerConfiguration().getContainerName(); - var containerName = String.format(dockerContainerName, nodeNumber); - var privateKey = variables.getValidatorKeypairs().get(nodeNumber).getPrivateKey(); - var primaryPort = configuration.getPrimaryPort(); - var secondaryPort = configuration.getSecondaryPort(); - var remoteSeeds = generateSequentialRemoteSeedsConfig(variables, configuration, nodeNumber); - - var environment = createEnvironmentProperties(privateKey, containerName, remoteSeeds, - variables.getGenesisTransaction(), primaryPort, secondaryPort); - List exposedPorts = Lists.newArrayList(); - var hostConfig = createHostConfigWithPortBindings(nodeNumber, exposedPorts, primaryPort, secondaryPort); - - dockerClient.startNewNode(configuration.getDockerConfiguration().getImage(), - containerName, - environment, - hostConfig, - exposedPorts, - networkName - ); - }); - - logger.info("Network started. Waiting for all nodes to be UP..."); - waitUntilLocalNodesAreUp(configuration, MAX_NUMBER_OF_NODES); - logger.info("All nodes are UP"); - return variables; - } + private DockerNetworkCreator() {} - /** - * will bring up a fresh new node, without a pre-existing ledger, and sync it to the network - * - * @param host the newly initialized node will be hosted here - */ - public static void initializeAndStartNode(RadixNetworkConfiguration configuration, String host, String genesisTx, - int networkId, String seedsRemote) { - var dockerClient = new RemoteDockerClient(configuration); - var dockerConfiguration = configuration.getDockerConfiguration(); - var containerName = dockerConfiguration.getContainerName(); - loginDockerRepository(dockerClient, host, dockerConfiguration.getDockerLogin()); - - // prepare env properties - var universeVariables = UniverseUtils.generateEnvironmentVariables(1); - var nodePrivateKey = universeVariables.getValidatorKeypairs().get(0).getPrivateKey(); - var env = DockerNetworkCreator.createEnvironmentProperties(nodePrivateKey, host, seedsRemote, - genesisTx, 8080, 3333); - env.add("RADIXDLT_NETWORK_ID=" + networkId); - var envString = String.join(" -e ", env); - - // actually start the node - dockerClient.runCommand(host, "docker stop " + containerName + " && docker rm " + containerName); - var command = String.format("docker run --name %s -d -e %s %s", - containerName, - envString, - dockerConfiguration.getImage()); - logger.info("Starting node with command [{}]", command); - var output = dockerClient.runCommand(host, command).replace("\n", ""); - if (StringUtils.isNotBlank(output)) { - logger.info(output); - } - - //wait until UP or SYNCING - await().pollInterval(Durations.FIVE_SECONDS).atMost(Durations.FIVE_MINUTES).until(() -> { - var healthResponse = dockerClient.runCommand(host, - DOCKER_EXEC_COMMAND + containerName + " bash -c 'curl -s localhost:3333/health'"); - var status = new JSONObject(healthResponse).getString("status"); - logger.info("Node is {}", status); - return Objects.equals(status, "UP") || Objects.equals(status, "SYNCING"); - }); - - // wait until full ledger sync - logger.info("Starting sync watch..."); - await().pollInterval(Durations.FIVE_SECONDS).atMost(Duration.ofHours(1)).until(() -> { - var metricsString = dockerClient.runCommand(host, - DOCKER_EXEC_COMMAND + containerName + " bash -c 'curl -s localhost:3333/metrics'"); - var metrics = new Metrics(metricsString); - logger.info("Version {} out of {}. {}% synced.", metrics.getCurrentVersion(), metrics.getTargetVersion(), - String.format("%.2f", 100.0 * metrics.getCurrentVersion() / metrics.getTargetVersion())); - return metrics.getVersionDiff() == 0L; - }); - logger.info("Sync complete!"); - } + public static UniverseVariables createNewLocalNetwork( + RadixNetworkConfiguration configuration, LocalDockerClient dockerClient) { + var numberOfNodes = configuration.getDockerConfiguration().getInitialNumberOfNodes(); + logger.info("Initializing new docker network with {} nodes...", numberOfNodes); + var variables = UniverseUtils.generateEnvironmentVariables(MAX_NUMBER_OF_NODES); - /** - * will bring up a fresh new node, without a pre-existing ledger, and sync it to the network - * - * @param host this will host the newly initialized node - * @param trustedNodeHost the network id, seeds remote and genesis tx will be fetched from here - */ - public static void initializeAndStartNodeFromTrustedNode(RadixNetworkConfiguration configuration, String host, - String trustedNodeHost) { - var dockerConfiguration = configuration.getDockerConfiguration(); - var sshConfiguration = configuration.getSshConfiguration(); - var containerName = dockerConfiguration.getContainerName(); - - var trustedContainerName = TestingUtils.getEnvWithDefault("RADIXDLT_TRUSTED_DOCKER_CONTAINER_NAME", - containerName); - var trustedSshUser = TestingUtils.getEnvWithDefault( - "RADIXDLT_SYSTEM_TESTING_TRUSTED_SSH_USER", configuration.getSshConfiguration().getUser()); - var trustedDockerClient = new RemoteDockerClient(new SshConfiguration(sshConfiguration.getSshKeyLocation(), - sshConfiguration.getSshKeyPassphrase(), trustedSshUser, sshConfiguration.getPort()), trustedContainerName); - var genesisTransaction = getGenesisTransaction(trustedDockerClient, trustedNodeHost, trustedContainerName); - var networkId = getNetworkId(trustedDockerClient, trustedNodeHost, trustedContainerName); - var seedsRemote = getSeedsRemote(trustedDockerClient, trustedNodeHost, trustedContainerName); - initializeAndStartNode(configuration, host, genesisTransaction, networkId, seedsRemote); + // docker network stuff + var networkName = configuration.getDockerConfiguration().getNetworkName(); + if (!Boolean.parseBoolean(System.getenv("RADIXDLT_DOCKER_DO_NOT_WIPE_NETWORK"))) { + dockerClient.createNetwork(networkName); + } else { + return variables; } - private static List createEnvironmentProperties(String privateKey, String hostIpAddress, String remoteSeeds, - String genesisTransaction, int primaryPort, int secondaryPort) { - List env = Lists.newArrayList(); - - // java opts - env.add("'JAVA_OPTS=-server -Xmx512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:+AlwaysPreTouch -Dguice_bytecode_gen_option=DISABLED " - + "-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStoreType=jks -Djava.security.egd=file:/dev/urandom " - + "-Dcom.sun.management.jmxremote.port=9011 -Dcom.sun.management.jmxremote.rmi.port=9011 -Dcom.sun.management.jmxremote.authenticate" - + "=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost -agentlib:jdwp=transport=dt_socket," - + "address=50505,suspend=n,server=y'"); - - // per node variables - env.add("RADIXDLT_NETWORK_SEEDS_REMOTE=" + remoteSeeds); - env.add("RADIXDLT_NODE_KEY=" + privateKey); - env.add("RADIXDLT_HOST_IP_ADDRESS=" + hostIpAddress); - - // common variables - env.add("RADIXDLT_LOG_LEVEL=debug"); - env.add("RADIXDLT_CLIENT_API_PORT=" + primaryPort); - env.add("RADIXDLT_NODE_API_PORT=" + secondaryPort); - env.add("RADIXDLT_ENABLE_CLIENT_API=true"); - env.add("RADIXDLT_ARCHIVE_API_ENABLE=true"); - env.add("RADIXDLT_ACCOUNT_API_ENABLE=true"); - env.add("RADIXDLT_METRICS_API_ENABLE=true"); - env.add("RADIXDLT_CONSTRUCT_API_ENABLE=true"); - env.add("RADIXDLT_CHAOS_API_ENABLE=true"); - env.add("RADIXDLT_FAUCET_API_ENABLE=true"); - env.add("RADIXDLT_HEALTH_API_ENABLE=true"); - env.add("RADIXDLT_SYSTEM_API_ENABLE=true"); - env.add("RADIXDLT_VALIDATION_API_ENABLE=true"); - env.add("RADIXDLT_TRANSACTIONS_API_ENABLE=true"); - env.add("RADIXDLT_UNIVERSE_API_ENABLE=true"); - env.add("RADIXDLT_VERSION_API_ENABLE=true"); - - // genesis transaction - env.add("RADIXDLT_GENESIS_TXN=" + genesisTransaction); - - return env; + // actually starting the radix network + IntStream.range(0, numberOfNodes) + .forEach( + nodeNumber -> { + var dockerContainerName = configuration.getDockerConfiguration().getContainerName(); + var containerName = String.format(dockerContainerName, nodeNumber); + var privateKey = variables.getValidatorKeypairs().get(nodeNumber).getPrivateKey(); + var primaryPort = configuration.getPrimaryPort(); + var secondaryPort = configuration.getSecondaryPort(); + var remoteSeeds = + generateSequentialRemoteSeedsConfig(variables, configuration, nodeNumber); + + var environment = + createEnvironmentProperties( + privateKey, + containerName, + remoteSeeds, + variables.getGenesisTransaction(), + primaryPort, + secondaryPort); + List exposedPorts = Lists.newArrayList(); + var hostConfig = + createHostConfigWithPortBindings( + nodeNumber, exposedPorts, primaryPort, secondaryPort); + + dockerClient.startNewNode( + configuration.getDockerConfiguration().getImage(), + containerName, + environment, + hostConfig, + exposedPorts, + networkName); + }); + + logger.info("Network started. Waiting for all nodes to be UP..."); + waitUntilLocalNodesAreUp(configuration, MAX_NUMBER_OF_NODES); + logger.info("All nodes are UP"); + return variables; + } + + /** + * will bring up a fresh new node, without a pre-existing ledger, and sync it to the network + * + * @param host the newly initialized node will be hosted here + */ + public static void initializeAndStartNode( + RadixNetworkConfiguration configuration, + String host, + String genesisTx, + int networkId, + String seedsRemote) { + var dockerClient = new RemoteDockerClient(configuration); + var dockerConfiguration = configuration.getDockerConfiguration(); + var containerName = dockerConfiguration.getContainerName(); + loginDockerRepository(dockerClient, host, dockerConfiguration.getDockerLogin()); + + // prepare env properties + var universeVariables = UniverseUtils.generateEnvironmentVariables(1); + var nodePrivateKey = universeVariables.getValidatorKeypairs().get(0).getPrivateKey(); + var env = + DockerNetworkCreator.createEnvironmentProperties( + nodePrivateKey, host, seedsRemote, genesisTx, 8080, 3333); + env.add("RADIXDLT_NETWORK_ID=" + networkId); + var envString = String.join(" -e ", env); + + // actually start the node + dockerClient.runCommand( + host, "docker stop " + containerName + " && docker rm " + containerName); + var command = + String.format( + "docker run --name %s -d -e %s %s", + containerName, envString, dockerConfiguration.getImage()); + logger.info("Starting node with command [{}]", command); + var output = dockerClient.runCommand(host, command).replace("\n", ""); + if (StringUtils.isNotBlank(output)) { + logger.info(output); } - private static void waitUntilLocalNodesAreUp(RadixNetworkConfiguration configuration, int numberOfNodes) { - var secondaryPort = configuration.getSecondaryPort(); - var httpClient = RadixHttpClient.fromRadixNetworkConfiguration(configuration); - - var rootUrls = IntStream.range(0, numberOfNodes).mapToObj(index -> "http://localhost:" + (secondaryPort + index)) + // wait until UP or SYNCING + await() + .pollInterval(Durations.FIVE_SECONDS) + .atMost(Durations.FIVE_MINUTES) + .until( + () -> { + var healthResponse = + dockerClient.runCommand( + host, + DOCKER_EXEC_COMMAND + + containerName + + " bash -c 'curl -s localhost:3333/health'"); + var status = new JSONObject(healthResponse).getString("status"); + logger.info("Node is {}", status); + return Objects.equals(status, "UP") || Objects.equals(status, "SYNCING"); + }); + + // wait until full ledger sync + logger.info("Starting sync watch..."); + await() + .pollInterval(Durations.FIVE_SECONDS) + .atMost(Duration.ofHours(1)) + .until( + () -> { + var metricsString = + dockerClient.runCommand( + host, + DOCKER_EXEC_COMMAND + + containerName + + " bash -c 'curl -s localhost:3333/metrics'"); + var metrics = new Metrics(metricsString); + logger.info( + "Version {} out of {}. {}% synced.", + metrics.getCurrentVersion(), + metrics.getTargetVersion(), + String.format( + "%.2f", 100.0 * metrics.getCurrentVersion() / metrics.getTargetVersion())); + return metrics.getVersionDiff() == 0L; + }); + logger.info("Sync complete!"); + } + + /** + * will bring up a fresh new node, without a pre-existing ledger, and sync it to the network + * + * @param host this will host the newly initialized node + * @param trustedNodeHost the network id, seeds remote and genesis tx will be fetched from here + */ + public static void initializeAndStartNodeFromTrustedNode( + RadixNetworkConfiguration configuration, String host, String trustedNodeHost) { + var dockerConfiguration = configuration.getDockerConfiguration(); + var sshConfiguration = configuration.getSshConfiguration(); + var containerName = dockerConfiguration.getContainerName(); + + var trustedContainerName = + TestingUtils.getEnvWithDefault("RADIXDLT_TRUSTED_DOCKER_CONTAINER_NAME", containerName); + var trustedSshUser = + TestingUtils.getEnvWithDefault( + "RADIXDLT_SYSTEM_TESTING_TRUSTED_SSH_USER", + configuration.getSshConfiguration().getUser()); + var trustedDockerClient = + new RemoteDockerClient( + new SshConfiguration( + sshConfiguration.getSshKeyLocation(), + sshConfiguration.getSshKeyPassphrase(), + trustedSshUser, + sshConfiguration.getPort()), + trustedContainerName); + var genesisTransaction = + getGenesisTransaction(trustedDockerClient, trustedNodeHost, trustedContainerName); + var networkId = getNetworkId(trustedDockerClient, trustedNodeHost, trustedContainerName); + var seedsRemote = getSeedsRemote(trustedDockerClient, trustedNodeHost, trustedContainerName); + initializeAndStartNode(configuration, host, genesisTransaction, networkId, seedsRemote); + } + + private static List createEnvironmentProperties( + String privateKey, + String hostIpAddress, + String remoteSeeds, + String genesisTransaction, + int primaryPort, + int secondaryPort) { + List env = Lists.newArrayList(); + + // java opts + env.add( + "'JAVA_OPTS=-server -Xmx512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:+AlwaysPreTouch" + + " -Dguice_bytecode_gen_option=DISABLED" + + " -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts" + + " -Djavax.net.ssl.trustStoreType=jks -Djava.security.egd=file:/dev/urandom" + + " -Dcom.sun.management.jmxremote.port=9011" + + " -Dcom.sun.management.jmxremote.rmi.port=9011" + + " -Dcom.sun.management.jmxremote.authenticate=false" + + " -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost" + + " -agentlib:jdwp=transport=dt_socket,address=50505,suspend=n,server=y'"); + + // per node variables + env.add("RADIXDLT_NETWORK_SEEDS_REMOTE=" + remoteSeeds); + env.add("RADIXDLT_NODE_KEY=" + privateKey); + env.add("RADIXDLT_HOST_IP_ADDRESS=" + hostIpAddress); + + // common variables + env.add("RADIXDLT_LOG_LEVEL=debug"); + env.add("RADIXDLT_CLIENT_API_PORT=" + primaryPort); + env.add("RADIXDLT_NODE_API_PORT=" + secondaryPort); + env.add("RADIXDLT_ENABLE_CLIENT_API=true"); + env.add("RADIXDLT_ARCHIVE_API_ENABLE=true"); + env.add("RADIXDLT_ACCOUNT_API_ENABLE=true"); + env.add("RADIXDLT_METRICS_API_ENABLE=true"); + env.add("RADIXDLT_CONSTRUCT_API_ENABLE=true"); + env.add("RADIXDLT_CHAOS_API_ENABLE=true"); + env.add("RADIXDLT_FAUCET_API_ENABLE=true"); + env.add("RADIXDLT_HEALTH_API_ENABLE=true"); + env.add("RADIXDLT_SYSTEM_API_ENABLE=true"); + env.add("RADIXDLT_VALIDATION_API_ENABLE=true"); + env.add("RADIXDLT_TRANSACTIONS_API_ENABLE=true"); + env.add("RADIXDLT_UNIVERSE_API_ENABLE=true"); + env.add("RADIXDLT_VERSION_API_ENABLE=true"); + + // genesis transaction + env.add("RADIXDLT_GENESIS_TXN=" + genesisTransaction); + + return env; + } + + private static void waitUntilLocalNodesAreUp( + RadixNetworkConfiguration configuration, int numberOfNodes) { + var secondaryPort = configuration.getSecondaryPort(); + var httpClient = RadixHttpClient.fromRadixNetworkConfiguration(configuration); + + var rootUrls = + IntStream.range(0, numberOfNodes) + .mapToObj(index -> "http://localhost:" + (secondaryPort + index)) .collect(Collectors.toList()); - rootUrls.parallelStream().forEach(rootUrl -> - TestingUtils.waitForNodeToBeUp(httpClient, rootUrl) - ); - } - - private static String generateSequentialRemoteSeedsConfig(UniverseVariables variables, RadixNetworkConfiguration configuration, - int nodeNumber) { - var seeds = IntStream.range(0, variables.getValidatorKeypairs().size()).mapToObj(index -> { - if (index == nodeNumber) { - return null; - } - var dockerContainerName = configuration.getDockerConfiguration().getContainerName(); - var containerName = String.format(dockerContainerName, index); - var keyPair = variables.getValidatorKeypairs().get(index); - return "radix://" + keyPair.getPublicKey() + "@" + containerName; - }).filter(Objects::nonNull).collect(Collectors.toList()); - - return String.join(",", seeds); - } - - private static HostConfig createHostConfigWithPortBindings(int index, List exposedPorts, int primaryPort, - int secondaryPort) { - var portBindings = new Ports(); - - // primary port (client API) - int exposedPrimaryPortNumber = primaryPort + index; - var exposedPrimaryPort = ExposedPort.tcp(primaryPort); - portBindings.bind(exposedPrimaryPort, Ports.Binding.bindPort(exposedPrimaryPortNumber)); - exposedPorts.add(exposedPrimaryPort); - - // secondary port (node API) - int exposedSecondaryPortNumber = secondaryPort + index; - var exposedSecondaryPort = ExposedPort.tcp(secondaryPort); - portBindings.bind(exposedSecondaryPort, Ports.Binding.bindPort(exposedSecondaryPortNumber)); - exposedPorts.add(exposedSecondaryPort); - - return HostConfig.newHostConfig().withPortBindings(portBindings).withCapAdd(Capability.NET_ADMIN); - } - - private static String getGenesisTransaction(RemoteDockerClient dockerClient, String host, String containerName) { - logger.debug("Will fetch genesis tx from {} ", host); - // hardcoded genesis tx file location, might break - var genesis = dockerClient.runCommand(host, DOCKER_EXEC_COMMAND + containerName + " bash -c 'cat ./genesis.json'"); - if (StringUtils.isBlank(genesis)) { - throw new IllegalArgumentException("Genesis tx not found, cannot proceed."); - } - var genesisTransactionHex = new JSONObject(genesis).getString("genesis"); - logger.debug("Got genesis tx"); - return genesisTransactionHex; - } - - private static int getNetworkId(RemoteDockerClient dockerClient, String host, String containerName) { - logger.debug("Will fetch network ID from {} ", host); - var networkId = dockerClient.runCommand(host, DOCKER_EXEC_COMMAND + containerName - + " bash -c 'env | grep 'NETWORK_ID' | rev | head -c1'"); - logger.debug("Got network ID: {}", networkId); - try { - return Integer.parseInt(networkId); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Network ID was not a number: " + networkId); - } - } + rootUrls.parallelStream() + .forEach(rootUrl -> TestingUtils.waitForNodeToBeUp(httpClient, rootUrl)); + } + + private static String generateSequentialRemoteSeedsConfig( + UniverseVariables variables, RadixNetworkConfiguration configuration, int nodeNumber) { + var seeds = + IntStream.range(0, variables.getValidatorKeypairs().size()) + .mapToObj( + index -> { + if (index == nodeNumber) { + return null; + } + var dockerContainerName = + configuration.getDockerConfiguration().getContainerName(); + var containerName = String.format(dockerContainerName, index); + var keyPair = variables.getValidatorKeypairs().get(index); + return "radix://" + keyPair.getPublicKey() + "@" + containerName; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); - private static String getSeedsRemote(RemoteDockerClient dockerClient, String host, String containerName) { - logger.debug("Will fetch remote seeds from {} ", host); - var seedsRemote = dockerClient.runCommand(host, DOCKER_EXEC_COMMAND + containerName - + " bash -c 'env | grep SEEDS_REMOTE | cut -f2 -d\"=\"'").replace("\n", ""); - logger.debug("Got seeds remote: {}", seedsRemote); - return seedsRemote; + return String.join(",", seeds); + } + + private static HostConfig createHostConfigWithPortBindings( + int index, List exposedPorts, int primaryPort, int secondaryPort) { + var portBindings = new Ports(); + + // primary port (client API) + int exposedPrimaryPortNumber = primaryPort + index; + var exposedPrimaryPort = ExposedPort.tcp(primaryPort); + portBindings.bind(exposedPrimaryPort, Ports.Binding.bindPort(exposedPrimaryPortNumber)); + exposedPorts.add(exposedPrimaryPort); + + // secondary port (node API) + int exposedSecondaryPortNumber = secondaryPort + index; + var exposedSecondaryPort = ExposedPort.tcp(secondaryPort); + portBindings.bind(exposedSecondaryPort, Ports.Binding.bindPort(exposedSecondaryPortNumber)); + exposedPorts.add(exposedSecondaryPort); + + return HostConfig.newHostConfig() + .withPortBindings(portBindings) + .withCapAdd(Capability.NET_ADMIN); + } + + private static String getGenesisTransaction( + RemoteDockerClient dockerClient, String host, String containerName) { + logger.debug("Will fetch genesis tx from {} ", host); + // hardcoded genesis tx file location, might break + var genesis = + dockerClient.runCommand( + host, DOCKER_EXEC_COMMAND + containerName + " bash -c 'cat ./genesis.json'"); + if (StringUtils.isBlank(genesis)) { + throw new IllegalArgumentException("Genesis tx not found, cannot proceed."); } - - /** - * Images may need to login a docker repository and this method can run such a command at a host - */ - private static String loginDockerRepository(RemoteDockerClient dockerClient, String host, String dockerLoginCommand) { - var output = dockerClient.runCommand(host, dockerLoginCommand).replace("\n", ""); - logger.info(output); - return output; + var genesisTransactionHex = new JSONObject(genesis).getString("genesis"); + logger.debug("Got genesis tx"); + return genesisTransactionHex; + } + + private static int getNetworkId( + RemoteDockerClient dockerClient, String host, String containerName) { + logger.debug("Will fetch network ID from {} ", host); + var networkId = + dockerClient.runCommand( + host, + DOCKER_EXEC_COMMAND + + containerName + + " bash -c 'env | grep 'NETWORK_ID' | rev | head -c1'"); + logger.debug("Got network ID: {}", networkId); + try { + return Integer.parseInt(networkId); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Network ID was not a number: " + networkId); } - + } + + private static String getSeedsRemote( + RemoteDockerClient dockerClient, String host, String containerName) { + logger.debug("Will fetch remote seeds from {} ", host); + var seedsRemote = + dockerClient + .runCommand( + host, + DOCKER_EXEC_COMMAND + + containerName + + " bash -c 'env | grep SEEDS_REMOTE | cut -f2 -d\"=\"'") + .replace("\n", ""); + logger.debug("Got seeds remote: {}", seedsRemote); + return seedsRemote; + } + + /** + * Images may need to login a docker repository and this method can run such a command at a host + */ + private static String loginDockerRepository( + RemoteDockerClient dockerClient, String host, String dockerLoginCommand) { + var output = dockerClient.runCommand(host, dockerLoginCommand).replace("\n", ""); + logger.info(output); + return output; + } } - diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/LocalDockerClient.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/LocalDockerClient.java index 0218e1fb37..138853219a 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/LocalDockerClient.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/LocalDockerClient.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client.docker; import com.github.dockerjava.api.command.CreateContainerResponse; @@ -12,118 +76,145 @@ import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.radixdlt.test.network.DockerConfiguration; import com.radixdlt.test.utils.TestingUtils; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * A tcp, socket-based client that connects to a local daemon, consuming Docker's Engine API: https://docs.docker.com/engine/api/v1.40/ + * A tcp, socket-based client that connects to a local daemon, consuming Docker's Engine API: + * https://docs.docker.com/engine/api/v1.40/ */ public class LocalDockerClient implements DockerClient { - private static Logger logger = LoggerFactory.getLogger(LocalDockerClient.class); + private static Logger logger = LoggerFactory.getLogger(LocalDockerClient.class); - // the unix default is unix:///var/run/docker.sock. tcp://localhost:2375 might work on windows - private final String dockerSocketUrl; - private final String networkName; + // the unix default is unix:///var/run/docker.sock. tcp://localhost:2375 might work on windows + private final String dockerSocketUrl; + private final String networkName; - private com.github.dockerjava.api.DockerClient dockerClient; + private com.github.dockerjava.api.DockerClient dockerClient; - public LocalDockerClient(DockerConfiguration dockerConfiguration) { - this.dockerSocketUrl = dockerConfiguration.getSocketUrl(); - this.networkName = dockerConfiguration.getNetworkName(); - connect(); - } + public LocalDockerClient(DockerConfiguration dockerConfiguration) { + this.dockerSocketUrl = dockerConfiguration.getSocketUrl(); + this.networkName = dockerConfiguration.getNetworkName(); + connect(); + } - public void connect() { - var config = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost(dockerSocketUrl).build(); - var httpClient = new ApacheDockerHttpClient.Builder().dockerHost(config.getDockerHost()) - .sslConfig(config.getSSLConfig()).build(); - dockerClient = DockerClientImpl.getInstance(config, httpClient); - try { - dockerClient.pingCmd().exec(); - } catch (UnsatisfiedLinkError | NoClassDefFoundError e) { // this happens when you try to connect to a socket via windows - throw new DockerClientException("Could not connect to socket " + dockerSocketUrl - + ". Are you running the tests from windows?"); - } + public void connect() { + var config = + DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost(dockerSocketUrl) + .build(); + var httpClient = + new ApacheDockerHttpClient.Builder() + .dockerHost(config.getDockerHost()) + .sslConfig(config.getSSLConfig()) + .build(); + dockerClient = DockerClientImpl.getInstance(config, httpClient); + try { + dockerClient.pingCmd().exec(); + } catch (UnsatisfiedLinkError + | NoClassDefFoundError e) { // this happens when you try to connect to a socket via windows + throw new DockerClientException( + "Could not connect to socket " + + dockerSocketUrl + + ". Are you running the tests from windows?"); } + } - @SuppressWarnings("deprecation") - public String runCommand(String containerId, String... commands) { - var output = new ByteArrayOutputStream(); - var error = new ByteArrayOutputStream(); - var cmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true) - .withCmd(commands).exec(); - try { - dockerClient.execStartCmd(cmdResponse.getId()) - .exec(new ExecStartResultCallback(output, error)) - .awaitCompletion(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new DockerClientException(e.getMessage(), e); - } - if (StringUtils.isNotBlank(error.toString())) { - throw new DockerClientException(error.toString()); - } - return output.toString(StandardCharsets.UTF_8); + @SuppressWarnings("deprecation") + public String runCommand(String containerId, String... commands) { + var output = new ByteArrayOutputStream(); + var error = new ByteArrayOutputStream(); + var cmdResponse = + dockerClient.execCreateCmd(containerId).withAttachStdout(true).withCmd(commands).exec(); + try { + dockerClient + .execStartCmd(cmdResponse.getId()) + .exec(new ExecStartResultCallback(output, error)) + .awaitCompletion(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new DockerClientException(e.getMessage(), e); } + if (StringUtils.isNotBlank(error.toString())) { + throw new DockerClientException(error.toString()); + } + return output.toString(StandardCharsets.UTF_8); + } - public void startNewNode(String image, String containerName, List environment, HostConfig hostConfig, List exposedPorts, - String networkName) { - var environmentArray = environment.toArray(new String[0]); - CreateContainerResponse createContainer = dockerClient.createContainerCmd(image) + public void startNewNode( + String image, + String containerName, + List environment, + HostConfig hostConfig, + List exposedPorts, + String networkName) { + var environmentArray = environment.toArray(new String[0]); + CreateContainerResponse createContainer = + dockerClient + .createContainerCmd(image) .withName(containerName) .withEnv(environmentArray) .withHostConfig(hostConfig) .withExposedPorts(exposedPorts) .exec(); - TestingUtils.sleepMillis(500); - dockerClient.startContainerCmd(createContainer.getId()).exec(); - TestingUtils.sleepMillis(500); - dockerClient.connectToNetworkCmd().withNetworkId(networkName).withContainerId(containerName).exec(); - } + TestingUtils.sleepMillis(500); + dockerClient.startContainerCmd(createContainer.getId()).exec(); + TestingUtils.sleepMillis(500); + dockerClient + .connectToNetworkCmd() + .withNetworkId(networkName) + .withContainerId(containerName) + .exec(); + } - public void createNetwork(String networkName) { - try { - dockerClient.inspectNetworkCmd().withNetworkId(networkName).exec(); - cleanup(); - } catch (NotFoundException e) { - // all good, proceed - } catch (BadRequestException e) { // weird edge case - cleanup(); - } - dockerClient.createNetworkCmd().withName(networkName).exec(); + public void createNetwork(String networkName) { + try { + dockerClient.inspectNetworkCmd().withNetworkId(networkName).exec(); + cleanup(); + } catch (NotFoundException e) { + // all good, proceed + } catch (BadRequestException e) { // weird edge case + cleanup(); } + dockerClient.createNetworkCmd().withName(networkName).exec(); + } - @Override - public void restartNode(String containerName) { - dockerClient.restartContainerCmd(containerName).exec(); - } + @Override + public void restartNode(String containerName) { + dockerClient.restartContainerCmd(containerName).exec(); + } - @Override - public void stopNode(String containerName) { - dockerClient.stopContainerCmd(containerName).exec(); - } + @Override + public void stopNode(String containerName) { + dockerClient.stopContainerCmd(containerName).exec(); + } - @Override - public void cleanup(String... parameters) { - var count = new AtomicInteger(); - dockerClient.listContainersCmd().withShowAll(true).exec().forEach(container -> { - if (container.getNetworkSettings().getNetworks().keySet().contains(networkName)) { + @Override + public void cleanup(String... parameters) { + var count = new AtomicInteger(); + dockerClient + .listContainersCmd() + .withShowAll(true) + .exec() + .forEach( + container -> { + if (container.getNetworkSettings().getNetworks().keySet().contains(networkName)) { dockerClient.stopContainerCmd(container.getId()).exec(); dockerClient.removeContainerCmd(container.getId()).exec(); logger.debug("Removed container '{}'", container.getId()); - } - count.incrementAndGet(); - }); - dockerClient.removeNetworkCmd(networkName).exec(); - logger.debug("Removed existing network '{}', along with its {} containers", networkName, count.intValue()); - } - + } + count.incrementAndGet(); + }); + dockerClient.removeNetworkCmd(networkName).exec(); + logger.debug( + "Removed existing network '{}', along with its {} containers", + networkName, + count.intValue()); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/RemoteDockerClient.java b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/RemoteDockerClient.java index 7954eb7401..b060fdfd09 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/RemoteDockerClient.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/network/client/docker/RemoteDockerClient.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.network.client.docker; import com.github.dockerjava.api.exception.DockerClientException; @@ -8,127 +72,128 @@ import com.radixdlt.test.network.RadixNetworkConfiguration; import com.radixdlt.test.network.SshConfiguration; import com.radixdlt.test.utils.TestingUtils; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -/** - * A class that can run commands (including docker commands e.g. 'docker restart') over ssh - */ +/** A class that can run commands (including docker commands e.g. 'docker restart') over ssh */ public class RemoteDockerClient implements DockerClient { - // testing the remote client - public static void main(String[] args) { - var configuration = RadixNetworkConfiguration.fromEnv(); - DockerNetworkCreator.initializeAndStartNodeFromTrustedNode(configuration, "host", "trusted host"); - } - - private static Logger logger = LoggerFactory.getLogger(RemoteDockerClient.class); - - private final SshConfiguration sshConfiguration; - private final String containerName; - - public RemoteDockerClient(RadixNetworkConfiguration configuration) { - this(configuration.getSshConfiguration(), configuration.getDockerConfiguration().getContainerName()); - } - - public RemoteDockerClient(SshConfiguration sshConfiguration, String containerName) { - this.sshConfiguration = sshConfiguration; - this.containerName = containerName; - } - - /** - * this is basically the official example from jsch, copied - */ - public String runCommand(String nodeLocator, String... commands) { - logger.debug("Run command [{}] at {}", commands, nodeLocator); - checkProperties(); - var jsch = new JSch(); - Session session = null; - ChannelExec channel = null; - try { - jsch.addIdentity(sshConfiguration.getSshKeyLocation(), sshConfiguration.getSshKeyPassphrase()); - session = jsch.getSession(sshConfiguration.getUser(), nodeLocator, sshConfiguration.getPort()); - var config = new java.util.Properties(); - config.put("StrictHostKeyChecking", "no"); - session.setConfig(config); - session.connect(10000); - logger.trace("Connected via ssh to {}", nodeLocator); - - channel = (ChannelExec) session.openChannel("exec"); - channel.setCommand(String.join(" ", commands)); - try (var outputBuffer = new ByteArrayOutputStream(); var errorBuffer = new ByteArrayOutputStream()) { - var inputStream = channel.getInputStream(); - var errorStream = channel.getExtInputStream(); - channel.connect(); - - byte[] tmp = new byte[1024]; - while (true) { - while (inputStream.available() > 0) { - int i = inputStream.read(tmp, 0, 1024); - if (i < 0) { - break; - } - outputBuffer.write(tmp, 0, i); - } - while (errorStream.available() > 0) { - int i = errorStream.read(tmp, 0, 1024); - if (i < 0) { - break; - } - errorBuffer.write(tmp, 0, i); - } - if (channel.isClosed()) { - if ((inputStream.available() > 0) || (errorStream.available() > 0)) { - continue; - } - logger.debug("Command exit-status: {}", channel.getExitStatus()); - break; - } - TestingUtils.sleepMillis(250); - } - var error = errorBuffer.toString(StandardCharsets.UTF_8); - var output = outputBuffer.toString(StandardCharsets.UTF_8); - return (StringUtils.isBlank(output)) ? error : output; + // testing the remote client + public static void main(String[] args) { + var configuration = RadixNetworkConfiguration.fromEnv(); + DockerNetworkCreator.initializeAndStartNodeFromTrustedNode( + configuration, "host", "trusted host"); + } + + private static Logger logger = LoggerFactory.getLogger(RemoteDockerClient.class); + + private final SshConfiguration sshConfiguration; + private final String containerName; + + public RemoteDockerClient(RadixNetworkConfiguration configuration) { + this( + configuration.getSshConfiguration(), + configuration.getDockerConfiguration().getContainerName()); + } + + public RemoteDockerClient(SshConfiguration sshConfiguration, String containerName) { + this.sshConfiguration = sshConfiguration; + this.containerName = containerName; + } + + /** this is basically the official example from jsch, copied */ + public String runCommand(String nodeLocator, String... commands) { + logger.debug("Run command [{}] at {}", commands, nodeLocator); + checkProperties(); + var jsch = new JSch(); + Session session = null; + ChannelExec channel = null; + try { + jsch.addIdentity( + sshConfiguration.getSshKeyLocation(), sshConfiguration.getSshKeyPassphrase()); + session = + jsch.getSession(sshConfiguration.getUser(), nodeLocator, sshConfiguration.getPort()); + var config = new java.util.Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + session.connect(10000); + logger.trace("Connected via ssh to {}", nodeLocator); + + channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(String.join(" ", commands)); + try (var outputBuffer = new ByteArrayOutputStream(); + var errorBuffer = new ByteArrayOutputStream()) { + var inputStream = channel.getInputStream(); + var errorStream = channel.getExtInputStream(); + channel.connect(); + + byte[] tmp = new byte[1024]; + while (true) { + while (inputStream.available() > 0) { + int i = inputStream.read(tmp, 0, 1024); + if (i < 0) { + break; } - } catch (IOException | JSchException e) { - // any error here should be escalated - throw new DockerClientException(e.getMessage(), e); - } finally { - if (session != null) { - session.disconnect(); + outputBuffer.write(tmp, 0, i); + } + while (errorStream.available() > 0) { + int i = errorStream.read(tmp, 0, 1024); + if (i < 0) { + break; } - if (channel != null) { - channel.disconnect(); + errorBuffer.write(tmp, 0, i); + } + if (channel.isClosed()) { + if ((inputStream.available() > 0) || (errorStream.available() > 0)) { + continue; } + logger.debug("Command exit-status: {}", channel.getExitStatus()); + break; + } + TestingUtils.sleepMillis(250); } + var error = errorBuffer.toString(StandardCharsets.UTF_8); + var output = outputBuffer.toString(StandardCharsets.UTF_8); + return (StringUtils.isBlank(output)) ? error : output; + } + } catch (IOException | JSchException e) { + // any error here should be escalated + throw new DockerClientException(e.getMessage(), e); + } finally { + if (session != null) { + session.disconnect(); + } + if (channel != null) { + channel.disconnect(); + } } + } - private void checkProperties() { - if (sshConfiguration.getPort() == -1 || StringUtils.isBlank(sshConfiguration.getUser())) { - throw new IllegalArgumentException("You need to set RADIXDLT_SYSTEM_TESTING_SSH_KEY_USER and " - + "RADIXDLT_SYSTEM_TESTING_SSH_KEY_PORT to run commands over SSH"); - } - } - - @Override - public void restartNode(String nodeLocator) { - runCommand(nodeLocator, "docker restart " + containerName); - } - - @Override - public void stopNode(String nodeLocator) { - runCommand(nodeLocator, "docker stop " + containerName); + private void checkProperties() { + if (sshConfiguration.getPort() == -1 || StringUtils.isBlank(sshConfiguration.getUser())) { + throw new IllegalArgumentException( + "You need to set RADIXDLT_SYSTEM_TESTING_SSH_KEY_USER and " + + "RADIXDLT_SYSTEM_TESTING_SSH_KEY_PORT to run commands over SSH"); } - - @Override - public void cleanup(String... parameters) { - List.of(parameters).forEach(host -> runCommand(host, "docker start " + containerName)); - } - + } + + @Override + public void restartNode(String nodeLocator) { + runCommand(nodeLocator, "docker restart " + containerName); + } + + @Override + public void stopNode(String nodeLocator) { + runCommand(nodeLocator, "docker stop " + containerName); + } + + @Override + public void cleanup(String... parameters) { + List.of(parameters).forEach(host -> runCommand(host, "docker start " + containerName)); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestFailureException.java b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestFailureException.java index 9bcf721ed4..d49b27199c 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestFailureException.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestFailureException.java @@ -1,16 +1,77 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.utils; -/** - * A runtime exception which is thrown when a cucumber/acceptance test is supposed to fail - */ +/** A runtime exception which is thrown when a cucumber/acceptance test is supposed to fail */ public class TestFailureException extends RuntimeException { - public TestFailureException(String message) { - super(message); - } - - public TestFailureException(Exception e) { - super(e); - } + public TestFailureException(String message) { + super(message); + } + public TestFailureException(Exception e) { + super(e); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestingUtils.java b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestingUtils.java index c27b2c4c13..9bb34ca80e 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestingUtils.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TestingUtils.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.utils; +import static org.awaitility.Awaitility.await; + import com.google.common.base.Stopwatch; import com.radixdlt.client.lib.dto.TokenInfo; import com.radixdlt.client.lib.dto.TransactionDTO; @@ -14,185 +80,173 @@ import com.radixdlt.utils.Ints; import com.radixdlt.utils.UInt256; import com.radixdlt.utils.functional.Failure; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.awaitility.Durations; import org.awaitility.core.ConditionTimeoutException; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; - -import static org.awaitility.Awaitility.await; - -/** - * Various testing utilities - */ +/** Various testing utilities */ public final class TestingUtils { - private static final Logger logger = LogManager.getLogger(); - - public static final Duration MAX_TIME_TO_WAIT_FOR_NODES_UP = Durations.TWO_MINUTES; - - private TestingUtils() { - - } - - public static boolean isNullOrEmpty(String string) { - return string == null || string.isEmpty(); - } - - public static ECKeyPair createKeyPairFromNumber(int privateKey) { - var pk = new byte[ECKeyPair.BYTES]; - Ints.copyTo(privateKey, pk, ECKeyPair.BYTES - Integer.BYTES); - - try { - return ECKeyPair.fromPrivateKey(pk); - } catch (PrivateKeyException | PublicKeyException e) { - throw new IllegalArgumentException("Error while generating public key", e); - } - } - - public static void sleep(int seconds) { - sleepMillis(1000L * seconds); - } - - public static void sleepMillis(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - throw new TestFailureException(e); - } - } - - public static String getEnvWithDefault(String envName, String defaultValue) { - String envValue = System.getenv(envName); - return (envValue == null || envValue.isBlank()) ? defaultValue : envValue; - } - - public static int getEnvWithDefault(String envName, int defaultValue) { - String envValue = System.getenv(envName); - return (envValue == null || envValue.isBlank()) ? defaultValue : Integer.parseInt(envValue); - } - - /** - * Will wait until the native token balance reaches the given amount - */ - public static void waitForBalanceToReach(Account account, UInt256 amount) { - try { - await().atMost(Durations.ONE_MINUTE).until(() -> - account.getOwnNativeTokenBalance().getAmount().compareTo(amount) >= 0); - } catch (ConditionTimeoutException e) { - throw new TestFailureException("Account's balance did not reach " + amount); - } - } - - /** - * Will wait until the account's native token balance increases by any amount - */ - public static void waitForBalanceToIncrease(Account account) { - UInt256 initialAmount = account.getOwnNativeTokenBalance().getAmount(); - try { - await().atMost(Durations.ONE_MINUTE).until(() -> - account.getOwnNativeTokenBalance().getAmount().compareTo(initialAmount) > 0); - } catch (ConditionTimeoutException e) { - throw new TestFailureException("Account's balance did not decrease"); - } - } - - /** - * Waits until the account's native balance decreases by any amount - */ - public static void waitForBalanceToDecrease(Account account) { - UInt256 initialAmount = account.getOwnNativeTokenBalance().getAmount(); - try { - await().atMost(Durations.ONE_MINUTE).until(() -> - account.getOwnNativeTokenBalance().getAmount().compareTo(initialAmount) < 0); - } catch (ConditionTimeoutException e) { - throw new TestFailureException("Account's balance did not decrease"); - } - } - - /** - * Waits until the account's native balance changes, up or down - */ - public static void waitForBalanceToChange(Account account) { - UInt256 initialAmount = account.getOwnNativeTokenBalance().getAmount(); - try { - await().atMost(Durations.ONE_MINUTE).until(() -> - account.getOwnNativeTokenBalance().getAmount().compareTo(initialAmount) != 0); - } catch (ConditionTimeoutException e) { - throw new TestFailureException("Account's balance did not change"); - } - } - - /** - * Uses a default duration (might want to externalize this) - */ - public static void waitForNodeToBeUp(RadixHttpClient httpClient, String rootUrl) { - await().pollDelay(Durations.TWO_HUNDRED_MILLISECONDS).atMost(MAX_TIME_TO_WAIT_FOR_NODES_UP).ignoreExceptions().until(() -> { - var status = httpClient.getHealthStatus(rootUrl); - if (!status.equals(RadixHttpClient.HealthStatus.UP)) { + private static final Logger logger = LogManager.getLogger(); + + public static final Duration MAX_TIME_TO_WAIT_FOR_NODES_UP = Durations.TWO_MINUTES; + + private TestingUtils() {} + + public static boolean isNullOrEmpty(String string) { + return string == null || string.isEmpty(); + } + + public static ECKeyPair createKeyPairFromNumber(int privateKey) { + var pk = new byte[ECKeyPair.BYTES]; + Ints.copyTo(privateKey, pk, ECKeyPair.BYTES - Integer.BYTES); + + try { + return ECKeyPair.fromPrivateKey(pk); + } catch (PrivateKeyException | PublicKeyException e) { + throw new IllegalArgumentException("Error while generating public key", e); + } + } + + public static void sleep(int seconds) { + sleepMillis(1000L * seconds); + } + + public static void sleepMillis(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new TestFailureException(e); + } + } + + public static String getEnvWithDefault(String envName, String defaultValue) { + String envValue = System.getenv(envName); + return (envValue == null || envValue.isBlank()) ? defaultValue : envValue; + } + + public static int getEnvWithDefault(String envName, int defaultValue) { + String envValue = System.getenv(envName); + return (envValue == null || envValue.isBlank()) ? defaultValue : Integer.parseInt(envValue); + } + + /** Will wait until the native token balance reaches the given amount */ + public static void waitForBalanceToReach(Account account, UInt256 amount) { + try { + await() + .atMost(Durations.ONE_MINUTE) + .until(() -> account.getOwnNativeTokenBalance().getAmount().compareTo(amount) >= 0); + } catch (ConditionTimeoutException e) { + throw new TestFailureException("Account's balance did not reach " + amount); + } + } + + /** Will wait until the account's native token balance increases by any amount */ + public static void waitForBalanceToIncrease(Account account) { + UInt256 initialAmount = account.getOwnNativeTokenBalance().getAmount(); + try { + await() + .atMost(Durations.ONE_MINUTE) + .until(() -> account.getOwnNativeTokenBalance().getAmount().compareTo(initialAmount) > 0); + } catch (ConditionTimeoutException e) { + throw new TestFailureException("Account's balance did not decrease"); + } + } + + /** Waits until the account's native balance decreases by any amount */ + public static void waitForBalanceToDecrease(Account account) { + UInt256 initialAmount = account.getOwnNativeTokenBalance().getAmount(); + try { + await() + .atMost(Durations.ONE_MINUTE) + .until(() -> account.getOwnNativeTokenBalance().getAmount().compareTo(initialAmount) < 0); + } catch (ConditionTimeoutException e) { + throw new TestFailureException("Account's balance did not decrease"); + } + } + + /** Waits until the account's native balance changes, up or down */ + public static void waitForBalanceToChange(Account account) { + UInt256 initialAmount = account.getOwnNativeTokenBalance().getAmount(); + try { + await() + .atMost(Durations.ONE_MINUTE) + .until( + () -> account.getOwnNativeTokenBalance().getAmount().compareTo(initialAmount) != 0); + } catch (ConditionTimeoutException e) { + throw new TestFailureException("Account's balance did not change"); + } + } + + /** Uses a default duration (might want to externalize this) */ + public static void waitForNodeToBeUp(RadixHttpClient httpClient, String rootUrl) { + await() + .pollDelay(Durations.TWO_HUNDRED_MILLISECONDS) + .atMost(MAX_TIME_TO_WAIT_FOR_NODES_UP) + .ignoreExceptions() + .until( + () -> { + var status = httpClient.getHealthStatus(rootUrl); + if (!status.equals(RadixHttpClient.HealthStatus.UP)) { return false; - } - logger.debug("Node at {} is UP", rootUrl); - return true; - }); - } - - public static void waitForNodeToBeUp(RadixNode node, RadixNetworkConfiguration configuration) { - String healthRootUrl = node.getRootUrl() + ":" + node.getSecondaryPort(); - waitForNodeToBeUp(RadixHttpClient.fromRadixNetworkConfiguration(configuration), healthRootUrl); - } - - /** - * expects a token creation AID - */ - public static TokenInfo getTokenInfoFromAID(Account account, AID aid) { - var transaction = account.lookup(aid); - return getTokenInfoFromTransaction(account, transaction); - } - - /** - * expects a token creation AID - */ - public static String getRriFromAID(Account account, AID aid) { - var transaction = account.lookup(aid); - return getTokenInfoFromTransaction(account, transaction).getRri(); - } - - /** - * expects a token creation transaction dto - */ - public static TokenInfo getTokenInfoFromTransaction(Account account, TransactionDTO transaction) { - if (transaction.getEvents().isEmpty() || transaction.getEvents().get(0).getRri().isEmpty()) { - throw new TestFailureException("Token creation transaction did not contain the correct event"); - } - var rriOpt = transaction.getEvents().get(0).getRri(); - var rri = rriOpt.orElseThrow(() -> new TestFailureException("Token creation action did not have an rri")); - return account.token().describe(rri); - } - - public static R toTestFailureException(Failure failure) { - throw new TestFailureException(failure.message()); - } - - /** - * Waits for the given # of epochs to pass - */ - public static void waitEpochs(Account account, int numberOfEpochsToWait) { - IntStream.range(0, numberOfEpochsToWait).forEach(i -> waitUntilEndNextEpoch(account)); - } - - public static void waitUntilEndNextEpoch(Account account) { - var currentEpoch = account.ledger().epoch().getHeader().getEpoch(); - logger.debug("Waiting for epoch {} to end...", currentEpoch); - var stopwatch = Stopwatch.createStarted(); - await().pollInterval(Durations.ONE_SECOND).atMost(Duration.ofMinutes(30)).until(() -> - account.ledger().epoch().getHeader().getEpoch() > currentEpoch - ); - logger.debug("Epoch {} ended", currentEpoch); - stopwatch.elapsed(TimeUnit.MILLISECONDS); - } - + } + logger.debug("Node at {} is UP", rootUrl); + return true; + }); + } + + public static void waitForNodeToBeUp(RadixNode node, RadixNetworkConfiguration configuration) { + String healthRootUrl = node.getRootUrl() + ":" + node.getSecondaryPort(); + waitForNodeToBeUp(RadixHttpClient.fromRadixNetworkConfiguration(configuration), healthRootUrl); + } + + /** expects a token creation AID */ + public static TokenInfo getTokenInfoFromAID(Account account, AID aid) { + var transaction = account.lookup(aid); + return getTokenInfoFromTransaction(account, transaction); + } + + /** expects a token creation AID */ + public static String getRriFromAID(Account account, AID aid) { + var transaction = account.lookup(aid); + return getTokenInfoFromTransaction(account, transaction).getRri(); + } + + /** expects a token creation transaction dto */ + public static TokenInfo getTokenInfoFromTransaction(Account account, TransactionDTO transaction) { + if (transaction.getEvents().isEmpty() || transaction.getEvents().get(0).getRri().isEmpty()) { + throw new TestFailureException( + "Token creation transaction did not contain the correct event"); + } + var rriOpt = transaction.getEvents().get(0).getRri(); + var rri = + rriOpt.orElseThrow( + () -> new TestFailureException("Token creation action did not have an rri")); + return account.token().describe(rri); + } + + public static R toTestFailureException(Failure failure) { + throw new TestFailureException(failure.message()); + } + + /** Waits for the given # of epochs to pass */ + public static void waitEpochs(Account account, int numberOfEpochsToWait) { + IntStream.range(0, numberOfEpochsToWait).forEach(i -> waitUntilEndNextEpoch(account)); + } + + public static void waitUntilEndNextEpoch(Account account) { + var currentEpoch = account.ledger().epoch().getHeader().getEpoch(); + logger.debug("Waiting for epoch {} to end...", currentEpoch); + var stopwatch = Stopwatch.createStarted(); + await() + .pollInterval(Durations.ONE_SECOND) + .atMost(Duration.ofMinutes(30)) + .until(() -> account.ledger().epoch().getHeader().getEpoch() > currentEpoch); + logger.debug("Epoch {} ended", currentEpoch); + stopwatch.elapsed(TimeUnit.MILLISECONDS); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TransactionUtils.java b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TransactionUtils.java index 6dcf9b1817..b7a7edea06 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TransactionUtils.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/TransactionUtils.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.utils; +import static org.awaitility.Awaitility.await; + import com.radixdlt.application.tokens.Amount; import com.radixdlt.client.lib.api.AccountAddress; import com.radixdlt.client.lib.api.TransactionRequest; @@ -10,189 +76,247 @@ import com.radixdlt.identifiers.AID; import com.radixdlt.test.account.Account; import com.radixdlt.utils.UInt256; -import org.awaitility.core.ConditionTimeoutException; - import java.time.Duration; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; - -import static org.awaitility.Awaitility.await; +import org.awaitility.core.ConditionTimeoutException; /** - * Various helper utils that make working with our api easier. Most of the methods here use the imperative API and - * are probably suited for simple use cases or testing. + * Various helper utils that make working with our api easier. Most of the methods here use the + * imperative API and are probably suited for simple use cases or testing. */ public final class TransactionUtils { - public static final Duration DEFAULT_TX_CONFIRMATION_PATIENCE = Duration.ofMinutes(1); + public static final Duration DEFAULT_TX_CONFIRMATION_PATIENCE = Duration.ofMinutes(1); - private TransactionUtils() { + private TransactionUtils() {} - } + public static TransactionRequest createTokenTransferRequest( + AccountAddress from, + AccountAddress to, + String tokenRri, + UInt256 amount, + Optional message) { + return message + .map( + s -> + TransactionRequest.createBuilder(from) + .transfer(from, to, amount, tokenRri) + .message(s) + .build()) + .orElseGet( + () -> + TransactionRequest.createBuilder(from) + .transfer(from, to, amount, tokenRri) + .build()); + } - public static TransactionRequest createTokenTransferRequest(AccountAddress from, AccountAddress to, String tokenRri, - UInt256 amount, Optional message) { - return message.map(s -> TransactionRequest.createBuilder(from) - .transfer(from, to, amount, tokenRri) - .message(s) - .build()).orElseGet(() -> TransactionRequest.createBuilder(from) - .transfer(from, to, amount, tokenRri) - .build()); - } + public static TransactionRequest createStakingRequest( + AccountAddress from, ValidatorAddress unstakeFrom, Amount stake, Optional message) { + return message + .map( + s -> + TransactionRequest.createBuilder(from) + .stake(from, unstakeFrom, stake.toSubunits()) + .message(s) + .build()) + .orElseGet( + () -> + TransactionRequest.createBuilder(from) + .stake(from, unstakeFrom, stake.toSubunits()) + .build()); + } - public static TransactionRequest createStakingRequest(AccountAddress from, ValidatorAddress unstakeFrom, - Amount stake, Optional message) { - return message.map(s -> TransactionRequest.createBuilder(from) - .stake(from, unstakeFrom, stake.toSubunits()) - .message(s) - .build()).orElseGet(() -> TransactionRequest.createBuilder(from) - .stake(from, unstakeFrom, stake.toSubunits()) - .build()); - } + public static TransactionRequest createUnstakingRequest( + AccountAddress from, ValidatorAddress unstakeFrom, Amount stake, Optional message) { + return message + .map( + s -> + TransactionRequest.createBuilder(from) + .unstake(from, unstakeFrom, stake.toSubunits()) + .message(s) + .build()) + .orElseGet( + () -> + TransactionRequest.createBuilder(from) + .unstake(from, unstakeFrom, stake.toSubunits()) + .build()); + } - public static TransactionRequest createUnstakingRequest(AccountAddress from, ValidatorAddress unstakeFrom, - Amount stake, Optional message) { - return message.map(s -> TransactionRequest.createBuilder(from) - .unstake(from, unstakeFrom, stake.toSubunits()) - .message(s) - .build()).orElseGet(() -> TransactionRequest.createBuilder(from) - .unstake(from, unstakeFrom, stake.toSubunits()) - .build()); - } + public static TransactionRequest createMintRequest( + AccountAddress from, Amount amount, String rri, Optional message) { + return message + .map( + s -> + TransactionRequest.createBuilder(from) + .mint(from, amount.toSubunits(), rri) + .message(s) + .build()) + .orElseGet( + () -> + TransactionRequest.createBuilder(from) + .mint(from, amount.toSubunits(), rri) + .build()); + } - public static TransactionRequest createMintRequest(AccountAddress from, Amount amount, String rri, Optional message) { - return message.map(s -> TransactionRequest.createBuilder(from) - .mint(from, amount.toSubunits(), rri) - .message(s) - .build()).orElseGet(() -> TransactionRequest.createBuilder(from) - .mint(from, amount.toSubunits(), rri) - .build()); - } + public static TransactionRequest createBurnRequest( + AccountAddress from, Amount amount, String rri, Optional message) { + return message + .map( + s -> + TransactionRequest.createBuilder(from) + .burn(from, amount.toSubunits(), rri) + .message(s) + .build()) + .orElseGet( + () -> + TransactionRequest.createBuilder(from) + .burn(from, amount.toSubunits(), rri) + .build()); + } - public static TransactionRequest createBurnRequest(AccountAddress from, Amount amount, String rri, Optional message) { - return message.map(s -> TransactionRequest.createBuilder(from) - .burn(from, amount.toSubunits(), rri) - .message(s) - .build()).orElseGet(() -> TransactionRequest.createBuilder(from) - .burn(from, amount.toSubunits(), rri) - .build()); - } + /** Stakes tokens and waits for transaction confirmation */ + public static AID stake( + Account account, ValidatorAddress to, Amount amount, Optional message) { + var request = createStakingRequest(account.getAddress(), to, amount, message); + return buildFinalizeAndSubmitTransaction(account, request, true); + } - /** - * Stakes tokens and waits for transaction confirmation - */ - public static AID stake(Account account, ValidatorAddress to, Amount amount, Optional message) { - var request = createStakingRequest(account.getAddress(), to, amount, message); - return buildFinalizeAndSubmitTransaction(account, request, true); - } + /** Unstakes tokens and waits for transaction confirmation */ + public static AID unstake( + Account account, ValidatorAddress validatorAddress, Amount amount, Optional message) { + var request = createUnstakingRequest(account.getAddress(), validatorAddress, amount, message); + return buildFinalizeAndSubmitTransaction(account, request, true); + } - /** - * Unstakes tokens and waits for transaction confirmation - */ - public static AID unstake(Account account, ValidatorAddress validatorAddress, Amount amount, Optional message) { - var request = createUnstakingRequest(account.getAddress(), validatorAddress, amount, message); - return buildFinalizeAndSubmitTransaction(account, request, true); - } + /** Mints tokens and waits for transaction confirmation */ + public static AID mint(Account account, Amount amount, String rri, Optional message) { + var request = createMintRequest(account.getAddress(), amount, rri, message); + return buildFinalizeAndSubmitTransaction(account, request, true); + } - /** - * Mints tokens and waits for transaction confirmation - */ - public static AID mint(Account account, Amount amount, String rri, Optional message) { - var request = createMintRequest(account.getAddress(), amount, rri, message); - return buildFinalizeAndSubmitTransaction(account, request, true); - } + /** Burns tokens and waits for transaction confirmation */ + public static AID burn(Account account, Amount amount, String rri, Optional message) { + var request = createBurnRequest(account.getAddress(), amount, rri, message); + return buildFinalizeAndSubmitTransaction(account, request, true); + } - /** - * Burns tokens and waits for transaction confirmation - */ - public static AID burn(Account account, Amount amount, String rri, Optional message) { - var request = createBurnRequest(account.getAddress(), amount, rri, message); - return buildFinalizeAndSubmitTransaction(account, request, true); - } + /** Executes an XRD transfer and waits for transaction confirmation */ + public static AID nativeTokenTransfer( + Account sender, Account receiver, Amount amount, Optional message) { + var request = + TransactionUtils.createTokenTransferRequest( + sender.getAddress(), + receiver.getAddress(), + sender.getNativeToken().getRri(), + amount.toSubunits(), + message); + return buildFinalizeAndSubmitTransaction(sender, request, true); + } - /** - * Executes an XRD transfer and waits for transaction confirmation - */ - public static AID nativeTokenTransfer(Account sender, Account receiver, Amount amount, Optional message) { - var request = TransactionUtils.createTokenTransferRequest(sender.getAddress(), receiver.getAddress(), - sender.getNativeToken().getRri(), amount.toSubunits(), message); - return buildFinalizeAndSubmitTransaction(sender, request, true); - } - - /** - * Submits a fixed supply token creation transaction and waits for its confirmation - */ - public static AID createFixedSupplyToken(Account creator, String symbol, String name, String description, - String iconUrl, String tokenUrl, Amount supply) { - var key = creator.getKeyPair().getPublicKey(); - var request = TransactionRequest.createBuilder(creator.getAddress()) - .createFixed(creator.getAddress(), key, symbol, name, description, iconUrl, tokenUrl, supply.toSubunits()) + /** Submits a fixed supply token creation transaction and waits for its confirmation */ + public static AID createFixedSupplyToken( + Account creator, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl, + Amount supply) { + var key = creator.getKeyPair().getPublicKey(); + var request = + TransactionRequest.createBuilder(creator.getAddress()) + .createFixed( + creator.getAddress(), + key, + symbol, + name, + description, + iconUrl, + tokenUrl, + supply.toSubunits()) .build(); - return buildFinalizeAndSubmitTransaction(creator, request, true); - } + return buildFinalizeAndSubmitTransaction(creator, request, true); + } - public static AID createMutableSupplyToken(Account creator, String symbol, String name, String description, - String iconUrl, String tokenUrl) { - var key = creator.getKeyPair().getPublicKey(); - var request = TransactionRequest.createBuilder(creator.getAddress()) - .createMutable(key, symbol, name, Optional.of(description), Optional.of(iconUrl), Optional.of(tokenUrl)).build(); - return buildFinalizeAndSubmitTransaction(creator, request, true); - } - - - /** - * Builds, finalizes and submits a transaction. Can optionally wait for it to become CONFIRMED - * - * @return the {@link AID} of the submitted transaction - */ - public static AID buildFinalizeAndSubmitTransaction(Account account, TransactionRequest request, boolean waitForConfirmation) { - var keyPair = account.getKeyPair(); - var builtTransaction = account.transaction().build(request); - var finalizedTransaction = account.transaction().finalize(builtTransaction.toFinalized(keyPair), false); - var submittedTransaction = account.transaction().submit(finalizedTransaction); + public static AID createMutableSupplyToken( + Account creator, + String symbol, + String name, + String description, + String iconUrl, + String tokenUrl) { + var key = creator.getKeyPair().getPublicKey(); + var request = + TransactionRequest.createBuilder(creator.getAddress()) + .createMutable( + key, + symbol, + name, + Optional.of(description), + Optional.of(iconUrl), + Optional.of(tokenUrl)) + .build(); + return buildFinalizeAndSubmitTransaction(creator, request, true); + } - if (waitForConfirmation) { - waitForConfirmation(account, submittedTransaction.getTxId()); - } + /** + * Builds, finalizes and submits a transaction. Can optionally wait for it to become CONFIRMED + * + * @return the {@link AID} of the submitted transaction + */ + public static AID buildFinalizeAndSubmitTransaction( + Account account, TransactionRequest request, boolean waitForConfirmation) { + var keyPair = account.getKeyPair(); + var builtTransaction = account.transaction().build(request); + var finalizedTransaction = + account.transaction().finalize(builtTransaction.toFinalized(keyPair), false); + var submittedTransaction = account.transaction().submit(finalizedTransaction); - return submittedTransaction.getTxId(); + if (waitForConfirmation) { + waitForConfirmation(account, submittedTransaction.getTxId()); } - /** - * Will block until the a transaction for the given txID is found - */ - public static TransactionDTO lookupTransaction(Account account, AID txId) { - AtomicReference transaction = new AtomicReference<>(); - try { - await().atMost(DEFAULT_TX_CONFIRMATION_PATIENCE).ignoreException(RadixApiException.class).until(() -> { + return submittedTransaction.getTxId(); + } + + /** Will block until the a transaction for the given txID is found */ + public static TransactionDTO lookupTransaction(Account account, AID txId) { + AtomicReference transaction = new AtomicReference<>(); + try { + await() + .atMost(DEFAULT_TX_CONFIRMATION_PATIENCE) + .ignoreException(RadixApiException.class) + .until( + () -> { transaction.set(account.transaction().lookup(txId)); return true; - }); - } catch (ConditionTimeoutException e) { - throw new TestFailureException("Transaction " + txId + " was not found within " + DEFAULT_TX_CONFIRMATION_PATIENCE); - } - return transaction.get(); + }); + } catch (ConditionTimeoutException e) { + throw new TestFailureException( + "Transaction " + txId + " was not found within " + DEFAULT_TX_CONFIRMATION_PATIENCE); } + return transaction.get(); + } - /** - * Will block until the given transaction is CONFIRMED - */ - public static void waitForConfirmation(Account account, AID txId, Duration patience) { - try { - await().atMost(patience).until(() -> { + /** Will block until the given transaction is CONFIRMED */ + public static void waitForConfirmation(Account account, AID txId, Duration patience) { + try { + await() + .atMost(patience) + .until( + () -> { var status = account.transaction().status(txId); return status.getStatus().equals(TransactionStatus.CONFIRMED); - }); - } catch (ConditionTimeoutException e) { - throw new TestFailureException("Transaction (" + txId + ") was not CONFIRMED within " + patience); - } - } - - /** - * Will block until the given transaction is CONFIRMED. Will have the default patience - */ - public static void waitForConfirmation(Account account, AID txId) { - waitForConfirmation(account, txId, DEFAULT_TX_CONFIRMATION_PATIENCE); + }); + } catch (ConditionTimeoutException e) { + throw new TestFailureException( + "Transaction (" + txId + ") was not CONFIRMED within " + patience); } + } + /** Will block until the given transaction is CONFIRMED. Will have the default patience */ + public static void waitForConfirmation(Account account, AID txId) { + waitForConfirmation(account, txId, DEFAULT_TX_CONFIRMATION_PATIENCE); + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseUtils.java b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseUtils.java index 4a89fd2658..c46ec0e3a0 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseUtils.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseUtils.java @@ -1,72 +1,131 @@ -package com.radixdlt.test.utils.universe; +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ -import org.apache.commons.lang.StringUtils; -import org.radix.GenerateUniverses; +package com.radixdlt.test.utils.universe; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.lang.StringUtils; +import org.radix.GenerateUniverses; -/** - * Helps when generating universes and such - */ +/** Helps when generating universes and such */ public class UniverseUtils { - private UniverseUtils() { - - } - - /** - * is probably not thread safe - */ - public static UniverseVariables generateEnvironmentVariables(int numberOfValidators) { - // create a custom stream to hold system.out output until the generation finishes. - // this could be super messy when run concurrently - var outputAsString = getUniverseGenerationOutputAsString(numberOfValidators); - var output = outputAsString.split("\\R"); - var keyPairs = IntStream.range(0, numberOfValidators) - .mapToObj(index -> new ValidatorKeypair()).collect(Collectors.toList()); - var variables = new UniverseVariables(); - variables.setValidatorKeypairs(keyPairs); + private UniverseUtils() {} - for (String outputLine : output) { - if (!outputLine.toLowerCase().contains("export")) { - continue; - } + /** is probably not thread safe */ + public static UniverseVariables generateEnvironmentVariables(int numberOfValidators) { + // create a custom stream to hold system.out output until the generation finishes. + // this could be super messy when run concurrently + var outputAsString = getUniverseGenerationOutputAsString(numberOfValidators); + var output = outputAsString.split("\\R"); + var keyPairs = + IntStream.range(0, numberOfValidators) + .mapToObj(index -> new ValidatorKeypair()) + .collect(Collectors.toList()); + var variables = new UniverseVariables(); + variables.setValidatorKeypairs(keyPairs); - if (outputLine.contains("PUBKEY")) { - var publicKey = StringUtils.substringAfter(outputLine, "PUBKEY="); - var validatorNumber = Integer.parseInt(StringUtils.substringBetween(outputLine, "VALIDATOR_", "_PUBKEY")); - keyPairs.get(validatorNumber).setPublicKey(publicKey); - } else if (outputLine.contains("PRIVKEY")) { - var privateKey = StringUtils.substringAfter(outputLine, "PRIVKEY="); - var validatorNumber = Integer.parseInt(StringUtils.substringBetween(outputLine, "VALIDATOR_", "_PRIVKEY")); - keyPairs.get(validatorNumber).setPrivateKey(privateKey); - } else if (outputLine.contains("RADIXDLT_GENESIS_TXN")) { - var genesisTransaction = StringUtils.substringAfter(outputLine, "RADIXDLT_GENESIS_TXN="); - variables.setGenesisTransaction(genesisTransaction); - } + for (String outputLine : output) { + if (!outputLine.toLowerCase().contains("export")) { + continue; + } - } - - return variables; + if (outputLine.contains("PUBKEY")) { + var publicKey = StringUtils.substringAfter(outputLine, "PUBKEY="); + var validatorNumber = + Integer.parseInt(StringUtils.substringBetween(outputLine, "VALIDATOR_", "_PUBKEY")); + keyPairs.get(validatorNumber).setPublicKey(publicKey); + } else if (outputLine.contains("PRIVKEY")) { + var privateKey = StringUtils.substringAfter(outputLine, "PRIVKEY="); + var validatorNumber = + Integer.parseInt(StringUtils.substringBetween(outputLine, "VALIDATOR_", "_PRIVKEY")); + keyPairs.get(validatorNumber).setPrivateKey(privateKey); + } else if (outputLine.contains("RADIXDLT_GENESIS_TXN")) { + var genesisTransaction = StringUtils.substringAfter(outputLine, "RADIXDLT_GENESIS_TXN="); + variables.setGenesisTransaction(genesisTransaction); + } } - private static String getUniverseGenerationOutputAsString(int numberOfValidators) { - var outputStream = new ByteArrayOutputStream(); - var printStream = new PrintStream(outputStream); - var old = System.out; - System.setOut(printStream); + return variables; + } - try { - GenerateUniverses.main(new String[]{"-v", String.valueOf(numberOfValidators)}); - return outputStream.toString(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - System.setOut(old); - } - } + private static String getUniverseGenerationOutputAsString(int numberOfValidators) { + var outputStream = new ByteArrayOutputStream(); + var printStream = new PrintStream(outputStream); + var old = System.out; + System.setOut(printStream); + try { + GenerateUniverses.main(new String[] {"-v", String.valueOf(numberOfValidators)}); + return outputStream.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + System.setOut(old); + } + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseVariables.java b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseVariables.java index 49bdcd34d0..85bdddca34 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseVariables.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/UniverseVariables.java @@ -1,29 +1,90 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.utils.universe; import java.util.List; -/** - * holds the output from the universe generation - */ +/** holds the output from the universe generation */ public class UniverseVariables { - private List validatorKeypairs; - private String genesisTransaction; - - public List getValidatorKeypairs() { - return validatorKeypairs; - } + private List validatorKeypairs; + private String genesisTransaction; - public void setValidatorKeypairs(List validatorKeypairs) { - this.validatorKeypairs = validatorKeypairs; - } + public List getValidatorKeypairs() { + return validatorKeypairs; + } - public String getGenesisTransaction() { - return genesisTransaction; - } + public void setValidatorKeypairs(List validatorKeypairs) { + this.validatorKeypairs = validatorKeypairs; + } - public void setGenesisTransaction(String genesisTransaction) { - this.genesisTransaction = genesisTransaction; - } + public String getGenesisTransaction() { + return genesisTransaction; + } + public void setGenesisTransaction(String genesisTransaction) { + this.genesisTransaction = genesisTransaction; + } } diff --git a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/ValidatorKeypair.java b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/ValidatorKeypair.java index bc1db03f4d..ced0e80207 100644 --- a/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/ValidatorKeypair.java +++ b/radixdlt-regression/src/main/java/com/radixdlt/test/utils/universe/ValidatorKeypair.java @@ -1,27 +1,88 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.utils.universe; -/** - * a pojo holding the (string) keys generated via GenerateUniverses - */ +/** a pojo holding the (string) keys generated via GenerateUniverses */ public class ValidatorKeypair { - private String privateKey; - private String publicKey; - - public String getPrivateKey() { - return privateKey; - } + private String privateKey; + private String publicKey; - public void setPrivateKey(String privateKey) { - this.privateKey = privateKey; - } + public String getPrivateKey() { + return privateKey; + } - public String getPublicKey() { - return publicKey; - } + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } - public void setPublicKey(String publicKey) { - this.publicKey = publicKey; - } + public String getPublicKey() { + return publicKey; + } + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/TokenCreationProperties.java b/radixdlt-regression/src/test/java/com/radixdlt/TokenCreationProperties.java index 6965a08d44..04fc45531b 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/TokenCreationProperties.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/TokenCreationProperties.java @@ -1,52 +1,120 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt; -/** - * A POJO that holds info needed to create tokens - */ +/** A POJO that holds info needed to create tokens */ public class TokenCreationProperties { - private final String symbol; - private final String name; - private final String description; - private final String iconUrl; - private final String tokenInfoUrl; - private final int amount; - - public TokenCreationProperties(String symbol, String name, String description, String iconUrl, String tokenInfoUrl) { - this(symbol, name, description, iconUrl, tokenInfoUrl, 0); - } - - public TokenCreationProperties(String symbol, String name, String description, String iconUrl, String tokenInfoUrl, int amount) { - this.symbol = symbol; - this.name = name; - this.description = description; - this.iconUrl = iconUrl; - this.tokenInfoUrl = tokenInfoUrl; - this.amount = amount; - } - - public String getSymbol() { - return symbol; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public String getIconUrl() { - return iconUrl; - } - - public String getTokenInfoUrl() { - return tokenInfoUrl; - } - - public int getAmount() { - return amount; - } + private final String symbol; + private final String name; + private final String description; + private final String iconUrl; + private final String tokenInfoUrl; + private final int amount; + + public TokenCreationProperties( + String symbol, String name, String description, String iconUrl, String tokenInfoUrl) { + this(symbol, name, description, iconUrl, tokenInfoUrl, 0); + } + + public TokenCreationProperties( + String symbol, + String name, + String description, + String iconUrl, + String tokenInfoUrl, + int amount) { + this.symbol = symbol; + this.name = name; + this.description = description; + this.iconUrl = iconUrl; + this.tokenInfoUrl = tokenInfoUrl; + this.amount = amount; + } + + public String getSymbol() { + return symbol; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getIconUrl() { + return iconUrl; + } + + public String getTokenInfoUrl() { + return tokenInfoUrl; + } + public int getAmount() { + return amount; + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/Fees.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/Fees.java index a7a01c1ac4..40d6d44ecf 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/Fees.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/Fees.java @@ -1,87 +1,169 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.fees; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.radixdlt.application.tokens.Amount; import com.radixdlt.test.RadixNetworkTest; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class Fees extends RadixNetworkTest { - private static final Logger logger = LogManager.getLogger(); - - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1); - } - - @Given("I have an account with {int} XRD at a suitable Radix network") - public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { - faucet(account1, Amount.ofTokens(tokens)); - } - - @When("I transfer {int} XRD, attaching a small message to the transaction") - public void i_transfer_xrd_attaching_a_small_message_to_the_transaction(int tokens) { - this.txBuffer = account1.transfer(account2, Amount.ofTokens(tokens), Optional.of(" ")); - } - - @When("I transfer {int} XRD, attaching a large message to the transaction") - public void i_transfer_xrd_attaching_a_large_message_to_the_transaction(int tokens) { - var largeMessage = "Z".repeat(255); // 255 chars is the max - txBuffer = account1.transfer(account2, Amount.ofTokens(tokens), Optional.of(largeMessage)); - } - - @When("I create a fixed supply token") - public void i_create_a_fixed_supply_token() { - txBuffer = account1.fixedSupplyToken("acceptancetestftoken", "acceptancetestftoken" + System.currentTimeMillis(), - "description", "https://www.iconurl.com", "https://www.tokenurl.com", Amount.ofTokens(1000)); - } - - @When("I create a mutable supply token") - public void i_create_a_mutable_supply_token() { - txBuffer = account1.mutableSupplyToken("acceptancetestmtoken", "acceptancetestmtoken" + System.currentTimeMillis(), - "description", "https://www.iconurl.com", "https://www.tokenurl.com"); - } - - @When("I stake {int} XRD to a validator") - public void i_stake_xrd_to_a_validator(int tokens) { - var firstValidator = account1.validator().list(1, Optional.empty()).getValidators().get(0); - var aid = account1.stake(firstValidator.getAddress(), Amount.ofTokens(tokens), Optional.empty()); - var transaction = account1.lookup(aid); - logger.info(transaction); - logger.info(transaction.getFee()); - this.txBuffer = transaction.getTxID(); - } - - @Then("I can observe that I have paid fees proportional to the message bytes") - public void i_can_observe_that_i_have_paid_fees_proportional_to_the_message_bytes() { - var transaction = account1.lookup(txBuffer); - var feesMinor = transaction.getFee(); - var feesMajor = Amount.ofSubunits(feesMinor); - var message = transaction.getMessage().get(); - logger.debug("Paid {}({})XRD in fees for token transfer with message '{}'", feesMinor, feesMajor, message); - - // This token transfer seems to have a baseline cost of 0.0726, so we expect to pay this plus 0.0002 per char. - // WARN: this is hardcoded for now, but when the new /health endpoint is merged then we can't get fork information - var feePerByteMicros = Amount.ofMicroTokens(200); - var feeBaselineMicros = Amount.ofMicroTokens(72600); - var messageCostMicros = feePerByteMicros.times(message.length()); - var totalCostMicros = Amount.ofSubunits(feeBaselineMicros.toSubunits().add(messageCostMicros.toSubunits())); - assertEquals(totalCostMicros.toSubunits(), feesMinor); - } - - @Then("I can observe that I have paid {int} extra XRD in fees") - public void i_can_observe_that_i_have_paid_extra_xrd_fees(int extraFeeTokens) { - var tokens = Amount.ofSubunits(account1.lookup(txBuffer).getFee()); - assertTrue("Fees were less than " + extraFeeTokens + " XRD", - tokens.toSubunits().compareTo(Amount.ofTokens(extraFeeTokens).toSubunits()) > 0); - } - + private static final Logger logger = LogManager.getLogger(); + + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1); + } + + @Given("I have an account with {int} XRD at a suitable Radix network") + public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { + faucet(account1, Amount.ofTokens(tokens)); + } + + @When("I transfer {int} XRD, attaching a small message to the transaction") + public void i_transfer_xrd_attaching_a_small_message_to_the_transaction(int tokens) { + this.txBuffer = account1.transfer(account2, Amount.ofTokens(tokens), Optional.of(" ")); + } + + @When("I transfer {int} XRD, attaching a large message to the transaction") + public void i_transfer_xrd_attaching_a_large_message_to_the_transaction(int tokens) { + var largeMessage = "Z".repeat(255); // 255 chars is the max + txBuffer = account1.transfer(account2, Amount.ofTokens(tokens), Optional.of(largeMessage)); + } + + @When("I create a fixed supply token") + public void i_create_a_fixed_supply_token() { + txBuffer = + account1.fixedSupplyToken( + "acceptancetestftoken", + "acceptancetestftoken" + System.currentTimeMillis(), + "description", + "https://www.iconurl.com", + "https://www.tokenurl.com", + Amount.ofTokens(1000)); + } + + @When("I create a mutable supply token") + public void i_create_a_mutable_supply_token() { + txBuffer = + account1.mutableSupplyToken( + "acceptancetestmtoken", + "acceptancetestmtoken" + System.currentTimeMillis(), + "description", + "https://www.iconurl.com", + "https://www.tokenurl.com"); + } + + @When("I stake {int} XRD to a validator") + public void i_stake_xrd_to_a_validator(int tokens) { + var firstValidator = account1.validator().list(1, Optional.empty()).getValidators().get(0); + var aid = + account1.stake(firstValidator.getAddress(), Amount.ofTokens(tokens), Optional.empty()); + var transaction = account1.lookup(aid); + logger.info(transaction); + logger.info(transaction.getFee()); + this.txBuffer = transaction.getTxID(); + } + + @Then("I can observe that I have paid fees proportional to the message bytes") + public void i_can_observe_that_i_have_paid_fees_proportional_to_the_message_bytes() { + var transaction = account1.lookup(txBuffer); + var feesMinor = transaction.getFee(); + var feesMajor = Amount.ofSubunits(feesMinor); + var message = transaction.getMessage().get(); + logger.debug( + "Paid {}({})XRD in fees for token transfer with message '{}'", + feesMinor, + feesMajor, + message); + + // This token transfer seems to have a baseline cost of 0.0726, so we expect to pay this plus + // 0.0002 per char. + // WARN: this is hardcoded for now, but when the new /health endpoint is merged then we can't + // get fork information + var feePerByteMicros = Amount.ofMicroTokens(200); + var feeBaselineMicros = Amount.ofMicroTokens(72600); + var messageCostMicros = feePerByteMicros.times(message.length()); + var totalCostMicros = + Amount.ofSubunits(feeBaselineMicros.toSubunits().add(messageCostMicros.toSubunits())); + assertEquals(totalCostMicros.toSubunits(), feesMinor); + } + + @Then("I can observe that I have paid {int} extra XRD in fees") + public void i_can_observe_that_i_have_paid_extra_xrd_fees(int extraFeeTokens) { + var tokens = Amount.ofSubunits(account1.lookup(txBuffer).getFee()); + assertTrue( + "Fees were less than " + extraFeeTokens + " XRD", + tokens.toSubunits().compareTo(Amount.ofTokens(extraFeeTokens).toSubunits()) > 0); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/RunFees.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/RunFees.java index 4487503826..b089dd879b 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/RunFees.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fees/RunFees.java @@ -63,14 +63,17 @@ */ package com.radixdlt.acceptance.fees; -import org.junit.runner.RunWith; +import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; import io.cucumber.junit.CucumberOptions.SnippetType; -import io.cucumber.junit.Cucumber; +import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunFees { - // Stub for running cucumber tests + // Stub for running cucumber tests } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/FixedSupplyTokens.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/FixedSupplyTokens.java index d03592dc2c..dc62e2f8d3 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/FixedSupplyTokens.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/FixedSupplyTokens.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.fixed_supply_tokens; +import static org.junit.Assert.assertEquals; + import com.radixdlt.TokenCreationProperties; import com.radixdlt.application.tokens.Amount; import com.radixdlt.assertions.Assertions; @@ -9,65 +75,77 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; - import java.util.Optional; -import static org.junit.Assert.assertEquals; - public class FixedSupplyTokens extends RadixNetworkTest { - private TokenCreationProperties tokenProperties; + private TokenCreationProperties tokenProperties; - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1, Amount.ofTokens(110)); - } + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1, Amount.ofTokens(110)); + } - @When("I create a fixed supply token with properties: {string}, {string}, {string}, {string}, {string}, {int} total supply") - public void i_create_a_fixed_supply_token_with_properties(String symbol, String name, String description, - String iconUrl, String tokenInfoUrl, int amount) { - this.tokenProperties = new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl, amount); - txBuffer = account1.fixedSupplyToken(symbol, name, description, iconUrl, tokenInfoUrl, Amount.ofTokens(amount)); - } + @When( + "I create a fixed supply token with properties: {string}, {string}, {string}, {string}," + + " {string}, {int} total supply") + public void i_create_a_fixed_supply_token_with_properties( + String symbol, + String name, + String description, + String iconUrl, + String tokenInfoUrl, + int amount) { + this.tokenProperties = + new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl, amount); + txBuffer = + account1.fixedSupplyToken( + symbol, name, description, iconUrl, tokenInfoUrl, Amount.ofTokens(amount)); + } - @When("I create a fixed supply token with a total supply of {int}") - public void i_create_a_fixed_supply_token_with_a_supply_of(int totalSupplyTokens) { - var symbol = "fsymbol"; - var name = "fname"; - var description = "acceptance test token"; - var iconUrl = "http://icon.com"; - var tokenInfoUrl = "http://token.com"; - this.tokenProperties = new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl, totalSupplyTokens); - txBuffer = account1.fixedSupplyToken(symbol, name, description, iconUrl, tokenInfoUrl, Amount.ofTokens(totalSupplyTokens)); - } + @When("I create a fixed supply token with a total supply of {int}") + public void i_create_a_fixed_supply_token_with_a_supply_of(int totalSupplyTokens) { + var symbol = "fsymbol"; + var name = "fname"; + var description = "acceptance test token"; + var iconUrl = "http://icon.com"; + var tokenInfoUrl = "http://token.com"; + this.tokenProperties = + new TokenCreationProperties( + symbol, name, description, iconUrl, tokenInfoUrl, totalSupplyTokens); + txBuffer = + account1.fixedSupplyToken( + symbol, name, description, iconUrl, tokenInfoUrl, Amount.ofTokens(totalSupplyTokens)); + } - @Then("I can observe that the token has been created, with the correct values") - public void i_can_observe_that_the_token_has_been_created_with_the_correct_values() { - Assertions.assertTokenProperties(account1, txBuffer, tokenProperties); - } + @Then("I can observe that the token has been created, with the correct values") + public void i_can_observe_that_the_token_has_been_created_with_the_correct_values() { + Assertions.assertTokenProperties(account1, txBuffer, tokenProperties); + } - @Then("I can send {int} of my new tokens to another account") - public void i_can_send_of_my_new_tokens_to_another_account(int tokensToTransfer) { - var rri = TestingUtils.getRriFromAID(account1, txBuffer); - account1.transfer(account2, Amount.ofTokens(tokensToTransfer), rri, Optional.empty()); - } + @Then("I can send {int} of my new tokens to another account") + public void i_can_send_of_my_new_tokens_to_another_account(int tokensToTransfer) { + var rri = TestingUtils.getRriFromAID(account1, txBuffer); + account1.transfer(account2, Amount.ofTokens(tokensToTransfer), rri, Optional.empty()); + } - @Then("I can observe that the token has been created, with a total supply of {int}") - public void i_can_observe_that_the_token_has_been_created_with_a_total_supply_of(int totalSupplyTokens) { - var tokenInfo = TestingUtils.getTokenInfoFromAID(account1, txBuffer); - assertEquals(Amount.ofTokens(totalSupplyTokens).toSubunits(), tokenInfo.getCurrentSupply()); - } + @Then("I can observe that the token has been created, with a total supply of {int}") + public void i_can_observe_that_the_token_has_been_created_with_a_total_supply_of( + int totalSupplyTokens) { + var tokenInfo = TestingUtils.getTokenInfoFromAID(account1, txBuffer); + assertEquals(Amount.ofTokens(totalSupplyTokens).toSubunits(), tokenInfo.getCurrentSupply()); + } - @Then("I cannot transfer more than the total supply") - public void i_cannot_transfer_more_than_the_total_supply() { - var rri = TestingUtils.getRriFromAID(account1, txBuffer); - var totalSupplyPlusOneToken = Amount.ofTokens(tokenProperties.getAmount() + 1); - try { - account1.transfer(account2, totalSupplyPlusOneToken, rri, Optional.empty()); - } catch (RadixApiException e) { - // sadly we don't get a more specific exception - assertEquals("Transaction submission failed: Not enough balance for transfer.", e.getMessage()); - } + @Then("I cannot transfer more than the total supply") + public void i_cannot_transfer_more_than_the_total_supply() { + var rri = TestingUtils.getRriFromAID(account1, txBuffer); + var totalSupplyPlusOneToken = Amount.ofTokens(tokenProperties.getAmount() + 1); + try { + account1.transfer(account2, totalSupplyPlusOneToken, rri, Optional.empty()); + } catch (RadixApiException e) { + // sadly we don't get a more specific exception + assertEquals( + "Transaction submission failed: Not enough balance for transfer.", e.getMessage()); } - + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/RunFixedSupplyTokens.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/RunFixedSupplyTokens.java index 65bfa8c8a5..e3d59eece6 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/RunFixedSupplyTokens.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/fixed_supply_tokens/RunFixedSupplyTokens.java @@ -64,14 +64,16 @@ package com.radixdlt.acceptance.fixed_supply_tokens; -import org.junit.runner.RunWith; - +import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; import io.cucumber.junit.CucumberOptions.SnippetType; -import io.cucumber.junit.Cucumber; +import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunFixedSupplyTokens { - // Stub for running cucumber tests + // Stub for running cucumber tests } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/Messaging.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/Messaging.java index c8602f6593..5018c7f151 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/Messaging.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/Messaging.java @@ -1,64 +1,129 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.messaging; -import com.radixdlt.test.RadixNetworkTest; -import com.radixdlt.test.utils.TestFailureException; +import static org.junit.Assert.*; + import com.radixdlt.application.tokens.Amount; import com.radixdlt.client.lib.api.sync.RadixApiException; import com.radixdlt.client.lib.dto.TransactionDTO; +import com.radixdlt.test.RadixNetworkTest; +import com.radixdlt.test.utils.TestFailureException; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; +import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Optional; - -import static org.junit.Assert.*; - public class Messaging extends RadixNetworkTest { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private String expectedMessage; + private String expectedMessage; - @Given("I have two accounts with funds at a suitable Radix network") - public void i_have_two_accounts_with_funds_at_a_suitable_radix_network() { - faucet(account1); - faucet(account2); - } + @Given("I have two accounts with funds at a suitable Radix network") + public void i_have_two_accounts_with_funds_at_a_suitable_radix_network() { + faucet(account1); + faucet(account2); + } - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1); - } + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1); + } - @Given("I send the plaintext message {string} from the first account to the second") - public void i_send_the_plaintext_message_from_the_first_account_to_the_second(String message) { - txBuffer = account1.transfer(account2, Amount.ofTokens(1), Optional.of(message)); - expectedMessage = message; - logger.info("Submitted token transfer ({}) with message '{}'", txBuffer.toString(), message); - } - - @Then("my message can be read") - public void my_message_can_be_read() { - TransactionDTO transactionWithMessage = account1.lookup(txBuffer); - assertEquals(expectedMessage, transactionWithMessage.getMessage().orElseThrow(() -> - new TestFailureException("No message found in transaction"))); - } + @Given("I send the plaintext message {string} from the first account to the second") + public void i_send_the_plaintext_message_from_the_first_account_to_the_second(String message) { + txBuffer = account1.transfer(account2, Amount.ofTokens(1), Optional.of(message)); + expectedMessage = message; + logger.info("Submitted token transfer ({}) with message '{}'", txBuffer.toString(), message); + } - @Given("I send a plaintext message with more than {int} characters") - public void i_send_a_plaintext_message_with_more_than_characters(Integer characters) { - String message = "!".repeat(characters + 1); - try { - account1.transfer(account2, Amount.ofTokens(1), Optional.of(message)); - } catch (RadixApiException e) { - assertTrue(e.getMessage().contains("Data length is 256 but must be <= 255")); - txBuffer = null; - } - } + @Then("my message can be read") + public void my_message_can_be_read() { + TransactionDTO transactionWithMessage = account1.lookup(txBuffer); + assertEquals( + expectedMessage, + transactionWithMessage + .getMessage() + .orElseThrow(() -> new TestFailureException("No message found in transaction"))); + } - @Then("the transaction will not be submitted") - public void the_transaction_will_not_be_submitted() { - assertNull(txBuffer); + @Given("I send a plaintext message with more than {int} characters") + public void i_send_a_plaintext_message_with_more_than_characters(Integer characters) { + String message = "!".repeat(characters + 1); + try { + account1.transfer(account2, Amount.ofTokens(1), Optional.of(message)); + } catch (RadixApiException e) { + assertTrue(e.getMessage().contains("Data length is 256 but must be <= 255")); + txBuffer = null; } + } + @Then("the transaction will not be submitted") + public void the_transaction_will_not_be_submitted() { + assertNull(txBuffer); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/RunMessaging.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/RunMessaging.java index 0c9d942e22..cac0ff2e5e 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/RunMessaging.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/messaging/RunMessaging.java @@ -70,7 +70,10 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunMessaging { - // Stub for running cucumber tests -} \ No newline at end of file + // Stub for running cucumber tests +} diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/MultiActionTransactions.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/MultiActionTransactions.java index 90d5b9744e..187dbe8819 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/MultiActionTransactions.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/MultiActionTransactions.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.multi_action_transactions; +import static org.junit.Assert.assertEquals; + import com.radixdlt.application.tokens.Amount; import com.radixdlt.assertions.Assertions; import com.radixdlt.client.lib.api.TransactionRequest; @@ -7,53 +73,68 @@ import com.radixdlt.test.utils.TransactionUtils; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; - import java.util.Optional; -import static org.junit.Assert.assertEquals; - public class MultiActionTransactions extends RadixNetworkTest { - private static final Amount AMOUNT_TO_TRANSFER = Amount.ofTokens(2); - private static final Amount STAKE_AMOUNT = Amount.ofTokens(90); - - @Given("I have an account with {int} XRD at a suitable Radix network") - public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { - faucet(account1, Amount.ofTokens(tokens)); - } - - @Given("I cannot submit a transaction with two actions: transfer {int} XRD to account2 and transfer {int} XRD to account3") - public void i_submit_a_transaction_with_two_actions_transfer_xrd_to_account2_and_transfer_xrd_to_account3(int tokensTo2, int tokensTo3) { - var nativeRri = account1.getNativeToken().getRri(); - Assertions.runExpectingRadixApiException(() -> { - var request = TransactionRequest.createBuilder(account1.getAddress()) - .transfer(account1.getAddress(), account2.getAddress(), Amount.ofTokens(tokensTo2).toSubunits(), nativeRri) - .transfer(account1.getAddress(), getTestAccount(2).getAddress(), Amount.ofTokens(tokensTo3).toSubunits(), nativeRri) - .build(); - txBuffer = TransactionUtils.buildFinalizeAndSubmitTransaction(account1, request, true); - }, "Not enough balance for transfer"); - } - - @Given("I submit a transaction with three actions: two transfers and one staking") - public void i_submit_a_transaction_with_three_actions_two_transfers_and_one_staking() { - var nativeRri = account1.getNativeToken().getRri(); - var account1Address = account1.getAddress(); - var validatorAddress = account1.validator().list(100, Optional.empty()).getValidators().get(0).getAddress(); - var request = TransactionRequest.createBuilder(account1Address) - .transfer(account1Address, account2.getAddress(), AMOUNT_TO_TRANSFER.toSubunits(), nativeRri) - .transfer(account1Address, account2.getAddress(), AMOUNT_TO_TRANSFER.toSubunits(), nativeRri) + private static final Amount AMOUNT_TO_TRANSFER = Amount.ofTokens(2); + private static final Amount STAKE_AMOUNT = Amount.ofTokens(90); + + @Given("I have an account with {int} XRD at a suitable Radix network") + public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { + faucet(account1, Amount.ofTokens(tokens)); + } + + @Given( + "I cannot submit a transaction with two actions: transfer {int} XRD to account2 and transfer" + + " {int} XRD to account3") + public void + i_submit_a_transaction_with_two_actions_transfer_xrd_to_account2_and_transfer_xrd_to_account3( + int tokensTo2, int tokensTo3) { + var nativeRri = account1.getNativeToken().getRri(); + Assertions.runExpectingRadixApiException( + () -> { + var request = + TransactionRequest.createBuilder(account1.getAddress()) + .transfer( + account1.getAddress(), + account2.getAddress(), + Amount.ofTokens(tokensTo2).toSubunits(), + nativeRri) + .transfer( + account1.getAddress(), + getTestAccount(2).getAddress(), + Amount.ofTokens(tokensTo3).toSubunits(), + nativeRri) + .build(); + txBuffer = TransactionUtils.buildFinalizeAndSubmitTransaction(account1, request, true); + }, + "Not enough balance for transfer"); + } + + @Given("I submit a transaction with three actions: two transfers and one staking") + public void i_submit_a_transaction_with_three_actions_two_transfers_and_one_staking() { + var nativeRri = account1.getNativeToken().getRri(); + var account1Address = account1.getAddress(); + var validatorAddress = + account1.validator().list(100, Optional.empty()).getValidators().get(0).getAddress(); + var request = + TransactionRequest.createBuilder(account1Address) + .transfer( + account1Address, account2.getAddress(), AMOUNT_TO_TRANSFER.toSubunits(), nativeRri) + .transfer( + account1Address, account2.getAddress(), AMOUNT_TO_TRANSFER.toSubunits(), nativeRri) .stake(account1Address, validatorAddress, STAKE_AMOUNT.toSubunits()) .build(); - txBuffer = TransactionUtils.buildFinalizeAndSubmitTransaction(account1, request, true); - } - - @Then("I can observe the actions taking effect") - public void i_can_observe_the_actions_taking_effect() { - // this is a bit hardcoded. if more scenariors are addded, we will need to parameterized - var actions = account1.lookup(txBuffer).getActions(); - assertEquals(actions.get(0).getAmount().get(), AMOUNT_TO_TRANSFER.toSubunits()); - assertEquals(actions.get(1).getAmount().get(), AMOUNT_TO_TRANSFER.toSubunits()); - assertEquals(actions.get(2).getAmount().get(), STAKE_AMOUNT.toSubunits()); - } + txBuffer = TransactionUtils.buildFinalizeAndSubmitTransaction(account1, request, true); + } + @Then("I can observe the actions taking effect") + public void i_can_observe_the_actions_taking_effect() { + // this is a bit hardcoded. if more scenariors are addded, we will need to parameterized + var actions = account1.lookup(txBuffer).getActions(); + assertEquals(actions.get(0).getAmount().get(), AMOUNT_TO_TRANSFER.toSubunits()); + assertEquals(actions.get(1).getAmount().get(), AMOUNT_TO_TRANSFER.toSubunits()); + assertEquals(actions.get(2).getAmount().get(), STAKE_AMOUNT.toSubunits()); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/RunMultiActionTransactions.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/RunMultiActionTransactions.java index 906e9e85a7..59dca5643e 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/RunMultiActionTransactions.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/multi_action_transactions/RunMultiActionTransactions.java @@ -70,7 +70,10 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunMultiActionTransactions { - // Stub for running cucumber tests + // Stub for running cucumber tests } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/MutableSupplyTokens.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/MutableSupplyTokens.java index 800714f7d4..b9b3edc81b 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/MutableSupplyTokens.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/MutableSupplyTokens.java @@ -1,5 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.mutable_supply_tokens; +import static org.junit.Assert.assertEquals; + import com.radixdlt.TokenCreationProperties; import com.radixdlt.application.tokens.Amount; import com.radixdlt.assertions.Assertions; @@ -8,70 +74,72 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; - import java.util.Optional; -import static org.junit.Assert.assertEquals; - public class MutableSupplyTokens extends RadixNetworkTest { - private TokenCreationProperties tokenProperties; - - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1, Amount.ofTokens(110)); - } + private TokenCreationProperties tokenProperties; - @Given("I have an account with {int} XRD at a suitable Radix network") - public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { - faucet(account1, Amount.ofTokens(tokens)); - } + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1, Amount.ofTokens(110)); + } - @When("I create a mutable supply token") - public void i_create_a_mutable_supply_token() { - var symbol = "msymbol"; - var name = "mname"; - var description = "acceptance test token"; - var iconUrl = "http://icon.com"; - var tokenInfoUrl = "http://token.com"; - this.tokenProperties = new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl); - txBuffer = account1.mutableSupplyToken(symbol, name, description, iconUrl, tokenInfoUrl); - } + @Given("I have an account with {int} XRD at a suitable Radix network") + public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { + faucet(account1, Amount.ofTokens(tokens)); + } - @When("I create a mutable supply token with properties: {string}, {string}, {string}, {string}, {string}") - public void i_create_a_fixed_supply_token_with_properties(String symbol, String name, String description, String iconUrl, String tokenInfoUrl) { - this.tokenProperties = new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl); - txBuffer = account1.mutableSupplyToken(symbol, name, description, iconUrl, tokenInfoUrl); - } + @When("I create a mutable supply token") + public void i_create_a_mutable_supply_token() { + var symbol = "msymbol"; + var name = "mname"; + var description = "acceptance test token"; + var iconUrl = "http://icon.com"; + var tokenInfoUrl = "http://token.com"; + this.tokenProperties = + new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl); + txBuffer = account1.mutableSupplyToken(symbol, name, description, iconUrl, tokenInfoUrl); + } - @Then("I can observe that the token has been created, with the correct values") - public void i_can_observe_that_the_token_has_been_created_with_the_correct_values() { - Assertions.assertTokenProperties(account1, txBuffer, tokenProperties); - } + @When( + "I create a mutable supply token with properties: {string}, {string}, {string}, {string}," + + " {string}") + public void i_create_a_fixed_supply_token_with_properties( + String symbol, String name, String description, String iconUrl, String tokenInfoUrl) { + this.tokenProperties = + new TokenCreationProperties(symbol, name, description, iconUrl, tokenInfoUrl); + txBuffer = account1.mutableSupplyToken(symbol, name, description, iconUrl, tokenInfoUrl); + } - @Then("I can send {int} of my new tokens to another account") - public void i_can_send_of_my_new_tokens_to_another_account(int tokensToTransfer) { - var rri = TestingUtils.getRriFromAID(account1, txBuffer); - account1.transfer(account2, Amount.ofTokens(tokensToTransfer), rri, Optional.empty()); - } + @Then("I can observe that the token has been created, with the correct values") + public void i_can_observe_that_the_token_has_been_created_with_the_correct_values() { + Assertions.assertTokenProperties(account1, txBuffer, tokenProperties); + } - @Then("I can mint {int} of this token") - public void i_can_mint_of_this_token(int tokensToMint) { - var rri = TestingUtils.getRriFromAID(account1, txBuffer); - account1.mint(Amount.ofTokens(tokensToMint), rri, Optional.empty()); - } + @Then("I can send {int} of my new tokens to another account") + public void i_can_send_of_my_new_tokens_to_another_account(int tokensToTransfer) { + var rri = TestingUtils.getRriFromAID(account1, txBuffer); + account1.transfer(account2, Amount.ofTokens(tokensToTransfer), rri, Optional.empty()); + } - @Then("I can burn {int} of this token") - public void i_can_burn_of_this_token(int tokensToBurn) { - var rri = TestingUtils.getRriFromAID(account1, txBuffer); - account1.burn(Amount.ofTokens(tokensToBurn), rri, Optional.empty()); - } + @Then("I can mint {int} of this token") + public void i_can_mint_of_this_token(int tokensToMint) { + var rri = TestingUtils.getRriFromAID(account1, txBuffer); + account1.mint(Amount.ofTokens(tokensToMint), rri, Optional.empty()); + } - @Then("the total supply should be {int}") - public void the_total_supply_should_be(int expectedTotalSupply) { - var currentSupplyAmount = Amount.ofSubunits(TestingUtils.getTokenInfoFromAID(account1, txBuffer).getCurrentSupply()); - var expectedTotalSupplyAmount = Amount.ofTokens(expectedTotalSupply); - assertEquals(expectedTotalSupplyAmount.toSubunits(), currentSupplyAmount.toSubunits()); - } + @Then("I can burn {int} of this token") + public void i_can_burn_of_this_token(int tokensToBurn) { + var rri = TestingUtils.getRriFromAID(account1, txBuffer); + account1.burn(Amount.ofTokens(tokensToBurn), rri, Optional.empty()); + } + @Then("the total supply should be {int}") + public void the_total_supply_should_be(int expectedTotalSupply) { + var currentSupplyAmount = + Amount.ofSubunits(TestingUtils.getTokenInfoFromAID(account1, txBuffer).getCurrentSupply()); + var expectedTotalSupplyAmount = Amount.ofTokens(expectedTotalSupply); + assertEquals(expectedTotalSupplyAmount.toSubunits(), currentSupplyAmount.toSubunits()); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/RunMutableSupplyTokens.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/RunMutableSupplyTokens.java index 87573af2cc..d78e410d63 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/RunMutableSupplyTokens.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/mutable_supply_tokens/RunMutableSupplyTokens.java @@ -64,14 +64,16 @@ package com.radixdlt.acceptance.mutable_supply_tokens; -import org.junit.runner.RunWith; - +import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; import io.cucumber.junit.CucumberOptions.SnippetType; -import io.cucumber.junit.Cucumber; +import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunMutableSupplyTokens { - // Stub for running cucumber tests + // Stub for running cucumber tests } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/RunStaking.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/RunStaking.java index fb76cafa54..f71f3771eb 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/RunStaking.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/RunStaking.java @@ -70,7 +70,10 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunStaking { - // Stub for running cucumber tests -} \ No newline at end of file + // Stub for running cucumber tests +} diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/Staking.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/Staking.java index 48da04519e..5a0c40b6e3 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/Staking.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/staking/Staking.java @@ -64,6 +64,9 @@ package com.radixdlt.acceptance.staking; +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertTrue; + import com.radixdlt.application.tokens.Amount; import com.radixdlt.assertions.Assertions; import com.radixdlt.client.lib.dto.ValidatorDTO; @@ -74,111 +77,119 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.assertj.core.util.Lists; import org.awaitility.Durations; import org.junit.Assert; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertTrue; - public class Staking extends RadixNetworkTest { - private static final Logger logger = LogManager.getLogger(); - - private List validatorsBuffer = Lists.newArrayList(); - private ValidatorDTO firstValidator; - private Amount stakedAmount; - - @Before - public void update_validator_information() { - updateValidatorInformation(); - } - - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1, Amount.ofTokens(110)); - } - - @Given("I have an account with {int} XRD at a suitable Radix network") - public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { - faucet(account1, Amount.ofTokens(tokens)); - } - - @When("I request validator information") - public void i_request_validator_information() { - logger.info("Found {} validators", validatorsBuffer.size()); - } - - @Then("I observe that validators have stakes delegated to them") - public void i_observe_that_validators_have_stakes_delegated_to_them() { - var totalDelegatedStakeAcrossNetwork = validatorsBuffer.stream() - .mapToDouble(value -> Double.parseDouble(value.getTotalDelegatedStake().toString())).sum(); - assertTrue("No stake was found in any validator, something is wrong with the test network", - totalDelegatedStakeAcrossNetwork > 0); - } - - @When("I stake {int} XRD to a validator") - public void i_stake_xrd_to_a_validator(int tokens) { - this.stakedAmount = Amount.ofTokens(tokens); - account1.stake(firstValidator.getAddress(), stakedAmount, Optional.empty()); - } - - @When("I unstake {int} XRD from the same validator") - public void i_unstake_xrd_from_the_same_validator(int tokensToUnstake) { - account1.unstake(firstValidator.getAddress(), Amount.ofTokens(tokensToUnstake), Optional.empty()); - } - - @Then("I observe that the validator has {int} XRD more stake") - public void i_observe_that_validator_having_xrd_more_stake(int stakeTokens) { - var expectedStake = Amount.ofTokens(stakeTokens); - var previousStake = firstValidator.getTotalDelegatedStake(); - await().pollInterval(Durations.ONE_SECOND).atMost(Durations.ONE_MINUTE).until(() -> { - updateValidatorInformation(); - var difference = firstValidator.getTotalDelegatedStake().subtract(previousStake); - return difference.compareTo(expectedStake.toSubunits()) > -1; - }); - } - - @Then("I observe that my stake is unstaked and I got my tokens back") - public void i_observe_that_my_stake_is_unstaked_and_i_got_my_tokens_back() { - throw new io.cucumber.java.PendingException(); - } - - @Then("I wait for {int} epochs to pass") - public void i_wait_for_two_epochs_to_pass(int epochsToWait) { - TestingUtils.waitEpochs(account1, epochsToWait); - } - - @Then("I cannot immediately unstake {int} XRD") - public void i_cannot_immediately_unstake_xrd(int tokensToUnstake) { - Assertions.runExpectingRadixApiException(() -> - account1.unstake(firstValidator.getAddress(), Amount.ofTokens(tokensToUnstake), Optional.empty()), - "Not enough balance for transfer" - ); + private static final Logger logger = LogManager.getLogger(); + + private List validatorsBuffer = Lists.newArrayList(); + private ValidatorDTO firstValidator; + private Amount stakedAmount; + + @Before + public void update_validator_information() { + updateValidatorInformation(); + } + + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1, Amount.ofTokens(110)); + } + + @Given("I have an account with {int} XRD at a suitable Radix network") + public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { + faucet(account1, Amount.ofTokens(tokens)); + } + + @When("I request validator information") + public void i_request_validator_information() { + logger.info("Found {} validators", validatorsBuffer.size()); + } + + @Then("I observe that validators have stakes delegated to them") + public void i_observe_that_validators_have_stakes_delegated_to_them() { + var totalDelegatedStakeAcrossNetwork = + validatorsBuffer.stream() + .mapToDouble(value -> Double.parseDouble(value.getTotalDelegatedStake().toString())) + .sum(); + assertTrue( + "No stake was found in any validator, something is wrong with the test network", + totalDelegatedStakeAcrossNetwork > 0); + } + + @When("I stake {int} XRD to a validator") + public void i_stake_xrd_to_a_validator(int tokens) { + this.stakedAmount = Amount.ofTokens(tokens); + account1.stake(firstValidator.getAddress(), stakedAmount, Optional.empty()); + } + + @When("I unstake {int} XRD from the same validator") + public void i_unstake_xrd_from_the_same_validator(int tokensToUnstake) { + account1.unstake( + firstValidator.getAddress(), Amount.ofTokens(tokensToUnstake), Optional.empty()); + } + + @Then("I observe that the validator has {int} XRD more stake") + public void i_observe_that_validator_having_xrd_more_stake(int stakeTokens) { + var expectedStake = Amount.ofTokens(stakeTokens); + var previousStake = firstValidator.getTotalDelegatedStake(); + await() + .pollInterval(Durations.ONE_SECOND) + .atMost(Durations.ONE_MINUTE) + .until( + () -> { + updateValidatorInformation(); + var difference = firstValidator.getTotalDelegatedStake().subtract(previousStake); + return difference.compareTo(expectedStake.toSubunits()) > -1; + }); + } + + @Then("I observe that my stake is unstaked and I got my tokens back") + public void i_observe_that_my_stake_is_unstaked_and_i_got_my_tokens_back() { + throw new io.cucumber.java.PendingException(); + } + + @Then("I wait for {int} epochs to pass") + public void i_wait_for_two_epochs_to_pass(int epochsToWait) { + TestingUtils.waitEpochs(account1, epochsToWait); + } + + @Then("I cannot immediately unstake {int} XRD") + public void i_cannot_immediately_unstake_xrd(int tokensToUnstake) { + Assertions.runExpectingRadixApiException( + () -> + account1.unstake( + firstValidator.getAddress(), Amount.ofTokens(tokensToUnstake), Optional.empty()), + "Not enough balance for transfer"); + } + + @Then("I receive some emissions") + public void i_receive_xrd_in_emissions() { + var latestStakedAmount = + account1.account().stakes(account1.getAddress()).stream() + .filter( + stakePositions -> stakePositions.getValidator().equals(firstValidator.getAddress())) + .collect(Collectors.toList()) + .get(0) + .getAmount(); + var difference = latestStakedAmount.subtract(stakedAmount.toSubunits()); + assertTrue("No emissions were received", difference.compareTo(UInt256.ZERO) > 0); + } + + private void updateValidatorInformation() { + validatorsBuffer.clear(); + validatorsBuffer = account1.validator().list(10000, Optional.empty()).getValidators(); + if (validatorsBuffer.isEmpty()) { + Assert.fail("No validators were found in the network, test cannot proceed."); } - - @Then("I receive some emissions") - public void i_receive_xrd_in_emissions() { - var latestStakedAmount = account1.account().stakes(account1.getAddress()).stream() - .filter(stakePositions -> stakePositions.getValidator().equals(firstValidator.getAddress())) - .collect(Collectors.toList()).get(0).getAmount(); - var difference = latestStakedAmount.subtract(stakedAmount.toSubunits()); - assertTrue("No emissions were received", difference.compareTo(UInt256.ZERO) > 0); - } - - private void updateValidatorInformation() { - validatorsBuffer.clear(); - validatorsBuffer = account1.validator().list(10000, Optional.empty()).getValidators(); - if (validatorsBuffer.isEmpty()) { - Assert.fail("No validators were found in the network, test cannot proceed."); - } - firstValidator = validatorsBuffer.get(0); - } - + firstValidator = validatorsBuffer.get(0); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/RunTokenConstraints.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/RunTokenConstraints.java index c99bc2ffc9..d0c9f8b938 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/RunTokenConstraints.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/RunTokenConstraints.java @@ -63,14 +63,17 @@ */ package com.radixdlt.acceptance.token_constraints; -import org.junit.runner.RunWith; +import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; import io.cucumber.junit.CucumberOptions.SnippetType; -import io.cucumber.junit.Cucumber; +import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunTokenConstraints { - // Stub for running cucumber tests + // Stub for running cucumber tests } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/TokenConstraints.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/TokenConstraints.java index ac86737df9..a107274e65 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/TokenConstraints.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_constraints/TokenConstraints.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.token_constraints; import com.radixdlt.application.tokens.Amount; @@ -8,30 +72,35 @@ public class TokenConstraints extends RadixNetworkTest { - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1, Amount.ofTokens(110)); - } - - @Then("I cannot create a token with symbol {string}") - public void i_cannot_create_a_token_with_symbol(String symbol) { - Assertions.runExpectingRadixApiException(() -> - account1.mutableSupplyToken(symbol, "name", "description", "http://icon.com", "http://token.com"), - "invalid token symbol: " + symbol); - } + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1, Amount.ofTokens(110)); + } - @Then("I cannot create a token with token info url {string}") - public void i_cannot_create_a_token_with_token_info_url(String tokenInfoUrl) { - Assertions.runExpectingRadixApiException(() -> - account1.mutableSupplyToken("symbol", "name", "description", "http://icon.com", tokenInfoUrl), - "not a valid URL"); - } + @Then("I cannot create a token with symbol {string}") + public void i_cannot_create_a_token_with_symbol(String symbol) { + Assertions.runExpectingRadixApiException( + () -> + account1.mutableSupplyToken( + symbol, "name", "description", "http://icon.com", "http://token.com"), + "invalid token symbol: " + symbol); + } - @Then("I cannot create a token with an icon rul {string}") - public void i_cannot_create_a_token_with_an_icon_url(String iconUrl) { - Assertions.runExpectingRadixApiException(() -> - account1.mutableSupplyToken("symbol", "name", "description", iconUrl, "http://token.com"), - "not a valid URL"); - } + @Then("I cannot create a token with token info url {string}") + public void i_cannot_create_a_token_with_token_info_url(String tokenInfoUrl) { + Assertions.runExpectingRadixApiException( + () -> + account1.mutableSupplyToken( + "symbol", "name", "description", "http://icon.com", tokenInfoUrl), + "not a valid URL"); + } + @Then("I cannot create a token with an icon rul {string}") + public void i_cannot_create_a_token_with_an_icon_url(String iconUrl) { + Assertions.runExpectingRadixApiException( + () -> + account1.mutableSupplyToken( + "symbol", "name", "description", iconUrl, "http://token.com"), + "not a valid URL"); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/RunTokenTransfer.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/RunTokenTransfer.java index 7bf276f448..9c3fe5bcbf 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/RunTokenTransfer.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/RunTokenTransfer.java @@ -70,7 +70,10 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunTokenTransfer { - // Stub for running cucumber tests -} \ No newline at end of file + // Stub for running cucumber tests +} diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/TokenTransfer.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/TokenTransfer.java index e211dce511..b5a70c1205 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/TokenTransfer.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/token_transfer/TokenTransfer.java @@ -1,5 +1,72 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.token_transfer; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + import com.radixdlt.application.tokens.Amount; import com.radixdlt.client.lib.api.sync.RadixApiException; import com.radixdlt.test.RadixNetworkTest; @@ -7,84 +74,84 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - public class TokenTransfer extends RadixNetworkTest { - private static final Logger logger = LogManager.getLogger(); - - private Amount amountBuffer; - - @Given("I have two accounts with funds at a suitable Radix network") - public void i_have_two_accounts_with_funds_at_a_suitable_radix_network() { - faucet(account1); - faucet(account2); + private static final Logger logger = LogManager.getLogger(); + + private Amount amountBuffer; + + @Given("I have two accounts with funds at a suitable Radix network") + public void i_have_two_accounts_with_funds_at_a_suitable_radix_network() { + faucet(account1); + faucet(account2); + } + + @Given("I have an account with {int} XRD at a suitable Radix network") + public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { + faucet(account1, Amount.ofTokens(tokens)); + } + + @When("I transfer {int} XRD to myself") + public void i_transfer_xrd_to_myself(int tokensToTransfer) { + this.amountBuffer = Amount.ofSubunits(account1.getOwnNativeTokenBalance().getAmount()); + this.txBuffer = + account1.transfer(account1, Amount.ofTokens(tokensToTransfer), Optional.empty()); + } + + @And("I transfer {int} XRD from the first account to the second") + public void i_transfer_xrd_from_the_first_account_to_the_second(int amount) { + var account1BalanceBefore = account1.getOwnNativeTokenBalance().getAmount(); + var account2BalanceBefore = account2.getOwnNativeTokenBalance().getAmount(); + var transferredAmount = Amount.ofTokens(amount); + + var transferTxId = account1.transfer(account2, transferredAmount, Optional.of("hello!")); + var fee = account1.lookup(transferTxId).getFee(); + + assertEquals( + account1BalanceBefore.subtract(transferredAmount.toSubunits()).subtract(fee), + account1.getOwnNativeTokenBalance().getAmount()); + assertEquals( + account2BalanceBefore.add(transferredAmount.toSubunits()), + account2.getOwnNativeTokenBalance().getAmount()); + } + + @Then("I have the same amount of tokens, minus fees") + public void i_have_the_same_amount_of_tokens_minus_fees() { + var amount = Amount.ofSubunits(account1.getOwnNativeTokenBalance().getAmount()); + var expectedAmount = amountBuffer.toSubunits().subtract(account1.lookup(txBuffer).getFee()); + assertEquals(expectedAmount, amount.toSubunits()); + } + + @Then("the second account can transfer {int} XRD back to the first") + public void that_account_can_transfer_xrd_back_to_me(int amount) { + var account1BalanceBefore = account1.getOwnNativeTokenBalance().getAmount(); + var account2BalanceBefore = account2.getOwnNativeTokenBalance().getAmount(); + var transferredAmount = Amount.ofTokens(amount); + + var transferTxId = account2.transfer(account1, transferredAmount, Optional.of("hello back!")); + var fee = account2.lookup(transferTxId).getFee(); + + assertEquals( + account2BalanceBefore.subtract(transferredAmount.toSubunits()).subtract(fee), + account2.getOwnNativeTokenBalance().getAmount()); + assertEquals( + account1BalanceBefore.add(transferredAmount.toSubunits()), + account1.getOwnNativeTokenBalance().getAmount()); + } + + @Then("I cannot transfer {int} XRD to another account") + public void i_cannot_transfer_xrd_to_another_account(int tokensToTransfer) { + boolean hasTransferred; + try { + account1.transfer(account2, Amount.ofTokens(tokensToTransfer), Optional.empty()); + hasTransferred = true; + } catch (RadixApiException e) { + hasTransferred = false; } - - @Given("I have an account with {int} XRD at a suitable Radix network") - public void i_have_an_account_with_xrd_at_a_suitable_radix_network(int tokens) { - faucet(account1, Amount.ofTokens(tokens)); - } - - @When("I transfer {int} XRD to myself") - public void i_transfer_xrd_to_myself(int tokensToTransfer) { - this.amountBuffer = Amount.ofSubunits(account1.getOwnNativeTokenBalance().getAmount()); - this.txBuffer = account1.transfer(account1, Amount.ofTokens(tokensToTransfer), Optional.empty()); - } - - @And("I transfer {int} XRD from the first account to the second") - public void i_transfer_xrd_from_the_first_account_to_the_second(int amount) { - var account1BalanceBefore = account1.getOwnNativeTokenBalance().getAmount(); - var account2BalanceBefore = account2.getOwnNativeTokenBalance().getAmount(); - var transferredAmount = Amount.ofTokens(amount); - - var transferTxId = account1.transfer(account2, transferredAmount, Optional.of("hello!")); - var fee = account1.lookup(transferTxId).getFee(); - - assertEquals(account1BalanceBefore.subtract(transferredAmount.toSubunits()).subtract(fee), - account1.getOwnNativeTokenBalance().getAmount()); - assertEquals(account2BalanceBefore.add(transferredAmount.toSubunits()), - account2.getOwnNativeTokenBalance().getAmount()); - } - - @Then("I have the same amount of tokens, minus fees") - public void i_have_the_same_amount_of_tokens_minus_fees() { - var amount = Amount.ofSubunits(account1.getOwnNativeTokenBalance().getAmount()); - var expectedAmount = amountBuffer.toSubunits().subtract(account1.lookup(txBuffer).getFee()); - assertEquals(expectedAmount, amount.toSubunits()); - } - - @Then("the second account can transfer {int} XRD back to the first") - public void that_account_can_transfer_xrd_back_to_me(int amount) { - var account1BalanceBefore = account1.getOwnNativeTokenBalance().getAmount(); - var account2BalanceBefore = account2.getOwnNativeTokenBalance().getAmount(); - var transferredAmount = Amount.ofTokens(amount); - - var transferTxId = account2.transfer(account1, transferredAmount, Optional.of("hello back!")); - var fee = account2.lookup(transferTxId).getFee(); - - assertEquals(account2BalanceBefore.subtract(transferredAmount.toSubunits()).subtract(fee), - account2.getOwnNativeTokenBalance().getAmount()); - assertEquals(account1BalanceBefore.add(transferredAmount.toSubunits()), - account1.getOwnNativeTokenBalance().getAmount()); - } - - @Then("I cannot transfer {int} XRD to another account") - public void i_cannot_transfer_xrd_to_another_account(int tokensToTransfer) { - boolean hasTransferred; - try { - account1.transfer(account2, Amount.ofTokens(tokensToTransfer), Optional.empty()); - hasTransferred = true; - } catch (RadixApiException e) { - hasTransferred = false; - } - assertFalse(hasTransferred); - } - + assertFalse(hasTransferred); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/RunTransactionLookup.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/RunTransactionLookup.java index a74973c164..00e5e9d7f7 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/RunTransactionLookup.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/RunTransactionLookup.java @@ -70,7 +70,10 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(snippets = SnippetType.UNDERSCORE, monochrome = true, plugin = { "pretty" }) +@CucumberOptions( + snippets = SnippetType.UNDERSCORE, + monochrome = true, + plugin = {"pretty"}) public class RunTransactionLookup { - // Stub for running cucumber tests + // Stub for running cucumber tests } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/TransactionLookup.java b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/TransactionLookup.java index da6f81c0f7..0c9a0ea324 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/TransactionLookup.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/acceptance/transaction_lookup/TransactionLookup.java @@ -1,102 +1,183 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.acceptance.transaction_lookup; -import com.radixdlt.test.RadixNetworkTest; -import com.radixdlt.assertions.Assertions; -import com.radixdlt.test.utils.TestFailureException; -import com.radixdlt.test.utils.TransactionUtils; +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.radixdlt.application.tokens.Amount; +import com.radixdlt.assertions.Assertions; import com.radixdlt.client.lib.dto.TransactionHistory; import com.radixdlt.client.lib.dto.TransactionStatus; +import com.radixdlt.test.RadixNetworkTest; +import com.radixdlt.test.utils.TestFailureException; +import com.radixdlt.test.utils.TransactionUtils; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.awaitility.Durations; - import java.util.Collections; import java.util.Optional; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; - -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.awaitility.Durations; public class TransactionLookup extends RadixNetworkTest { - private static final Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); - private static final Amount FIXED_TRANSFERAL_AMOUNT = Amount.ofMicroTokens(1); + private static final Amount FIXED_TRANSFERAL_AMOUNT = Amount.ofMicroTokens(1); - private Amount expectedAmount; + private Amount expectedAmount; - @Given("I have an account with funds at a suitable Radix network") - public void i_have_an_account_with_funds_at_a_suitable_radix_network() { - faucet(account1); - } + @Given("I have an account with funds at a suitable Radix network") + public void i_have_an_account_with_funds_at_a_suitable_radix_network() { + faucet(account1); + } - @Given("I transfer {int} XRD anywhere") - public void i_transfer_xrd_anywhere(int amount) { - expectedAmount = Amount.ofTokens(amount); - txBuffer = account1.transfer(account2, expectedAmount, Optional.empty()); - logger.info("Submitted transaction {}", txBuffer); - } + @Given("I transfer {int} XRD anywhere") + public void i_transfer_xrd_anywhere(int amount) { + expectedAmount = Amount.ofTokens(amount); + txBuffer = account1.transfer(account2, expectedAmount, Optional.empty()); + logger.info("Submitted transaction {}", txBuffer); + } - @Then("I can lookup my transaction and observe it contains the expected information") - public void i_can_lookup_my_transaction_and_observe_it_contains_the_expected_information() { - var transaction = account1.lookup(txBuffer); - Assertions.assertNativeTokenTransferTransaction(account1, account2, expectedAmount, transaction); - } + @Then("I can lookup my transaction and observe it contains the expected information") + public void i_can_lookup_my_transaction_and_observe_it_contains_the_expected_information() { + var transaction = account1.lookup(txBuffer); + Assertions.assertNativeTokenTransferTransaction( + account1, account2, expectedAmount, transaction); + } - @Given("I perform {int} token transfers") - public void i_perform_token_transfers(int numOfTransactions) { - if (numOfTransactions > 5) { - throw new TestFailureException("Too many transactions, should be < 6"); - } - IntStream.range(0, numOfTransactions).forEach(count -> { - var receiver = getTestAccount(count + 1); - account1.transfer(receiver, FIXED_TRANSFERAL_AMOUNT, Optional.empty()); - }); + @Given("I perform {int} token transfers") + public void i_perform_token_transfers(int numOfTransactions) { + if (numOfTransactions > 5) { + throw new TestFailureException("Too many transactions, should be < 6"); } - - @Then("I can observe those {int} transactions in my transaction history") - public void i_can_observe_those_transactions_in_my_transaction_history(Integer numOfExpectedTransactions) { - // wait until 5 transactions are visible in the history - var history = new AtomicReference(); - await().until(() -> { - var historyBuffer = account1.account().history(account1.getAddress(), 100, OptionalLong.empty()); - if (historyBuffer.getTransactions().size() >= 6) { + IntStream.range(0, numOfTransactions) + .forEach( + count -> { + var receiver = getTestAccount(count + 1); + account1.transfer(receiver, FIXED_TRANSFERAL_AMOUNT, Optional.empty()); + }); + } + + @Then("I can observe those {int} transactions in my transaction history") + public void i_can_observe_those_transactions_in_my_transaction_history( + Integer numOfExpectedTransactions) { + // wait until 5 transactions are visible in the history + var history = new AtomicReference(); + await() + .until( + () -> { + var historyBuffer = + account1.account().history(account1.getAddress(), 100, OptionalLong.empty()); + if (historyBuffer.getTransactions().size() >= 6) { history.set(historyBuffer); return true; - } - return false; - }); - - var transactions = history.get().getTransactions().stream() - .filter(transactionDTO -> transactionDTO.getActions().get(0).getFrom().get().equals(account1.getAddress())) + } + return false; + }); + + var transactions = + history.get().getTransactions().stream() + .filter( + transactionDTO -> + transactionDTO + .getActions() + .get(0) + .getFrom() + .get() + .equals(account1.getAddress())) .collect(Collectors.toList()); - assertEquals(numOfExpectedTransactions.intValue(), transactions.size()); - - // This is a way to test ordering. The most recent transaction should be first in this list. - Collections.reverse(transactions); - IntStream.range(0, numOfExpectedTransactions).forEach(count -> { - var transactionDTO = transactions.get(count); - var receiver = getTestAccount(count + 1); - Assertions.assertNativeTokenTransferTransaction(account1, receiver, FIXED_TRANSFERAL_AMOUNT, transactionDTO); - }); - } - - @Then("I can check the status of my transaction") - public void i_can_check_the_status_of_my_transaction() { - var status = account1.transaction().status(txBuffer).getStatus(); - assertTrue(status.equals(TransactionStatus.PENDING) || status.equals(TransactionStatus.CONFIRMED)); - } - - @Then("It should be quickly CONFIRMED") - public void it_should_be_quickly_confirmed() { - TransactionUtils.waitForConfirmation(account1, txBuffer, Durations.FIVE_SECONDS); - } - + assertEquals(numOfExpectedTransactions.intValue(), transactions.size()); + + // This is a way to test ordering. The most recent transaction should be first in this list. + Collections.reverse(transactions); + IntStream.range(0, numOfExpectedTransactions) + .forEach( + count -> { + var transactionDTO = transactions.get(count); + var receiver = getTestAccount(count + 1); + Assertions.assertNativeTokenTransferTransaction( + account1, receiver, FIXED_TRANSFERAL_AMOUNT, transactionDTO); + }); + } + + @Then("I can check the status of my transaction") + public void i_can_check_the_status_of_my_transaction() { + var status = account1.transaction().status(txBuffer).getStatus(); + assertTrue( + status.equals(TransactionStatus.PENDING) || status.equals(TransactionStatus.CONFIRMED)); + } + + @Then("It should be quickly CONFIRMED") + public void it_should_be_quickly_confirmed() { + TransactionUtils.waitForConfirmation(account1, txBuffer, Durations.FIVE_SECONDS); + } } diff --git a/radixdlt-regression/src/test/java/com/radixdlt/assertions/Assertions.java b/radixdlt-regression/src/test/java/com/radixdlt/assertions/Assertions.java index b2978c6153..2920b1992e 100644 --- a/radixdlt-regression/src/test/java/com/radixdlt/assertions/Assertions.java +++ b/radixdlt-regression/src/test/java/com/radixdlt/assertions/Assertions.java @@ -1,5 +1,73 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.assertions; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.radixdlt.TokenCreationProperties; import com.radixdlt.application.tokens.Amount; import com.radixdlt.client.lib.api.ActionType; @@ -12,56 +80,57 @@ import com.radixdlt.test.utils.TestingUtils; import org.junit.Assert; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -/** - * Custom assertions (and wrappers for assertions) for the cucumber/acceptance tests - */ +/** Custom assertions (and wrappers for assertions) for the cucumber/acceptance tests */ public class Assertions { - private Assertions() { - - } + private Assertions() {} - public static void assertNativeTokenTransferTransaction(Account account1, Account account2, Amount expectedAmount, - TransactionDTO transactionDto) { - assertTrue(transactionDto.getMessage().isEmpty()); - Assert.assertEquals(1, transactionDto.getActions().size()); - Action singleAction = transactionDto.getActions().get(0); - Assert.assertEquals(expectedAmount.toSubunits(), - singleAction.getAmount().orElseThrow(() -> new TestFailureException("no amount in transaction"))); - Assert.assertEquals(account1.getAddress(), - singleAction.getFrom().orElseThrow(() -> new TestFailureException("no sender in transaction"))); - Assert.assertEquals(account2.getAddress(), - singleAction.getTo().orElseThrow(() -> new TestFailureException("no receiver in transaction"))); - Assert.assertEquals(ActionType.TRANSFER, singleAction.getType()); - } + public static void assertNativeTokenTransferTransaction( + Account account1, Account account2, Amount expectedAmount, TransactionDTO transactionDto) { + assertTrue(transactionDto.getMessage().isEmpty()); + Assert.assertEquals(1, transactionDto.getActions().size()); + Action singleAction = transactionDto.getActions().get(0); + Assert.assertEquals( + expectedAmount.toSubunits(), + singleAction + .getAmount() + .orElseThrow(() -> new TestFailureException("no amount in transaction"))); + Assert.assertEquals( + account1.getAddress(), + singleAction + .getFrom() + .orElseThrow(() -> new TestFailureException("no sender in transaction"))); + Assert.assertEquals( + account2.getAddress(), + singleAction + .getTo() + .orElseThrow(() -> new TestFailureException("no receiver in transaction"))); + Assert.assertEquals(ActionType.TRANSFER, singleAction.getType()); + } - /** - * Will fetch token info based on the AID and compare the field values to the expected ones - */ - public static void assertTokenProperties(Account account, AID tx, TokenCreationProperties expectedProperties) { - var transaction = account.lookup(tx); - var tokenInfo = TestingUtils.getTokenInfoFromTransaction(account, transaction); - assertEquals(transaction.getEvents().get(0).getRri().get(), tokenInfo.getRri()); - assertEquals(Amount.ofTokens(expectedProperties.getAmount()).toSubunits(), tokenInfo.getCurrentSupply()); - assertEquals(expectedProperties.getSymbol(), tokenInfo.getSymbol()); - assertEquals(expectedProperties.getName(), tokenInfo.getName()); - assertEquals(expectedProperties.getDescription(), tokenInfo.getDescription()); - assertEquals(expectedProperties.getTokenInfoUrl(), tokenInfo.getTokenInfoURL()); - assertEquals(expectedProperties.getIconUrl(), tokenInfo.getIconURL()); - } + /** Will fetch token info based on the AID and compare the field values to the expected ones */ + public static void assertTokenProperties( + Account account, AID tx, TokenCreationProperties expectedProperties) { + var transaction = account.lookup(tx); + var tokenInfo = TestingUtils.getTokenInfoFromTransaction(account, transaction); + assertEquals(transaction.getEvents().get(0).getRri().get(), tokenInfo.getRri()); + assertEquals( + Amount.ofTokens(expectedProperties.getAmount()).toSubunits(), tokenInfo.getCurrentSupply()); + assertEquals(expectedProperties.getSymbol(), tokenInfo.getSymbol()); + assertEquals(expectedProperties.getName(), tokenInfo.getName()); + assertEquals(expectedProperties.getDescription(), tokenInfo.getDescription()); + assertEquals(expectedProperties.getTokenInfoUrl(), tokenInfo.getTokenInfoURL()); + assertEquals(expectedProperties.getIconUrl(), tokenInfo.getIconURL()); + } - public static void runExpectingRadixApiException(Runnable function, String message) { - try { - function.run(); - fail("No RadxiApiException was thrown"); - } catch (RadixApiException e) { - assertTrue("Actual message '" + e.getMessage() + "' is not equal to expected '" + message + "'", - e.getMessage().contains(message)); - } + public static void runExpectingRadixApiException(Runnable function, String message) { + try { + function.run(); + fail("No RadxiApiException was thrown"); + } catch (RadixApiException e) { + assertTrue( + "Actual message '" + e.getMessage() + "' is not equal to expected '" + message + "'", + e.getMessage().contains(message)); } - + } } diff --git a/radixdlt-regression/system-tests/README.md b/radixdlt-regression/system-tests/README.md index da67430212..dcd46ea9c4 100644 --- a/radixdlt-regression/system-tests/README.md +++ b/radixdlt-regression/system-tests/README.md @@ -4,15 +4,15 @@ For mac or linux: ``` -./gradlew systemTests +./gradlew systemTests ``` This should start a local radix docker network, run the tests, and then remove the network & containers. -On Windows, you should expose the docker daemon on a local port. Then set its URL: +On Windows, you should expose the docker daemon on a local port. Then set its URL: ``` set RADIXDLT_DOCKER_DAEMON_URL=tcp://localhost:2375 -``` +``` Run the gradle task: ``` @@ -30,23 +30,23 @@ Do not create a local network with: ``` RADIXDLT_DOCKER_INITIALIZE_NETWORK=false ``` -Then use the RADIXDLT_JSON_RPC_API_* properties to point to a Radix JSON-RPC API. +Then use the RADIXDLT_JSON_RPC_API_* properties to point to a Radix JSON-RPC API. ### Properties These are the env vars that the framework uses for configuration: * `RADIXDLT_JSON_RPC_API_ROOT_URL`: The JSON-RPC API of the network to test. -* `RADIXDLT_JSON_RPC_API_PRIMARY_PORT`: Radix JSON-RPC APIs have two ports: A primary one for public functionality and a secondary one for internal ones. +* `RADIXDLT_JSON_RPC_API_PRIMARY_PORT`: Radix JSON-RPC APIs have two ports: A primary one for public functionality and a secondary one for internal ones. * `RADIXDLT_JSON_RPC_API_SECONDARY_PORT`: See above. * `RADIXDLT_BASIC_AUTH`: If your node is using basic auth, set the username:password here. * `RADIXDLT_FAUCET_URL`: A faucet the tests will use to get tokens from. Docker-related properties: -* `RADIXDLT_DOCKER_DAEMON_URL`: The system tests use docker to create networks and manipulate containers. Set the non-TLS docker daemon URL of your docker installation here. -* `RADIXDLT_DOCKER_CONTAINER_NAME`: The naming pattern of the containers to be created. Uses wildcard %d for the container number. -* `RADIXDLT_DOCKER_INITIALIZE_NETWORK`: Will create a new radix network via docker, before running the tests. When running system tests, this is true by default. +* `RADIXDLT_DOCKER_DAEMON_URL`: The system tests use docker to create networks and manipulate containers. Set the non-TLS docker daemon URL of your docker installation here. +* `RADIXDLT_DOCKER_CONTAINER_NAME`: The naming pattern of the containers to be created. Uses wildcard %d for the container number. +* `RADIXDLT_DOCKER_INITIALIZE_NETWORK`: Will create a new radix network via docker, before running the tests. When running system tests, this is true by default. * `RADIXDLT_DOCKER_IMAGE`: The radix node image to be used, defaults to radixdlt/radixdlt-core:develop. * `RADIXDLT_DOCKER_INITIAL_NUMBER_OF_NODES`: If a network is to be initialized, it will consist of this number of nodes. -* `RADIXDLT_DOCKER_NETWORK_NAME`: The name of the actual docker network created for system testing. \ No newline at end of file +* `RADIXDLT_DOCKER_NETWORK_NAME`: The name of the actual docker network created for system testing. diff --git a/radixdlt-regression/system-tests/build.gradle b/radixdlt-regression/system-tests/build.gradle index aa19215018..0e76778425 100644 --- a/radixdlt-regression/system-tests/build.gradle +++ b/radixdlt-regression/system-tests/build.gradle @@ -113,4 +113,3 @@ task chaosExperiments(type: Test) { enabled false } } - diff --git a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/TransactionHistoryCreator.java b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/TransactionHistoryCreator.java index ad094fa0bb..40338d95b1 100644 --- a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/TransactionHistoryCreator.java +++ b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/TransactionHistoryCreator.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test; import org.apache.logging.log4j.LogManager; @@ -5,17 +69,13 @@ public class TransactionHistoryCreator { - protected static final Logger logger = LogManager.getLogger(); - - public static void main(String[] args) { - doStuff("a", "a"); - } - - private TransactionHistoryCreator() { - } + protected static final Logger logger = LogManager.getLogger(); - public static void doStuff(String... images) { + public static void main(String[] args) { + doStuff("a", "a"); + } - } + private TransactionHistoryCreator() {} + public static void doStuff(String... images) {} } diff --git a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/AnsibleImageWrapper.java b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/AnsibleImageWrapper.java index 3909ec8289..7030f5dd0c 100644 --- a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/AnsibleImageWrapper.java +++ b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/AnsibleImageWrapper.java @@ -66,124 +66,140 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.platform.commons.util.StringUtils; - import java.util.List; import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.platform.commons.util.StringUtils; /** - * We have our own image with ansible and several playbook. We use this image via docker. - * This class has the docker commands needed to utilize these playbooks. + * We have our own image with ansible and several playbook. We use this image via docker. This class + * has the docker commands needed to utilize these playbooks. */ @SuppressWarnings("deprecation") public class AnsibleImageWrapper { - private static final Logger logger = LogManager.getLogger(); - - public static final String DEFAULT_ANSIBLE_IMAGE = "eu.gcr.io/lunar-arc-236318/node-ansible"; - private static final String KEY_VOLUME_NAME_USED_FOR_COPYING = "key-volume"; - - private final String image; - private final String clusterName; - private Set nodeAddresses; - - public static AnsibleImageWrapper createWithDefaultImage() { - String sshKeyLocation = getSshIdentityLocation(); - String clusterName = Optional.ofNullable(System.getenv("TESTNET_NAME")).orElseThrow(); - // the ansible image needs an SSH key because it runs tasks over SSH - AnsibleImageWrapper wrapper = new AnsibleImageWrapper(DEFAULT_ANSIBLE_IMAGE, clusterName); - wrapper.copyfileToNamedVolume(sshKeyLocation); - wrapper.pullImage(); - return wrapper; - } - - public static String getSshIdentityLocation() { - return Optional.ofNullable(System.getenv("SSH_IDENTITY")) - .orElse(System.getenv("HOME") + "/.ssh/id_rsa"); + private static final Logger logger = LogManager.getLogger(); + + public static final String DEFAULT_ANSIBLE_IMAGE = "eu.gcr.io/lunar-arc-236318/node-ansible"; + private static final String KEY_VOLUME_NAME_USED_FOR_COPYING = "key-volume"; + + private final String image; + private final String clusterName; + private Set nodeAddresses; + + public static AnsibleImageWrapper createWithDefaultImage() { + String sshKeyLocation = getSshIdentityLocation(); + String clusterName = Optional.ofNullable(System.getenv("TESTNET_NAME")).orElseThrow(); + // the ansible image needs an SSH key because it runs tasks over SSH + AnsibleImageWrapper wrapper = new AnsibleImageWrapper(DEFAULT_ANSIBLE_IMAGE, clusterName); + wrapper.copyfileToNamedVolume(sshKeyLocation); + wrapper.pullImage(); + return wrapper; + } + + public static String getSshIdentityLocation() { + return Optional.ofNullable(System.getenv("SSH_IDENTITY")) + .orElse(System.getenv("HOME") + "/.ssh/id_rsa"); + } + + private AnsibleImageWrapper(String image, String clusterName) { + this.image = image; + this.clusterName = clusterName; + } + + /** + * The key needs to be copied to a volume, which is created with the help of a temp dummy + * container + */ + @SuppressWarnings("deprecation") // CmdHelper is a placeholder + private void copyfileToNamedVolume(String localFileLocation) { + CmdHelper.runCommand( + String.format( + "docker container create --name dummy -v %s:%s curlimages/curl:7.70.0", + KEY_VOLUME_NAME_USED_FOR_COPYING, "/ansible/ssh")); + CmdHelper.runCommand( + String.format("docker cp %s dummy:%s", localFileLocation, "/ansible/ssh/testnet")); + CmdHelper.runCommand("docker rm -f dummy"); + } + + @SuppressWarnings("deprecation") // CmdHelper is a placeholder + private void pullImage() { + CmdHelper.runCommand("docker pull " + image); + } + + @SuppressWarnings("deprecation") // CmdHelper is a placeholder + public String runPlaybook(String playbook, String options, String tag) { + String awsAccessKeyId = System.getenv("AWS_ACCESS_KEY_ID"); + String awsSecretAccessKey = System.getenv("AWS_SECRET_ACCESS_KEY"); + String testnetName = + clusterName.contains("_") + ? clusterName.substring(0, clusterName.indexOf("_")) + : clusterName; + List commandParts = + Lists.newArrayList( + "docker", + "run", + "--rm", + "-v", + "key-volume:/ansible/ssh", + "--name", + "node-ansible", + "-e", + "AWS_ACCESS_KEY_ID=" + awsAccessKeyId, + "-e", + "AWS_SECRET_ACCESS_KEY=" + awsSecretAccessKey, + image, + "-i", + "aws-plugin-inventory/" + testnetName + "_aws_ec2.yml", + playbook, + "--limit", + clusterName, + "-t", + tag); + if (StringUtils.isNotBlank(options)) { + commandParts.add("-e"); + commandParts.add("\"optionsArgs='" + options + "'\""); } - - private AnsibleImageWrapper(String image, String clusterName) { - this.image = image; - this.clusterName = clusterName; - } - - /** - * The key needs to be copied to a volume, which is created with the help of a temp dummy container - */ - @SuppressWarnings("deprecation") // CmdHelper is a placeholder - private void copyfileToNamedVolume(String localFileLocation) { - CmdHelper.runCommand(String.format("docker container create --name dummy -v %s:%s curlimages/curl:7.70.0", - KEY_VOLUME_NAME_USED_FOR_COPYING, "/ansible/ssh")); - CmdHelper.runCommand(String.format("docker cp %s dummy:%s", - localFileLocation, "/ansible/ssh/testnet")); - CmdHelper.runCommand("docker rm -f dummy"); - } - - @SuppressWarnings("deprecation") // CmdHelper is a placeholder - private void pullImage() { - CmdHelper.runCommand("docker pull " + image); - } - - @SuppressWarnings("deprecation") // CmdHelper is a placeholder - public String runPlaybook(String playbook, String options, String tag) { - String awsAccessKeyId = System.getenv("AWS_ACCESS_KEY_ID"); - String awsSecretAccessKey = System.getenv("AWS_SECRET_ACCESS_KEY"); - String testnetName = clusterName.contains("_") ? clusterName.substring(0, clusterName.indexOf("_")) : clusterName; - List commandParts = Lists.newArrayList( - "docker", "run", "--rm", "-v", "key-volume:/ansible/ssh", "--name", "node-ansible", - "-e", "AWS_ACCESS_KEY_ID=" + awsAccessKeyId, "-e", "AWS_SECRET_ACCESS_KEY=" + awsSecretAccessKey, - image, "-i", "aws-plugin-inventory/" + testnetName + "_aws_ec2.yml", playbook, "--limit", clusterName, "-t", tag - ); - if (StringUtils.isNotBlank(options)) { - commandParts.add("-e"); - commandParts.add("\"optionsArgs='" + options + "'\""); - } - String[] commandArrayWithoutEmptyStrings = - commandParts.stream().filter(StringUtils::isNotBlank).toArray(String[]::new); - logger.info("Running docker command: {}", commandParts); - return CmdHelper.runCommand(commandArrayWithoutEmptyStrings, null, true).toString(); - } - - public String runPlaybook(String playbook, String tag) { - return runPlaybook(playbook, null, tag); - } - - /** - * Uses the output of the check task to get a list of node IPs - */ - public Set getNodeAddressList() { - if (nodeAddresses == null) { - nodeAddresses = Sets.newHashSet(); - String playbookOutput = runPlaybook("check.yml", "check"); - Matcher matcher = Pattern.compile("(?<=\\[).+?(?=\\])").matcher(playbookOutput); - while (matcher.find()) { - String raw = matcher.group(0); - if (raw.split("[.]").length == 4) { - nodeAddresses.add(raw); - } - } - logger.info("Found {} nodes", nodeAddresses.size()); + String[] commandArrayWithoutEmptyStrings = + commandParts.stream().filter(StringUtils::isNotBlank).toArray(String[]::new); + logger.info("Running docker command: {}", commandParts); + return CmdHelper.runCommand(commandArrayWithoutEmptyStrings, null, true).toString(); + } + + public String runPlaybook(String playbook, String tag) { + return runPlaybook(playbook, null, tag); + } + + /** Uses the output of the check task to get a list of node IPs */ + public Set getNodeAddressList() { + if (nodeAddresses == null) { + nodeAddresses = Sets.newHashSet(); + String playbookOutput = runPlaybook("check.yml", "check"); + Matcher matcher = Pattern.compile("(?<=\\[).+?(?=\\])").matcher(playbookOutput); + while (matcher.find()) { + String raw = matcher.group(0); + if (raw.split("[.]").length == 4) { + nodeAddresses.add(raw); } - return nodeAddresses; - } - - /** - * returns one of the node addresses - */ - public String getRandomNodeHost() { - List addressList = Lists.newArrayList(getNodeAddressList()); - return addressList.get(new Random().nextInt(addressList.size())); + } + logger.info("Found {} nodes", nodeAddresses.size()); } - - @SuppressWarnings("deprecation") // CmdHelper is a placeholder - public void tearDown() { - CmdHelper.runCommand("docker volume rm -f " + KEY_VOLUME_NAME_USED_FOR_COPYING); - } - + return nodeAddresses; + } + + /** returns one of the node addresses */ + public String getRandomNodeHost() { + List addressList = Lists.newArrayList(getNodeAddressList()); + return addressList.get(new Random().nextInt(addressList.size())); + } + + @SuppressWarnings("deprecation") // CmdHelper is a placeholder + public void tearDown() { + CmdHelper.runCommand("docker volume rm -f " + KEY_VOLUME_NAME_USED_FOR_COPYING); + } } diff --git a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/CmdHelper.java b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/CmdHelper.java index 7c4b285242..796916b1e8 100644 --- a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/CmdHelper.java +++ b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/chaos/ansible/CmdHelper.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.chaos.ansible; /** @@ -6,16 +70,13 @@ @Deprecated public class CmdHelper { - private CmdHelper() { - - } - - public static void runCommand(String cmd) { - throw new RuntimeException("Unimplemented"); - } + private CmdHelper() {} - public static Object runCommand(String[] cmd, Object object, boolean bool) { - throw new RuntimeException("Unimplemented"); - } + public static void runCommand(String cmd) { + throw new RuntimeException("Unimplemented"); + } + public static Object runCommand(String[] cmd, Object object, boolean bool) { + throw new RuntimeException("Unimplemented"); + } } diff --git a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/SmokeTests.java b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/SmokeTests.java index e098ef565b..9af4d0c12e 100644 --- a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/SmokeTests.java +++ b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/SmokeTests.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.system; import com.radixdlt.test.system.harness.SystemTest; @@ -5,13 +69,12 @@ class SmokeTests extends SystemTest { - @Test - void network_does_not_lose_liveness_when_nodes_restart() { - runCheck("liveness"); - var firstNode = radixNetwork.getNodes().get(0); - restartNode(firstNode); - runCheck("liveness", 60); - waitForNodeToBeUp(firstNode); - } - + @Test + void network_does_not_lose_liveness_when_nodes_restart() { + runCheck("liveness"); + var firstNode = radixNetwork.getNodes().get(0); + restartNode(firstNode); + runCheck("liveness", 60); + waitForNodeToBeUp(firstNode); + } } diff --git a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/TransactionsTests.java b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/TransactionsTests.java index c9f9b76107..24c0fb87e6 100644 --- a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/TransactionsTests.java +++ b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/TransactionsTests.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.system; import com.radixdlt.application.tokens.Amount; @@ -15,6 +79,7 @@ import com.radixdlt.test.utils.TestFailureException; import com.radixdlt.test.utils.TestingUtils; import com.radixdlt.test.utils.TransactionUtils; +import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.BeforeEach; @@ -24,175 +89,214 @@ import org.junit.jupiter.api.TestMethodOrder; import org.junit.platform.commons.util.StringUtils; -import java.util.Optional; - @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class TransactionsTests extends SystemTest { - protected static final Logger logger = LogManager.getLogger(); + protected static final Logger logger = LogManager.getLogger(); - private static final String BIP32_PATH = "m/44'/1022'/0'/0/0'"; - private static final Amount AMOUNT_TO_STAKE = Amount.ofTokens(100); - private static final Amount TOKENS_TO_MINT = Amount.ofTokens(250000); + private static final String BIP32_PATH = "m/44'/1022'/0'/0/0'"; + private static final Amount AMOUNT_TO_STAKE = Amount.ofTokens(100); + private static final Amount TOKENS_TO_MINT = Amount.ofTokens(250000); - // in case there is no seed phrase, we use a known address for this test - private static final int HARDCODED_ADDRESS_PRIVATE_KEY = 5; + // in case there is no seed phrase, we use a known address for this test + private static final int HARDCODED_ADDRESS_PRIVATE_KEY = 5; - private final Account account; - private final String version; - private final ValidatorDTO firstValidator; - private String mutableTokenRri; + private final Account account; + private final String version; + private final ValidatorDTO firstValidator; + private String mutableTokenRri; - TransactionsTests() { - // env properties specific to this test: - var seedPhrase = TestingUtils.getEnvWithDefault("RADIXDLT_TESTING_ACCOUNT_SEED_PHRASE", ""); - // the node version of the first node in the network. This will be saved in the transaction message and will tell us - // which version this transaction came from - this.version = radixNetwork.getVersionOfFirstNode(); + TransactionsTests() { + // env properties specific to this test: + var seedPhrase = TestingUtils.getEnvWithDefault("RADIXDLT_TESTING_ACCOUNT_SEED_PHRASE", ""); + // the node version of the first node in the network. This will be saved in the transaction + // message and will tell us + // which version this transaction came from + this.version = radixNetwork.getVersionOfFirstNode(); - var configuration = radixNetwork.getConfiguration(); - this.account = StringUtils.isNotBlank(seedPhrase) + var configuration = radixNetwork.getConfiguration(); + this.account = + StringUtils.isNotBlank(seedPhrase) ? initializeAccountFromSeedPhrase(configuration, seedPhrase) - : Account.initialize(configuration.getJsonRpcRootUrl(), configuration.getPrimaryPort(), - configuration.getSecondaryPort(), TestingUtils.createKeyPairFromNumber(HARDCODED_ADDRESS_PRIVATE_KEY), configuration.getBasicAuth()); - logger.info("Using address {} with balances: {}", account.getAddressForNetwork(), - account.getOwnTokenBalances().getTokenBalances()); - firstValidator = account.validator().list(100, Optional.empty()).getValidators().get(0); - } + : Account.initialize( + configuration.getJsonRpcRootUrl(), + configuration.getPrimaryPort(), + configuration.getSecondaryPort(), + TestingUtils.createKeyPairFromNumber(HARDCODED_ADDRESS_PRIVATE_KEY), + configuration.getBasicAuth()); + logger.info( + "Using address {} with balances: {}", + account.getAddressForNetwork(), + account.getOwnTokenBalances().getTokenBalances()); + firstValidator = account.validator().list(100, Optional.empty()).getValidators().get(0); + } - @BeforeEach - void setup() { - callFaucetIfNeeded(account); - } + @BeforeEach + void setup() { + callFaucetIfNeeded(account); + } - @Test - void token_transfer() { - var txId = account.transfer(account1, Amount.ofMicroTokens(2500), createTestMessageOptional()); - logger.info("Token transfer txId: {}", txId); - } + @Test + void token_transfer() { + var txId = account.transfer(account1, Amount.ofMicroTokens(2500), createTestMessageOptional()); + logger.info("Token transfer txId: {}", txId); + } - @Test - @Order(1) - void stake() { - callFaucetIfNeeded(account, AMOUNT_TO_STAKE); - logger.info("Staking {} to validator {}...", AMOUNT_TO_STAKE, firstValidator); - var txId = account.stake(firstValidator.getAddress(), AMOUNT_TO_STAKE, createTestMessageOptional()); - logger.info("Stake tokens txId: {}", txId); - } + @Test + @Order(1) + void stake() { + callFaucetIfNeeded(account, AMOUNT_TO_STAKE); + logger.info("Staking {} to validator {}...", AMOUNT_TO_STAKE, firstValidator); + var txId = + account.stake(firstValidator.getAddress(), AMOUNT_TO_STAKE, createTestMessageOptional()); + logger.info("Stake tokens txId: {}", txId); + } - @Test - @Order(2) - void multi_action_transaction() { - callFaucetIfNeeded(account, AMOUNT_TO_STAKE.times(3)); // fees are high - var nativeTokenRri = account.getNativeToken().getRri(); - var publicKey = account.getKeyPair().getPublicKey(); - var timestamp = System.currentTimeMillis(); - var request = TransactionRequest.createBuilder(account.getAddress()) + @Test + @Order(2) + void multi_action_transaction() { + callFaucetIfNeeded(account, AMOUNT_TO_STAKE.times(3)); // fees are high + var nativeTokenRri = account.getNativeToken().getRri(); + var publicKey = account.getKeyPair().getPublicKey(); + var timestamp = System.currentTimeMillis(); + var request = + TransactionRequest.createBuilder(account.getAddress()) .stake(account.getAddress(), firstValidator.getAddress(), AMOUNT_TO_STAKE.toSubunits()) - .transfer(account.getAddress(), account1.getAddress(), Amount.ofMicroTokens(1).toSubunits(), nativeTokenRri) - .transfer(account.getAddress(), account2.getAddress(), Amount.ofMicroTokens(2).toSubunits(), nativeTokenRri) - .createMutable(publicKey, "mtt2" + timestamp, "mtesttoken2" + timestamp, - Optional.empty(), Optional.empty(), Optional.empty()) - .createFixed(account.getAddress(), publicKey, "ftt2" + timestamp, "ftesttoken2", "description", - "https://www.icon.com", "https://www.token.com", Amount.ofTokens(1000000).toSubunits()) + .transfer( + account.getAddress(), + account1.getAddress(), + Amount.ofMicroTokens(1).toSubunits(), + nativeTokenRri) + .transfer( + account.getAddress(), + account2.getAddress(), + Amount.ofMicroTokens(2).toSubunits(), + nativeTokenRri) + .createMutable( + publicKey, + "mtt2" + timestamp, + "mtesttoken2" + timestamp, + Optional.empty(), + Optional.empty(), + Optional.empty()) + .createFixed( + account.getAddress(), + publicKey, + "ftt2" + timestamp, + "ftesttoken2", + "description", + "https://www.icon.com", + "https://www.token.com", + Amount.ofTokens(1000000).toSubunits()) .build(); - logger.info("Submitting a multi-action transaction..."); - var txId = TransactionUtils.buildFinalizeAndSubmitTransaction(account, request, true); - logger.info("Multi-action txId: {}", txId); - logger.info("Waiting until end of current epoch..."); - TestingUtils.waitUntilEndNextEpoch(account); - } + logger.info("Submitting a multi-action transaction..."); + var txId = TransactionUtils.buildFinalizeAndSubmitTransaction(account, request, true); + logger.info("Multi-action txId: {}", txId); + logger.info("Waiting until end of current epoch..."); + TestingUtils.waitUntilEndNextEpoch(account); + } - @Test - @Order(3) - void unstake() { - var totalStake = AMOUNT_TO_STAKE.times(2); - logger.info("Unstaking {} from validator {}...", totalStake, firstValidator); - var txId = account.unstake(firstValidator.getAddress(), AMOUNT_TO_STAKE.times(2), Optional.empty()); - logger.info("Unstake tokens txId: {}", txId); - } + @Test + @Order(3) + void unstake() { + var totalStake = AMOUNT_TO_STAKE.times(2); + logger.info("Unstaking {} from validator {}...", totalStake, firstValidator); + var txId = + account.unstake(firstValidator.getAddress(), AMOUNT_TO_STAKE.times(2), Optional.empty()); + logger.info("Unstake tokens txId: {}", txId); + } - @Test - @Order(4) - void mutable_supply_token_creation() { - callFaucetIfNeeded(account, Amount.ofTokens(100)); - var timestamp = System.currentTimeMillis(); - var txId = account.mutableSupplyToken( + @Test + @Order(4) + void mutable_supply_token_creation() { + callFaucetIfNeeded(account, Amount.ofTokens(100)); + var timestamp = System.currentTimeMillis(); + var txId = + account.mutableSupplyToken( "mtt" + timestamp, "mtesttoken" + timestamp, "A mutable supply token created for testing. Version " + version, "https://www.mtesttoken.ico", "https://www.mtesttoken.com"); - logger.info("Mutable supply token txId: {}", txId); - setMutableTokenRri(txId); - logger.info("Mutable supply token's rri: {}", mutableTokenRri); - } + logger.info("Mutable supply token txId: {}", txId); + setMutableTokenRri(txId); + logger.info("Mutable supply token's rri: {}", mutableTokenRri); + } - @Test - @Order(5) - void mint_mutable_supply_token() { - var txId = account.mint(TOKENS_TO_MINT, mutableTokenRri, createTestMessageOptional()); - logger.info("Token {} mint txId: {}", mutableTokenRri, txId); - } + @Test + @Order(5) + void mint_mutable_supply_token() { + var txId = account.mint(TOKENS_TO_MINT, mutableTokenRri, createTestMessageOptional()); + logger.info("Token {} mint txId: {}", mutableTokenRri, txId); + } - @Test - @Order(6) - void burn_mutable_supply_token() { - var txId = account.burn(TOKENS_TO_MINT, mutableTokenRri, createTestMessageOptional()); - logger.info("Token {} burn txId: {}", mutableTokenRri, txId); - } + @Test + @Order(6) + void burn_mutable_supply_token() { + var txId = account.burn(TOKENS_TO_MINT, mutableTokenRri, createTestMessageOptional()); + logger.info("Token {} burn txId: {}", mutableTokenRri, txId); + } - @Test - void fixed_supply_token_creation() { - callFaucetIfNeeded(account, Amount.ofTokens(100)); - var timestamp = System.currentTimeMillis(); - var supply = Amount.ofTokens(1000000); - var txId = account.fixedSupplyToken( + @Test + void fixed_supply_token_creation() { + callFaucetIfNeeded(account, Amount.ofTokens(100)); + var timestamp = System.currentTimeMillis(); + var supply = Amount.ofTokens(1000000); + var txId = + account.fixedSupplyToken( "ftt" + timestamp, "ftesttoken_" + timestamp, "A fixed supply (" + supply + ") token created for testing. Version " + version, "https://www.ftesttoken.ico", "https://www.ftesttoken.com", supply); - logger.info("Fixed supply token txId: {}", txId); - } + logger.info("Fixed supply token txId: {}", txId); + } - private Account initializeAccountFromSeedPhrase(RadixNetworkConfiguration configuration, String seedPhrase) { - logger.info("Will generate keypair from seed phrase..."); - try { - var keyPairDerivation = DefaultHDKeyPairDerivation.fromMnemonicString(seedPhrase); - var bip32path = BitcoinJBIP32Path.fromString(BIP32_PATH); - var keyPair = keyPairDerivation.deriveKeyAtPath(bip32path).keyPair(); - return Account.initialize(configuration.getJsonRpcRootUrl(), configuration.getPrimaryPort(), - configuration.getSecondaryPort(), keyPair, configuration.getBasicAuth()); - } catch (MnemonicException | HDPathException e) { - throw new RuntimeException("Could not generate keypair from seed phrase", e); - } + private Account initializeAccountFromSeedPhrase( + RadixNetworkConfiguration configuration, String seedPhrase) { + logger.info("Will generate keypair from seed phrase..."); + try { + var keyPairDerivation = DefaultHDKeyPairDerivation.fromMnemonicString(seedPhrase); + var bip32path = BitcoinJBIP32Path.fromString(BIP32_PATH); + var keyPair = keyPairDerivation.deriveKeyAtPath(bip32path).keyPair(); + return Account.initialize( + configuration.getJsonRpcRootUrl(), + configuration.getPrimaryPort(), + configuration.getSecondaryPort(), + keyPair, + configuration.getBasicAuth()); + } catch (MnemonicException | HDPathException e) { + throw new RuntimeException("Could not generate keypair from seed phrase", e); } + } - private void setMutableTokenRri(AID txId) { - var events = account.transaction().lookup(txId).getEvents(); - if (events.size() != 1) { - throw new TestFailureException("Expected only 1 token_created action, got " + events); - } - Event event = events.get(0); - event.getRri().map(rri -> this.mutableTokenRri = rri).orElseThrow(() -> new TestFailureException("No rri found in event")); + private void setMutableTokenRri(AID txId) { + var events = account.transaction().lookup(txId).getEvents(); + if (events.size() != 1) { + throw new TestFailureException("Expected only 1 token_created action, got " + events); } + Event event = events.get(0); + event + .getRri() + .map(rri -> this.mutableTokenRri = rri) + .orElseThrow(() -> new TestFailureException("No rri found in event")); + } - private Optional createTestMessageOptional() { - return Optional.of("Autogenerated test transaction from version '" + version + "'"); - } + private Optional createTestMessageOptional() { + return Optional.of("Autogenerated test transaction from version '" + version + "'"); + } - private void callFaucetIfNeeded(Account account, Amount amount) { - if (account.getOwnNativeTokenBalance().getAmount().compareTo(amount.toSubunits()) < 1) { - faucet(account, amount); - } + private void callFaucetIfNeeded(Account account, Amount amount) { + if (account.getOwnNativeTokenBalance().getAmount().compareTo(amount.toSubunits()) < 1) { + faucet(account, amount); } + } - private void callFaucetIfNeeded(Account account) { - if (account.getOwnNativeTokenBalance().getAmount().compareTo(Amount.ofTokens(10).toSubunits()) < 1) { - faucet(account); - } + private void callFaucetIfNeeded(Account account) { + if (account.getOwnNativeTokenBalance().getAmount().compareTo(Amount.ofTokens(10).toSubunits()) + < 1) { + faucet(account); } - + } } diff --git a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/harness/SystemTest.java b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/harness/SystemTest.java index 2a17d711e1..f476f4e35c 100644 --- a/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/harness/SystemTest.java +++ b/radixdlt-regression/system-tests/src/test/java/com/radixdlt/test/system/harness/SystemTest.java @@ -1,3 +1,67 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + package com.radixdlt.test.system.harness; import com.radixdlt.test.RadixNetworkTest; @@ -11,39 +75,38 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SystemTest extends RadixNetworkTest { - protected static final Logger logger = LogManager.getLogger(); + protected static final Logger logger = LogManager.getLogger(); - public void restartNode(RadixNode node) { - radixNetwork.getDockerClient().restartNode(node.getContainerName()); - logger.info("Restarted (docker restart) node {}", node); - } + public void restartNode(RadixNode node) { + radixNetwork.getDockerClient().restartNode(node.getContainerName()); + logger.info("Restarted (docker restart) node {}", node); + } - public void stopNode(RadixNode node) { - radixNetwork.getDockerClient().stopNode(node.getContainerName()); - logger.info("Stopped (docker stop) node {}", node); - } + public void stopNode(RadixNode node) { + radixNetwork.getDockerClient().stopNode(node.getContainerName()); + logger.info("Stopped (docker stop) node {}", node); + } - public void runCommand(RadixNode node, String command) { - radixNetwork.getDockerClient().runCommand(node.getRootUrl(), command); - } + public void runCommand(RadixNode node, String command) { + radixNetwork.getDockerClient().runCommand(node.getRootUrl(), command); + } - public void waitForNodeToBeUp(RadixNode node) { - TestingUtils.waitForNodeToBeUp(node, radixNetwork.getConfiguration()); - logger.info("Node {} is UP", node); - } + public void waitForNodeToBeUp(RadixNode node) { + TestingUtils.waitForNodeToBeUp(node, radixNetwork.getConfiguration()); + logger.info("Node {} is UP", node); + } - @AfterAll - public void teardown() { - switch (radixNetwork.getConfiguration().getType()) { - case LOCALNET: - if (Boolean.parseBoolean(System.getenv("RADIXDLT_DOCKER_DO_NOT_WIPE_NETWORK"))) { - return; - } - radixNetwork.getDockerClient().cleanup(); - break; - case TESTNET: - break; + @AfterAll + public void teardown() { + switch (radixNetwork.getConfiguration().getType()) { + case LOCALNET: + if (Boolean.parseBoolean(System.getenv("RADIXDLT_DOCKER_DO_NOT_WIPE_NETWORK"))) { + return; } + radixNetwork.getDockerClient().cleanup(); + break; + case TESTNET: + break; } - + } }