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 d90be45e29..927a29706c 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 @@ -48,13 +48,13 @@ public void start(SimulationNodes.RunningNetwork network) { List nodes = network.getNodes(); this.disposable = Observable.interval(1, 1, TimeUnit.SECONDS) .map(i -> nodes.get(random.nextInt(nodes.size()))) - .map(node -> network.getDispatcher(NodeApplicationRequest.class, node)) - .subscribe(d -> { + .subscribe(node -> { + var d = network.getDispatcher(NodeApplicationRequest.class, node); var builder = TxActionListBuilder.create(); if (random.nextBoolean()) { - builder.registerAsValidator(); + builder.registerAsValidator(node.getKey()); } else { - builder.unregisterAsValidator(); + builder.unregisterAsValidator(node.getKey()); } var request = NodeApplicationRequest.create(builder.build()); 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 c28bf7d29c..7ec64a1715 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 @@ -44,8 +44,10 @@ public void start(SimulationNodes.RunningNetwork network) { this.disposable = Observable.fromIterable(nodes) .concatMap(i -> Observable.timer(3, TimeUnit.SECONDS).map(l -> i)) .doOnNext(validationRegistrations::onNext) - .map(node -> network.getDispatcher(NodeApplicationRequest.class, node)) - .subscribe(d -> d.dispatch(NodeApplicationRequest.create(new RegisterValidator()))); + .subscribe(node -> { + var d = network.getDispatcher(NodeApplicationRequest.class, node); + d.dispatch(NodeApplicationRequest.create(new RegisterValidator(node.getKey()))); + }); } @Override 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 f2c7845ed0..77d6a9aa53 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 @@ -165,8 +165,7 @@ public NodeEventProcessor updateChecker() { ViewQuorumReached.class, (node, viewQuorumReached) -> { if (viewQuorumReached.votingResult() instanceof FormedQC - && ((FormedQC) viewQuorumReached.votingResult()) - .getQC().getCommittedAndLedgerStateProof().isPresent()) { + && ((FormedQC) viewQuorumReached.votingResult()).getQC().getCommitted().isPresent()) { lastNodeToCommit = network.lookup(node); } } 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 b2b7c8ce2f..0884094275 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/ConsensusModule.java @@ -251,12 +251,14 @@ private BFTSync bftSync( @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, @@ -277,11 +279,13 @@ private VertexStore vertexStore( EventDispatcher highQCUpdateEventDispatcher, EventDispatcher committedSender, BFTConfiguration bftConfiguration, - Ledger ledger + Ledger ledger, + Hasher hasher ) { return VertexStore.create( bftConfiguration.getVertexStoreState(), ledger, + hasher, updateSender, rebuildUpdateDispatcher, highQCUpdateEventDispatcher, 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 40fb78bcec..f33c334be7 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsConsensusModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/EpochsConsensusModule.java @@ -243,12 +243,14 @@ private BFTSyncFactory bftSyncFactory( ScheduledEventDispatcher timeoutDispatcher, Random random, @BFTSyncPatienceMillis int bftSyncPatienceMillis, - SystemCounters counters + SystemCounters counters, + Hasher hasher ) { return (vertexStore, pacemakerState, configuration) -> new BFTSync( self, syncRequestRateLimiter, vertexStore, + hasher, pacemakerState, Comparator.comparingLong((LedgerHeader h) -> h.getAccumulatorState().getStateVersion()), requestSender, @@ -267,11 +269,13 @@ private VertexStoreFactory vertexStoreFactory( EventDispatcher rebuildUpdateDispatcher, EventDispatcher highQCUpdateEventDispatcher, EventDispatcher committedDispatcher, - Ledger ledger + Ledger ledger, + Hasher hasher ) { return vertexStoreState -> VertexStore.create( vertexStoreState, ledger, + hasher, updateSender, rebuildUpdateDispatcher, highQCUpdateEventDispatcher, 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 61204b9bee..80e5c5ad1c 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerRecoveryModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/LedgerRecoveryModule.java @@ -116,7 +116,8 @@ private static VerifiedVertexStoreState serializedToVerifiedVertexStore( serializedVertexStoreState.getHighQC(), verifiedRoot, vertices, - serializedVertexStoreState.getHighestTC() + serializedVertexStoreState.getHighestTC(), + hasher ); } @@ -133,7 +134,7 @@ private static VerifiedVertexStoreState epochProofToGenesisVertexStore( lastEpochProof.timestamp() ); var genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); - return VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty()); + return VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher); } @Provides 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 058fadfc72..a597e16a2f 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SyncServiceModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SyncServiceModule.java @@ -32,7 +32,6 @@ import com.radixdlt.environment.EventDispatcher; import com.radixdlt.environment.RemoteEventProcessorOnRunner; import com.radixdlt.environment.ScheduledEventProducerOnRunner; -import com.radixdlt.ledger.DtoTxnsAndProof; import com.radixdlt.ledger.LedgerUpdate; import com.radixdlt.ledger.VerifiedTxnsAndProof; import com.radixdlt.store.LastProof; @@ -109,14 +108,11 @@ private VerifiedSyncResponseSender verifiedSyncResponseSender( EventDispatcher syncCommandsDispatcher ) { return resp -> { - DtoTxnsAndProof txnsAndProof = resp.getTxnsAndProof(); + var txnsAndProof = resp.getTxnsAndProof(); // TODO: Stateful ledger header verification: // TODO: -verify rootHash matches - LedgerProof nextHeader = new LedgerProof( - txnsAndProof.getTail().getOpaque0(), - txnsAndProof.getTail().getOpaque1(), - txnsAndProof.getTail().getOpaque2(), - txnsAndProof.getTail().getOpaque3(), + var nextHeader = new LedgerProof( + txnsAndProof.getTail().getOpaque(), txnsAndProof.getTail().getLedgerHeader(), txnsAndProof.getTail().getSignatures() ); 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 099a68a2f0..3d7821dd2e 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemInfoModule.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/SystemInfoModule.java @@ -32,17 +32,16 @@ import com.radixdlt.consensus.bft.PacemakerTimeout; import com.radixdlt.consensus.epoch.EpochViewUpdate; import com.radixdlt.counters.SystemCountersImpl; -import com.radixdlt.environment.EventProcessor; +import com.radixdlt.environment.EventProcessorOnRunner; import com.radixdlt.environment.LocalEvents; -import com.radixdlt.environment.ProcessWithSystemInfoRunner; +import com.radixdlt.environment.Runners; +import com.radixdlt.epochs.EpochsLedgerUpdate; import com.radixdlt.systeminfo.InMemorySystemInfo; -import com.radixdlt.systeminfo.SystemInfoRunner; import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.epoch.EpochView; import com.radixdlt.middleware2.InfoSupplier; import com.radixdlt.counters.SystemCounters; -import com.radixdlt.properties.RuntimeProperties; import org.radix.Radix; @@ -50,47 +49,62 @@ * Module which manages system info */ public class SystemInfoModule extends AbstractModule { - private static final int DEFAULT_VERTEX_BUFFER_SIZE = 16; - @Override protected void configure() { bind(SystemCounters.class).to(SystemCountersImpl.class).in(Scopes.SINGLETON); - bind(SystemInfoRunner.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); + eventBinder.addBinding().toInstance(EpochsLedgerUpdate.class); } @ProvidesIntoSet - private EventProcessor epochViewEventProcessor(InMemorySystemInfo inMemorySystemInfo) { - return v -> inMemorySystemInfo.processView(v.getEpochView()); + private EventProcessorOnRunner epochsLedgerUpdateProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + EpochsLedgerUpdate.class, + inMemorySystemInfo.ledgerUpdateEventProcessor() + ); } @ProvidesIntoSet - private EventProcessor timeoutEventProcessor(InMemorySystemInfo inMemorySystemInfo) { - return inMemorySystemInfo::processTimeout; + private EventProcessorOnRunner epochViewEventProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + EpochViewUpdate.class, + v -> inMemorySystemInfo.processView(v.getEpochView()) + ); } @ProvidesIntoSet - private EventProcessor committedUpdateEventProcessor(InMemorySystemInfo inMemorySystemInfo) { - return inMemorySystemInfo.bftCommittedUpdateEventProcessor(); + private EventProcessorOnRunner timeoutEventProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + EpochLocalTimeoutOccurrence.class, + inMemorySystemInfo::processTimeout + ); } @ProvidesIntoSet - @ProcessWithSystemInfoRunner - private EventProcessor highQCProcessor(InMemorySystemInfo inMemorySystemInfo) { - return inMemorySystemInfo.bftHighQCEventProcessor(); + private EventProcessorOnRunner committedUpdateEventProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + BFTCommittedUpdate.class, + inMemorySystemInfo.bftCommittedUpdateEventProcessor() + ); } - @Provides - @Singleton - private InMemorySystemInfo inMemorySystemInfo(RuntimeProperties runtimeProperties) { - final int vertexBufferSize = runtimeProperties.get("api.debug.vertex_buffer_size", DEFAULT_VERTEX_BUFFER_SIZE); - return new InMemorySystemInfo(vertexBufferSize); + @ProvidesIntoSet + private EventProcessorOnRunner highQCProcessor(InMemorySystemInfo inMemorySystemInfo) { + return new EventProcessorOnRunner<>( + Runners.SYSTEM_INFO, + BFTHighQCUpdate.class, + inMemorySystemInfo.bftHighQCEventProcessor() + ); } @Provides 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 new file mode 100644 index 0000000000..82f3f5ccdd --- /dev/null +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusHasher.java @@ -0,0 +1,64 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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.consensus; + +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(); + } + + 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 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 c845bb020e..ebf8123de3 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 @@ -134,10 +134,6 @@ public QuorumCertificate highestCommittedQC() { return this.highestCommittedQC == null ? this.highestQC : this.highestCommittedQC; } - public LedgerProof proof() { - return this.highestCommittedQC().getCommittedAndLedgerStateProof().orElseThrow().getSecond(); - } - @Override public int hashCode() { return Objects.hash(this.highestQC, this.highestCommittedQC, this.highestTC); 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 329d8cca33..3881788fd1 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 @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableSet; +import com.radixdlt.client.ValidatorAddress; import com.radixdlt.consensus.bft.BFTValidator; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.consensus.bft.View; @@ -30,6 +31,10 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; +import com.radixdlt.utils.Bytes; +import org.json.JSONArray; +import org.json.JSONObject; + import java.util.Objects; import java.util.Optional; import javax.annotation.concurrent.Immutable; @@ -88,6 +93,29 @@ private LedgerHeader( this.timestamp = timestamp; } + public JSONObject asJSONObject() { + var json = new JSONObject() + .put("epoch", epoch) + .put("view", view.number()) + .put("version", accumulatorState.getStateVersion()) + .put("accumulator", Bytes.toHexString(accumulatorState.getAccumulatorHash().asBytes())) + .put("timestamp", timestamp); + + if (nextValidators != null) { + var validators = new JSONArray(); + for (var v : nextValidators) { + var validatorAddress = ValidatorAddress.of(v.getNode().getKey()); + validators.put(new JSONObject() + .put("address", validatorAddress) + .put("stake", v.getPower()) + ); + } + json.put("nextValidators", validators); + } + + return json; + } + public static LedgerHeader mocked() { return new LedgerHeader(0, View.genesis(), new AccumulatorState(0, HashUtils.zero256()), 0, null); 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 5d33b67857..e944924fa9 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 @@ -32,6 +32,9 @@ import com.radixdlt.serialization.SerializerConstants; import com.radixdlt.serialization.SerializerDummy; import com.radixdlt.serialization.SerializerId2; +import com.radixdlt.utils.Bytes; +import org.json.JSONObject; + import java.util.Comparator; import java.util.Objects; import java.util.Optional; @@ -48,24 +51,9 @@ public final class LedgerProof { SerializerDummy serializer = SerializerDummy.DUMMY; // proposed - @JsonProperty("opaque0") - @DsonOutput(Output.ALL) - private final BFTHeader opaque0; - - // parent - @JsonProperty("opaque1") - @DsonOutput(Output.ALL) - private final BFTHeader opaque1; - - // committed view - @JsonProperty("opaque2") + @JsonProperty("opaque") @DsonOutput(Output.ALL) - private final long opaque2; - - // committed vertexId - @JsonProperty("opaque3") - @DsonOutput(Output.ALL) - private final HashCode opaque3; + private final HashCode opaque; // committed ledgerState @JsonProperty("ledgerState") @@ -78,28 +66,25 @@ public final class LedgerProof { @JsonCreator public LedgerProof( - @JsonProperty("opaque0") BFTHeader opaque0, - @JsonProperty("opaque1") BFTHeader opaque1, - @JsonProperty("opaque2") long opaque2, - @JsonProperty("opaque3") HashCode opaque3, + @JsonProperty("opaque") HashCode opaque, @JsonProperty("ledgerState") LedgerHeader ledgerHeader, @JsonProperty("signatures") TimestampedECDSASignatures signatures ) { - this.opaque0 = Objects.requireNonNull(opaque0); - this.opaque1 = Objects.requireNonNull(opaque1); - this.opaque2 = opaque2; - this.opaque3 = Objects.requireNonNull(opaque3); + this.opaque = Objects.requireNonNull(opaque); this.ledgerHeader = Objects.requireNonNull(ledgerHeader); this.signatures = Objects.requireNonNull(signatures); } + public JSONObject asJSON() { + return new JSONObject() + .put("opaque", Bytes.toHexString(opaque.asBytes())) + .put("header", ledgerHeader.asJSONObject()) + .put("sigs", signatures.asJSON()); + } + public static LedgerProof genesis(AccumulatorState accumulatorState, BFTValidatorSet nextValidators, long timestamp) { LedgerHeader genesisLedgerHeader = LedgerHeader.genesis(accumulatorState, nextValidators, timestamp); - BFTHeader header = BFTHeader.ofGenesisAncestor(genesisLedgerHeader); return new LedgerProof( - header, - header, - 0, HashUtils.zero256(), genesisLedgerHeader, new TimestampedECDSASignatures() @@ -126,10 +111,7 @@ public int compare(LedgerProof p0, LedgerProof p1) { public DtoLedgerProof toDto() { return new DtoLedgerProof( - opaque0, - opaque1, - opaque2, - opaque3, + opaque, ledgerHeader, signatures ); @@ -180,7 +162,7 @@ public ImmutableList getSignersWithout(BFTNode remove) { @Override public int hashCode() { - return Objects.hash(opaque0, opaque1, opaque2, opaque3, ledgerHeader, signatures); + return Objects.hash(opaque, ledgerHeader, signatures); } @Override @@ -190,10 +172,7 @@ public boolean equals(Object o) { } LedgerProof other = (LedgerProof) o; - return Objects.equals(this.opaque0, other.opaque0) - && Objects.equals(this.opaque1, other.opaque1) - && this.opaque2 == other.opaque2 - && Objects.equals(this.opaque3, other.opaque3) + return Objects.equals(this.opaque, other.opaque) && Objects.equals(this.ledgerHeader, other.ledgerHeader) && Objects.equals(this.signatures, other.signatures); } 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 598e89a312..62ab94753d 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 @@ -100,8 +100,7 @@ public PendingVotes(Hasher hasher) { */ public VoteProcessingResult insertVote(Vote vote, BFTValidatorSet validatorSet) { final BFTNode node = vote.getAuthor(); - final TimestampedVoteData timestampedVoteData = vote.getTimestampedVoteData(); - final VoteData voteData = timestampedVoteData.getVoteData(); + final VoteData voteData = vote.getVoteData(); final HashCode voteDataHash = this.hasher.hash(voteData); final View voteView = voteData.getProposed().getView(); @@ -119,16 +118,14 @@ public VoteProcessingResult insertVote(Vote vote, BFTValidatorSet validatorSet) } private Optional processVoteForQC(Vote vote, BFTValidatorSet validatorSet) { - final TimestampedVoteData timestampedVoteData = vote.getTimestampedVoteData(); - final VoteData voteData = timestampedVoteData.getVoteData(); + 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, timestampedVoteData.getNodeTimestamp(), vote.getSignature()); + final boolean signatureAdded = validationState.addSignature(node, vote.getTimestamp(), vote.getSignature()); if (signatureAdded && validationState.complete()) { return Optional.of(new QuorumCertificate(voteData, validationState.signatures())); @@ -151,8 +148,7 @@ private Optional processVoteForTC(Vote vote, BFTValidatorSet final ValidationState validationState = this.timeoutVoteState.computeIfAbsent(voteTimeoutHash, k -> validatorSet.newValidationState()); - final boolean signatureAdded = - validationState.addSignature(node, vote.getTimestampedVoteData().getNodeTimestamp(), timeoutSignature); + final boolean signatureAdded = validationState.addSignature(node, vote.getTimestamp(), timeoutSignature); if (signatureAdded && validationState.complete()) { return Optional.of(new TimeoutCertificate( 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 0191bdc640..bccbb6bbf3 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 @@ -20,6 +20,7 @@ import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.VerifiedVertex; import com.radixdlt.consensus.bft.View; +import com.radixdlt.crypto.Hasher; import com.radixdlt.utils.Pair; import java.util.Objects; @@ -87,13 +88,15 @@ public BFTHeader getParent() { return voteData.getParent(); } - public Optional> getCommittedAndLedgerStateProof() { + public Optional getCommitted() { + return voteData.getCommitted(); + } + + public Optional> getCommittedAndLedgerStateProof(Hasher hasher) { return voteData.getCommitted().map(committed -> { - LedgerProof ledgerStateProof = new LedgerProof( - voteData.getProposed(), - voteData.getParent(), - committed.getView().number(), - committed.getVertexId(), + var opaque = hasher.hash(voteData); + var ledgerStateProof = new LedgerProof( + opaque, committed.getLedgerHeader(), signatures ); 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 3c814e0e58..5ab7735754 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 @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.client.ValidatorAddress; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.serialization.DsonOutput; @@ -28,6 +29,8 @@ import com.radixdlt.utils.Bytes; import com.radixdlt.utils.Pair; import com.radixdlt.utils.UInt256; +import org.json.JSONArray; +import org.json.JSONObject; import java.util.ArrayList; import java.util.Comparator; @@ -44,7 +47,7 @@ *

* Note that the timestamps can be used together with the * {@link VoteData} in a {@link QuorumCertificate} to reconstruct - * {@link TimestampedVoteData} in order to validate signatures. + * {@link ConsensusHasher} in order to validate signatures. */ @Immutable @SerializerId2("consensus.timestamped_ecdsa_signatures") @@ -66,6 +69,19 @@ public static TimestampedECDSASignatures from(@JsonProperty("signatures") Map { + var obj = new JSONObject() + .put("address", ValidatorAddress.of(node.getKey())) + .put("signature", sig.signature().toHexString()) + .put("timestamp", sig.timestamp()); + json.put(obj); + }); + + return json; + } + /** * Returns a new empty instance. */ diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedVoteData.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedVoteData.java deleted file mode 100644 index 1d64ca01b9..0000000000 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/TimestampedVoteData.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * (C) Copyright 2020 Radix DLT Ltd - * - * Radix DLT Ltd licenses this file to you 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.consensus; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.primitives.Longs; -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; - -/** - * {@link VoteData} with a timestamp. - */ -@Immutable -@SerializerId2("consensus.timestamped_vote_data") -public final class TimestampedVoteData { - @JsonProperty(SerializerConstants.SERIALIZER_NAME) - @DsonOutput(value = {Output.API, Output.WIRE, Output.PERSIST}) - SerializerDummy serializer = SerializerDummy.DUMMY; - - @JsonProperty("vote_data") - @DsonOutput(Output.ALL) - private final VoteData voteData; - - @JsonProperty("node_timestamp") - @DsonOutput(Output.ALL) - private final long nodeTimestamp; - - @JsonCreator - public TimestampedVoteData( - @JsonProperty("vote_data") VoteData voteData, - @JsonProperty("node_timestamp") long nodeTimestamp - ) { - this.nodeTimestamp = nodeTimestamp; - this.voteData = voteData; - } - - public VoteData getVoteData() { - return this.voteData; - } - - public BFTHeader getProposed() { - return this.voteData == null ? null : this.voteData.getProposed(); - } - - public long getNodeTimestamp() { - return this.nodeTimestamp; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o instanceof TimestampedVoteData) { - TimestampedVoteData that = (TimestampedVoteData) o; - return this.nodeTimestamp == that.nodeTimestamp && Objects.equals(this.voteData, that.voteData); - } - return false; - } - - @Override - public int hashCode() { - return Longs.hashCode(this.nodeTimestamp) * 31 + Objects.hashCode(this.voteData); - } -} \ No newline at end of file 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 b725d0965e..2fa68aab75 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 @@ -19,10 +19,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.hash.HashCode; import com.google.errorprone.annotations.Immutable; import com.radixdlt.consensus.bft.BFTNode; import com.radixdlt.consensus.bft.View; import com.radixdlt.crypto.ECDSASignature; +import com.radixdlt.crypto.Hasher; import com.radixdlt.crypto.exception.PublicKeyException; import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; @@ -51,7 +53,11 @@ public final class Vote implements ConsensusEvent { @JsonProperty("vote_data") @DsonOutput(Output.ALL) - private final TimestampedVoteData voteData; + private final VoteData voteData; + + @JsonProperty("ts") + @DsonOutput(Output.ALL) + private final long timestamp; @JsonProperty("signature") @DsonOutput(Output.ALL) @@ -62,23 +68,26 @@ public final class Vote implements ConsensusEvent { @JsonCreator Vote( @JsonProperty("author") byte[] author, - @JsonProperty("vote_data") TimestampedVoteData voteData, + @JsonProperty("vote_data") VoteData voteData, + @JsonProperty("ts") long timestamp, @JsonProperty("signature") ECDSASignature signature, @JsonProperty("high_qc") HighQC highQC, @JsonProperty("timeout_signature") ECDSASignature timeoutSignature ) throws PublicKeyException { - this(BFTNode.fromPublicKeyBytes(author), voteData, signature, highQC, Optional.ofNullable(timeoutSignature)); + this(BFTNode.fromPublicKeyBytes(author), voteData, timestamp, signature, highQC, Optional.ofNullable(timeoutSignature)); } public Vote( BFTNode author, - TimestampedVoteData voteData, + VoteData voteData, + long timestamp, ECDSASignature signature, HighQC highQC, Optional timeoutSignature ) { this.author = Objects.requireNonNull(author); this.voteData = Objects.requireNonNull(voteData); + this.timestamp = timestamp; this.signature = Objects.requireNonNull(signature); this.highQC = Objects.requireNonNull(highQC); this.timeoutSignature = Objects.requireNonNull(timeoutSignature); @@ -86,7 +95,7 @@ public Vote( @Override public long getEpoch() { - return voteData.getVoteData().getProposed().getLedgerHeader().getEpoch(); + return voteData.getProposed().getLedgerHeader().getEpoch(); } @Override @@ -105,11 +114,21 @@ public View getView() { } public VoteData getVoteData() { - return voteData.getVoteData(); + return voteData; } - public TimestampedVoteData getTimestampedVoteData() { - 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() { @@ -124,6 +143,7 @@ public Vote withTimeoutSignature(ECDSASignature timeoutSignature) { return new Vote( this.author, this.voteData, + this.timestamp, this.signature, this.highQC, Optional.of(timeoutSignature) @@ -154,7 +174,7 @@ public String toString() { @Override public int hashCode() { - return Objects.hash(this.author, this.voteData, this.signature, this.highQC, this.timeoutSignature); + return Objects.hash(this.author, this.voteData, this.timestamp, this.signature, this.highQC, this.timeoutSignature); } @Override @@ -166,6 +186,7 @@ public boolean equals(Object o) { Vote other = (Vote) o; return 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/bft/BFTEventPreprocessor.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/BFTEventPreprocessor.java index e2fbfae0c0..9c805acacf 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 @@ -227,8 +227,9 @@ private boolean syncUp(HighQC highQC, BFTNode author, Runnable whenSynced) { // TODO: need to do the same checks on pacemaker side // TODO: move this to an epoch preprocessor final boolean endOfEpoch = highQC.highestCommittedQC() - .getCommittedAndLedgerStateProof() - .orElseThrow(() -> new IllegalStateException("Invalid High QC")).getSecond().isEndOfEpoch(); + .getCommitted() + .orElseThrow(() -> new IllegalStateException("Invalid High QC")).getLedgerHeader() + .isEndOfEpoch(); if (!endOfEpoch) { whenSynced.run(); } 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 c99eb608fa..6e4caab368 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 @@ -17,6 +17,7 @@ package com.radixdlt.consensus.bft; +import com.google.common.hash.HashCode; import com.radixdlt.SecurityCritical; import com.radixdlt.SecurityCritical.SecurityKind; import com.radixdlt.consensus.BFTEventProcessor; @@ -71,14 +72,22 @@ public void processViewUpdate(ViewUpdate viewUpdate) { @Override public void processVote(Vote vote) { validAuthor(vote).ifPresent(node -> { - boolean verifiedVoteData = verify(node, vote.getTimestampedVoteData(), vote.getSignature(), vote); + 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 (verifiedVoteData && verifiedTimeoutData) { - forwardTo.processVote(vote); + if (!verifiedTimeoutData) { + log.warn("Ignoring invalid timeout data {}", vote); + return; } + + forwardTo.processVote(vote); }); } @@ -120,11 +129,15 @@ private Optional validAuthor(ConsensusEvent event) { return Optional.of(node); } - private boolean verify(BFTNode author, Object hashable, ECDSASignature signature, Object what) { - boolean verified = this.verifier.verify(author.getKey(), this.hasher.hash(hashable), signature); + 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/VerifiedVertexStoreState.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/consensus/bft/VerifiedVertexStoreState.java index 622db8e8cc..f81861a265 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 @@ -24,6 +24,7 @@ import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.consensus.LedgerProof; +import com.radixdlt.crypto.Hasher; import com.radixdlt.store.berkeley.SerializedVertexStoreState; import com.radixdlt.utils.Pair; import java.util.HashMap; @@ -63,19 +64,21 @@ private VerifiedVertexStoreState( public static VerifiedVertexStoreState create( HighQC highQC, VerifiedVertex root, - Optional highestTC + Optional highestTC, + Hasher hasher ) { - return create(highQC, root, ImmutableList.of(), highestTC); + return create(highQC, root, ImmutableList.of(), highestTC, hasher); } public static VerifiedVertexStoreState create( HighQC highQC, VerifiedVertex root, ImmutableList vertices, - Optional highestTC + Optional highestTC, + Hasher hasher ) { final Pair headers = highQC.highestCommittedQC() - .getCommittedAndLedgerStateProof() + .getCommittedAndLedgerStateProof(hasher) .orElseThrow(() -> new IllegalStateException(String.format("highQC=%s does not have commit", highQC))); LedgerProof rootHeader = headers.getSecond(); BFTHeader bftHeader = headers.getFirst(); @@ -120,9 +123,9 @@ public static VerifiedVertexStoreState create( return new VerifiedVertexStoreState(highQC, rootHeader, root, idToVertex, vertices, highestTC); } - public VerifiedVertexStoreState prune() { - if (highQC.highestQC().getCommittedAndLedgerStateProof().isPresent()) { - Pair newHeaders = highQC.highestQC().getCommittedAndLedgerStateProof().get(); + 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()); 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 d5228bb7d7..6463fd3e81 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 @@ -27,9 +27,9 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.TimeoutCertificate; +import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.EventDispatcher; -import com.radixdlt.utils.Pair; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -51,6 +51,7 @@ public final class VertexStore { private final EventDispatcher bftRebuildDispatcher; private final EventDispatcher bftCommittedDispatcher; + private final Hasher hasher; private final Ledger ledger; private final Map vertices = new HashMap<>(); @@ -64,6 +65,7 @@ public final class VertexStore { private VertexStore( Ledger ledger, + Hasher hasher, VerifiedVertex rootVertex, QuorumCertificate commitQC, QuorumCertificate highestQC, @@ -74,6 +76,7 @@ private VertexStore( 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); @@ -88,6 +91,7 @@ private VertexStore( public static VertexStore create( VerifiedVertexStoreState vertexStoreState, Ledger ledger, + Hasher hasher, EventDispatcher bftUpdateDispatcher, EventDispatcher bftRebuildDispatcher, EventDispatcher bftHighQCUpdateDispatcher, @@ -95,6 +99,7 @@ public static VertexStore create( ) { VertexStore vertexStore = new VertexStore( ledger, + hasher, vertexStoreState.getRoot(), vertexStoreState.getHighQC().highestCommittedQC(), vertexStoreState.getHighQC().highestQC(), @@ -112,11 +117,12 @@ public static VertexStore create( // 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(); + VerifiedVertexStoreState pruned = vertexStoreState.prune(hasher); if (!pruned.equals(vertexStoreState)) { return create( pruned, ledger, + hasher, bftUpdateDispatcher, bftRebuildDispatcher, bftHighQCUpdateDispatcher, @@ -199,7 +205,7 @@ public boolean addQC(QuorumCertificate qc) { } boolean isHighQC = qc.getView().gt(highestQC.getView()); - boolean isHighCommit = qc.getCommittedAndLedgerStateProof().isPresent(); + boolean isHighCommit = qc.getCommittedAndLedgerStateProof(hasher).isPresent(); if (!isHighQC && !isHighCommit) { return true; } @@ -209,8 +215,7 @@ public boolean addQC(QuorumCertificate qc) { } if (isHighCommit) { - qc.getCommittedAndLedgerStateProof().map(Pair::getFirst) - .ifPresent(header -> this.commit(header, qc)); + 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(); @@ -241,7 +246,8 @@ private VerifiedVertexStoreState getState() { this.highQC(), this.rootVertex, verticesBuilder.build(), - this.highestTC + this.highestTC, + hasher ); } 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 e5ac0ac4b4..cbb2cce934 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 @@ -29,7 +29,6 @@ import com.radixdlt.consensus.HashSigner; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.TimestampedVoteData; import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.VoteData; @@ -200,13 +199,11 @@ public Vote createVote( HighQC highQC ) { final VoteData voteData = constructVoteData(proposedVertex, proposedHeader); - final TimestampedVoteData timestampedVoteData = new TimestampedVoteData(voteData, timestamp); - - final HashCode voteHash = hasher.hash(timestampedVoteData); + 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, timestampedVoteData, signature, highQC, Optional.empty()); + return new Vote(this.self, voteData, timestamp, signature, highQC, Optional.empty()); } public Optional getLastVote(View view) { 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 cb8f0d9176..f8f45ec8e3 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 @@ -40,6 +40,7 @@ import com.radixdlt.consensus.liveness.PacemakerReducer; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.crypto.Hasher; import com.radixdlt.environment.EventDispatcher; import com.radixdlt.environment.EventProcessor; import com.radixdlt.environment.RemoteEventDispatcher; @@ -96,9 +97,10 @@ private static class SyncState { private SyncStage syncStage; private final LinkedList fetched = new LinkedList<>(); - SyncState(HighQC highQC, BFTNode author) { + SyncState(HighQC highQC, BFTNode author, Hasher hasher) { this.localSyncId = highQC.highestQC().getProposed().getVertexId(); - Pair pair = highQC.highestCommittedQC().getCommittedAndLedgerStateProof() + Pair pair = highQC.highestCommittedQC() + .getCommittedAndLedgerStateProof(hasher) .orElseThrow(() -> new IllegalStateException("committedQC must have a commit")); this.committedHeader = pair.getFirst(); this.committedProof = pair.getSecond(); @@ -129,6 +131,7 @@ public String toString() { 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; @@ -148,6 +151,7 @@ public BFTSync( @Self BFTNode self, RateLimiter syncRequestRateLimiter, VertexStore vertexStore, + Hasher hasher, PacemakerReducer pacemakerReducer, Comparator ledgerHeaderComparator, RemoteEventDispatcher requestSender, @@ -161,6 +165,7 @@ public BFTSync( 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; @@ -248,7 +253,7 @@ private boolean requiresLedgerSync(SyncState syncState) { } private void startSync(HighQC highQC, BFTNode author) { - final SyncState syncState = new SyncState(highQC, author); + final SyncState syncState = new SyncState(highQC, author, hasher); syncing.put(syncState.localSyncId, syncState); if (requiresLedgerSync(syncState)) { this.doCommittedSync(syncState); @@ -356,11 +361,12 @@ private void rebuildAndSyncQC(SyncState syncState) { ImmutableList nonRootVertices = syncState.fetched.stream() .skip(1) .collect(ImmutableList.toImmutableList()); - VerifiedVertexStoreState vertexStoreState = VerifiedVertexStoreState.create( + var vertexStoreState = VerifiedVertexStoreState.create( HighQC.from(syncState.highQC().highestCommittedQC()), syncState.fetched.get(0), nonRootVertices, - vertexStore.getHighestTimeoutCertificate() + vertexStore.getHighestTimeoutCertificate(), + hasher ); if (vertexStore.tryRebuild(vertexStoreState)) { // TODO: Move pacemaker outside of sync diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessWithSystemInfoRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessWithSystemInfoRunner.java deleted file mode 100644 index bd221719bc..0000000000 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/environment/ProcessWithSystemInfoRunner.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * (C) Copyright 2020 Radix DLT Ltd - * - * Radix DLT Ltd licenses this file to you 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.environment; - -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; - -/** - * To be used with SystemInfoRunner, this annotation indicates that - * the processor should be processed in the SystemInfoRunner thread. - */ -@Qualifier -@Target({ FIELD, PARAMETER, METHOD }) -@Retention(RUNTIME) -public @interface ProcessWithSystemInfoRunner { - -} 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 6e51482e62..c0675b804c 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 @@ -6,6 +6,7 @@ public final class Runners { public static final String APPLICATION = "application"; public static final String CHAOS = "chaos"; public static final String CONSENSUS = "consensus"; + public static final String SYSTEM_INFO = "info"; private Runners() { } 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 b093d37ecd..60f45859f3 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 @@ -29,6 +29,8 @@ 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.List; @@ -43,7 +45,7 @@ * Executes chaos related events */ public final class ModuleRunnerImpl implements ModuleRunner { - + private static final Logger logger = LogManager.getLogger(); private final Scheduler singleThreadScheduler; private final ScheduledExecutorService executorService; private final String threadName; @@ -142,6 +144,8 @@ public void start() { return; } + logger.info("Starting Runner: {}", this.threadName); + final var disposables = this.subscriptions.stream() .map(s -> s.subscribe(singleThreadScheduler)) .collect(Collectors.toList()); 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 003945509e..fed0b4bf46 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 @@ -144,6 +144,20 @@ private RxEnvironment rxEnvironment( ); } + @ProvidesIntoMap + @StringMapKey(Runners.SYSTEM_INFO) + @Singleton + public ModuleRunner systemInfoRunner( + @Self BFTNode self, + Set> processors, + RxEnvironment rxEnvironment + ) { + final var runnerName = Runners.SYSTEM_INFO; + final var builder = ModuleRunnerImpl.builder(); + addProcessorsOnRunner(processors, rxEnvironment, runnerName, builder); + return builder.build("SystemInfo " + self); + } + @ProvidesIntoMap @StringMapKey(Runners.CHAOS) @Singleton @@ -290,7 +304,10 @@ private void addProcessorsOnRunner( .map(EventProcessorOnRunner::getEventClass) .collect(Collectors.toSet()); eventClasses.forEach(eventClass -> - allProcessors.forEach(p -> addToBuilder(eventClass, rxEnvironment, p, builder)) + allProcessors + .stream() + .filter(p -> p.getRunnerName().equals(runnerName)) + .forEach(p -> addToBuilder(eventClass, rxEnvironment, p, builder)) ); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochChangeManager.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochChangeManager.java index b1e092b3bc..db81a1dde4 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochChangeManager.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/epochs/EpochChangeManager.java @@ -64,13 +64,18 @@ private void sendLedgerUpdate(LedgerUpdate ledgerUpdate) { header.timestamp() ); QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); - final VerifiedVertexStoreState initialState = - VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty()); + final var initialState = + VerifiedVertexStoreState.create( + HighQC.from(genesisQC), + verifiedGenesisVertex, + Optional.empty(), + hasher + ); BFTConfiguration bftConfiguration = new BFTConfiguration(validatorSet, initialState); return new EpochChange(header, bftConfiguration); }); - EpochsLedgerUpdate epochsLedgerUpdate = new EpochsLedgerUpdate(ledgerUpdate, epochChangeOptional.orElse(null)); + var epochsLedgerUpdate = new EpochsLedgerUpdate(ledgerUpdate, epochChangeOptional.orElse(null)); this.epochsLedgerUpdateSender.dispatch(epochsLedgerUpdate); } 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 3bd926241b..e4c554f3ed 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 @@ -20,11 +20,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.hash.HashCode; -import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.TimestampedECDSASignatures; -import com.radixdlt.consensus.VoteData; -import com.radixdlt.consensus.bft.View; import com.radixdlt.serialization.DsonOutput; import com.radixdlt.serialization.DsonOutput.Output; import com.radixdlt.serialization.SerializerConstants; @@ -44,24 +41,9 @@ public final class DtoLedgerProof { SerializerDummy serializer = SerializerDummy.DUMMY; // proposed - @JsonProperty("opaque0") + @JsonProperty("opaque") @DsonOutput(Output.ALL) - private final BFTHeader opaque0; - - // parent - @JsonProperty("opaque1") - @DsonOutput(Output.ALL) - private final BFTHeader opaque1; - - // committed view - @JsonProperty("opaque2") - @DsonOutput(Output.ALL) - private final long opaque2; - - // committed vertexId - @JsonProperty("opaque3") - @DsonOutput(Output.ALL) - private final HashCode opaque3; + private final HashCode opaque; // committed ledgerState @JsonProperty("ledgerState") @@ -74,47 +56,17 @@ public final class DtoLedgerProof { @JsonCreator public DtoLedgerProof( - @JsonProperty("opaque0") BFTHeader opaque0, - @JsonProperty("opaque1") BFTHeader opaque1, - @JsonProperty("opaque2") long opaque2, - @JsonProperty("opaque3") HashCode opaque3, + @JsonProperty("opaque") HashCode opaque, @JsonProperty("ledgerState") LedgerHeader ledgerHeader, @JsonProperty("signatures") TimestampedECDSASignatures signatures ) { - this.opaque0 = Objects.requireNonNull(opaque0); - this.opaque1 = Objects.requireNonNull(opaque1); - this.opaque2 = opaque2; - this.opaque3 = Objects.requireNonNull(opaque3); + this.opaque = Objects.requireNonNull(opaque); this.ledgerHeader = Objects.requireNonNull(ledgerHeader); this.signatures = Objects.requireNonNull(signatures); } - public VoteData toVoteData() { - return new VoteData( - this.opaque0, - this.opaque1, - new BFTHeader( - View.of(this.opaque2), - this.opaque3, - this.ledgerHeader - ) - ); - } - - public BFTHeader getOpaque0() { - return opaque0; - } - - public BFTHeader getOpaque1() { - return opaque1; - } - - public long getOpaque2() { - return opaque2; - } - - public HashCode getOpaque3() { - return opaque3; + public HashCode getOpaque() { + return opaque; } public TimestampedECDSASignatures getSignatures() { @@ -139,16 +91,13 @@ public boolean equals(Object o) { return false; } DtoLedgerProof that = (DtoLedgerProof) o; - return opaque2 == that.opaque2 - && Objects.equals(opaque0, that.opaque0) - && Objects.equals(opaque1, that.opaque1) - && Objects.equals(opaque3, that.opaque3) - && Objects.equals(ledgerHeader, that.ledgerHeader) - && Objects.equals(signatures, that.signatures); + return Objects.equals(opaque, that.opaque) + && Objects.equals(ledgerHeader, that.ledgerHeader) + && Objects.equals(signatures, that.signatures); } @Override public int hashCode() { - return Objects.hash(opaque0, opaque1, opaque2, opaque3, ledgerHeader, signatures); + return Objects.hash(opaque, ledgerHeader, signatures); } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RegisteredValidators.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RegisteredValidators.java index 13d5aacc27..8a8dab5cb9 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RegisteredValidators.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/RegisteredValidators.java @@ -17,12 +17,11 @@ package com.radixdlt.statecomputer; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.radixdlt.atommodel.validators.ValidatorParticle; import com.radixdlt.crypto.ECPublicKey; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.BiFunction; @@ -32,69 +31,49 @@ * Wrapper class for registered validators */ public final class RegisteredValidators { - private final Map validators; + private final Set validatorParticles; - private RegisteredValidators(Map validators) { - this.validators = validators; + private RegisteredValidators(Set validatorParticles) { + this.validatorParticles = validatorParticles; } public static RegisteredValidators create() { - return new RegisteredValidators(Map.of()); - } - - public RegisteredValidators combine(RegisteredValidators v) { - var map = ImmutableMap.builder() - .putAll(validators) - .putAll(v.validators) - .build(); - - return new RegisteredValidators(map); + return new RegisteredValidators(Set.of()); } public RegisteredValidators add(ValidatorParticle particle) { - var validator = particle.getKey(); - - if (validators.containsKey(validator)) { - //TODO: should we merge details??? - return this; - } - - var map = ImmutableMap.builder() - .putAll(validators) - .put(particle.getKey(), ValidatorDetails.fromParticle(particle)) + var map = ImmutableSet.builder() + .addAll(validatorParticles) + .add(particle) .build(); return new RegisteredValidators(map); } public RegisteredValidators remove(ValidatorParticle particle) { - var validator = particle.getKey(); - - if (!validators.containsKey(validator)) { - return this; - } - return new RegisteredValidators( - validators.entrySet().stream() - .filter(e -> !e.getKey().equals(validator)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + validatorParticles.stream() + .filter(e -> !e.equals(particle)) + .collect(Collectors.toSet()) ); } public Set toSet() { - return Set.copyOf(validators.keySet()); + return validatorParticles.stream() + .map(ValidatorParticle::getKey) + .collect(Collectors.toSet()); } public List map(BiFunction mapper) { - return validators.entrySet() + return validatorParticles .stream() - .map(entry -> mapper.apply(entry.getKey(), entry.getValue())) + .map(p -> mapper.apply(p.getKey(), ValidatorDetails.fromParticle(p))) .collect(Collectors.toList()); } @Override public int hashCode() { - return Objects.hash(validators); + return Objects.hash(validatorParticles); } @Override @@ -104,6 +83,6 @@ public boolean equals(Object o) { } var other = (RegisteredValidators) o; - return Objects.equals(this.validators, other.validators); + return Objects.equals(this.validatorParticles, other.validatorParticles); } } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/ValidatorsReducer.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/ValidatorsReducer.java index 251ee9663a..58d6eee63d 100644 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/ValidatorsReducer.java +++ b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/statecomputer/ValidatorsReducer.java @@ -51,14 +51,18 @@ public BiFunction return (prev, p) -> { if (p.isRegisteredForNextEpoch()) { return prev.add(p); - } else { - return prev.remove(p); } + return prev; }; } @Override public BiFunction inputReducer() { - return (prev, p) -> prev; + return (prev, p) -> { + if (p.isRegisteredForNextEpoch()) { + return prev.remove(p); + } + return prev; + }; } } 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 15668ebc84..7dc5d4080f 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 @@ -118,7 +118,7 @@ public VerifiedTxnsAndProof get() { // Initial validator registration for (var validatorKey : validatorKeys) { - var validatorTxn = branch.construct(validatorKey.getPublicKey(), new RegisterValidator()) + var validatorTxn = branch.construct(new RegisterValidator(validatorKey.getPublicKey())) .signAndBuild(validatorKey::sign); branch.execute(List.of(validatorTxn), PermissionLevel.SYSTEM); genesisTxns.add(validatorTxn); @@ -128,14 +128,13 @@ public VerifiedTxnsAndProof get() { for (var stakeDelegation : stakeDelegations) { var delegateAddr = REAddr.ofPubKeyAccount(stakeDelegation.staker().getPublicKey()); var stakerTxn = branch.construct( - stakeDelegation.staker().getPublicKey(), new StakeTokens(delegateAddr, stakeDelegation.delegate(), stakeDelegation.amount()) ).signAndBuild(stakeDelegation.staker()::sign); branch.execute(List.of(stakerTxn), PermissionLevel.SYSTEM); genesisTxns.add(stakerTxn); } - var systemTxn = branch.construct(new SystemNextEpoch(0, 0)) + var systemTxn = branch.construct(new SystemNextEpoch(timestamp, 0)) .buildWithoutSignature(); branch.execute(List.of(systemTxn), PermissionLevel.SYSTEM); genesisTxns.add(systemTxn); 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 f44aa2a968..6104ad1f7e 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 @@ -17,19 +17,14 @@ package com.radixdlt.sync.validation; -import com.google.common.hash.HashCode; import com.google.inject.Inject; import com.radixdlt.consensus.HashVerifier; import com.radixdlt.crypto.Hasher; import com.radixdlt.consensus.TimestampedECDSASignature; -import com.radixdlt.consensus.TimestampedVoteData; -import com.radixdlt.consensus.VoteData; +import com.radixdlt.consensus.ConsensusHasher; import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.ledger.DtoTxnsAndProof; -import com.radixdlt.ledger.DtoLedgerProof; import com.radixdlt.sync.messages.remote.SyncResponse; -import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -48,18 +43,16 @@ public RemoteSyncResponseSignaturesVerifier(Hasher hasher, HashVerifier hashVeri } public boolean verifyResponseSignatures(SyncResponse syncResponse) { - DtoTxnsAndProof commandsAndProof = syncResponse.getTxnsAndProof(); - DtoLedgerProof endHeader = commandsAndProof.getTail(); + var commandsAndProof = syncResponse.getTxnsAndProof(); + var endHeader = commandsAndProof.getTail(); - // TODO: Figure out where this reconstruction should take place - VoteData voteData = endHeader.toVoteData(); - Map signatures = endHeader.getSignatures().getSignatures(); + var opaque = endHeader.getOpaque(); + var header = endHeader.getLedgerHeader(); + var signatures = endHeader.getSignatures().getSignatures(); for (Entry nodeAndSignature : signatures.entrySet()) { - BFTNode node = nodeAndSignature.getKey(); - TimestampedECDSASignature signature = nodeAndSignature.getValue(); - final TimestampedVoteData timestampedVoteData = new TimestampedVoteData(voteData, signature.timestamp()); - final HashCode voteDataHash = this.hasher.hash(timestampedVoteData); - + 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; } 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 48f6cbfa01..bdd8e0f56b 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 @@ -17,6 +17,7 @@ package com.radixdlt.systeminfo; +import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.bft.BFTHighQCUpdate; import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; @@ -24,20 +25,24 @@ import com.radixdlt.consensus.bft.View; import com.radixdlt.consensus.epoch.EpochView; import com.radixdlt.environment.EventProcessor; +import com.radixdlt.epochs.EpochsLedgerUpdate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.util.concurrent.atomic.AtomicReference; /** * Manages system information to be consumed by clients such as the api. */ public final class InMemorySystemInfo { + private static final Logger logger = LogManager.getLogger(); 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 = new AtomicReference<>(); + private final AtomicReference epochsLedgerProof = new AtomicReference<>(); - public InMemorySystemInfo(int vertexBufferSize) { - if (vertexBufferSize < 0) { - throw new IllegalArgumentException("vertexBufferSize must be >= 0 but was " + vertexBufferSize); - } + public InMemorySystemInfo() { } public void processTimeout(EpochLocalTimeoutOccurrence timeout) { @@ -48,6 +53,13 @@ public void processView(EpochView epochView) { currentView.set(epochView); } + public EventProcessor ledgerUpdateEventProcessor() { + return update -> { + this.ledgerProof.set(update.getBase().getTail()); + update.getEpochChange().ifPresent(e -> epochsLedgerProof.set(update.getBase().getTail())); + }; + } + public EventProcessor bftHighQCEventProcessor() { return update -> this.highQC.set(update.getHighQC().highestQC()); } @@ -58,6 +70,14 @@ public EventProcessor bftCommittedUpdateEventProcessor() { }; } + public LedgerProof getCurrentProof() { + return ledgerProof.get(); + } + + public LedgerProof getEpochProof() { + return epochsLedgerProof.get(); + } + public EpochView getCurrentView() { return this.currentView.get(); } diff --git a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/SystemInfoRunner.java b/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/SystemInfoRunner.java deleted file mode 100644 index d29a6f4488..0000000000 --- a/radixdlt-core/radixdlt/src/main/java/com/radixdlt/systeminfo/SystemInfoRunner.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * (C) Copyright 2020 Radix DLT Ltd - * - * Radix DLT Ltd licenses this file to you 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.systeminfo; - -import com.google.inject.Inject; -import com.radixdlt.consensus.bft.BFTCommittedUpdate; -import com.radixdlt.consensus.bft.BFTHighQCUpdate; -import com.radixdlt.consensus.liveness.EpochLocalTimeoutOccurrence; -import com.radixdlt.consensus.epoch.EpochViewUpdate; -import com.radixdlt.environment.EventProcessor; -import com.radixdlt.environment.ProcessWithSystemInfoRunner; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.schedulers.Schedulers; -import java.util.Objects; -import java.util.Set; - -/** - * Processes system info events - */ -public final class SystemInfoRunner { - - private final Observable currentViews; - private final Set> viewEventProcessors; - - private final Observable timeouts; - private final Set> timeoutEventProcessors; - - private final Observable bftUpdates; - private final Set> bftUpdateProcessors; - - private final Observable bftCommittedUpdates; - private final Set> committedProcessors; - - @Inject - public SystemInfoRunner( - Observable currentViews, - Set> viewEventProcessors, - Observable timeouts, - Set> timeoutEventProcessors, - Observable bftUpdates, - @ProcessWithSystemInfoRunner Set> bftUpdateProcessors, - Observable bftCommittedUpdates, - Set> committedProcessors - ) { - this.currentViews = Objects.requireNonNull(currentViews); - this.viewEventProcessors = Objects.requireNonNull(viewEventProcessors); - this.timeouts = Objects.requireNonNull(timeouts); - this.timeoutEventProcessors = Objects.requireNonNull(timeoutEventProcessors); - this.bftUpdates = Objects.requireNonNull(bftUpdates); - this.bftUpdateProcessors = Objects.requireNonNull(bftUpdateProcessors); - this.bftCommittedUpdates = Objects.requireNonNull(bftCommittedUpdates); - this.committedProcessors = Objects.requireNonNull(committedProcessors); - } - - public void start() { - this.currentViews - .observeOn(Schedulers.io()) - .subscribe(e -> viewEventProcessors.forEach(p -> p.process(e))); - - this.timeouts - .observeOn(Schedulers.io()) - .subscribe(e -> timeoutEventProcessors.forEach(p -> p.process(e))); - - this.bftUpdates - .observeOn(Schedulers.io()) - .subscribe(e -> bftUpdateProcessors.forEach(p -> p.process(e))); - - this.bftCommittedUpdates - .observeOn(Schedulers.io()) - .subscribe(e -> committedProcessors.forEach(p -> p.process(e))); - } -} 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 42f767a123..aa0154db80 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/GenerateUniverses.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/GenerateUniverses.java @@ -79,6 +79,7 @@ import java.io.FileWriter; import java.io.IOException; import java.math.BigDecimal; +import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Paths; import java.security.Security; @@ -97,7 +98,6 @@ private GenerateUniverses() { } = new BigDecimal(UInt256s.toBigInteger(TokenDefinitionUtils.SUB_UNITS)); private static final String DEFAULT_UNIVERSE = UniverseType.DEVELOPMENT.toString().toLowerCase(); private static final String DEFAULT_TIMESTAMP = String.valueOf(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli()); - private static final String DEFAULT_KEYSTORE = "universe.ks"; private static final String DEFAULT_STAKE = "5000000"; private static final String VALIDATOR_TEMPLATE = "validator%s.ks"; private static final String STAKER_TEMPLATE = "staker%s.ks"; @@ -115,9 +115,10 @@ public static void main(String[] args) { options.addOption("h", "help", false, "Show usage information (this message)"); options.addOption("c", "no-cbor-output", false, "Suppress DSON output"); options.addOption("i", "issue-default-tokens", false, "Issue tokens to default keys 1, 2, 3, 4 and 5 (dev universe only)"); + options.addOption("pk", "pubkey-issuance", true, "Pub key (hex) to issue tokens to"); + options.addOption("m", "amount-issuance", true, "Amount to issue pub key"); options.addOption("j", "no-json-output", false, "Suppress JSON output"); - options.addOption("k", "keystore", true, "Specify universe keystore (default: " + DEFAULT_KEYSTORE + ")"); - options.addOption("p", "include-private-keys", false, "Include universe, validator and staking private keys in output"); + options.addOption("p", "include-private-keys", false, "Include validator and staking private keys in output"); options.addOption("S", "stake-amounts", true, "Amount of stake for each staked node (default: " + DEFAULT_STAKE + ")"); options.addOption("t", "universe-type", true, "Specify universe type (default: " + DEFAULT_UNIVERSE + ")"); options.addOption("T", "universe-timestamp", true, "Specify universe timestamp (default: " + DEFAULT_TIMESTAMP + ")"); @@ -212,6 +213,24 @@ public static void main(String[] args) { ); } + if (cmd.hasOption("pk")) { + try { + var hexPubKey = cmd.getOptionValue("pk"); + var pubKey = ECPublicKey.fromHex(hexPubKey); + final UInt256 tokenAmt; + if (cmd.hasOption("a")) { + var amountStr = cmd.getOptionValue("a"); + var amt = new BigInteger(amountStr); + tokenAmt = unitsToSubunits(new BigDecimal(amt)); + } else { + tokenAmt = unitsToSubunits(DEFAULT_ISSUANCE); + } + tokenIssuancesBuilder.add(TokenIssuance.of(pubKey, tokenAmt)); + } catch (PublicKeyException e) { + throw new IllegalStateException("Invalid pub key", e); + } + } + if (universeType == UniverseType.DEVELOPMENT) { // Issue tokens to initial validators for now to support application services // FIXME: Remove this 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 f26e288afa..2f11d7845a 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/Radix.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/Radix.java @@ -29,7 +29,6 @@ import com.radixdlt.utils.MemoryLeakDetector; import com.radixdlt.ModuleRunner; import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.systeminfo.SystemInfoRunner; import com.radixdlt.network.addressbook.PeerManager; import com.radixdlt.properties.RuntimeProperties; @@ -146,11 +145,16 @@ public static void start(RuntimeProperties properties) { final PeerManager peerManager = injector.getInstance(PeerManager.class); peerManager.start(); + /* final SystemInfoRunner infoStateRunner = injector.getInstance(SystemInfoRunner.class); infoStateRunner.start(); + */ final Map moduleRunners = injector.getInstance(Key.get(new TypeLiteral>() { })); + final ModuleRunner systemInfoRunner = moduleRunners.get(Runners.SYSTEM_INFO); + systemInfoRunner.start(); + final ModuleRunner syncRunner = moduleRunners.get(Runners.SYNC); syncRunner.start(); diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/NodeController.java b/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/NodeController.java index 4f3c68f95b..4448986f74 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/NodeController.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/NodeController.java @@ -36,6 +36,7 @@ import com.radixdlt.atom.actions.TransferToken; import com.radixdlt.atom.actions.UnregisterValidator; import com.radixdlt.atom.actions.UnstakeTokens; +import com.radixdlt.atom.actions.UpdateValidator; import com.radixdlt.client.AccountAddress; import com.radixdlt.client.Rri; import com.radixdlt.client.ValidatorAddress; @@ -222,12 +223,23 @@ private TxAction parseAction(JSONObject actionObject) throws IllegalArgumentExce var toString = paramsObject.getString("to"); var toDelegate = ValidatorAddress.parse(toString); var amt = parseAmount(paramsObject, "amount"); - return new MoveStake(fromDelegate, toDelegate, amt); + return new MoveStake(account, fromDelegate, toDelegate, amt); + } + case "RegisterValidator": { + var name = paramsObject.has("name") ? paramsObject.getString("name") : null; + var url = paramsObject.has("url") ? paramsObject.getString("url") : null; + return new RegisterValidator(bftKey, name, url); + } + case "UnregisterValidator": { + var name = paramsObject.has("name") ? paramsObject.getString("name") : null; + var url = paramsObject.has("url") ? paramsObject.getString("url") : null; + return new UnregisterValidator(bftKey, name, url); + } + case "UpdateValidator": { + var name = paramsObject.has("name") ? paramsObject.getString("name") : null; + var url = paramsObject.has("url") ? paramsObject.getString("url") : null; + return new UpdateValidator(bftKey, name, url); } - case "RegisterAsValidator": - return new RegisterValidator(); - case "UnregisterAsValidator": - return new UnregisterValidator(); default: throw new IllegalArgumentException("Bad action object: " + actionObject); } diff --git a/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/SystemController.java b/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/SystemController.java index 2bb1dc7beb..e3ae6e2bc1 100644 --- a/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/SystemController.java +++ b/radixdlt-core/radixdlt/src/main/java/org/radix/api/http/SystemController.java @@ -17,10 +17,9 @@ package org.radix.api.http; -import com.radixdlt.DefaultSerialization; import com.radixdlt.ledger.VerifiedTxnsAndProof; -import com.radixdlt.serialization.DsonOutput; import com.radixdlt.statecomputer.checkpoint.Genesis; +import com.radixdlt.systeminfo.InMemorySystemInfo; import com.radixdlt.utils.Bytes; import org.json.JSONArray; import org.json.JSONObject; @@ -37,22 +36,35 @@ public final class SystemController implements Controller { private final SystemService systemService; private final VerifiedTxnsAndProof genesis; + private final InMemorySystemInfo inMemorySystemInfo; @Inject public SystemController( + InMemorySystemInfo inMemorySystemInfo, SystemService systemService, @Genesis VerifiedTxnsAndProof genesis ) { + this.inMemorySystemInfo = inMemorySystemInfo; this.systemService = systemService; this.genesis = genesis; } @Override public void configureRoutes(final RoutingHandler handler) { - // System routes handler.get("/system/info", this::respondWithLocalSystem); - // Universe routes handler.get("/system/checkpoints", this::respondWithGenesis); + handler.get("/system/proof", this::respondWithCurrentProof); + handler.get("/system/epochproof", this::respondWithEpochProof); + } + + void respondWithCurrentProof(final HttpServerExchange exchange) { + var proof = inMemorySystemInfo.getCurrentProof(); + respond(exchange, proof == null ? new JSONObject() : proof.asJSON()); + } + + void respondWithEpochProof(final HttpServerExchange exchange) { + var proof = inMemorySystemInfo.getEpochProof(); + respond(exchange, proof == null ? new JSONObject() : proof.asJSON()); } @VisibleForTesting @@ -66,9 +78,7 @@ void respondWithGenesis(final HttpServerExchange exchange) { var txns = new JSONArray(); genesis.getTxns().forEach(txn -> txns.put(Bytes.toHexString(txn.getPayload()))); jsonObject.put("txns", txns); - - var proof = DefaultSerialization.getInstance().toJsonObject(genesis.getProof(), DsonOutput.Output.ALL); - jsonObject.put("proof", proof); + jsonObject.put("proof", genesis.getProof().asJSON()); respond(exchange, jsonObject); } 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 b8f2e8fd18..6e5a7b9c3b 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ConsensusModuleTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/ConsensusModuleTest.java @@ -40,6 +40,7 @@ import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HashSigner; import com.radixdlt.consensus.HighQC; +import com.radixdlt.consensus.Sha256Hasher; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.bft.BFTCommittedUpdate; import com.radixdlt.consensus.bft.BFTHighQCUpdate; @@ -109,8 +110,7 @@ public class ConsensusModuleTest { @Inject private VertexStore vertexStore; - @Inject - private Hasher hasher; + private Hasher hasher = Sha256Hasher.withDefaultSerialization(); private BFTConfiguration bftConfiguration; @@ -125,7 +125,7 @@ public void setup() { QuorumCertificate qc = QuorumCertificate.ofGenesis(hashedGenesis, LedgerHeader.genesis(accumulatorState, null, 0)); BFTValidatorSet validatorSet = BFTValidatorSet.from(Stream.of(BFTValidator.from(BFTNode.random(), UInt256.ONE))); VerifiedVertexStoreState vertexStoreState = - VerifiedVertexStoreState.create(HighQC.from(qc), hashedGenesis, Optional.empty()); + VerifiedVertexStoreState.create(HighQC.from(qc), hashedGenesis, Optional.empty(), hasher); this.bftConfiguration = new BFTConfiguration(validatorSet, vertexStoreState); this.ecKeyPair = ECKeyPair.generateNew(); this.requestSender = rmock(RemoteEventDispatcher.class); diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixEngineStateComputerTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixEngineStateComputerTest.java index 16310f3c28..ac5b0088cb 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixEngineStateComputerTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/RadixEngineStateComputerTest.java @@ -38,7 +38,6 @@ import com.radixdlt.atom.actions.SystemNextEpoch; import com.radixdlt.atom.actions.SystemNextView; import com.radixdlt.atommodel.system.SystemParticle; -import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.LedgerProof; @@ -211,7 +210,7 @@ private Txn systemUpdateCommand(long nextView, long nextEpoch) throws TxBuilderE } private Txn registerCommand(ECKeyPair keyPair) throws TxBuilderException { - return radixEngine.construct(keyPair.getPublicKey(), new RegisterValidator()) + return radixEngine.construct(new RegisterValidator(keyPair.getPublicKey())) .signAndBuild(keyPair::sign); } @@ -294,11 +293,8 @@ public void preparing_system_update_from_vertex_should_fail() throws TxBuilderEx public void committing_epoch_high_views_should_fail() throws TxBuilderException { // Arrange var cmd0 = systemUpdateCommand(10, 1); - LedgerProof ledgerProof = new LedgerProof( - mock(BFTHeader.class), - mock(BFTHeader.class), - 0, - HashUtils.zero256(), + var ledgerProof = new LedgerProof( + HashUtils.random256(), LedgerHeader.create(0, View.of(11), new AccumulatorState(3, HashUtils.zero256()), 0), new TimestampedECDSASignatures() ); @@ -320,11 +316,8 @@ public void committing_epoch_change_with_additional_cmds_should_fail() throws Ex ECKeyPair keyPair = ECKeyPair.generateNew(); var cmd0 = systemUpdateCommand(0, 2); var cmd1 = registerCommand(keyPair); - LedgerProof ledgerProof = new LedgerProof( - mock(BFTHeader.class), - mock(BFTHeader.class), - 0, - HashUtils.zero256(), + var ledgerProof = new LedgerProof( + HashUtils.random256(), LedgerHeader.create(0, View.of(9), new AccumulatorState(3, HashUtils.zero256()), 0), new TimestampedECDSASignatures() ); @@ -343,14 +336,11 @@ public void committing_epoch_change_with_additional_cmds_should_fail() throws Ex @Test public void committing_epoch_change_with_different_validator_signed_should_fail() throws Exception { // Arrange - ECKeyPair keyPair = ECKeyPair.generateNew(); + var keyPair = ECKeyPair.generateNew(); var cmd0 = systemUpdateCommand(0, 2); var cmd1 = registerCommand(keyPair); - LedgerProof ledgerProof = new LedgerProof( - mock(BFTHeader.class), - mock(BFTHeader.class), - 0, - HashUtils.zero256(), + 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))) ), @@ -372,11 +362,8 @@ public void committing_epoch_change_with_different_validator_signed_should_fail( public void committing_epoch_change_when_there_shouldnt_be_one__should_fail() throws TxBuilderException { // Arrange var cmd0 = systemUpdateCommand(1, 1); - LedgerProof ledgerProof = new LedgerProof( - mock(BFTHeader.class), - mock(BFTHeader.class), - 0, - HashUtils.zero256(), + 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))) ), diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/client/store/berkeley/TransactionParserTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/client/store/berkeley/TransactionParserTest.java index 932c713173..754690b1fe 100644 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/client/store/berkeley/TransactionParserTest.java +++ b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/client/store/berkeley/TransactionParserTest.java @@ -91,7 +91,7 @@ public void setup() throws Exception { .mint(this.tokenRri, this.tokenOwnerAcct, UInt256.TEN) .build() ).buildWithoutSignature(); - var validatorBuilder = this.engine.construct(this.validatorKeyPair.getPublicKey(), new RegisterValidator()); + var validatorBuilder = this.engine.construct(new RegisterValidator(this.validatorKeyPair.getPublicKey())); var txn1 = validatorBuilder.signAndBuild(this.validatorKeyPair::sign); engine.execute(List.of(txn0, txn1), null, PermissionLevel.SYSTEM); 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 15b622231f..eb87fbdad4 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 @@ -58,8 +58,8 @@ public void testGetters() { when(l0.getView()).thenReturn(view); when(l0.timestamp()).thenReturn(2468L); when(l0.isEndOfEpoch()).thenReturn(true); - LedgerProof ledgerHeaderAndProof = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + var ledgerHeaderAndProof = new LedgerProof( + HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class) ); @@ -74,15 +74,15 @@ public void testGetters() { public void testComparsionBetweenDifferentEpochs() { LedgerHeader l0 = mock(LedgerHeader.class); when(l0.getEpoch()).thenReturn(1L); - LedgerProof s0 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + var s0 = new LedgerProof( + HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class) ); LedgerHeader l1 = mock(LedgerHeader.class); when(l1.getEpoch()).thenReturn(2L); LedgerProof s1 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class) ); @@ -98,7 +98,7 @@ public void testComparsionBetweenDifferentStateVersions() { when(accumulatorState.getStateVersion()).thenReturn(2L); when(l0.getAccumulatorState()).thenReturn(accumulatorState); LedgerProof s0 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class) ); @@ -108,7 +108,7 @@ public void testComparsionBetweenDifferentStateVersions() { when(accumulatorState1.getStateVersion()).thenReturn(3L); when(l1.getAccumulatorState()).thenReturn(accumulatorState1); LedgerProof s1 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class) ); @@ -125,7 +125,7 @@ public void testComparsionWithEndOfEpoch() { when(l0.getAccumulatorState()).thenReturn(accumulatorState); when(l0.isEndOfEpoch()).thenReturn(false); LedgerProof s0 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class) ); @@ -136,7 +136,7 @@ public void testComparsionWithEndOfEpoch() { when(l1.getAccumulatorState()).thenReturn(accumulatorState1); when(l1.isEndOfEpoch()).thenReturn(true); LedgerProof s1 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class) ); @@ -153,7 +153,7 @@ public void testComparsionEqual() { when(l0.getAccumulatorState()).thenReturn(accumulatorState); when(l0.isEndOfEpoch()).thenReturn(true); LedgerProof s0 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l0, mock(TimestampedECDSASignatures.class) ); @@ -164,7 +164,7 @@ public void testComparsionEqual() { when(l1.getAccumulatorState()).thenReturn(accumulatorState1); when(l1.isEndOfEpoch()).thenReturn(true); LedgerProof s1 = new LedgerProof( - mock(BFTHeader.class), mock(BFTHeader.class), 0, mock(HashCode.class), + HashUtils.random256(), l1, mock(TimestampedECDSASignatures.class) ); 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 7d79868ee8..f331460a1b 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 @@ -228,9 +228,10 @@ private Vote makeVoteWithoutSignatureFor(BFTNode author, View parentView, HashCo 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); - TimestampedVoteData timestampedVoteData = new TimestampedVoteData(voteData, 123456L); + when(vote.getHashOfData(any())) + .thenReturn(Vote.getHashOfData(hasher, voteData, 123456L)); when(vote.getVoteData()).thenReturn(voteData); - when(vote.getTimestampedVoteData()).thenReturn(timestampedVoteData); + when(vote.getTimestamp()).thenReturn(123456L); when(vote.getAuthor()).thenReturn(author); when(vote.getView()).thenReturn(parentView); when(vote.getEpoch()).thenReturn(0L); diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedVoteDataTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedVoteDataTest.java deleted file mode 100644 index abd43f7e4f..0000000000 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/consensus/TimestampedVoteDataTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * (C) Copyright 2020 Radix DLT Ltd - * - * Radix DLT Ltd licenses this file to you 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.consensus; - -import com.google.common.hash.HashCode; -import com.radixdlt.crypto.HashUtils; -import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Test; - -public class TimestampedVoteDataTest { - @Test - public void equalsContract() { - EqualsVerifier.forClass(TimestampedVoteData.class) - .withPrefabValues(HashCode.class, HashUtils.random256(), HashUtils.random256()) - .verify(); - } -} \ No newline at end of file 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 29863b4089..5a992a6ff7 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 @@ -38,17 +38,15 @@ public class VoteTest { private BFTNode author; private Vote testObject; private VoteData voteData; - private TimestampedVoteData timestampedVoteData; 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.timestampedVoteData = new TimestampedVoteData(this.voteData, 123456L); this.author = mock(BFTNode.class); this.highQC = mock(HighQC.class); - this.testObject = new Vote(author, timestampedVoteData, ECDSASignature.zeroSignature(), highQC, Optional.empty()); + this.testObject = new Vote(author, this.voteData, 123456L, ECDSASignature.zeroSignature(), highQC, Optional.empty()); } @Test 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 8d9c5add8a..fdd0b3199b 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 @@ -20,11 +20,11 @@ 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 com.radixdlt.utils.Pair; import org.junit.Before; import org.junit.Test; @@ -59,8 +59,9 @@ public void when_view_update__then_should_process_cached_events() { when(proposal.getView()).thenReturn(View.of(4)); when(proposal.highQC()).thenReturn(proposalHighQc); when(proposalHighQc.highestCommittedQC()).thenReturn(proposalHighestCommittedQc); - when(proposalHighestCommittedQc.getCommittedAndLedgerStateProof()) - .thenReturn(Optional.of(Pair.of(mock(BFTHeader.class), proposalLedgerProof))); + 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); 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 ad3784cc27..617f97e47b 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 @@ -21,14 +21,17 @@ import static org.mockito.Mockito.mock; import com.google.common.hash.HashCode; +import com.radixdlt.DefaultSerialization; import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.Sha256Hasher; import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.UnverifiedVertex; import com.radixdlt.consensus.VoteData; import com.radixdlt.crypto.HashUtils; +import com.radixdlt.crypto.Hasher; import com.radixdlt.ledger.AccumulatorState; import org.junit.Before; import org.junit.Test; @@ -38,6 +41,7 @@ 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 ); @@ -46,6 +50,7 @@ public class VerifiedVertexStoreStateCreationTest { public void setup() { this.genesisHash = HashUtils.zero256(); this.genesisVertex = new VerifiedVertex(UnverifiedVertex.createGenesis(MOCKED_HEADER), genesisHash); + this.hasher = new Sha256Hasher(DefaultSerialization.getInstance()); } @Test @@ -57,7 +62,8 @@ public void creating_vertex_store_with_root_not_committed_should_fail() { VerifiedVertexStoreState.create( HighQC.from(badRootQC), genesisVertex, - Optional.empty() + Optional.empty(), + hasher ) ).isInstanceOf(IllegalStateException.class); } @@ -72,7 +78,8 @@ public void creating_vertex_store_with_committed_qc_not_matching_vertex_should_f VerifiedVertexStoreState.create( HighQC.from(badRootQC), genesisVertex, - Optional.empty() + Optional.empty(), + hasher ) ).isInstanceOf(IllegalStateException.class); } @@ -86,7 +93,8 @@ public void creating_vertex_store_with_qc_not_matching_vertex_should_fail() { VerifiedVertexStoreState.create( HighQC.from(badRootQC), genesisVertex, - Optional.empty() + Optional.empty(), + hasher ) ).isInstanceOf(IllegalStateException.class); } 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 9dcf73b54a..8e9362a950 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 @@ -94,8 +94,9 @@ public void setUp() { 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()), + VerifiedVertexStoreState.create(HighQC.from(rootQC), genesisVertex, Optional.empty(), hasher), ledger, + hasher, bftUpdateSender, rebuildUpdateEventDispatcher, bftHighQCUpdateEventDispatcher, @@ -198,7 +199,8 @@ public void rebuilding_should_emit_updates() { HighQC.from(vertices.get(3).getQC()), vertices.get(0), vertices.stream().skip(1).collect(ImmutableList.toImmutableList()), - sut.getHighestTimeoutCertificate() + sut.getHighestTimeoutCertificate(), + hasher ); // Act 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 3651fcc717..49236e1f3b 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 @@ -238,7 +238,7 @@ BFTConfiguration bftConfiguration(@Self BFTNode self, Hasher hasher, BFTValidato var qc = QuorumCertificate.ofGenesis(verifiedVertex, LedgerHeader.genesis(accumulatorState, validatorSet, 0)); return new BFTConfiguration( validatorSet, - VerifiedVertexStoreState.create(HighQC.from(qc), verifiedVertex, Optional.empty()) + VerifiedVertexStoreState.create(HighQC.from(qc), verifiedVertex, Optional.empty(), hasher) ); } }; @@ -273,7 +273,7 @@ public void should_not_send_consensus_messages_if_not_part_of_new_epoch() { QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(verifiedGenesisVertex, nextLedgerHeader); BFTConfiguration bftConfiguration = new BFTConfiguration( nextValidatorSet, - VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty()) + VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesisVertex, Optional.empty(), hasher) ); LedgerProof proof = mock(LedgerProof.class); when(proof.getEpoch()).thenReturn(header.getEpoch() + 1); 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 3e587493c6..16597b619b 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 @@ -17,6 +17,7 @@ package com.radixdlt.consensus.safety; +import com.radixdlt.crypto.HashUtils; import org.junit.Before; import org.junit.Test; @@ -53,7 +54,8 @@ public class SafetyRulesTest { public void setup() { this.safetyState = mock(SafetyState.class); Hasher hasher = mock(Hasher.class); - when(hasher.hash(any())).thenReturn(mock(HashCode.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); 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 ff53d33a42..fc207aedf5 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 @@ -17,46 +17,12 @@ package com.radixdlt.ledger; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - import com.google.common.hash.HashCode; -import com.radixdlt.consensus.BFTHeader; -import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.TimestampedECDSASignatures; -import com.radixdlt.consensus.VoteData; import com.radixdlt.crypto.HashUtils; import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Before; import org.junit.Test; public class DtoLedgerProofTest { - private DtoLedgerProof ledgerHeaderAndProof; - private BFTHeader opaque0; - private BFTHeader opaque1; - private long opaque2 = 12345; - private HashCode opaque3; - private LedgerHeader ledgerHeader; - private TimestampedECDSASignatures signatures; - - @Before - public void setup() { - this.opaque0 = mock(BFTHeader.class); - this.opaque1 = mock(BFTHeader.class); - this.opaque3 = mock(HashCode.class); - this.ledgerHeader = mock(LedgerHeader.class); - this.signatures = mock(TimestampedECDSASignatures.class); - this.ledgerHeaderAndProof = new DtoLedgerProof( - opaque0, opaque1, opaque2, opaque3, ledgerHeader, signatures - ); - } - - @Test - public void when_get_vote_data__then_should_not_be_null() { - VoteData voteData = ledgerHeaderAndProof.toVoteData(); - assertThat(voteData).isNotNull(); - } - @Test public void equalsContract() { EqualsVerifier.forClass(DtoLedgerProof.class) 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 5ca6c58ed7..d43c2ba0c0 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 @@ -27,9 +27,7 @@ import com.google.common.collect.ImmutableList; 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.LedgerHeader; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.TimestampedECDSASignatures; @@ -109,7 +107,7 @@ public void setup() { this.genesis = UnverifiedVertex.createGenesis(ledgerHeader); this.genesisVertex = new VerifiedVertex(genesis, hasher.hash(genesis)); this.genesisQC = QuorumCertificate.ofGenesis(genesisVertex, ledgerHeader); - this.currentLedgerHeader = this.genesisQC.getCommittedAndLedgerStateProof() + this.currentLedgerHeader = this.genesisQC.getCommittedAndLedgerStateProof(hasher) .map(Pair::getSecond).orElseThrow(); this.sut = new StateComputerLedger( @@ -135,7 +133,7 @@ public void genesisIsEndOfEpoch(boolean endOfEpoch) { this.genesis = UnverifiedVertex.createGenesis(ledgerHeader); this.genesisVertex = new VerifiedVertex(genesis, hasher.hash(genesis)); this.genesisQC = QuorumCertificate.ofGenesis(genesisVertex, ledgerHeader); - this.currentLedgerHeader = this.genesisQC.getCommittedAndLedgerStateProof() + this.currentLedgerHeader = this.genesisQC.getCommittedAndLedgerStateProof(hasher) .map(Pair::getSecond).orElseThrow(); this.sut = new StateComputerLedger( @@ -226,10 +224,7 @@ public void should_do_nothing_if_committing_lower_state_version() { 1234 ); final LedgerProof header = new LedgerProof( - mock(BFTHeader.class), - mock(BFTHeader.class), - 12345, - mock(HashCode.class), + HashUtils.random256(), ledgerHeader, new TimestampedECDSASignatures() ); 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 4a23702747..8b767249dc 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 @@ -34,6 +34,7 @@ import com.radixdlt.consensus.bft.ViewUpdate; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.crypto.HashUtils; +import com.radixdlt.crypto.Hasher; import com.radixdlt.ledger.AccumulatorState; import com.radixdlt.store.LastEpochProof; import com.radixdlt.store.LastProof; @@ -68,7 +69,8 @@ private ViewUpdate view(BFTConfiguration configuration, ProposerElection propose @Provides private BFTConfiguration configuration( @LastEpochProof LedgerProof proof, - BFTValidatorSet validatorSet + BFTValidatorSet validatorSet, + Hasher hasher ) { var accumulatorState = new AccumulatorState(0, genesisHash); UnverifiedVertex genesis = UnverifiedVertex.createGenesis(LedgerHeader.genesis(accumulatorState, validatorSet, 0)); @@ -82,7 +84,7 @@ private BFTConfiguration configuration( QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(verifiedGenesis, nextLedgerHeader); return new BFTConfiguration( validatorSet, - VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesis, Optional.empty()) + VerifiedVertexStoreState.create(HighQC.from(genesisQC), verifiedGenesis, Optional.empty(), hasher) ); } 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 aa63a54d16..33f81e34b7 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 @@ -28,15 +28,14 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.LedgerHeader; import com.radixdlt.consensus.TimestampedECDSASignatures; import com.radixdlt.consensus.LedgerProof; import com.radixdlt.consensus.bft.BFTNode; -import com.google.common.hash.HashCode; import com.radixdlt.consensus.bft.BFTValidatorSet; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.crypto.HashUtils; import com.radixdlt.environment.RemoteEventDispatcher; import com.radixdlt.ledger.AccumulatorState; import com.radixdlt.ledger.DtoLedgerProof; @@ -97,9 +96,7 @@ public void setUp() { public void when_remote_sync_request__then_process_it() { SyncRequest request = mock(SyncRequest.class); DtoLedgerProof header = mock(DtoLedgerProof.class); - when(header.getOpaque0()).thenReturn(mock(BFTHeader.class)); - when(header.getOpaque1()).thenReturn(mock(BFTHeader.class)); - when(header.getOpaque3()).thenReturn(mock(HashCode.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); @@ -117,9 +114,7 @@ public void when_remote_sync_request__then_process_it() { public void when_remote_sync_request_and_unable__then_dont_do_anything() { SyncRequest request = mock(SyncRequest.class); DtoLedgerProof header = mock(DtoLedgerProof.class); - when(header.getOpaque0()).thenReturn(mock(BFTHeader.class)); - when(header.getOpaque1()).thenReturn(mock(BFTHeader.class)); - when(header.getOpaque3()).thenReturn(mock(HashCode.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); @@ -130,9 +125,7 @@ public void when_remote_sync_request_and_unable__then_dont_do_anything() { @Test public void when_remote_sync_request_and_null_return__then_dont_do_anything() { DtoLedgerProof header = mock(DtoLedgerProof.class); - when(header.getOpaque0()).thenReturn(mock(BFTHeader.class)); - when(header.getOpaque1()).thenReturn(mock(BFTHeader.class)); - when(header.getOpaque3()).thenReturn(mock(HashCode.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)); 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 4ccefefc6a..9ac445bf13 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 @@ -119,12 +119,9 @@ public VerifiedTxnsAndProof build() { base.getProof().timestamp(), base.getProof().getNextValidatorSet().orElse(null) ); - TimestampedECDSASignatures signatures = overwriteSignatures != null ? overwriteSignatures : base.getProof().getSignatures(); - LedgerProof headerAndProof = new LedgerProof( - base.getProof().toDto().getOpaque0(), - base.getProof().toDto().getOpaque1(), - base.getProof().toDto().getOpaque2(), - base.getProof().toDto().getOpaque3(), + var signatures = overwriteSignatures != null ? overwriteSignatures : base.getProof().getSignatures(); + var headerAndProof = new LedgerProof( + base.getProof().toDto().getOpaque(), ledgerHeader, signatures ); diff --git a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifierTest.java b/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifierTest.java deleted file mode 100644 index 4451e482f0..0000000000 --- a/radixdlt-core/radixdlt/src/test/java/com/radixdlt/sync/validation/RemoteSyncResponseSignaturesVerifierTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * (C) Copyright 2020 Radix DLT Ltd - * - * Radix DLT Ltd licenses this file to you 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.sync.validation; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.common.collect.ImmutableMap; -import com.google.common.hash.HashCode; -import com.radixdlt.consensus.HashVerifier; -import com.radixdlt.crypto.Hasher; -import com.radixdlt.consensus.TimestampedECDSASignature; -import com.radixdlt.consensus.TimestampedECDSASignatures; -import com.radixdlt.consensus.VoteData; -import com.radixdlt.consensus.bft.BFTNode; -import com.radixdlt.crypto.ECPublicKey; -import com.radixdlt.ledger.DtoTxnsAndProof; -import com.radixdlt.ledger.DtoLedgerProof; -import com.radixdlt.sync.messages.remote.SyncResponse; -import org.junit.Before; -import org.junit.Test; - -public class RemoteSyncResponseSignaturesVerifierTest { - private RemoteSyncResponseSignaturesVerifier verifier; - private Hasher hasher; - private HashVerifier hashVerifier; - - private SyncResponse response; - private HashCode headerHash; - - @Before - public void setup() { - this.hasher = mock(Hasher.class); - this.hashVerifier = mock(HashVerifier.class); - this.verifier = new RemoteSyncResponseSignaturesVerifier(hasher, hashVerifier); - - this.response = mock(SyncResponse.class); - DtoTxnsAndProof commandsAndProof = mock(DtoTxnsAndProof.class); - when(response.getTxnsAndProof()).thenReturn(commandsAndProof); - DtoLedgerProof tail = mock(DtoLedgerProof.class); - VoteData voteData = mock(VoteData.class); - when(tail.toVoteData()).thenReturn(voteData); - when(commandsAndProof.getTail()).thenReturn(tail); - this.headerHash = mock(HashCode.class); - when(hasher.hash(any())).thenReturn(headerHash); - TimestampedECDSASignatures timestampedECDSASignatures = mock(TimestampedECDSASignatures.class); - BFTNode node = mock(BFTNode.class); - when(node.getKey()).thenReturn(mock(ECPublicKey.class)); - when(timestampedECDSASignatures.getSignatures()).thenReturn( - ImmutableMap.of(node, mock(TimestampedECDSASignature.class)) - ); - when(tail.getSignatures()).thenReturn(timestampedECDSASignatures); - } - - @Test - public void given_a_valid_response__when_process__then_should_send_valid() { - when(hashVerifier.verify(any(), eq(headerHash), any())).thenReturn(true); - - assertTrue(this.verifier.verifyResponseSignatures(response)); - } - - @Test - public void given_an_invalid_response__when_process__then_should_send_invalid() { - when(hashVerifier.verify(any(), eq(headerHash), any())).thenReturn(false); - - assertFalse(this.verifier.verifyResponseSignatures(response)); - } -} 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 e50554047f..fb60abcd04 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 @@ -20,6 +20,7 @@ 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; @@ -45,6 +46,8 @@ public HashCode hash(Object o) { @Override public HashCode hashBytes(byte[] bytes) { - throw new UnsupportedOperationException(); + 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 9c9f3e8010..d2a2f5f8a3 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 @@ -27,7 +27,6 @@ import com.radixdlt.consensus.TimeoutCertificate; import com.radixdlt.consensus.TimestampedECDSASignature; import com.radixdlt.consensus.TimestampedECDSASignatures; -import com.radixdlt.consensus.TimestampedVoteData; import com.radixdlt.consensus.Vote; import com.radixdlt.consensus.VoteData; import com.radixdlt.consensus.bft.BFTNode; @@ -56,7 +55,8 @@ public static QuorumCertificate randomQC() { public static Vote randomVote() { return new Vote( BFTNode.random(), - new TimestampedVoteData(randomVoteData(), random.nextLong()), + randomVoteData(), + random.nextLong(), randomECDSASignature(), randomHighQC(), Optional.of(randomECDSASignature()) diff --git a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TimestampedVoteDataSerializeTest.java b/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TimestampedVoteDataSerializeTest.java deleted file mode 100644 index 1ad8a2b289..0000000000 --- a/radixdlt-core/radixdlt/src/test/java/org/radix/serialization/TimestampedVoteDataSerializeTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (C) Copyright 2020 Radix DLT Ltd - * - * Radix DLT Ltd licenses this file to you 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 org.radix.serialization; - -import com.radixdlt.consensus.LedgerHeader; -import com.radixdlt.consensus.VoteData; -import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.TimestampedVoteData; -import com.radixdlt.consensus.BFTHeader; -import com.radixdlt.crypto.HashUtils; -import com.radixdlt.ledger.AccumulatorState; - -public class TimestampedVoteDataSerializeTest extends SerializeObject { - public TimestampedVoteDataSerializeTest() { - super(TimestampedVoteData.class, TimestampedVoteDataSerializeTest::get); - } - - private static TimestampedVoteData 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); - VoteData voteData = new VoteData(proposed, parent, committed); - return new TimestampedVoteData(voteData, System.currentTimeMillis()); - } -} 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 34a1acd1d6..75cb0b79df 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 @@ -26,12 +26,10 @@ import com.radixdlt.crypto.ECDSASignature; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.consensus.bft.View; -import com.radixdlt.consensus.TimestampedVoteData; import com.radixdlt.consensus.BFTHeader; import com.radixdlt.consensus.HighQC; import com.radixdlt.consensus.Vote; import com.radixdlt.crypto.HashUtils; -import com.radixdlt.ledger.AccumulatorState; import java.util.Optional; @@ -48,10 +46,9 @@ private static Vote get() { BFTHeader header = new BFTHeader(view, id, ledgerHeader); BFTHeader parent = new BFTHeader(View.of(1234567890L), HashUtils.random256(), ledgerHeader); VoteData voteData = new VoteData(header, parent, null); - TimestampedVoteData timestampedVoteData = new TimestampedVoteData(voteData, 123456L); 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, timestampedVoteData, ECDSASignature.zeroSignature(), highQC, Optional.empty()); + return new Vote(author, voteData, 123456L, ECDSASignature.zeroSignature(), highQC, Optional.empty()); } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxActionListBuilder.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxActionListBuilder.java index 955e90388e..84eeb3a4de 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/TxActionListBuilder.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/TxActionListBuilder.java @@ -25,6 +25,7 @@ import com.radixdlt.atom.actions.SplitToken; import com.radixdlt.atom.actions.TransferToken; import com.radixdlt.atom.actions.UnregisterValidator; +import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; @@ -53,14 +54,14 @@ public TxActionListBuilder createMutableToken(MutableTokenDefinition def) { return this; } - public TxActionListBuilder registerAsValidator() { - var action = new RegisterValidator(); + public TxActionListBuilder registerAsValidator(ECPublicKey validatorKey) { + var action = new RegisterValidator(validatorKey, null, null); actions.add(action); return this; } - public TxActionListBuilder unregisterAsValidator() { - var action = new UnregisterValidator(); + public TxActionListBuilder unregisterAsValidator(ECPublicKey validatorKey) { + var action = new UnregisterValidator(validatorKey, null, null); actions.add(action); return this; } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MoveStake.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MoveStake.java index 8b6945163b..a53934e0c5 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MoveStake.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/MoveStake.java @@ -26,12 +26,16 @@ import com.radixdlt.identifiers.REAddr; import com.radixdlt.utils.UInt256; +import java.util.Objects; + public final class MoveStake implements TxAction { private final ECPublicKey from; private final ECPublicKey to; private final UInt256 amount; + private final REAddr accountAddr; - public MoveStake(ECPublicKey from, ECPublicKey to, UInt256 amount) { + public MoveStake(REAddr accountAddr, ECPublicKey from, ECPublicKey to, UInt256 amount) { + this.accountAddr = Objects.requireNonNull(accountAddr); this.from = from; this.to = to; this.amount = amount; @@ -39,16 +43,13 @@ public MoveStake(ECPublicKey from, ECPublicKey to, UInt256 amount) { @Override public void execute(TxBuilder txBuilder) throws TxBuilderException { - var user = txBuilder.getUserOrFail("Must have an address."); - var userAcct = REAddr.ofPubKeyAccount(user); - txBuilder.swapFungible( StakedTokensParticle.class, - p -> p.getOwner().equals(userAcct) && p.getDelegateKey().equals(from), + p -> p.getOwner().equals(accountAddr) && p.getDelegateKey().equals(from), StakedTokensParticle::getAmount, - amt -> new StakedTokensParticle(from, userAcct, amt), + amt -> new StakedTokensParticle(from, accountAddr, amt), amount, "Not enough staked." - ).with(amt -> new StakedTokensParticle(to, userAcct, amt)); + ).with(amt -> new StakedTokensParticle(to, accountAddr, amt)); } } 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 55d200dec6..59548a6e02 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 @@ -23,21 +23,44 @@ import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atommodel.validators.ValidatorParticle; import com.radixdlt.constraintmachine.SubstateWithArg; +import com.radixdlt.crypto.ECPublicKey; +import java.util.Objects; import java.util.Optional; public final class RegisterValidator implements TxAction { + private final ECPublicKey validatorKey; + private final String name; + private final String url; + + public RegisterValidator(ECPublicKey validatorKey) { + this(validatorKey, null, null); + } + + public RegisterValidator( + ECPublicKey validatorKey, + String name, + String url + ) { + this.validatorKey = Objects.requireNonNull(validatorKey); + this.name = name; + this.url = url; + } + @Override public void execute(TxBuilder txBuilder) throws TxBuilderException { - var user = txBuilder.getUserOrFail("Must be a user to register."); - txBuilder.swap( ValidatorParticle.class, - p -> p.getKey().equals(user) && !p.isRegisteredForNextEpoch(), - Optional.of(SubstateWithArg.noArg(new ValidatorParticle(user, false))), + p -> p.getKey().equals(validatorKey) && !p.isRegisteredForNextEpoch(), + Optional.of(SubstateWithArg.noArg(new ValidatorParticle(validatorKey, false))), "Already a validator" ).with( - substateDown -> new ValidatorParticle(user, true, substateDown.getName(), substateDown.getUrl()) + substateDown -> new ValidatorParticle( + validatorKey, + true, + name == null ? substateDown.getName() : name, + url == null ? substateDown.getUrl() : url + ) ); } } 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 a0e92b7dc3..7b550c8b1a 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 @@ -22,18 +22,38 @@ import com.radixdlt.atom.TxBuilder; import com.radixdlt.atom.TxBuilderException; import com.radixdlt.atommodel.validators.ValidatorParticle; +import com.radixdlt.crypto.ECPublicKey; + +import java.util.Objects; public final class UnregisterValidator implements TxAction { + private final ECPublicKey validatorKey; + private final String name; + private final String url; + + public UnregisterValidator( + ECPublicKey validatorKey, + String name, + String url + ) { + this.validatorKey = Objects.requireNonNull(validatorKey); + this.name = name; + this.url = url; + } + @Override public void execute(TxBuilder txBuilder) throws TxBuilderException { - var user = txBuilder.getUserOrFail("Must have address"); - txBuilder.swap( ValidatorParticle.class, - p -> p.getKey().equals(user) && p.isRegisteredForNextEpoch(), + p -> p.getKey().equals(validatorKey) && p.isRegisteredForNextEpoch(), "Already unregistered." ).with( - substateDown -> new ValidatorParticle(user, false, substateDown.getName(), substateDown.getUrl()) + substateDown -> new ValidatorParticle( + validatorKey, + false, + name == null ? substateDown.getName() : name, + url == null ? substateDown.getUrl() : url + ) ); } } diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidator.java b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidator.java new file mode 100644 index 0000000000..38af598a9c --- /dev/null +++ b/radixdlt-engine/src/main/java/com/radixdlt/atom/actions/UpdateValidator.java @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2021 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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.atom.actions; + +import com.radixdlt.atom.TxAction; +import com.radixdlt.atom.TxBuilder; +import com.radixdlt.atom.TxBuilderException; +import com.radixdlt.atommodel.validators.ValidatorParticle; +import com.radixdlt.crypto.ECPublicKey; + +import java.util.Objects; + +public class UpdateValidator implements TxAction { + private final ECPublicKey validatorKey; + private final String name; + private final String url; + + public UpdateValidator( + ECPublicKey validatorKey, + String name, + String url + ) { + this.validatorKey = Objects.requireNonNull(validatorKey); + this.name = name; + this.url = url; + } + + @Override + public void execute(TxBuilder txBuilder) throws TxBuilderException { + txBuilder.swap( + ValidatorParticle.class, + p -> p.getKey().equals(validatorKey), + "Invalid state." + ).with( + substateDown -> new ValidatorParticle( + validatorKey, + substateDown.isRegisteredForNextEpoch(), + name == null ? substateDown.getName() : name, + url == null ? substateDown.getUrl() : url + ) + ); + } +} diff --git a/radixdlt-engine/src/main/java/com/radixdlt/atommodel/validators/ValidatorConstraintScrypt.java b/radixdlt-engine/src/main/java/com/radixdlt/atommodel/validators/ValidatorConstraintScrypt.java index 49c01bc074..08379bfd20 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/atommodel/validators/ValidatorConstraintScrypt.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/atommodel/validators/ValidatorConstraintScrypt.java @@ -24,7 +24,6 @@ import com.radixdlt.atomos.ParticleDefinition; import com.radixdlt.atomos.Result; import com.radixdlt.atomos.SysCalls; -import com.radixdlt.constraintmachine.Particle; import com.radixdlt.constraintmachine.ReducerResult; import com.radixdlt.constraintmachine.SubstateWithArg; import com.radixdlt.constraintmachine.TransitionProcedure; @@ -32,7 +31,6 @@ import com.radixdlt.constraintmachine.InputOutputReducer; import com.radixdlt.constraintmachine.VoidReducerState; import com.radixdlt.constraintmachine.SignatureValidator; -import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.store.ImmutableIndex; import java.util.Objects; @@ -40,13 +38,7 @@ import java.util.regex.Pattern; /** - * Constraint Scrypt defining the Validator FSM, specifically the registration/unregistration flow. - *

- * The Validator FSM is implemented with two particles, UnregisteredValidatorParticle and RegisteredValidatorParticle, - * both carrying the address of the validator in question and a nonce. The first unregistered Validator particle - * for an address (with nonce 0) is virtualised as having an UP spin to initialise the FSM. Whenever a validator - * (identified by their address) transitions between the two states, the nonce must increase (to ensure uniqueness). - * The atom carrying a transition must be signed by the validator. + * Constraint Scrypt defining the Validator FSM. */ public class ValidatorConstraintScrypt implements ConstraintScrypt { @Override @@ -58,24 +50,9 @@ public void main(SysCalls os) { .build() ); - createTransition(os, - ValidatorParticle.class, - ValidatorParticle::getKey, - ValidatorParticle.class, - ValidatorParticle::getKey - ); - } - - private void createTransition( - SysCalls os, - Class inputParticle, - Function inputAddressMapper, - Class outputParticle, - Function outputAddressMapper - ) { os.createTransition( - new TransitionToken<>(inputParticle, outputParticle, TypeToken.of(VoidReducerState.class)), - new ValidatorTransitionProcedure<>(inputAddressMapper, outputAddressMapper) + new TransitionToken<>(ValidatorParticle.class, ValidatorParticle.class, TypeToken.of(VoidReducerState.class)), + new ValidatorTransitionProcedure() ); } @@ -97,28 +74,21 @@ private static Function checkAddressAndUrl(Function ur } @VisibleForTesting - static class ValidatorTransitionProcedure - implements TransitionProcedure { - private final Function inputAddressMapper; - private final Function outputAddressMapper; - - ValidatorTransitionProcedure( - Function inputAddressMapper, - Function outputAddressMapper - ) { - this.inputAddressMapper = inputAddressMapper; - this.outputAddressMapper = outputAddressMapper; - } + static class ValidatorTransitionProcedure + implements TransitionProcedure { @Override - public Result precondition(SubstateWithArg in, O outputParticle, VoidReducerState outputUsed, ImmutableIndex index) { - var inputAddress = inputAddressMapper.apply(in.getSubstate()); - var outputAddress = outputAddressMapper.apply(outputParticle); + public Result precondition( + SubstateWithArg in, + ValidatorParticle out, + VoidReducerState outputUsed, + ImmutableIndex index + ) { // ensure transition is between validator particles concerning the same validator address - if (!Objects.equals(inputAddress, outputAddress)) { + if (!Objects.equals(in.getSubstate().getKey(), out.getKey())) { return Result.error(String.format( "validator addresses do not match: %s != %s", - inputAddress, outputAddress + in.getSubstate().getKey(), out.getKey() )); } @@ -126,14 +96,14 @@ public Result precondition(SubstateWithArg in, O outputParticle, VoidReducerS } @Override - public InputOutputReducer inputOutputReducer() { + public InputOutputReducer inputOutputReducer() { return (input, output, index, outputUsed) -> ReducerResult.complete(Unknown.create()); } @Override - public SignatureValidator signatureValidator() { + public SignatureValidator signatureValidator() { // verify that the transition was authenticated by the validator address in question - return (i, o, index, pubKey) -> pubKey.map(inputAddressMapper.apply(i.getSubstate())::equals).orElse(false); + return (i, o, index, pubKey) -> pubKey.map(i.getSubstate().getKey()::equals).orElse(false); } } } 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 1de22a65f4..74761aab0f 100644 --- a/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngine.java +++ b/radixdlt-engine/src/main/java/com/radixdlt/engine/RadixEngine.java @@ -282,14 +282,6 @@ public TxBuilder construct(List actions) throws TxBuilderException { return engine.construct(actions); } - public TxBuilder construct(ECPublicKey user, TxAction action) throws TxBuilderException { - return engine.construct(user, action); - } - - public TxBuilder construct(ECPublicKey user, List actions) throws TxBuilderException { - return engine.construct(user, actions); - } - public U getComputedState(Class applicationStateClass) { return engine.getComputedState(applicationStateClass); } diff --git a/radixdlt-engine/src/test/java/com/radixdlt/engine/StakedTokensTest.java b/radixdlt-engine/src/test/java/com/radixdlt/engine/StakedTokensTest.java index 2fb6f0bdbd..1cab8d6df0 100644 --- a/radixdlt-engine/src/test/java/com/radixdlt/engine/StakedTokensTest.java +++ b/radixdlt-engine/src/test/java/com/radixdlt/engine/StakedTokensTest.java @@ -77,7 +77,7 @@ public void setup() throws Exception { .mint(this.tokenRri, tokenOwnerAccount, UInt256.TEN) .build() ).buildWithoutSignature(); - var validatorBuilder = this.engine.construct(this.validatorKeyPair.getPublicKey(), new RegisterValidator()); + var validatorBuilder = this.engine.construct(new RegisterValidator(this.validatorKeyPair.getPublicKey())); var txn1 = validatorBuilder.signAndBuild(this.validatorKeyPair::sign); this.engine.execute(List.of(txn0, txn1), null, PermissionLevel.SYSTEM); @@ -86,7 +86,6 @@ public void setup() throws Exception { @Test public void stake_tokens() throws Exception { var txn = engine.construct( - this.tokenOwnerKeyPair.getPublicKey(), new StakeTokens(this.tokenOwnerAccount, this.validatorKeyPair.getPublicKey(), UInt256.TEN) ).signAndBuild(this.tokenOwnerKeyPair::sign); @@ -96,13 +95,11 @@ public void stake_tokens() throws Exception { @Test public void unstake_tokens() throws Exception { var txn = engine.construct( - this.tokenOwnerKeyPair.getPublicKey(), new StakeTokens(this.tokenOwnerAccount, this.validatorKeyPair.getPublicKey(), UInt256.TEN) ).signAndBuild(this.tokenOwnerKeyPair::sign); this.engine.execute(List.of(txn)); var txn2 = engine.construct( - this.tokenOwnerKeyPair.getPublicKey(), new UnstakeTokens(this.tokenOwnerAccount, this.validatorKeyPair.getPublicKey(), UInt256.TEN) ).signAndBuild(this.tokenOwnerKeyPair::sign); this.engine.execute(List.of(txn2)); @@ -111,13 +108,11 @@ public void unstake_tokens() throws Exception { @Test public void unstake_partial_tokens() throws Exception { var txn = engine.construct( - this.tokenOwnerKeyPair.getPublicKey(), new StakeTokens(this.tokenOwnerAccount, this.validatorKeyPair.getPublicKey(), UInt256.TEN) ).signAndBuild(this.tokenOwnerKeyPair::sign); this.engine.execute(List.of(txn)); var txn2 = engine.construct( - this.tokenOwnerKeyPair.getPublicKey(), new UnstakeTokens(this.tokenOwnerAccount, this.validatorKeyPair.getPublicKey(), UInt256.SEVEN) ).signAndBuild(this.tokenOwnerKeyPair::sign); this.engine.execute(List.of(txn2)); @@ -126,14 +121,18 @@ public void unstake_partial_tokens() throws Exception { @Test public void move_staked_tokens() throws Exception { var txn = this.engine.construct( - this.tokenOwnerKeyPair.getPublicKey(), new StakeTokens(this.tokenOwnerAccount, this.validatorKeyPair.getPublicKey(), UInt256.TEN) ).signAndBuild(this.tokenOwnerKeyPair::sign); this.engine.execute(List.of(txn)); - var atom2 = this.engine.construct(this.tokenOwnerKeyPair.getPublicKey(), new MoveStake(validatorKeyPair.getPublicKey(), - ECKeyPair.generateNew().getPublicKey(), UInt256.FIVE)) - .signAndBuild(this.tokenOwnerKeyPair::sign); + var atom2 = this.engine.construct( + new MoveStake( + this.tokenOwnerAccount, + validatorKeyPair.getPublicKey(), + ECKeyPair.generateNew().getPublicKey(), + UInt256.FIVE + ) + ).signAndBuild(this.tokenOwnerKeyPair::sign); this.engine.execute(List.of(atom2)); } }